/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to plugins.d/plymouth.c

  • Committer: Teddy Hogeborn
  • Date: 2016-03-17 20:40:55 UTC
  • Revision ID: teddy@recompile.se-20160317204055-bhsh5xsidq7w5cxu
Client: Fix plymouth agent; broken since 1.7.2.

Fix an very old memory bug in the plymouth agent (which has been
present since its apperance in version 1.2), but which was only
recently detected at run time due to the new -fsanitize=address
compile- time flag, which has been used since version 1.7.2.  This
detection of a memory access violation causes the program to abort,
making the Plymouth graphical boot system unable to accept interactive
input of passwords when using the Mandos client.

* plugins.d/plymouth.c (exec_and_wait): Fix memory allocation bug when
  allocating new_argv.  Also tolerate a zero-length argv.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*  -*- coding: utf-8 -*- */
2
2
/*
3
 
 * Usplash - Read a password from usplash and output it
 
3
 * Plymouth - Read a password from Plymouth and output it
4
4
 * 
5
 
 * Copyright © 2010 Teddy Hogeborn
6
 
 * Copyright © 2010 Björn Påhlsson
 
5
 * Copyright © 2010-2016 Teddy Hogeborn
 
6
 * Copyright © 2010-2016 Björn Påhlsson
7
7
 * 
8
8
 * This program is free software: you can redistribute it and/or
9
9
 * modify it under the terms of the GNU General Public License as
19
19
 * along with this program.  If not, see
20
20
 * <http://www.gnu.org/licenses/>.
21
21
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
22
 * Contact the authors at <mandos@recompile.se>.
23
23
 */
24
24
 
25
25
#define _GNU_SOURCE             /* asprintf(), TEMP_FAILURE_RETRY() */
36
36
#include <stddef.h>             /* NULL */
37
37
#include <string.h>             /* strchr(), memcmp() */
38
38
#include <stdio.h>              /* asprintf(), perror(), fopen(),
39
 
                                   fscanf() */
 
39
                                   fscanf(), vasprintf(), fprintf(),
 
40
                                   vfprintf() */
40
41
#include <unistd.h>             /* close(), readlink(), read(),
41
42
                                   fork(), setsid(), chdir(), dup2(),
42
43
                                   STDERR_FILENO, execv(), access() */
50
51
#include <error.h>              /* error() */
51
52
#include <errno.h>              /* TEMP_FAILURE_RETRY */
52
53
#include <argz.h>               /* argz_count(), argz_extract() */
 
54
#include <stdarg.h>             /* va_list, va_start(), ... */
53
55
 
54
56
sig_atomic_t interrupted_by_signal = 0;
55
 
const char plymouth_pid[] = "/dev/.initramfs/plymouth.pid";
 
57
 
 
58
/* Used by Ubuntu 11.04 (Natty Narwahl) */
 
59
const char plymouth_old_pid[] = "/dev/.initramfs/plymouth.pid";
 
60
/* Used by Ubuntu 11.10 (Oneiric Ocelot) */
 
61
const char plymouth_pid[] = "/run/initramfs/plymouth.pid";
 
62
 
56
63
const char plymouth_path[] = "/bin/plymouth";
57
64
const char plymouthd_path[] = "/sbin/plymouthd";
58
65
const char *plymouthd_default_argv[] = {"/sbin/plymouthd",
59
66
                                        "--mode=boot",
60
67
                                        "--attach-to-session",
61
 
                                        "--pid-file="
62
 
                                        "/dev/.initramfs/"
63
 
                                        "plymouth.pid",
64
68
                                        NULL };
65
69
 
66
70
static void termination_handler(__attribute__((unused))int signum){
70
74
  interrupted_by_signal = 1;
71
75
}
72
76
 
 
77
/* Function to use when printing errors */
 
78
__attribute__((format (gnu_printf, 3, 4)))
 
79
void error_plus(int status, int errnum, const char *formatstring,
 
80
                ...){
 
81
  va_list ap;
 
82
  char *text;
 
83
  int ret;
 
84
  
 
85
  va_start(ap, formatstring);
 
86
  ret = vasprintf(&text, formatstring, ap);
 
87
  if(ret == -1){
 
88
    fprintf(stderr, "Mandos plugin %s: ",
 
89
            program_invocation_short_name);
 
90
    vfprintf(stderr, formatstring, ap);
 
91
    fprintf(stderr, ": ");
 
92
    fprintf(stderr, "%s\n", strerror(errnum));
 
93
    error(status, errno, "vasprintf while printing error");
 
94
    return;
 
95
  }
 
96
  fprintf(stderr, "Mandos plugin ");
 
97
  error(status, errnum, "%s", text);
 
98
  free(text);
 
99
}
 
