/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: 2024-09-09 04:24:39 UTC
  • Revision ID: teddy@recompile.se-20240909042439-j85mr20uli2hnyis
Eliminate compiler warnings

Many programs use nested functions, which now result in a linker
warning about executable stack.  Hide this warning.  Also, rewrite a
loop in the plymouth plugin to avoid warning about signed overflow.
This change also makes the plugin pick the alphabetically first
process entry instead of the last, in case many plymouth processes are
found (which should be unlikely).

* Makefile (plugin-runner, dracut-module/password-agent,
  plugins.d/password-prompt, plugins.d/mandos-client,
  plugins.d/plymouth): New target; set LDFLAGS to add "-Xlinker
  --no-warn-execstack".
* plugins.d/plymouth.c (get_pid): When no pid files are found, and we
  are looking through the process list, go though it from the start
  instead of from the end, i.e. in normal alphabetical order and not
  in reverse order.

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-2022 Teddy Hogeborn
 
6
 * Copyright © 2010-2022 Björn Påhlsson
7
7
 * 
8
8
 * This file is part of Mandos.
9
9
 * 
23
23
 * Contact the authors at <mandos@recompile.se>.
24
24
 */
25
25
 
26
 
#define _GNU_SOURCE             /* asprintf(), TEMP_FAILURE_RETRY() */
27
 
#include <signal.h>             /* sig_atomic_t, struct sigaction,
28
 
                                   sigemptyset(), sigaddset(), SIGINT,
29
 
                                   SIGHUP, SIGTERM, sigaction(),
30
 
                                   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 */
31
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() */
32
73
#include <fcntl.h>              /* open(), O_RDONLY */
33
 
#include <iso646.h>             /* and, or, not*/
34
 
#include <sys/types.h>          /* size_t, ssize_t, pid_t, struct
35
 
                                   dirent, waitpid() */
36
 
#include <sys/wait.h>           /* waitpid() */
37
 
#include <stddef.h>             /* NULL */
38
 
#include <string.h>             /* strchr(), memcmp() */
39
 
#include <stdio.h>              /* asprintf(), perror(), fopen(),
40
 
                                   fscanf(), vasprintf(), fprintf(),
41
 
                                   vfprintf() */
42
 
#include <unistd.h>             /* close(), readlink(), read(),
43
 
                                   fork(), setsid(), chdir(), dup2(),
44
 
                                   STDERR_FILENO, execv(), access() */
45
 
#include <stdlib.h>             /* free(), EXIT_FAILURE, realloc(),
46
 
                                   EXIT_SUCCESS, malloc(), _exit(),
47
 
                                   getenv() */
48
 
#include <dirent.h>             /* scandir(), alphasort() */
49
 
#include <inttypes.h>           /* intmax_t, strtoumax(), SCNuMAX */
50
 
#include <sys/stat.h>           /* struct stat, lstat() */
51
 
#include <sysexits.h>           /* EX_OSERR, EX_UNAVAILABLE */
52
 
#include <error.h>              /* error() */
53
 
#include <errno.h>              /* TEMP_FAILURE_RETRY */
54
74
#include <argz.h>               /* argz_count(), argz_extract() */
55
 
#include <stdarg.h>             /* va_list, va_start(), ... */
56
75
 
57
76
sig_atomic_t interrupted_by_signal = 0;
 
77
const char *argp_program_version = "plymouth " VERSION;
 
78
const char *argp_program_bug_address = "<mandos@recompile.se>";
58
79
 
59
80
/* Used by Ubuntu 11.04 (Natty Narwahl) */
60
 
const char plymouth_old_pid[] = "/dev/.initramfs/plymouth.pid";
 
81
const char plymouth_old_old_pid[] = "/dev/.initramfs/plymouth.pid";
61
82
/* Used by Ubuntu 11.10 (Oneiric Ocelot) */
62
 
const char plymouth_pid[] = "/run/initramfs/plymouth.pid";
 
83
const char plymouth_old_pid[] = "/run/initramfs/plymouth.pid";
 
84
/* Used by Debian 9 (stretch) */
 
85
const char plymouth_pid[] = "/run/plymouth/pid";
63
86
 
64
87
const char plymouth_path[] = "/bin/plymouth";
65
88
const char plymouthd_path[] = "/sbin/plymouthd";
67
90
                                        "--mode=boot",
