/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: 2024-09-09 01:36:41 UTC
  • mto: This revision was merged to the branch mainline in revision 410.
  • Revision ID: teddy@recompile.se-20240909013641-6zu6kx2f7meu134k
Make all required directories when installing

When installing into a normal system, one can assume that target
directories, such as /usr/bin, already exists.  But when installing
into a subdirectory for the purpose of creating a package, one cannot
assume that all directories already exist.  Therefore, when
installing, we must not check if any directories exist, and must
instead always create any directories we want to install into.

* Makefile (confdir/mandos.conf, confdir/clients.conf, install-html):
  Use the "-D" option to "install" instead of creating the directory
  separately.
  (install-server): Move creation of $(CONFDIR) down to before it is
  needed.  Don't check if the $(TMPFILES) or $(SYSUSERS) directories
  exist; instead create them by using the "-D" option to "install".
  Create the $(PREFIX)/sbin directory.  Always use
  "--target-directory" if possible; i.e. if the file name is the same.
  Create the $(DBUSPOLICYDIR) and $(DESTDIR)/etc/init.d directories by
  using the "-D" option to "install".  Don't check if the $(SYSTEMD)
  directory exists; instead create it by using the "-D" option to
  "install".  Create the $(DESTDIR)/etc/default and $(MANDIR)/man8
  directories by using the "-D" option to "install".  Create the
  $(MANDIR)/man5 directories explicitly.
  (install-client-nokey): Remove unnecessary creation of the
  $(CONFDIR) directory.  Don't check if the $(SYSUSERS) directory
  exists; instead create it by using the "-D" option to "install".
  Move the "--directory" argument to be the first argument, for
  clarity.  Create the $(PREFIX)/sbin directory.  Use the "-D"
  argument to "install" when installing
  $(INITRAMFSTOOLS)/hooks/mandos,
  $(INITRAMFSTOOLS)/conf.d/mandos-conf,
  $(INITRAMFSTOOLS)/conf-hooks.d/zz-mandos,
  $(INITRAMFSTOOLS)/scripts/init-premount/mandos,
  $(INITRAMFSTOOLS)/scripts/local-premount/mandos,
  $(DRACUTMODULE)/ask-password-mandos.path, and
  $(DRACUTMODULE)/dracut-module/ask-password-mandos.service.  Create
  the $(MANDIR)/man8 directory.

Reported-By: Erich Eckner <erich@eckner.net>
Thanks: Erich Eckner <erich@eckner.net> for analysis

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-2011 Teddy Hogeborn
6
 
 * Copyright © 2010-2011 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
 
5
 * Copyright © 2010-2022 Teddy Hogeborn
 
6
 * Copyright © 2010-2022 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
14
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
18
 * General Public License for more details.
17
19
 * 
18
20
 * You should have received a copy of the GNU General Public License
19
 
 * along with this program.  If not, see
20
 
 * <http://www.gnu.org/licenses/>.
 
21
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
21
22
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
23
 * Contact the authors at <mandos@recompile.se>.
23
24
 */
24
25
 
25
 
#define _GNU_SOURCE             /* asprintf(), TEMP_FAILURE_RETRY() */
26
 
#include <signal.h>             /* sig_atomic_t, struct sigaction,
27
 
                                   sigemptyset(), sigaddset(), SIGINT,
28
 
                                   SIGHUP, SIGTERM, sigaction(),
29
 
                                   kill(), SIG_IGN */
 
26
#define _GNU_SOURCE             /* program_invocation_short_name,
 
27
                                   vasprintf(), asprintf(),
 
28
                                   TEMP_FAILURE_RETRY() */
 
29
#include <sys/types.h>          /* sig_atomic_t, pid_t, setuid(),
 
30
                                   geteuid(), setsid() */
 
31
#include <argp.h>               /* argp_program_version,
 
32
                                   argp_program_bug_address,
 
33
                                   struct argp_option,
 
34
                                   struct argp_state,
 
35
                                   ARGP_ERR_UNKNOWN, struct argp,
 
36
                                   argp_parse(), ARGP_IN_ORDER */
 
37
#include <stddef.h>             /* NULL, size_t */
30
38
#include <stdbool.h>            /* bool, false, true */
 
39
#include <stdio.h>              /* FILE, fprintf(), vfprintf(),
 
40
                                   vasprintf(), stderr, asprintf(),
 
41
                                   fopen(), fscanf(), fclose(),
 
42
                                   sscanf() */
 
43
#include <stdarg.h>             /* va_list, va_start(), vfprintf() */
 