100
 
73
101
/* Create prompt string */
74
102
char *makeprompt(void){
75
103
  int ret = 0;
76
104
  char *prompt;
77
105
  const char *const cryptsource = getenv("cryptsource");
78
106
  const char *const crypttarget = getenv("crypttarget");
79
 
  const char prompt_start[] = "Enter passphrase to unlock the disk";
 
107
  const char prompt_start[] = "Unlocking the disk";
 
108
  const char prompt_end[] = "Enter passphrase";
80
109
  
81
110
  if(cryptsource == NULL){
82
111
    if(crypttarget == NULL){
83
 
      ret = asprintf(&prompt, "%s: ", prompt_start);
 
112
      ret = asprintf(&prompt, "%s\n%s", prompt_start, prompt_end);
84
113
    } else {
85
 
      ret = asprintf(&prompt, "%s (%s): ", prompt_start,
86
 
                     crypttarget);
 
114
      ret = asprintf(&prompt, "%s (%s)\n%s", prompt_start,
 
115
                     crypttarget, prompt_end);
87
116
    }
88
117
  } else {
89
118
    if(crypttarget == NULL){
90
 
      ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource);
 
119
      ret = asprintf(&prompt, "%s %s\n%s", prompt_start, cryptsource,
 
120
                     prompt_end);
91
121
    } else {
92
 
      ret = asprintf(&prompt, "%s %s (%s): ", prompt_start,
93
 
                     cryptsource, crypttarget);
 
122
      ret = asprintf(&prompt, "%s %s (%s)\n%s", prompt_start,
 
123
                     cryptsource, crypttarget, prompt_end);
94
124
    }
95
125
  }
96
126
  if(ret == -1){
107
137
bool become_a_daemon(void){
108
138
  int ret = setuid(geteuid());
109
139
  if(ret == -1){
110
 
    error(0, errno, "setuid");
 
140
    error_plus(0, errno, "setuid");
111
141
  }
112
142
    
113
143
  setsid();
114
144
  ret = chdir("/");
115
145
  if(ret == -1){
116
 
    error(0, errno, "chdir");
 
146
    error_plus(0, errno, "chdir");
117
147
    return false;
118
148
  }
119
149
  ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
120
150
  if(ret == -1){
121
 
    error(0, errno, "dup2");
 
151
    error_plus(0, errno, "dup2");
122
152
    return false;
123
153
  }
124
154
  return true;
125
155
}
126
156
 
 
157
__attribute__((nonnull (2, 3)))
127
158
bool exec_and_wait(pid_t *pid_return, const char *path,
128
 
                   const char **argv, bool interruptable,
 
159
                   const char * const *argv, bool interruptable,
129
160
                   bool daemonize){
130
161
  int status;
131
162
  int ret;
132
163
  pid_t pid;
133
164
  pid = fork();
134
165
  if(pid == -1){
135
 
    error(0, errno, "fork");
 
166
    error_plus(0, errno, "fork");
136
167
    return false;
137
168
  }
138
169
  if(pid == 0){
142
173
        _exit(EX_OSERR);
143
174
      }
144
175
    }
145
 
 
146
 
    char **new_argv = NULL;
147
 
    char *tmp;
 
176
    
 
177
    char **new_argv = malloc(sizeof(const char *));
 
178
    if(new_argv == NULL){
 
179
      error_plus(0, errno, "malloc");
 
180
      _exit(EX_OSERR);
 
181
    }
 
182
    char **tmp;
148
183
    int i = 0;
149
 
    for (; argv[i]!=(char *)NULL; i++){
150
 
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1));
151
 
      if (tmp == NULL){
152
 
        error(0, errno, "realloc");
 
184
    for (; argv[i]!=NULL; i++){
 
185
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 2));
 
186
      if(tmp == NULL){
 
187
        error_plus(0, errno, "realloc");
153
188
        free(new_argv);
154
189
        _exit(EX_OSERR);
155
190
      }
156
 
      new_argv = (char **)tmp;
 
191
      new_argv = tmp;
157
192
      new_argv[i] = strdup(argv[i]);
