/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: 2019-08-02 22:16:53 UTC
  • mto: This revision was merged to the branch mainline in revision 386.
  • Revision ID: teddy@recompile.se-20190802221653-ic1iko9hbefzwsk7
Fix bug in server Debian package: Fails to start on first install

There has been a very long-standing bug where installation of the
server (the "mandos" Debian package) would fail to start the server
properly right after installation.  It would work on manual (re)start
after installation, or after reboot, and even after package purge and
reinstall, it would then work the first time.  The problem, it turns
out, is when the new "_mandos" user (and corresponding group) is
created, the D-Bus server is not reloaded, and is therefore not aware
of that user, and does not recognize the user and group name in the
/etc/dbus-1/system.d/mandos.conf file.  The Mandos server, when it
tries to start and access the D-Bus, is then not permitted to connect
to its D-Bus bus name, and disables D-Bus use as a fallback measure;
i.e. the server works, but it is not controllable via D-Bus commands
(via mandos-ctl or mandos-monitor).  The next time the D-Bus daemon is
reloaded for any reason, the new user & group would become visible to
the D-Bus daemon and after that, any restart of the Mandos server
would succeed and it would bind to its D-Bus name properly, and
thereby be visible and controllable by mandos-ctl & mandos-monitor.
This was mostly invisible when using sysvinit, but systemd makes the
problem visible since the systemd service file for the Mandos server
is configured to not consider the Mandos server "started" until the
D-Bus name has been bound; this makes the starting of the service wait
for 90 seconds and then fail with a timeout error.

Fixing this should also make the Debian CI autopkgtest tests work.

* debian/mandos.postinst (configure): After creating (or renaming)
                                      user & group, reload D-Bus
                                      daemon (if present).

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-2017 Teddy Hogeborn
6
 
 * Copyright © 2010-2017 Björn Påhlsson
 
5
 * Copyright © 2010-2019 Teddy Hogeborn
 
6
 * Copyright © 2010-2019 Björn Påhlsson
7
7
 * 
8
8
 * This file is part of Mandos.
9
9
 * 
53
53
#include <errno.h>              /* TEMP_FAILURE_RETRY */
54
54
#include <argz.h>               /* argz_count(), argz_extract() */
55
55
#include <stdarg.h>             /* va_list, va_start(), ... */
 
56
#include <argp.h>
56
57
 
57
58
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>";
58
61
 
59
62
/* Used by Ubuntu 11.04 (Natty Narwahl) */
60
 
const char plymouth_old_pid[] = "/dev/.initramfs/plymouth.pid";
 
63
const char plymouth_old_old_pid[] = "/dev/.initramfs/plymouth.pid";
61
64
/* Used by Ubuntu 11.10 (Oneiric Ocelot) */
62
 
const char plymouth_pid[] = "/run/initramfs/plymouth.pid";
 
65
const char plymouth_old_pid[] = "/run/initramfs/plymouth.pid";
 
66
/* Used by Debian 9 (stretch) */
 
67
const char plymouth_pid[] = "/run/plymouth/pid";
63
68
 
64
69
const char plymouth_path[] = "/bin/plymouth";
65
70
const char plymouthd_path[] = "/sbin/plymouthd";
67
72
                                        "--mode=boot",
68
73
                                        "--attach-to-session",
69
74
                                        NULL };
 
75
bool debug = false;
70
76
 
71
77
static void termination_handler(__attribute__((unused))int signum){
72
78
  if(interrupted_by_signal){
75
81
  interrupted_by_signal = 1;
76
82
}
77
83
 
 
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
 
78
92
/* Function to use when printing errors */
79
93
__attribute__((format (gnu_printf, 3, 4)))
80
94
void error_plus(int status, int errnum, const char *formatstring,
157
171
 
158
172
__attribute__((nonnull (2, 3)))
159
173
bool exec_and_wait(pid_t *pid_return, const char *path,
160
 
                   const char * const *argv, bool interruptable,
 
174
                   const char * const * const argv, bool interruptable,
161
175
                   bool daemonize){
162
176
  int status;
163
177
  int ret;
164
178
  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
 
165
186
  pid = fork();
166
187
  if(pid == -1){
167
188
    error_plus(0, errno, "fork");
207
228
          and ((not interrupted_by_signal)
208
229
               or (not interruptable)));
209
230
  if(interrupted_by_signal and interruptable){
 
231
    if(debug){
 
232
      fprintf_plus(stderr, "Interrupted by signal\n");
 
233
    }
210
234
    return false;
211
235
  }
212
236
  if(ret == -1){
213
237
    error_plus(0, errno, "waitpid");
214
238
    return false;
215
239
  }
 
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
  }
216
249
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
217
250
    return true;
218
251
  }
282
315
  }