44
#include <errno.h>              /* program_invocation_short_name,
 
45
                                   errno, ENOMEM, EINTR, ENOENT,
 
46
                                   error_t, EINVAL */
 
47
#include <string.h>             /* strerror(), strdup(), memcmp() */
 
48
#include <error.h>              /* error() */
 
49
#include <stdlib.h>             /* free(), getenv(), malloc(),
 
50
                                   reallocarray(), realloc(),
 
51
                                   EXIT_FAILURE, EXIT_SUCCESS */
 
52
#include <unistd.h>             /* TEMP_FAILURE_RETRY(), setuid(),
 
53
                                   geteuid(), setsid(), chdir(),
 
54
                                   dup2(), STDERR_FILENO,
 
55
                                   STDOUT_FILENO, fork(), _exit(),
 
56
                                   execv(), ssize_t, readlink(),
 
57
                                   close(), read(), access(), X_OK */
 
58
#include <signal.h>             /* kill(), SIGTERM, struct sigaction,
 
59
                                   sigemptyset(), SIGINT, SIGHUP,
 
60
                                   sigaddset(), SIG_IGN */
 
61
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
 
62
                                   WEXITSTATUS(), WIFSIGNALED(),
 
63
                                   WTERMSIG() */
 
64
#include <iso646.h>             /* not, and, or */
 
65
#include <sysexits.h>           /* EX_OSERR, EX_USAGE,
 
66
                                   EX_UNAVAILABLE */
 
67
#include <stdint.h>             /* SIZE_MAX */
 
68
#include <dirent.h>             /* struct dirent, scandir(),
 
69
                                   alphasort() */
 
70
#include <inttypes.h>           /* uintmax_t, strtoumax(), SCNuMAX,
 
71
                                   PRIuMAX */
 
72
#include <sys/stat.h>           /* struct stat, lstat(), S_ISLNK() */
31
73
#include <fcntl.h>              /* open(), O_RDONLY */
32
 
#include <iso646.h>             /* and, or, not*/
33
 
#include <sys/types.h>          /* size_t, ssize_t, pid_t, struct
34
 
                                   dirent, waitpid() */
35
 
#include <sys/wait.h>           /* waitpid() */
36
 
#include <stddef.h>             /* NULL */
37
 
#include <string.h>             /* strchr(), memcmp() */
38
 
#include <stdio.h>              /* asprintf(), perror(), fopen(),
39
 
                                   fscanf(), vasprintf(), fprintf(),
40
 
                                   vfprintf() */
41
 
#include <unistd.h>             /* close(), readlink(), read(),
42
 
                                   fork(), setsid(), chdir(), dup2(),
43
 
                                   STDERR_FILENO, execv(), access() */
44
 
#include <stdlib.h>             /* free(), EXIT_FAILURE, realloc(),
45
 
                                   EXIT_SUCCESS, malloc(), _exit(),
46
 
                                   getenv() */
47
 
#include <dirent.h>             /* scandir(), alphasort() */
48
 
#include <inttypes.h>           /* intmax_t, strtoumax(), SCNuMAX */
49
 
#include <sys/stat.h>           /* struct stat, lstat() */
50
 
#include <sysexits.h>           /* EX_OSERR, EX_UNAVAILABLE */
51
 
#include <error.h>              /* error() */
52
 
#include <errno.h>              /* TEMP_FAILURE_RETRY */
53
74
#include <argz.h>               /* argz_count(), argz_extract() */
54
 
#include <stdarg.h>             /* va_list, va_start(), ... */
55
75
 
56
76
sig_atomic_t interrupted_by_signal = 0;
57
 
const char plymouth_pid[] = "/dev/.initramfs/plymouth.pid";
 
77
const char *argp_program_version = "plymouth " VERSION;
 
78
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
79
 
 
80
/* Used by Ubuntu 11.04 (Natty Narwahl) */
 
81
const char plymouth_old_old_pid[] = "/dev/.initramfs/plymouth.pid";
 
82
/* Used by Ubuntu 11.10 (Oneiric Ocelot) */
 
83
const char plymouth_old_pid[] = "/run/initramfs/plymouth.pid";
 
84
/* Used by Debian 9 (stretch) */
 
85
const char plymouth_pid[] = "/run/plymouth/pid";
 
86
 
58
87
const char plymouth_path[] = "/bin/plymouth";
59
88
const char plymouthd_path[] = "/sbin/plymouthd";
60
89
const char *plymouthd_default_argv[] = {"/sbin/plymouthd",
61
90
                                        "--mode=boot",
62
91
                                        "--attach-to-session",
63
 
                                        "--pid-file="
64
 
                                        "/dev/.initramfs/"
65
 
                                        "plymouth.pid",
66
92
                                        NULL };
 