68
91
                                        "--attach-to-session",
69
92
                                        NULL };
 
93
bool debug = false;
70
94
 
71
95
static void termination_handler(__attribute__((unused))int signum){
72
96
  if(interrupted_by_signal){
75
99
  interrupted_by_signal = 1;
76
100
}
77
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
 
78
110
/* Function to use when printing errors */
79
111
__attribute__((format (gnu_printf, 3, 4)))
80
112
void error_plus(int status, int errnum, const char *formatstring,
157
189
 
158
190
__attribute__((nonnull (2, 3)))
159
191
bool exec_and_wait(pid_t *pid_return, const char *path,
160
 
                   const char * const *argv, bool interruptable,
 
192
                   const char * const * const argv, bool interruptable,
161
193
                   bool daemonize){
162
194
  int status;
163
195
  int ret;
164
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
 
165
204
  pid = fork();
166
205
  if(pid == -1){
167
206
    error_plus(0, errno, "fork");
183
222
    char **tmp;
184
223
    int i = 0;
185
224
    for (; argv[i] != NULL; i++){
186
 
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 2));
 
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
187
237
      if(tmp == NULL){
188
 
        error_plus(0, errno, "realloc");
 
238
        error_plus(0, errno, "reallocarray");
189
239
        free(new_argv);
190
240
        _exit(EX_OSERR);
191
241
      }
207
257
          and ((not interrupted_by_signal)
208
258
               or (not interruptable)));
209
259
  if(interrupted_by_signal and interruptable){
 
260
    if(debug){
 
261
      fprintf_plus(stderr, "Interrupted by signal\n");
 
262
    }
210
263
    return false;
211
264
  }
212
265
  if(ret == -1){
213
266
    error_plus(0, errno, "waitpid");
214
267
    return false;
215
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
  }
216
278
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
217
279
    return true;
218
280
  }
282
344
  }
283
345
  /* Try the old pid file location */
284
346
  if(proc_id == 0){
285
 
    pidfile = fopen(plymouth_pid, "r");
 
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");
286
359
    if(pidfile != NULL){
287
360
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
288
361
      if(ret != 1){
299
372
      error_plus(0, errno, "scandir");
300
373
    }
301
374
    if(ret > 0){
302
 
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &proc_id);
303
 
      if(ret < 0){
304
 
        error_plus(0, errno, "sscanf");
 
375
      const int num_entries = ret;
 
376
      for(int i = 0; i < num_entries; i++){
 
377
        if(proc_id == 0){
 
378
          ret = sscanf(direntries[i]->d_name, "%" SCNuMAX, &proc_id);
 
379
          if(ret < 0){
 
380
            error_plus(0, errno, "sscanf");
 
381
          }
 
382
        }
 
383
        free(direntries[i]);
305
384
      }
306
385
    }
307
386
    /* scandir might preallocate for this variable (man page unclear).
317
396
  return 0;
318
397
}
319
398
 
320
 
const char * const * getargv(pid_t pid){
 
399
char **getargv(pid_t pid){
321
400
  int cl_fd;
322
401
  char *cmdline_filename;
323
402
  ssize_t sret;
384
463
    return NULL;
385
464
  }
386
465
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
387
 
  return (const char * const *)argv;
 
466
  return argv;
388
467
}
389
468
 
390
469
int main(__attribute__((unused))int argc,
391
470
         __attribute__((unused))char **argv){
392
 
  char *prompt;
 
471
  char *prompt = NULL;
393
472
  char *prompt_arg;
394
473
  pid_t plymouth_command_pid;
395
474
  int ret;
396
475
  bool bret;
397
476
 
 
477
  {
 
478
    struct argp_option options[] = {
 
479
      { .name = "prompt", .key = 128, .arg = "PROMPT",
 
480
        .doc = "The prompt to show" },
 
481
      { .name = "debug", .key = 129,
 
482
        .doc = "Debug mode" },
 
483
      { .name = NULL }
 
484
    };
 
485
    
 
486
    __attribute__((nonnull(3)))
 
487
    error_t parse_opt (int key, char *arg, __attribute__((unused))
 
488
                       struct argp_state *state){
 
489
      errno = 0;
 
490
      switch (key){
 
491
      case 128:                 /* --prompt */
 
492
        prompt = arg;
 
493
        if(debug){
 
494
          fprintf_plus(stderr, "Custom prompt \"%s\"\n", prompt);
 
495
        }
 
496
        break;
 
497
      case 129:                 /* --debug */
 
498
        debug = true;
 
499
        break;
 
500
      default:
 
501
        return ARGP_ERR_UNKNOWN;
 
502
      }
 
503
      return errno;
 
504
    }
 
