/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-2019 Teddy Hogeborn
6
 
 * Copyright © 2010-2019 Björn Påhlsson
7
 
 * 
8
 
 * This file is part of Mandos.
9
 
 * 
10
 
 * Mandos is free software: you can redistribute it and/or modify it
11
 
 * under the terms of the GNU General Public License as published by
12
 
 * the Free Software Foundation, either version 3 of the License, or
13
 
 * (at your option) any later version.
14
 
 * 
15
 
 * Mandos is distributed in the hope that it will be useful, but
 
5
 * Copyright © 2010-2016 Teddy Hogeborn
 
6
 * Copyright © 2010-2016 Björn Påhlsson
 
7
 * 
 
8
 * This program is free software: you can redistribute it and/or
 
9
 * modify it under the terms of the GNU General Public License as
 
10
 * published by the Free Software Foundation, either version 3 of the
 
11
 * License, or (at your option) any later version.
 
12
 * 
 
13
 * This program is distributed in the hope that it will be useful, but
16
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of
17
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
16
 * General Public License for more details.
19
17
 * 
20
18
 * You should have received a copy of the GNU General Public License
21
 
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 
19
 * along with this program.  If not, see
 
20
 * <http://www.gnu.org/licenses/>.
22
21
 * 
23
22
 * Contact the authors at <mandos@recompile.se>.
24
23
 */
44
43
                                   STDERR_FILENO, execv(), access() */
45
44
#include <stdlib.h>             /* free(), EXIT_FAILURE, realloc(),
46
45
                                   EXIT_SUCCESS, malloc(), _exit(),
47
 
                                   getenv(), reallocarray() */
 
46
                                   getenv() */
48
47
#include <dirent.h>             /* scandir(), alphasort() */
49
48
#include <inttypes.h>           /* intmax_t, strtoumax(), SCNuMAX */
50
49
#include <sys/stat.h>           /* struct stat, lstat() */
53
52
#include <errno.h>              /* TEMP_FAILURE_RETRY */
54
53
#include <argz.h>               /* argz_count(), argz_extract() */
55
54
#include <stdarg.h>             /* va_list, va_start(), ... */
56
 
#include <argp.h>
57
55
 
58
56
sig_atomic_t interrupted_by_signal = 0;
59
 
const char *argp_program_version = "plymouth " VERSION;
60
 
const char *argp_program_bug_address = "<mandos@recompile.se>";
61
57
 
62
58
/* Used by Ubuntu 11.04 (Natty Narwahl) */
63
 
const char plymouth_old_old_pid[] = "/dev/.initramfs/plymouth.pid";
 
59
const char plymouth_old_pid[] = "/dev/.initramfs/plymouth.pid";
64
60
/* Used by Ubuntu 11.10 (Oneiric Ocelot) */
65
 
const char plymouth_old_pid[] = "/run/initramfs/plymouth.pid";
66
 
/* Used by Debian 9 (stretch) */
67
 
const char plymouth_pid[] = "/run/plymouth/pid";
 
61
const char plymouth_pid[] = "/run/initramfs/plymouth.pid";
68
62
 
69
63
const char plymouth_path[] = "/bin/plymouth";
70
64
const char plymouthd_path[] = "/sbin/plymouthd";
72
66
                                        "--mode=boot",
73
67
                                        "--attach-to-session",
74
68
                                        NULL };
75
 
bool debug = false;
76
69
 