93
bool debug = false;
67
94
 
68
95
static void termination_handler(__attribute__((unused))int signum){
69
96
  if(interrupted_by_signal){
72
99
  interrupted_by_signal = 1;
73
100
}
74
101
 
 
102
__attribute__((format (gnu_printf, 2, 3), nonnull))
 
103
int fprintf_plus(FILE *stream, const char *format, ...){
 
104
  va_list ap;
 
105
  va_start (ap, format);
 
106
  fprintf(stream, "Mandos plugin %s: ", program_invocation_short_name);
 
107
  return vfprintf(stream, format, ap);
 
108
}
 
109
 
75
110
/* Function to use when printing errors */
 
111
__attribute__((format (gnu_printf, 3, 4)))
76
112
void error_plus(int status, int errnum, const char *formatstring,
77
113
                ...){
78
114
  va_list ap;
81
117
  
82
118
  va_start(ap, formatstring);
83
119
  ret = vasprintf(&text, formatstring, ap);
84
 
  if (ret == -1){
 
120
  if(ret == -1){
85
121
    fprintf(stderr, "Mandos plugin %s: ",
86
122
            program_invocation_short_name);
87
123
    vfprintf(stderr, formatstring, ap);
151
187
  return true;
152
188
}
153
189
 
 
190
__attribute__((nonnull (2, 3)))
154
191
bool exec_and_wait(pid_t *pid_return, const char *path,
155
 
                   const char **argv, bool interruptable,
 
192
                   const char * const * const argv, bool interruptable,
156
193
                   bool daemonize){
157
194
  int status;
158
195
  int ret;
159
196
  pid_t pid;
 
197
  if(debug){
 
198
    for(const char * const *arg = argv; *arg != NULL; arg++){
 
199
      fprintf_plus(stderr, "exec_and_wait arg: %s\n", *arg);
 
200
    }
 
201
    fprintf_plus(stderr, "exec_and_wait end of args\n");
 
202
  }
 
203
 
160
204
  pid = fork();
161
205
  if(pid == -1){
162
206
    error_plus(0, errno, "fork");
170
214
      }
171
215
    }
172
216
    
173
 
    char **new_argv = NULL;
 
217
    char **new_argv = malloc(sizeof(const char *));
 
218
    if(new_argv == NULL){
 
219
      error_plus(0, errno, "malloc");
 
220
      _exit(EX_OSERR);
 
221
    }
174
222
    char **tmp;
175
223
    int i = 0;
176
 
    for (; argv[i]!=NULL; i++){
177
 
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1));
178
 
      if (tmp == NULL){
179
 
        error_plus(0, errno, "realloc");
 
224
    for (; argv[i] != NULL; i++){
 
225
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26)
 
226
      tmp = reallocarray(new_argv, ((size_t)i + 2),
 
227
                         sizeof(const char *));
 
228
#else
 
229
      if(((size_t)i + 2) > (SIZE_MAX / sizeof(const char *))){
 
230
        /* overflow */
 
231
        tmp = NULL;
 
232
        errno = ENOMEM;
 
233
      } else {
 
234
        tmp = realloc(new_argv, ((size_t)i + 2) * sizeof(const char *));
 
235
      }
 
236
#endif
 
237
      if(tmp == NULL){
 
238
        error_plus(0, errno, "reallocarray");
180
239
        free(new_argv);
181
240
        _exit(EX_OSERR);
182
241
      }
198
257
          and ((not interrupted_by_signal)
199
258
               or (not interruptable)));
200
259
  if(interrupted_by_signal and interruptable){
 
260
    if(debug){
 
261
      fprintf_plus(stderr, "Interrupted by signal\n");
 
262
    }
201
263
    return false;
202
264
  }
203
265
  if(ret == -1){
204
266
    error_plus(0, errno, "waitpid");
205
267
    return false;
206
268
  }
 
269
  if(debug){
 
270
    if(WIFEXITED(status)){
 
271
      fprintf_plus(stderr, "exec_and_wait exited: %d\n",
 
272
                   WEXITSTATUS(status));
 
273
    } else if(WIFSIGNALED(status)) {
 
274
      fprintf_plus(stderr, "exec_and_wait signaled: %d\n",
 
275
                   WTERMSIG(status));
 
276
    }
 
277
  }
207
278
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
208
279
    return true;
209
280
  }
210
281
  return false;
211
282
}
212
283
 
 
284
__attribute__((nonnull))
213
285
int is_plymouth(const struct dirent *proc_entry){
214
286
  int ret;
215
287
  {
216
 
    uintmax_t maxvalue;
 
288
    uintmax_t proc_id;
217
289
    char *tmp;
218
290
    errno = 0;
219
 
    maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
291
    proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
220
292
 
221
293
    if(errno != 0 or *tmp != '\0'
222
 
       or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
294
       or proc_id != (uintmax_t)((pid_t)proc_id)){
223
295
      return 0;
224
296
    }
225
297
  }
260
332
 
261
333
pid_t get_pid(void){
262
334
  int ret;
 
335
  uintmax_t proc_id = 0;
263
336
  FILE *pidfile = fopen(plymouth_pid, "r");
264
 
  uintmax_t maxvalue = 0;
 
337
  /* Try the new pid file location */
265
338
  if(pidfile != NULL){
266
 
    ret = fscanf(pidfile, "%" SCNuMAX, &maxvalue);
 
339
    ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
267
340
    if(ret != 1){
268
 
      maxvalue = 0;
 
341
      proc_id = 0;
269
342
    }
270
343
    fclose(pidfile);
271
344
  }
272
 
  if(maxvalue == 0){
 
345
  /* Try the old pid file location */
 
346
  if(proc_id == 0){
 
347
    pidfile = fopen(plymouth_old_pid, "r");
 
348
    if(pidfile != NULL){
 
349
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
 
350
      if(ret != 1){
 
351
        proc_id = 0;
 
352
      }
 
353
      fclose(pidfile);
 
354
    }
 
355
  }
 
356
  /* Try the old old pid file location */
 
357
  if(proc_id == 0){
 
358
    pidfile = fopen(plymouth_old_old_pid, "r");
 
359
    if(pidfile != NULL){
 
360
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
 
361
      if(ret != 1){
 
362
        proc_id = 0;
 
363
      }
 
364
      fclose(pidfile);
 
365
    }
 
366
  }
 
367
  /* Look for a plymouth process */
 
368
  if(proc_id == 0){
273
369
    struct dirent **direntries = NULL;
274
370
    ret = scandir("/proc", &direntries, is_plymouth, alphasort);
275
 
    if (ret == -1){
 
371
    if(ret == -1){
276
372
      error_plus(0, errno, "scandir");
277
373
    }
278
 
    if (ret > 0){
279
 
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &maxvalue);
280
 
      if (ret < 0){
281
 
        error_plus(0, errno, "sscanf");
 
374
    if(ret > 0){
 
375
      for(int i = ret-1; i >= 0; i--){
 
376
        if(proc_id == 0){
 
377
          ret = sscanf(direntries[i]->d_name, "%" SCNuMAX, &proc_id);
 
378
          if(ret < 0){
 
379
            error_plus(0, errno, "sscanf");
 
380
          }
 
381
        }
 
382
        free(direntries[i]);
282
383
      }
283
384
    }
284
385
    /* scandir might preallocate for this variable (man page unclear).
286
387
    free(direntries);
287
388
  }
288
389
  pid_t pid;
289
 
  pid = (pid_t)maxvalue;
290
 
  if((uintmax_t)pid == maxvalue){
 
390
  pid = (pid_t)proc_id;
 
391
  if((uintmax_t)pid == proc_id){
291
392
    return pid;
292
393
  }
293
394
  
294
395
  return 0;
295
396
}
296
397
 
297
 
const char **getargv(pid_t pid){
 
398
char **getargv(pid_t pid){
298
399
  int cl_fd;
299
400
  char *cmdline_filename;
300
401
  ssize_t sret;
361
462
    return NULL;
362
463
  }
363
464
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
364
 
  return (const char **)argv;
 
465
  return argv;
365
466
}
366
467
 
367
468
int main(__attribute__((unused))int argc,
368
469
         __attribute__((unused))char **argv){
369
 
  char *prompt;
 
470
  char *prompt = NULL;
370
471
  char *prompt_arg;
371
472
  pid_t plymouth_command_pid;
372
473
  int ret;
373
474
  bool bret;
374
475
 
 
476
  {
 
477
    struct argp_option options[] = {
 
478
      { .name = "prompt", .key = 128, .arg = "PROMPT",
 
479
        .doc = "The prompt to show" },
 
480
      { .name = "debug", .key = 129,
 
481
        .doc = "Debug mode" },
 
482
      { .name = NULL }
 
483
    };
 
484
    
 
485
    __attribute__((nonnull(3)))
 
486
    error_t parse_opt (int key, char *arg, __attribute__((unused))
 
487
                       struct argp_state *state){
 
488
      errno = 0;
 
489
      switch (key){
 
490
      case 128:                 /* --prompt */
 
491
        prompt = arg;
 
492
        if(debug){
 
493
          fprintf_plus(stderr, "Custom prompt \"%s\"\n", prompt);
 
494
        }
 
495
        break;
 
496
      case 129:                 /* --debug */
 
497
        debug = true;
 
498
        break;
 
499
      default:
 
500
        return ARGP_ERR_UNKNOWN;
 
501
      }
 
502
      return errno;
 
503
    }
 
504
    
 
505
    struct argp argp = { .options = options, .parser = parse_opt,
 
506
                         .args_doc = "",
 
507
                         .doc = "Mandos plymouth -- Read and"
 
508
                         " output a password" };
 
509
    ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, NULL);
 
510
    switch(ret){
 
511
    case 0:
 
512
      break;
 
513
    case ENOMEM:
 
514
    default:
 
515
      errno = ret;
 
516
      error_plus(0, errno, "argp_parse");
 
517
      return EX_OSERR;
 
518
    case EINVAL:
 
519
      error_plus(0, errno, "argp_parse");
 
520
      return EX_USAGE;
 
521
    }
 
522
  }
 
523
  
375
524
  /* test -x /bin/plymouth */
376
525
  ret = access(plymouth_path, X_OK);
377
526
  if(ret == -1){
378
527
    /* Plymouth is probably not installed.  Don't print an error
379
528
       message, just exit. */
 
529
    if(debug){
 
530
      fprintf_plus(stderr, "Plymouth (%s) not found\n",
 
531
                   plymouth_path);
 
532
    }
380
533
    exit(EX_UNAVAILABLE);
381
534
  }
382
535
  
416
569
    }
417
570
    /* Plymouth is probably not running.  Don't print an error
418
571
       message, just exit. */
 
572
    if(debug){
 
573
      fprintf_plus(stderr, "Plymouth not running\n");
 
574
    }
419
575
    exit(EX_UNAVAILABLE);
420
576
  }