505
    
 
506
    struct argp argp = { .options = options, .parser = parse_opt,
 
507
                         .args_doc = "",
 
508
                         .doc = "Mandos plymouth -- Read and"
 
509
                         " output a password" };
 
510
    ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, NULL);
 
511
    switch(ret){
 
512
    case 0:
 
513
      break;
 
514
    case ENOMEM:
 
515
    default:
 
516
      errno = ret;
 
517
      error_plus(0, errno, "argp_parse");
 
518
      return EX_OSERR;
 
519
    case EINVAL:
 
520
      error_plus(0, errno, "argp_parse");
 
521
      return EX_USAGE;
 
522
    }
 
523
  }
 
524
  
398
525
  /* test -x /bin/plymouth */
399
526
  ret = access(plymouth_path, X_OK);
400
527
  if(ret == -1){
401
528
    /* Plymouth is probably not installed.  Don't print an error
402
529
       message, just exit. */
 
530
    if(debug){
 
531
      fprintf_plus(stderr, "Plymouth (%s) not found\n",
 
532
                   plymouth_path);
 
533
    }
403
534
    exit(EX_UNAVAILABLE);
404
535
  }
405
536
  
439
570
    }
440
571
    /* Plymouth is probably not running.  Don't print an error
441
572
       message, just exit. */
 
573
    if(debug){
 
574
      fprintf_plus(stderr, "Plymouth not running\n");
 
575
    }
442
576
    exit(EX_UNAVAILABLE);
443
577
  }
444
578
  
445
 
  prompt = makeprompt();
446
 
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
447
 
  free(prompt);
 
579
  if(prompt != NULL){
 
580
    ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
 
581
  } else {
 
582
    char *made_prompt = makeprompt();
 
583
    ret = asprintf(&prompt_arg, "--prompt=%s", made_prompt);
 
584
    free(made_prompt);
 
585
  }
448
586
  if(ret == -1){
449
587
    error_plus(EX_OSERR, errno, "asprintf");
450
588
  }
451
589
  
452
590
  /* plymouth ask-for-password --prompt="$prompt" */
 
591
  if(debug){
 
592
    fprintf_plus(stderr, "Prompting for password via Plymouth\n");
 
593
  }
453
594
  bret = exec_and_wait(&plymouth_command_pid,
454
595
                       plymouth_path, (const char *[])
455
596
                       { plymouth_path, "ask-for-password",
465
606
  }
466
607
  kill_and_wait(plymouth_command_pid);
467
608
  
468
 
  const char * const *plymouthd_argv;
 
609
  char **plymouthd_argv = NULL;
469
610
  pid_t pid = get_pid();
470
611
  if(pid == 0){
471
612
    error_plus(0, 0, "plymouthd pid not found");
472
 
    plymouthd_argv = plymouthd_default_argv;
473
613
  } else {
474
614
    plymouthd_argv = getargv(pid);
475
615
  }
478
618
                       { plymouth_path, "quit", NULL },
479
619
                       false, false);
480
620
  if(not bret){
 
621
    if(plymouthd_argv != NULL){
 
622
      free(*plymouthd_argv);
 
623
      free(plymouthd_argv);
 
624
    }
481
625
    exit(EXIT_FAILURE);
482
626
  }
483
 
  bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv,
 
627
  bret = exec_and_wait(NULL, plymouthd_path,
 
628
                       (plymouthd_argv != NULL)
 
629
                       ? (const char * const *)plymouthd_argv
 
630
                       : plymouthd_default_argv,
484
631
                       false, true);
 
632
  if(plymouthd_argv != NULL){
 
633
    free(*plymouthd_argv);
 
634
    free(plymouthd_argv);
 
635
  }
485
636
  if(not bret){
486
637
    exit(EXIT_FAILURE);
487
638
  }