77
70
static void termination_handler(__attribute__((unused))int signum){
78
71
  if(interrupted_by_signal){
81
74
  interrupted_by_signal = 1;
82
75
}
83
76
 
84
 
__attribute__((format (gnu_printf, 2, 3), nonnull))
85
 
int fprintf_plus(FILE *stream, const char *format, ...){
86
 
  va_list ap;
87
 
  va_start (ap, format);
88
 
  fprintf(stream, "Mandos plugin %s: ", program_invocation_short_name);
89
 
  return vfprintf(stream, format, ap);
90
 
}
91
 
 
92
77
/* Function to use when printing errors */
93
78
__attribute__((format (gnu_printf, 3, 4)))
94
79
void error_plus(int status, int errnum, const char *formatstring,
171
156
 
172
157
__attribute__((nonnull (2, 3)))
173
158
bool exec_and_wait(pid_t *pid_return, const char *path,
174
 
                   const char * const * const argv, bool interruptable,
 
159
                   const char * const *argv, bool interruptable,
175
160
                   bool daemonize){
176
161
  int status;
177
162
  int ret;
178
163
  pid_t pid;
179
 
  if(debug){
180
 
    for(const char * const *arg = argv; *arg != NULL; arg++){
181
 
      fprintf_plus(stderr, "exec_and_wait arg: %s\n", *arg);
182
 
    }
183
 
    fprintf_plus(stderr, "exec_and_wait end of args\n");
184
 
  }
185
 
 
186
164
  pid = fork();
187
165
  if(pid == -1){
188
166
    error_plus(0, errno, "fork");
203
181
    }
204
182
    char **tmp;
205
183
    int i = 0;
206
 
    for (; argv[i] != NULL; i++){
207
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26)
208
 
      tmp = reallocarray(new_argv, ((size_t)i + 2),
209
 
                         sizeof(const char *));
210
 
#else
211
 
      if(((size_t)i + 2) > (SIZE_MAX / sizeof(const char *))){
212
 
        /* overflow */
213
 
        tmp = NULL;
214
 
        errno = ENOMEM;
215
 
      } else {
216
 
        tmp = realloc(new_argv, ((size_t)i + 2) * sizeof(const char *));
217
 
      }
218
 
#endif
 
184
    for (; argv[i]!=NULL; i++){
 
185
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 2));
219
186
      if(tmp == NULL){
220
 
        error_plus(0, errno, "reallocarray");
 
187
        error_plus(0, errno, "realloc");
221
188
        free(new_argv);
222
189
        _exit(EX_OSERR);
223
190
      }
239
206
          and ((not interrupted_by_signal)
240
207
               or (not interruptable)));
241
208
  if(interrupted_by_signal and interruptable){
242
 
    if(debug){
243
 
      fprintf_plus(stderr, "Interrupted by signal\n");
244
 
    }
245
209
    return false;
246
210
  }
247
211
  if(ret == -1){
248
212
    error_plus(0, errno, "waitpid");
249
213
    return false;
250
214
  }
251
 
  if(debug){
252
 
    if(WIFEXITED(status)){
253
 
      fprintf_plus(stderr, "exec_and_wait exited: %d\n",
254
 
                   WEXITSTATUS(status));
255
 
    } else if(WIFSIGNALED(status)) {
256
 
      fprintf_plus(stderr, "exec_and_wait signaled: %d\n",
257
 
                   WTERMSIG(status));
258
 
    }
259
 
  }
260
215
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
261
216
    return true;
262
217
  }
326
281
  }
327
282
  /* Try the old pid file location */
328
283
  if(proc_id == 0){
329
 
    pidfile = fopen(plymouth_old_pid, "r");
330
 
    if(pidfile != NULL){
331
 
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
332
 
      if(ret != 1){
333
 
        proc_id = 0;
334
 
      }
335
 
      fclose(pidfile);
336
 
    }
337
 
  }
338
 
  /* Try the old old pid file location */
339
 
  if(proc_id == 0){
340
 
    pidfile = fopen(plymouth_old_old_pid, "r");
 
284
    pidfile = fopen(plymouth_pid, "r");
341
285
    if(pidfile != NULL){
342
286
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
343
287
      if(ret != 1){
354
298
      error_plus(0, errno, "scandir");
355
299
    }
356
300
    if(ret > 0){
357
 
      for(int i = ret-1; i >= 0; i--){
358
 
        if(proc_id == 0){
359
 
          ret = sscanf(direntries[i]->d_name, "%" SCNuMAX, &proc_id);
360
 
          if(ret < 0){
361
 
            error_plus(0, errno, "sscanf");
362
 
          }
363
 
        }
364
 
        free(direntries[i]);
 
301
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &proc_id);
 
302
      if(ret < 0){
 
303
        error_plus(0, errno, "sscanf");
365
304
      }
366
305
    }
367
306
    /* scandir might preallocate for this variable (man page unclear).
377
316
  return 0;
378
317
}
379
318
 
380
 
char **getargv(pid_t pid){
 
319
const char * const * getargv(pid_t pid){
381
320
  int cl_fd;
382
321
  char *cmdline_filename;
383
322
  ssize_t sret;
444
383
    return NULL;
445
384
  }
446
385
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
447
 
  return argv;
 
386
  return (const char * const *)argv;
448
387
}
449
388
 
450
389
int main(__attribute__((unused))int argc,
451
390
         __attribute__((unused))char **argv){
452
 
  char *prompt = NULL;
 
391
  char *prompt;
453
392
  char *prompt_arg;
454
393
  pid_t plymouth_command_pid;
455
394
  int ret;
456
395
  bool bret;
457
396
 
458
 
  {
459
 
    struct argp_option options[] = {
460
 
      { .name = "prompt", .key = 128, .arg = "PROMPT",
461
 
        .doc = "The prompt to show" },
462
 
      { .name = "debug", .key = 129,
463
 
        .doc = "Debug mode" },
464
 
      { .name = NULL }
465
 
    };
466
 
    
467
 
    __attribute__((nonnull(3)))
468
 
    error_t parse_opt (int key, char *arg, __attribute__((unused))
469
 
                       struct argp_state *state){
470
 
      errno = 0;
471
 
      switch (key){
472
 
      case 128:                 /* --prompt */
473
 
        prompt = arg;
474
 
        if(debug){
475
 
          fprintf_plus(stderr, "Custom prompt \"%s\"\n", prompt);
476
 
        }
477
 
        break;
478
 
      case 129:                 /* --debug */
479
 
        debug = true;
480
 
        break;
481
 
      default:
482
 
        return ARGP_ERR_UNKNOWN;
483
 
      }
484
 
      return errno;
485
 
    }