421
577
  
422
 
  prompt = makeprompt();
423
 
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
424
 
  free(prompt);
 
578
  if(prompt != NULL){
 
579
    ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
 
580
  } else {
 
581
    char *made_prompt = makeprompt();
 
582
    ret = asprintf(&prompt_arg, "--prompt=%s", made_prompt);
 
583
    free(made_prompt);
 
584
  }
425
585
  if(ret == -1){
426
586
    error_plus(EX_OSERR, errno, "asprintf");
427
587
  }
428
588
  
429
589
  /* plymouth ask-for-password --prompt="$prompt" */
 
590
  if(debug){
 
591
    fprintf_plus(stderr, "Prompting for password via Plymouth\n");
 
592
  }
430
593
  bret = exec_and_wait(&plymouth_command_pid,
431
594
                       plymouth_path, (const char *[])
432
595
                       { plymouth_path, "ask-for-password",
442
605
  }
443
606
  kill_and_wait(plymouth_command_pid);
444
607
  
445
 
  const char **plymouthd_argv;
 
608
  char **plymouthd_argv = NULL;
446
609
  pid_t pid = get_pid();
447
610
  if(pid == 0){
448
611
    error_plus(0, 0, "plymouthd pid not found");
449
 
    plymouthd_argv = plymouthd_default_argv;
450
612
  } else {
451
613
    plymouthd_argv = getargv(pid);
452
614
  }
455
617
                       { plymouth_path, "quit", NULL },
456
618
                       false, false);
457
619
  if(not bret){
 
620
    if(plymouthd_argv != NULL){
 
621
      free(*plymouthd_argv);
 
622
      free(plymouthd_argv);
 
623
    }
458
624
    exit(EXIT_FAILURE);
459
625
  }
460
 
  bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv,
 
626
  bret = exec_and_wait(NULL, plymouthd_path,
 
627
                       (plymouthd_argv != NULL)
 
628
                       ? (const char * const *)plymouthd_argv
 
629
                       : plymouthd_default_argv,
461
630
                       false, true);
 
631
  if(plymouthd_argv != NULL){
 
632
    free(*plymouthd_argv);
 
633
    free(plymouthd_argv);
 
634
  }
462
635
  if(not bret){
463
636
    exit(EXIT_FAILURE);
464
637
  }