/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:
2
2
/*
3
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;
109
137
bool become_a_daemon(void){
110
138
  int ret = setuid(geteuid());
111
139
  if(ret == -1){
112
 
    error(0, errno, "setuid");
 
140
    error_plus(0, errno, "setuid");
113
141
  }
114
142
    
115
143
  setsid();
116
144
  ret = chdir("/");
117
145
  if(ret == -1){
118
 
    error(0, errno, "chdir");
 
146
    error_plus(0, errno, "chdir");
119
147
    return false;
120
148
  }
121
149
  ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
122
150
  if(ret == -1){
123
 
    error(0, errno, "dup2");
 
151
    error_plus(0, errno, "dup2");
124
152
    return false;
125
153
  }
126
154
  return true;
127
155
}
128
156
 
 
157
__attribute__((nonnull (2, 3)))
129
158
bool exec_and_wait(pid_t *pid_return, const char *path,
130
 
                   const char **argv, bool interruptable,
 
159
                   const char * const *argv, bool interruptable,
131
160
                   bool daemonize){
132
161
  int status;
133
162
  int ret;
134
163
  pid_t pid;
135
164
  pid = fork();
136
165
  if(pid == -1){
137
 
    error(0, errno, "fork");
 
166
    error_plus(0, errno, "fork");
138
167
    return false;
139
168
  }
140
169
  if(pid == 0){
145
174
      }
146
175
    }
147
176
    
148
 
    char **new_argv = NULL;
 
177
    char **new_argv = malloc(sizeof(const char *));
 
178
    if(new_argv == NULL){
 
179
      error_plus(0, errno, "malloc");
 
180
      _exit(EX_OSERR);
 
181
    }
149
182
    char **tmp;
150
183
    int i = 0;
151
184
    for (; argv[i]!=NULL; i++){
152
 
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1));
153
 
      if (tmp == NULL){
154
 
        error(0, errno, "realloc");
 
185
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 2));
 
186
      if(tmp == NULL){
 
187
        error_plus(0, errno, "realloc");
155
188
        free(new_argv);
156
189
        _exit(EX_OSERR);
157
190
      }
161
194
    new_argv[i] = NULL;
162
195
    
163
196
    execv(path, (char *const *)new_argv);
164
 
    error(0, errno, "execv");
 
197
    error_plus(0, errno, "execv");
165
198
    _exit(EXIT_FAILURE);
166
199
  }
167
200
  if(pid_return != NULL){
176
209
    return false;
177
210
  }
178
211
  if(ret == -1){
179
 
    error(0, errno, "waitpid");
 
212
    error_plus(0, errno, "waitpid");
180
213
    return false;
181
214
  }
182
215
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
185
218
  return false;
186
219
}
187
220
 
 
221
__attribute__((nonnull))
188
222
int is_plymouth(const struct dirent *proc_entry){
189
223
  int ret;
190
224
  {
191
 
    uintmax_t maxvalue;
 
225
    uintmax_t proc_id;
192
226
    char *tmp;
193
227
    errno = 0;
194
 
    maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
228
    proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
195
229
 
196
230
    if(errno != 0 or *tmp != '\0'
197
 
       or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
231
       or proc_id != (uintmax_t)((pid_t)proc_id)){
198
232
      return 0;
199
233
    }
200
234
  }
201
 
  char exe_target[sizeof(plymouth_path)];
 
235
  char exe_target[sizeof(plymouthd_path)];
202
236
  char *exe_link;
203
237
  ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name);
204
238
  if(ret == -1){
205
 
    error(0, errno, "asprintf");
 
239
    error_plus(0, errno, "asprintf");
206
240
    return 0;
207
241
  }
208
242
  
211
245
  if(ret == -1){
212
246
    free(exe_link);
213
247
    if(errno != ENOENT){
214
 
      error(0, errno, "lstat");
 
248
      error_plus(0, errno, "lstat");
215
249
    }
216
250
    return 0;
217
251
  }
225
259
  
226
260
  ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target));
227
261
  free(exe_link);
228
 
  if((sret != (ssize_t)sizeof(plymouth_path)-1) or
229
 
      (memcmp(plymouth_path, exe_target,
230
 
              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)){
231
265
    return 0;
232
266
  }
233
267
  return 1;
235
269
 
236
270
pid_t get_pid(void){
237
271
  int ret;
 
272
  uintmax_t proc_id = 0;
238
273
  FILE *pidfile = fopen(plymouth_pid, "r");
239
 
  uintmax_t maxvalue = 0;
 
274
  /* Try the new pid file location */
240
275
  if(pidfile != NULL){
241
 
    ret = fscanf(pidfile, "%" SCNuMAX, &maxvalue);
 
276
    ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
242
277
    if(ret != 1){
243
 
      maxvalue = 0;
 
278
      proc_id = 0;
244
279
    }
245
280
    fclose(pidfile);
246
281
  }