486
 
    
487
 
    struct argp argp = { .options = options, .parser = parse_opt,
488
 
                         .args_doc = "",
489
 
                         .doc = "Mandos plymouth -- Read and"
490
 
                         " output a password" };
491
 
    ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, NULL);
492
 
    switch(ret){
493
 
    case 0:
494
 
      break;
495
 
    case ENOMEM:
496
 
    default:
497
 
      errno = ret;
498
 
      error_plus(0, errno, "argp_parse");
499
 
      return EX_OSERR;
500
 
    case EINVAL:
501
 
      error_plus(0, errno, "argp_parse");
502
 
      return EX_USAGE;
503
 
    }
504
 
  }
505
 
  
506
397
  /* test -x /bin/plymouth */
507
398
  ret = access(plymouth_path, X_OK);
508
399
  if(ret == -1){
509
400
    /* Plymouth is probably not installed.  Don't print an error
510
401
       message, just exit. */
511
 
    if(debug){
512
 
      fprintf_plus(stderr, "Plymouth (%s) not found\n",
513
 
                   plymouth_path);
514
 
    }
515
402
    exit(EX_UNAVAILABLE);
516
403
  }
517
404
  
551
438
    }
552
439
    /* Plymouth is probably not running.  Don't print an error
553
440
       message, just exit. */
554
 
    if(debug){
555
 
      fprintf_plus(stderr, "Plymouth not running\n");
556
 
    }
557
441
    exit(EX_UNAVAILABLE);
558
442
  }
559
443
  
560
 
  if(prompt != NULL){
561
 
    ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
562
 
  } else {
563
 
    char *made_prompt = makeprompt();
564
 
    ret = asprintf(&prompt_arg, "--prompt=%s", made_prompt);
565
 
    free(made_prompt);
566
 
  }
 
444
  prompt = makeprompt();
 
445
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
 
446
  free(prompt);
567
447
  if(ret == -1){
568
448
    error_plus(EX_OSERR, errno, "asprintf");
569
449
  }
570
450
  
571
451
  /* plymouth ask-for-password --prompt="$prompt" */
572
 
  if(debug){
573
 
    fprintf_plus(stderr, "Prompting for password via Plymouth\n");
574
 
  }
575
452
  bret = exec_and_wait(&plymouth_command_pid,
576
453
                       plymouth_path, (const char *[])
577
454
                       { plymouth_path, "ask-for-password",
587
464
  }
588
465
  kill_and_wait(plymouth_command_pid);
589
466
  
590
 
  char **plymouthd_argv = NULL;
 
467
  const char * const *plymouthd_argv;
591
468
  pid_t pid = get_pid();
592
469
  if(pid == 0){
593
470
    error_plus(0, 0, "plymouthd pid not found");
 
471
    plymouthd_argv = plymouthd_default_argv;
594
472
  } else {
595
473
    plymouthd_argv = getargv(pid);
596
474
  }
599
477
                       { plymouth_path, "quit", NULL },
600
478
                       false, false);
601
479
  if(not bret){
602
 
    if(plymouthd_argv != NULL){
603
 
      free(*plymouthd_argv);
604
 
      free(plymouthd_argv);
605
 
    }
606
480
    exit(EXIT_FAILURE);
607
481
  }
608
 
  bret = exec_and_wait(NULL, plymouthd_path,
609
 
                       (plymouthd_argv != NULL)
610
 
                       ? (const char * const *)plymouthd_argv
611
 
                       : plymouthd_default_argv,
 
482
  bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv,
612
483
                       false, true);
613
 
  if(plymouthd_argv != NULL){
614
 
    free(*plymouthd_argv);
615
 
    free(plymouthd_argv);
616
 
  }
617
484
  if(not bret){
618
485
    exit(EXIT_FAILURE);
619
486
  }