/mandos/release

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

« 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
  • mto: (237.7.594 trunk)
  • mto: This revision was merged to the branch mainline in revision 341.
  • 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
 */
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++){
 
184
    for (; argv[i]!=NULL; i++){
207
185
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 2));
208
186
      if(tmp == NULL){
209
187
        error_plus(0, errno, "realloc");
228
206
          and ((not interrupted_by_signal)
229
207
               or (not interruptable)));
230
208
  if(interrupted_by_signal and interruptable){
231
 
    if(debug){
232
 
      fprintf_plus(stderr, "Interrupted by signal\n");
233
 
    }
234
209
    return false;
235
210
  }
236
211
  if(ret == -1){
237
212
    error_plus(0, errno, "waitpid");
238
213
    return false;
239
214
  }
240
 
  if(debug){
241
 
    if(WIFEXITED(status)){
242
 
      fprintf_plus(stderr, "exec_and_wait exited: %d\n",
243
 
                   WEXITSTATUS(status));
244
 
    } else if(WIFSIGNALED(status)) {
245
 
      fprintf_plus(stderr, "exec_and_wait signaled: %d\n",
246
 
                   WTERMSIG(status));
247
 
    }
248
 
  }
249
215
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
250
216
    return true;
251
217
  }
315
281
  }
316
282
  /* Try the old pid file location */
317
283
  if(proc_id == 0){
318
 
    pidfile = fopen(plymouth_old_pid, "r");
319
 
    if(pidfile != NULL){
320
 
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
321
 
      if(ret != 1){
322
 
        proc_id = 0;
323
 
      }
324
 
      fclose(pidfile);
325
 
    }
326
 
  }
327
 
  /* Try the old old pid file location */
328
 
  if(proc_id == 0){
329
 
    pidfile = fopen(plymouth_old_old_pid, "r");
 
284
    pidfile = fopen(plymouth_pid, "r");
330
285
    if(pidfile != NULL){
331
286
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
332
287
      if(ret != 1){
343
298
      error_plus(0, errno, "scandir");
344
299
    }
345
300
    if(ret > 0){
346
 
      for(int i = ret-1; i >= 0; i--){
347
 
        if(proc_id == 0){
348
 
          ret = sscanf(direntries[i]->d_name, "%" SCNuMAX, &proc_id);
349
 
          if(ret < 0){
350
 
            error_plus(0, errno, "sscanf");
351
 
          }
352
 
        }
353
 
        free(direntries[i]);
 
301
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &proc_id);
 
302
      if(ret < 0){
 
303
        error_plus(0, errno, "sscanf");
354
304
      }
355
305
    }
356
306
    /* scandir might preallocate for this variable (man page unclear).
366
316
  return 0;
367
317
}
368
318
 
369
 
char **getargv(pid_t pid){
 
319
const char * const * getargv(pid_t pid){
370
320
  int cl_fd;
371
321
  char *cmdline_filename;
372
322
  ssize_t sret;
433
383
    return NULL;
434
384
  }
435
385
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
436
 
  return argv;
 
386
  return (const char * const *)argv;
437
387
}
438
388
 
439
389
int main(__attribute__((unused))int argc,
440
390
         __attribute__((unused))char **argv){
441
 
  char *prompt = NULL;
 
391
  char *prompt;
442
392
  char *prompt_arg;
443
393
  pid_t plymouth_command_pid;
444
394
  int ret;
445
395
  bool bret;
446
396
 
447
 
  {
448
 
    struct argp_option options[] = {
449
 
      { .name = "prompt", .key = 128, .arg = "PROMPT",
450
 
        .doc = "The prompt to show" },
451
 
      { .name = "debug", .key = 129,
452
 
        .doc = "Debug mode" },
453
 
      { .name = NULL }
454
 
    };
455
 
    
456
 
    __attribute__((nonnull(3)))
457
 
    error_t parse_opt (int key, char *arg, __attribute__((unused))
458
 
                       struct argp_state *state){
459
 
      errno = 0;
460
 
      switch (key){
461
 
      case 128:                 /* --prompt */
462
 
        prompt = arg;
463
 
        if(debug){
464
 
          fprintf_plus(stderr, "Custom prompt \"%s\"\n", prompt);
465
 
        }
466
 
        break;
467
 
      case 129:                 /* --debug */
468
 
        debug = true;
469
 
        break;
470
 
      default:
471
 
        return ARGP_ERR_UNKNOWN;
472
 
      }
473
 
      return errno;
474
 
    }
