/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-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
 
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
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;
 
77
const char *argp_program_version = "plymouth " VERSION;
 
78
const char *argp_program_bug_address = "<mandos@recompile.se>";
57
79
 
58
80
/* Used by Ubuntu 11.04 (Natty Narwahl) */
59
 
const char plymouth_old_pid[] = "/dev/.initramfs/plymouth.pid";
 
81
const char plymouth_old_old_pid[] = "/dev/.initramfs/plymouth.pid";
60
82
/* Used by Ubuntu 11.10 (Oneiric Ocelot) */
61
 
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";
62
86
 
63
87
const char plymouth_path[] = "/bin/plymouth";
64
88
const char plymouthd_path[] = "/sbin/plymouthd";
66
90
                                        "--mode=boot",
67
91
                                        "--attach-to-session",
68
92
                                        NULL };
 
93
bool debug = false;
69
94
 
70
95
static void termination_handler(__attribute__((unused))int signum){
71
96
  if(interrupted_by_signal){
74
99
  interrupted_by_signal = 1;
75
100
}
76
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
 
77
110
/* Function to use when printing errors */
78
111
__attribute__((format (gnu_printf, 3, 4)))
79
112
void error_plus(int status, int errnum, const char *formatstring,
156
189
 
157
190
__attribute__((nonnull (2, 3)))
158
191
bool exec_and_wait(pid_t *pid_return, const char *path,
159
 
                   const char * const *argv, bool interruptable,
 
192
                   const char * const * const argv, bool interruptable,
160
193
                   bool daemonize){
161
194
  int status;
162
195
  int ret;
163
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
 
164
204
  pid = fork();
165
205
  if(pid == -1){
166
206
    error_plus(0, errno, "fork");
181
221
    }
182
222
    char **tmp;
183
223
    int i = 0;
184
 
    for (; argv[i]!=NULL; i++){
185
 
      tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 2));
 
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
186
237
      if(tmp == NULL){
187
 
        error_plus(0, errno, "realloc");
 
238
        error_plus(0, errno, "reallocarray");
188
239
        free(new_argv);
189
240
        _exit(EX_OSERR);
190
241
      }
206
257
          and ((not interrupted_by_signal)
207
258
               or (not interruptable)));
208
259
  if(interrupted_by_signal and interruptable){
 
260
    if(debug){
 
261
      fprintf_plus(stderr, "Interrupted by signal\n");
 
262
    }
209
263
    return false;
210
264
  }
211
265
  if(ret == -1){
212
266
    error_plus(0, errno, "waitpid");
213
267
    return false;
214
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
  }
215
278
  if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
216
279
    return true;
217
280
  }
281
344
  }
282
345
  /* Try the old pid file location */
283
346
  if(proc_id == 0){
284
 
    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");
285
359
    if(pidfile != NULL){
286
360
      ret = fscanf(pidfile, "%" SCNuMAX, &proc_id);
287
361
      if(ret != 1){
298
372
      error_plus(0, errno, "scandir");
299
373
    }
300
374
    if(ret > 0){
301
 
      ret = sscanf(direntries[0]->d_name, "%" SCNuMAX, &proc_id);
302
 
      if(ret < 0){
303
 
        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]);
304
384
      }
305
385
    }
306
386
    /* scandir might preallocate for this variable (man page unclear).
316
396
  return 0;
317
397
}
318
398
 
319
 
const char * const * getargv(pid_t pid){
 
399
char **getargv(pid_t pid){
320
400
  int cl_fd;
321
401
  char *cmdline_filename;
322
402
  ssize_t sret;
383
463
    return NULL;
384
464
  }
385
465
  argz_extract(cmdline, cmdline_len, argv); /* Create argv */
386
 
  return (const char * const *)argv;
 
466
  return argv;
387
467
}
388
468
 
389
469
int main(__attribute__((unused))int argc,
390
470
         __attribute__((unused))char **argv){
391
 
  char *prompt;
 
471
  char *prompt = NULL;
392
472
  char *prompt_arg;
393
473
  pid_t plymouth_command_pid;
394
474
  int ret;
395
475
  bool bret;
396
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
  
397
525
  /* test -x /bin/plymouth */
398
526
  ret = access(plymouth_path, X_OK);
399
527
  if(ret == -1){
400
528
    /* Plymouth is probably not installed.  Don't print an error
401
529
       message, just exit. */
 
530
    if(debug){
 
531
      fprintf_plus(stderr, "Plymouth (%s) not found\n",
 
532
                   plymouth_path);
 
533
    }
402
534
    exit(EX_UNAVAILABLE);
403
535
  }
404
536
  
438
570
    }
439
571
    /* Plymouth is probably not running.  Don't print an error
440
572
       message, just exit. */
 
573
    if(debug){
 
574
      fprintf_plus(stderr, "Plymouth not running\n");
 
575
    }
441
576
    exit(EX_UNAVAILABLE);
442
577
  }
443
578
  
444
 
  prompt = makeprompt();
445
 
  ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
446
 
  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
  }
447
586
  if(ret == -1){
448
587
    error_plus(EX_OSERR, errno, "asprintf");
449
588
  }
450
589
  
451
590
  /* plymouth ask-for-password --prompt="$prompt" */
 
591
  if(debug){
 
592
    fprintf_plus(stderr, "Prompting for password via Plymouth\n");
 
593
  }
452
594
  bret = exec_and_wait(&plymouth_command_pid,
453
595
                       plymouth_path, (const char *[])
454
596
                       { plymouth_path, "ask-for-password",
464
606
  }
465
607
  kill_and_wait(plymouth_command_pid);
466
608
  
467
 
  const char * const *plymouthd_argv;
 
609
  char **plymouthd_argv = NULL;
468
610
  pid_t pid = get_pid();
469
611
  if(pid == 0){
470
612
    error_plus(0, 0, "plymouthd pid not found");
471
 
    plymouthd_argv = plymouthd_default_argv;
472
613
  } else {
473
614
    plymouthd_argv = getargv(pid);
474
615
  }
477
618
                       { plymouth_path, "quit", NULL },
478
619
                       false, false);
479
620
  if(not bret){
 
621
    if(plymouthd_argv != NULL){
 
622
      free(*plymouthd_argv);
 
623
      free(plymouthd_argv);
 
624
    }
480
625
    exit(EXIT_FAILURE);
481
626
  }
482
 
  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,
483
631
                       false, true);
 
632
  if(plymouthd_argv != NULL){
 
633
    free(*plymouthd_argv);
 
634
    free(plymouthd_argv);
 
635
  }
484
636
  if(not bret){
485
637
    exit(EXIT_FAILURE);
486
638
  }