247
 
  if(maxvalue == 0){
248
 
    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;
249
296
    ret = scandir("/proc", &direntries, is_plymouth, alphasort);
250
 
    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);
251
309
  }
252
310
  pid_t pid;
253
 
  pid = (pid_t)maxvalue;
254
 
  if((uintmax_t)pid == maxvalue){
 
311
  pid = (pid_t)proc_id;
 
312
  if((uintmax_t)pid == proc_id){
255
313
    return pid;
256
314
  }
257
315
  
258
316
  return 0;
259
317
}
260
318
 
261
 
const char **getargv(pid_t pid){
 
319
const char * const * getargv(pid_t pid){
262
320
  int cl_fd;
263
321
  char *cmdline_filename;
264
322
  ssize_t sret;
267
325
  ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
268
326
                 (uintmax_t)pid);
269
327
  if(ret == -1){
270
 
    error(0, errno, "asprintf");
 
328
    error_plus(0, errno, "asprintf");
271
329
    return NULL;
272
330
  }
273
331
  
275
333
  cl_fd = open(cmdline_filename, O_RDONLY);
276
334
  free(cmdline_filename);
277
335
  if(cl_fd == -1){
278
 
    error(0, errno, "open");
 
336
    error_plus(0, errno, "open");
279
337
    return NULL;
280
338
  }
281
339
  
289
347
    if(cmdline_len + blocksize > cmdline_allocated){
290
348
      tmp = realloc(cmdline, cmdline_allocated + blocksize);
291
349
      if(tmp == NULL){
292
 
        error(0, errno, "realloc");
 
350
        error_plus(0, errno, "realloc");
293
351
        free(cmdline);
294
352
        close(cl_fd);
295
353
        return NULL;
302
360
    sret = read(cl_fd, cmdline + cmdline_len,
303
361
                cmdline_allocated - cmdline_len);
304
362
    if(sret == -1){
305
 
      error(0, errno, "read");
 
363
      error_plus(0, errno, "read");
306
364
      free(cmdline);
307
365
      close(cl_fd);
308
366
      return NULL;
311
369
  } while(sret != 0);
312
370
  ret = close(cl_fd);
313
371
  if(ret == -1){
314
 
    error(0, errno, "close");
 
372
    error_plus(0, errno, "close");
315
373
    free(cmdline);
316
374
    return NULL;
317
375
  }
320
378
  char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
321
379
                       * sizeof(char *)); /* Get number of args */
322
380
  if(argv == NULL){
323
 
    error(0, errno, "argv = malloc()");
 
381
    error_plus(0, errno, "argv = malloc()");
324
382
    free(cmdline);
325
383
    return NULL;
326
384
  }
327
385
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
328
 
  return (const char **)argv;
 
386
  return (const char * const *)argv;
329
387
}
330
388
 
331
389
int main(__attribute__((unused))int argc,
353
411
        *sig != 0; sig++){
354
412
      ret = sigaddset(&new_action.sa_mask, *sig);
355
413
      if(ret == -1){
356
 
        error(EX_OSERR, errno, "sigaddset");
 
414
        error_plus(EX_OSERR, errno, "sigaddset");
357
415
      }
358
416
      ret = sigaction(*sig, NULL, &old_action);
359
417
      if(ret == -1){
360
 
        error(EX_OSERR, errno, "sigaction");
 
418
        error_plus(EX_OSERR, errno, "sigaction");
361
419
      }
362
420
      if(old_action.sa_handler != SIG_IGN){
363
421
        ret = sigaction(*sig, &new_action, NULL);
364
422
        if(ret == -1){
365
 
          error(EX_OSERR, errno, "sigaction");
 
423
          error_plus(EX_OSERR, errno, "sigaction");
366
424
        }
367
425
      }
368
426
    }
387
445
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
388
446
  free(prompt);
389
447
  if(ret == -1){
390
 
    error(EX_OSERR, errno, "asprintf");
 
448
    error_plus(EX_OSERR, errno, "asprintf");
391
449
  }
392
450
  
393
451
  /* plymouth ask-for-password --prompt="$prompt" */
406
464
  }
407
465
  kill_and_wait(plymouth_command_pid);
408
466
  
409
 
  const char **plymouthd_argv;
 
467
  const char * const *plymouthd_argv;
410
468
  pid_t pid = get_pid();
411
469
  if(pid == 0){
412
 
    error(0, 0, "plymouthd pid not found");
 
470
    error_plus(0, 0, "plymouthd pid not found");
413
471
    plymouthd_argv = plymouthd_default_argv;
414
472
  } else {
415
473
    plymouthd_argv = getargv(pid);