158
193
    }
159
 
    new_argv[i] = (char *) NULL;
 
194
    new_argv[i] = NULL;
160
195
    
161
196
    execv(path, (char *const *)new_argv);
162
 
    error(0, errno, "execv");
 
197
    error_plus(0, errno, "execv");
163
198
    _exit(EXIT_FAILURE);
164
199
  }
165
200
  if(pid_return != NULL){
174
209
    return false;
175
210
  }
176
211
  if(ret == -1){
177
 
    error(0, errno, "waitpid");
 
212
    error_plus(0, errno, "waitpid");
178
213
    return false;
179
214
  }
180
215
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
183
218
  return false;
184
219
}
185
220
 
 
221
__attribute__((nonnull))
186
222
int is_plymouth(const struct dirent *proc_entry){
187
223
  int ret;
188
224
  {
189
 
    uintmax_t maxvalue;
 
225
    uintmax_t proc_id;
190
226
    char *tmp;
191
227
    errno = 0;
192
 
    maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
228
    proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
193
229
 
194
230
    if(errno != 0 or *tmp != '\0'
195
 
       or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
231
       or proc_id != (uintmax_t)((pid_t)proc_id)){
196
232
      return 0;
197
233
    }
198
234
  }
199
 
  char exe_target[sizeof(plymouth_path)];
 
235
  char exe_target[sizeof(plymouthd_path)];
200
236
  char *exe_link;
201
237
  ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name);
202
238
  if(ret == -1){
203
 
    error(0, errno, "asprintf");
 
239
    error_plus(0, errno, "asprintf");
204
240
    return 0;
205
241
  }
206
242
  
209
245
  if(ret == -1){
210
246
    free(exe_link);
211
247
    if(errno != ENOENT){
212
 
      error(0, errno, "lstat");
 
248
      error_plus(0, errno, "lstat");
213
249
    }
214
250
    return 0;
215
251
  }
223
259
  
224
260
  ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target));
225
261
  free(exe_link);
