/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
  }
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
  }
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
 
    if (ret == -1){
251
 
      error(0, errno, "scandir");
 
297
    if(ret == -1){
 
298
      error_plus(0, errno, "scandir");
252
299
    }
253
 
    if (ret > 0){
254
 
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &maxvalue);
255
 
      if (ret < 0){
256
 
        error(0, errno, "sscanf");
 
300
    if(ret > 0){
 
301
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &proc_id);
 
302
      if(ret < 0){
 
303
        error_plus(0, errno, "sscanf");
257
304
      }
258
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);
259
309
  }
260
310
  pid_t pid;
261
 
  pid = (pid_t)maxvalue;
262
 
  if((uintmax_t)pid == maxvalue){
 
311
  pid = (pid_t)proc_id;
 
312
  if((uintmax_t)pid == proc_id){
263
313
    return pid;
264
314
  }
265
315
  
266
316
  return 0;
267
317
}
268
318
 
269
 
const char **getargv(pid_t pid){
 
319
const char * const * getargv(pid_t pid){
270
320
  int cl_fd;
271
321
  char *cmdline_filename;
272
322
  ssize_t sret;
275
325
  ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
276
326
                 (uintmax_t)pid);
277
327
  if(ret == -1){
278
 
    error(0, errno, "asprintf");
 
328
    error_plus(0, errno, "asprintf");
279
329
    return NULL;
280
330
  }
281
331
  
283
333
  cl_fd = open(cmdline_filename, O_RDONLY);
284
334
  free(cmdline_filename);
285
335
  if(cl_fd == -1){
286
 
    error(0, errno, "open");
 
336
    error_plus(0, errno, "open");
287
337
    return NULL;
288
338
  }
289
339
  
297
347
    if(cmdline_len + blocksize > cmdline_allocated){
298
348
      tmp = realloc(cmdline, cmdline_allocated + blocksize);
299
349
      if(tmp == NULL){
300
 
        error(0, errno, "realloc");
 
350
        error_plus(0, errno, "realloc");
301
351
        free(cmdline);
302
352
        close(cl_fd);
303
353
        return NULL;
310
360
    sret = read(cl_fd, cmdline + cmdline_len,
311
361
                cmdline_allocated - cmdline_len);
312
362
    if(sret == -1){
313
 
      error(0, errno, "read");
 
363
      error_plus(0, errno, "read");
314
364
      free(cmdline);
315
365
      close(cl_fd);
316
366
      return NULL;
319
369
  } while(sret != 0);
320
370
  ret = close(cl_fd);
321
371
  if(ret == -1){
322
 
    error(0, errno, "close");
 
372
    error_plus(0, errno, "close");
323
373
    free(cmdline);
324
374
    return NULL;
325
375
  }
328
378
  char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
329
379
                       * sizeof(char *)); /* Get number of args */
330
380
  if(argv == NULL){
331
 
    error(0, errno, "argv = malloc()");
 
381
    error_plus(0, errno, "argv = malloc()");
332
382
    free(cmdline);
333
383
    return NULL;
334
384
  }
335
385
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
336
 
  return (const char **)argv;
 
386
  return (const char * const *)argv;
337
387
}
338
388
 
339
389
int main(__attribute__((unused))int argc,
361
411
        *sig != 0; sig++){
362
412
      ret = sigaddset(&new_action.sa_mask, *sig);
363
413
      if(ret == -1){
364
 
        error(EX_OSERR, errno, "sigaddset");
 
414
        error_plus(EX_OSERR, errno, "sigaddset");
365
415
      }
366
416
      ret = sigaction(*sig, NULL, &old_action);
367
417
      if(ret == -1){
368
 
        error(EX_OSERR, errno, "sigaction");
 
418
        error_plus(EX_OSERR, errno, "sigaction");
369
419
      }
370
420
      if(old_action.sa_handler != SIG_IGN){
371
421
        ret = sigaction(*sig, &new_action, NULL);
372
422
        if(ret == -1){
373
 
          error(EX_OSERR, errno, "sigaction");
 
423
          error_plus(EX_OSERR, errno, "sigaction");
374
424
        }
375
425
      }
376
426
    }
395
445
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
396
446
  free(prompt);
397
447
  if(ret == -1){
398
 
    error(EX_OSERR, errno, "asprintf");
 
448
    error_plus(EX_OSERR, errno, "asprintf");
399
449
  }
400
450
  
401
451
  /* plymouth ask-for-password --prompt="$prompt" */
414
464
  }
415
465
  kill_and_wait(plymouth_command_pid);
416
466
  
417
 
  const char **plymouthd_argv;
 
467
  const char * const *plymouthd_argv;
418
468
  pid_t pid = get_pid();
419
469
  if(pid == 0){
420
 
    error(0, 0, "plymouthd pid not found");
 
470
    error_plus(0, 0, "plymouthd pid not found");
421
471
    plymouthd_argv = plymouthd_default_argv;
422
472
  } else {
423
473
    plymouthd_argv = getargv(pid);