475
 
    
476
 
    struct argp argp = { .options = options, .parser = parse_opt,
477
 
                         .args_doc = "",
478
 
                         .doc = "Mandos plymouth -- Read and"
479
 
                         " output a password" };
480
 
    ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, NULL);
481
 
    switch(ret){
482
 
    case 0:
483
 
      break;
484
 
    case ENOMEM:
485
 
    default:
486
 
      errno = ret;
487
 
      error_plus(0, errno, "argp_parse");
488
 
      return EX_OSERR;
489
 
    case EINVAL:
490
 
      error_plus(0, errno, "argp_parse");
491
 
      return EX_USAGE;
492
 
    }
493
 
  }
494
 
  
495
397
  /* test -x /bin/plymouth */
496
398
  ret = access(plymouth_path, X_OK);
497
399
  if(ret == -1){
498
400
    /* Plymouth is probably not installed.  Don't print an error
499
401
       message, just exit. */
500
 
    if(debug){
501
 
      fprintf_plus(stderr, "Plymouth (%s) not found\n",
502
 
                   plymouth_path);
503
 
    }
504
402
    exit(EX_UNAVAILABLE);
505
403
  }
506
404
  
540
438
    }
541
439
    /* Plymouth is probably not running.  Don't print an error
542
440
       message, just exit. */
543
 
    if(debug){
544
 
      fprintf_plus(stderr, "Plymouth not running\n");
545
 
    }
546
441
    exit(EX_UNAVAILABLE);
547
442
  }
548
443
  
549
 
  if(prompt != NULL){
550
 
    ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
551
 
  } else {
552
 
    char *made_prompt = makeprompt();
553
 
    ret = asprintf(&prompt_arg, "--prompt=%s", made_prompt);
554
 
    free(made_prompt);
555
 
  }
 
444
  prompt = makeprompt();
 
445
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
 
446
  free(prompt);
556
447
  if(ret == -1){
557
448
    error_plus(EX_OSERR, errno, "asprintf");
558
449
  }
559
450
  
560
451
  /* plymouth ask-for-password --prompt="$prompt" */
561
 
  if(debug){
562
 
    fprintf_plus(stderr, "Prompting for password via Plymouth\n");
563
 
  }
564
452
  bret = exec_and_wait(&plymouth_command_pid,
565
453
                       plymouth_path, (const char *[])
566
454
                       { plymouth_path, "ask-for-password",
576
464
  }
577
465
  kill_and_wait(plymouth_command_pid);
578
466
  
579
 
  char **plymouthd_argv = NULL;
 
467
  const char * const *plymouthd_argv;
580
468
  pid_t pid = get_pid();
581
469
  if(pid == 0){
582
470
    error_plus(0, 0, "plymouthd pid not found");
 
471
    plymouthd_argv = plymouthd_default_argv;
583
472
  } else {
584
473
    plymouthd_argv = getargv(pid);
585
474
  }
588
477
                       { plymouth_path, "quit", NULL },
589
478
                       false, false);
590
479
  if(not bret){
591
 
    if(plymouthd_argv != NULL){
592
 
      free(*plymouthd_argv);
593
 
      free(plymouthd_argv);
594
 
    }
595
480
    exit(EXIT_FAILURE);
596
481
  }
597
 
  bret = exec_and_wait(NULL, plymouthd_path,
598
 
                       (plymouthd_argv != NULL)
599
 
                       ? (const char * const *)plymouthd_argv
600
 
                       : plymouthd_default_argv,
 
482
  bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv,
601
483
                       false, true);
602
 
  if(plymouthd_argv != NULL){
603
 
    free(*plymouthd_argv);
604
 
    free(plymouthd_argv);
605
 
  }
606
484
  if(not bret){
607
485
    exit(EXIT_FAILURE);
608
486
  }