283
316
  /* Try the old pid file location */
284
317
  if(proc_id == 0){
285
 
    pidfile = fopen(plymouth_pid, "r");
 
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");
286
330
    if(pidfile != NULL){
287
331
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
288
332
      if(ret != 1){
299
343
      error_plus(0, errno, "scandir");
300
344
    }
301
345
    if(ret > 0){
302
 
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &proc_id);
303
 
      if(ret < 0){
304
 
        error_plus(0, errno, "sscanf");
 
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]);
305
354
      }
306
355
    }
307
356
    /* scandir might preallocate for this variable (man page unclear).
317
366
  return 0;
318
367
}
319
368
 
320
 
const char * const * getargv(pid_t pid){
 
369
char **getargv(pid_t pid){
321
370
  int cl_fd;
322
371
  char *cmdline_filename;
323
372
  ssize_t sret;
384
433
    return NULL;
385
434
  }
386
435
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
387
 
  return (const char * const *)argv;
 
436
  return argv;
388
437
}
389
438
 
390
439
int main(__attribute__((unused))int argc,
391
440
         __attribute__((unused))char **argv){
392
 
  char *prompt;
 
441
  char *prompt = NULL;
393
442
  char *prompt_arg;
394
443
  pid_t plymouth_command_pid;
395
444
  int ret;
396
445
  bool bret;
397
446
 
 
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
  
398
495
  /* test -x /bin/plymouth */
399
496
  ret = access(plymouth_path, X_OK);
400
497
  if(ret == -1){
401
498
    /* Plymouth is probably not installed.  Don't print an error
402
499
       message, just exit. */
 
500
    if(debug){
 
501
      fprintf_plus(stderr, "Plymouth (%s) not found\n",
 
502
                   plymouth_path);
 
503
    }
403
504
    exit(EX_UNAVAILABLE);
404
505
  }
405
506
  
439
540
    }
440
541
    /* Plymouth is probably not running.  Don't print an error
441
542
       message, just exit. */
 
543
    if(debug){
 
544
      fprintf_plus(stderr, "Plymouth not running\n");
 
545
    }
442
546
    exit(EX_UNAVAILABLE);
443
547
  }
444
548
  
445
 
  prompt = makeprompt();
446
 
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
447
 
  free(prompt);
 
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
  }
448
556
  if(ret == -1){
449
557
    error_plus(EX_OSERR, errno, "asprintf");
450
558
  }
451
559
  
452
560
  /* plymouth ask-for-password --prompt="$prompt" */
 
561
  if(debug){
 
562
    fprintf_plus(stderr, "Prompting for password via Plymouth\n");
 
563
  }
453
564
  bret = exec_and_wait(&plymouth_command_pid,
454
565
                       plymouth_path, (const char *[])
455
566
                       { plymouth_path, "ask-for-password",
465
576
  }
466
577
  kill_and_wait(plymouth_command_pid);
467
578
  
468
 
  const char * const *plymouthd_argv;
 
579
  char **plymouthd_argv = NULL;
469
580
  pid_t pid = get_pid();
470
581
  if(pid == 0){
471
582
    error_plus(0, 0, "plymouthd pid not found");
472
 
    plymouthd_argv = plymouthd_default_argv;
473
583
  } else {
474
584
    plymouthd_argv = getargv(pid);
475
585
  }
478
588
                       { plymouth_path, "quit", NULL },
479
589
                       false, false);
480
590
  if(not bret){
 
591
    if(plymouthd_argv != NULL){
 
592
      free(*plymouthd_argv);
 
593
      free(plymouthd_argv);
 
594
    }
481
595
    exit(EXIT_FAILURE);
482
596
  }
483
 
  bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv,
 
597
  bret = exec_and_wait(NULL, plymouthd_path,
 
598
                       (plymouthd_argv != NULL)
 
599
                       ? (const char * const *)plymouthd_argv
 
600
                       : plymouthd_default_argv,
484
601
                       false, true);
 
602
  if(plymouthd_argv != NULL){
 
603
    free(*plymouthd_argv);
 
604
    free(plymouthd_argv);
 
605
  }
485
606
  if(not bret){
486
607
    exit(EXIT_FAILURE);
487
608
  }