226
 
  if((sret != (ssize_t)sizeof(plymouth_path)-1) or
227
 
      (memcmp(plymouth_path, exe_target,
228
 
              sizeof(plymouth_path)-1) != 0)){
 
262
  if((sret != (ssize_t)sizeof(plymouthd_path)-1) or
 
263
      (memcmp(plymouthd_path, exe_target,
 
264
              sizeof(plymouthd_path)-1) != 0)){
229
265
    return 0;
230
266
  }
231
267
  return 1;
233
269
 
234
270
pid_t get_pid(void){
235
271
  int ret;
 
272
  uintmax_t proc_id = 0;
236
273
  FILE *pidfile = fopen(plymouth_pid, "r");
237
 
  uintmax_t maxvalue = 0;
 
274
  /* Try the new pid file location */
238
275
  if(pidfile != NULL){
239
 
    ret = fscanf(pidfile, "%" SCNuMAX, &maxvalue);
 
276
    ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
240
277
    if(ret != 1){
241
 
      maxvalue = 0;
 
278
      proc_id = 0;
242
279
    }
243
280
    fclose(pidfile);
244
281
  }
245
 
  if(maxvalue == 0){
246
 
    struct dirent **direntries;
 
282
  /* Try the old pid file location */
 
283
  if(proc_id == 0){
 
284
    pidfile = fopen(plymouth_pid, "r");
 
285
    if(pidfile != NULL){
 
286
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
 
287
      if(ret != 1){
 
288
        proc_id = 0;
 
289
      }
 
290
      fclose(pidfile);
 
291
    }
 
292
  }
 
293
  /* Look for a plymouth process */
 
294
  if(proc_id == 0){
 
295
    struct dirent **direntries = NULL;
247
296
    ret = scandir("/proc", &direntries, is_plymouth, alphasort);
248
 
    sscanf(direntries[0]->d_name, "%" SCNuMAX, &maxvalue);
 
297
    if(ret == -1){
 
298
      error_plus(0, errno, "scandir");
 
299
    }
 
300
    if(ret > 0){
 
301
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &proc_id);
 
302
      if(ret < 0){
 
303
        error_plus(0, errno, "sscanf");
 
304
      }
 
305
    }
 
306
    /* scandir might preallocate for this variable (man page unclear).
 
307
       even if ret == 0, therefore we need to free it. */
 
308
    free(direntries);
249
309
  }
250
310
  pid_t pid;
251
 
  pid = (pid_t)maxvalue;
252
 
  if((uintmax_t)pid == maxvalue){
 
311
  pid = (pid_t)proc_id;
 
312
  if((uintmax_t)pid == proc_id){
253
313
    return pid;
254
314
  }
255
315
  
256
316
  return 0;
257
317
}
258
318
 
259
 
const char **getargv(pid_t pid){
 
319
const char * const * getargv(pid_t pid){
260
320
  int cl_fd;
261
321
  char *cmdline_filename;
262
322
  ssize_t sret;
265
325
  ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
266
326
                 (uintmax_t)pid);
267
327
  if(ret == -1){
268
 
    error(0, errno, "asprintf");
 
328
    error_plus(0, errno, "asprintf");
269
329
    return NULL;
270
330
  }
271
331
  
273
333
  cl_fd = open(cmdline_filename, O_RDONLY);
274
334
  free(cmdline_filename);
275
335
  if(cl_fd == -1){
276
 
    error(0, errno, "open");
 
336
    error_plus(0, errno, "open");
277
337
    return NULL;
278
338
  }
279
339
  
287
347
    if(cmdline_len + blocksize > cmdline_allocated){
288
348
      tmp = realloc(cmdline, cmdline_allocated + blocksize);
289
349
      if(tmp == NULL){
290
 
        error(0, errno, "realloc");
 
350
        error_plus(0, errno, "realloc");
291
351
        free(cmdline);
292
352
        close(cl_fd);
293
353
        return NULL;
300
360
    sret = read(cl_fd, cmdline + cmdline_len,
301
361
                cmdline_allocated - cmdline_len);
302
362
    if(sret == -1){
303
 
      error(0, errno, "read");
 
363
      error_plus(0, errno, "read");
304
364
      free(cmdline);
305
365
      close(cl_fd);
306
366
      return NULL;
309
369
  } while(sret != 0);
310
370
  ret = close(cl_fd);
311
371
  if(ret == -1){
312
 
    error(0, errno, "close");
 
372
    error_plus(0, errno, "close");
313
373
    free(cmdline);
314
374
    return NULL;
315
375
  }
318
378
  char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
319
379
                       * sizeof(char *)); /* Get number of args */
320
380
  if(argv == NULL){
321
 
    error(0, errno, "argv = malloc()");
 
381
    error_plus(0, errno, "argv = malloc()");
322
382
    free(cmdline);
323
383
    return NULL;
324
384
  }
325
385
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
326
 
  return (const char **)argv;
 
386
  return (const char * const *)argv;
327
387
}
328
388
 
329
389
int main(__attribute__((unused))int argc,
351
411
        *sig != 0; sig++){
352
412
      ret = sigaddset(&new_action.sa_mask, *sig);
353
413
      if(ret == -1){
354
 
        error(EX_OSERR, errno, "sigaddset");
 
414
        error_plus(EX_OSERR, errno, "sigaddset");
355
415
      }
356
416
      ret = sigaction(*sig, NULL, &old_action);
357
417
      if(ret == -1){
358
 
        error(EX_OSERR, errno, "sigaction");
 
418
        error_plus(EX_OSERR, errno, "sigaction");
359
419
      }
360
420
      if(old_action.sa_handler != SIG_IGN){
361
421
        ret = sigaction(*sig, &new_action, NULL);
362
422
        if(ret == -1){
363
 
          error(EX_OSERR, errno, "sigaction");
 
423
          error_plus(EX_OSERR, errno, "sigaction");
364
424
        }
365
425
      }
366
426
    }
385
445
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
386
446
  free(prompt);
387
447
  if(ret == -1){
388
 
    error(EX_OSERR, errno, "asprintf");
 
448
    error_plus(EX_OSERR, errno, "asprintf");
389
449
  }
390
450
  
391
451
  /* plymouth ask-for-password --prompt="$prompt" */
404
464
  }
405
465
  kill_and_wait(plymouth_command_pid);
406
466
  
407
 
  const char **plymouthd_argv;
 
467
  const char * const *plymouthd_argv;
408
468
  pid_t pid = get_pid();
409
469
  if(pid == 0){
410
 
    error(0, 0, "plymouthd pid not found");
 
470
    error_plus(0, 0, "plymouthd pid not found");
411
471
    plymouthd_argv = plymouthd_default_argv;
412
472
  } else {
413
473
    plymouthd_argv = getargv(pid);