/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/password-prompt.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
 * Password-prompt - Read a password from the terminal and print it
4
4
 * 
5
 
 * Copyright © 2008-2010 Teddy Hogeborn
6
 
 * Copyright © 2008-2010 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 © 2008-2019, 2021-2022 Teddy Hogeborn
 
6
 * Copyright © 2008-2019, 2021-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             /* getline(), asprintf() */
26
 
 
27
 
#include <termios.h>            /* struct termios, tcsetattr(),
28
 
                                   TCSAFLUSH, tcgetattr(), ECHO */
29
 
#include <unistd.h>             /* struct termios, tcsetattr(),
30
 
                                   STDIN_FILENO, TCSAFLUSH,
31
 
                                   tcgetattr(), ECHO, readlink() */
32
 
#include <signal.h>             /* sig_atomic_t, raise(), struct
33
 
                                   sigaction, sigemptyset(),
34
 
                                   sigaction(), sigaddset(), SIGINT,
35
 
                                   SIGQUIT, SIGHUP, SIGTERM,
 
26
#define _GNU_SOURCE             /* vasprintf(),
 
27
                                   program_invocation_short_name,
 
28
                                   asprintf(), getline() */
 
29
#include <sys/types.h>          /* sig_atomic_t, pid_t */
 
30
#include <stdbool.h>            /* bool, false, true */
 
31
#include <argp.h>               /* argp_program_version,
 
32
                                   argp_program_bug_address,
 
33
                                   struct argp_option,
 
34
                                   struct argp_state, argp_state_help,
 
35
                                   ARGP_HELP_STD_HELP,
 
36
                                   ARGP_HELP_EXIT_ERR,
 
37
                                   ARGP_HELP_EXIT_OK, ARGP_HELP_USAGE,
 
38
                                   argp_err_exit_status,
 
39
                                   ARGP_ERR_UNKNOWN, argp_parse(),
 
40
                                   ARGP_IN_ORDER, ARGP_NO_HELP */
 
41
#include <stdarg.h>             /* va_list, va_start(), vfprintf() */
 
42
#include <stdio.h>              /* vasprintf(), fprintf(), stderr,
 
43
                                   vfprintf(), asprintf(), getline(),
 
44
                                   stdin, feof(), clearerr(),
 
45
                                   fputc() */
 
46
#include <errno.h>              /* program_invocation_short_name,
 
47
                                   errno, ENOENT, error_t, ENOMEM,
 
48
                                   EINVAL, EBADF, ENOTTY, EFAULT,
 
49
                                   EFBIG, EIO, ENOSPC, EINTR */
 
50
#include <string.h>             /* strerror(), strrchr(), strcmp() */
 
51
#include <error.h>              /* error() */
 
52
#include <stdlib.h>             /* free(), realloc(), EXIT_SUCCESS,
 
53
                                   EXIT_FAILURE, getenv() */
 
54
#include <unistd.h>             /* access(), R_OK, ssize_t, close(),
 
55
                                   read(), STDIN_FILENO, write(),
 
56
                                   STDOUT_FILENO */
 
57
#include <dirent.h>             /* struct dirent, scandir(),
 
58
                                   alphasort() */
 
59
#include <inttypes.h>           /* uintmax_t, strtoumax() */
 
60
#include <iso646.h>             /* or, and, not */
 
61
#include <fcntl.h>              /* open(), O_RDONLY */
 
62
#include <stddef.h>             /* NULL, size_t */
 
63
#include <termios.h>            /* struct termios, tcgetattr(),
 
64
                                   tcflag_t, ECHO, tcsetattr(),
 
65
                                   TCSAFLUSH */
 
66
#include <signal.h>             /* struct sigaction, sigemptyset(),
 
67
                                   sigaddset(), SIGINT, SIGHUP,
 
68
                                   SIGTERM, SIG_IGN, SIG_DFL,
36
69
                                   raise() */
37
 
#include <stddef.h>             /* NULL, size_t, ssize_t */
38
 
#include <sys/types.h>          /* ssize_t, struct dirent, pid_t,
39
 
                                   ssize_t, open() */
40
 
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
41
 
                                   getenv(), free() */
42
 
#include <dirent.h>             /* scandir(), alphasort() */
43
 
#include <stdio.h>              /* fprintf(), stderr, getline(),
44
 
                                   stdin, feof(), fputc()
45
 
                                */
46
 
#include <errno.h>              /* errno, EBADF, ENOTTY, EINVAL,
47
 
                                   EFAULT, EFBIG, EIO, ENOSPC, EINTR
48
 
                                */
49
 
#include <error.h>              /* error() */
50
 
#include <iso646.h>             /* or, not */
51
 
#include <stdbool.h>            /* bool, false, true */
52
 
#include <inttypes.h>           /* strtoumax() */
53
 
#include <sys/stat.h>           /* struct stat, lstat(), open() */
54
 
#include <string.h>             /* strlen, rindex, memcmp */
55
 
#include <argp.h>               /* struct argp_option, struct
56
 
                                   argp_state, struct argp,
57
 
                                   argp_parse(), error_t,
58
 
                                   ARGP_KEY_ARG, ARGP_KEY_END,
59
 
                                   ARGP_ERR_UNKNOWN */
60
 
#include <sysexits.h>           /* EX_SOFTWARE, EX_OSERR,
61
 
                                   EX_UNAVAILABLE, EX_IOERR, EX_OK */
62
 
#include <fcntl.h>              /* open() */
 
70
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE,
 
71
                                   EX_IOERR, EX_OSFILE, EX_OK */
63
72
 
64
73
volatile sig_atomic_t quit_now = 0;
65
74
int signal_received;
66
75
bool debug = false;
67
76
const char *argp_program_version = "password-prompt " VERSION;
68
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
77
const char *argp_program_bug_address = "<mandos@recompile.se>";
69
78
 
70
79
/* Needed for conflict resolution */
71
80
const char plymouth_name[] = "plymouthd";
72
 
const char plymouth_alt_name[] = "plymouthd";
73
81
 
 
82
/* Function to use when printing errors */
 
83
__attribute__((format (gnu_printf, 3, 4)))
 
84
void error_plus(int status, int errnum, const char *formatstring,
 
85
                ...){
 
86
  va_list ap;
 
87
  char *text;
 
88
  int ret;
 
89
  
 
90
  va_start(ap, formatstring);
 
91
  ret = vasprintf(&text, formatstring, ap);
 
92
  if(ret == -1){
 
93
    fprintf(stderr, "Mandos plugin %s: ",
 
94
            program_invocation_short_name);
 
95
    vfprintf(stderr, formatstring, ap);
 
96
    fprintf(stderr, ": %s\n", strerror(errnum));
 
97
    error(status, errno, "vasprintf while printing error");
 
98
    return;
 
99
  }
 
100
  fprintf(stderr, "Mandos plugin ");
 
101
  error(status, errnum, "%s", text);
 
102
  free(text);
 
103
}
74
104
 
75
105
static void termination_handler(int signum){
76
106
  if(quit_now){
86
116
     from the terminal.  Password-prompt will exit if it detects
87
117
     plymouth since plymouth performs the same functionality.
88
118
   */
 
119
  if(access("/run/plymouth/pid", R_OK) == 0){
 
120
    return true;
 
121
  }
 
122
  
 
123
  __attribute__((nonnull))
89
124
  int is_plymouth(const struct dirent *proc_entry){
90
125
    int ret;
91
126
    int cl_fd;
92
127
    {
93
 
      uintmax_t maxvalue;
 
128
      uintmax_t proc_id;
94
129
      char *tmp;
95
130
      errno = 0;
96
 
      maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
131
      proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
97
132
      
98
133
      if(errno != 0 or *tmp != '\0'
99
 
         or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
134
         or proc_id != (uintmax_t)((pid_t)proc_id)){
100
135
        return 0;
101
136
      }
102
137
    }
103
138
    
104
139
    char *cmdline_filename;
105
 
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline", proc_entry->d_name);
 
140
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
 
141
                   proc_entry->d_name);
106
142
    if(ret == -1){
107
 
      error(0, errno, "asprintf");
 
143
      error_plus(0, errno, "asprintf");
108
144
      return 0;
109
145
    }
110
146
    
111
 
    /* Open /proc/<pid>/cmdline  */
 
147
    /* Open /proc/<pid>/cmdline */
112
148
    cl_fd = open(cmdline_filename, O_RDONLY);
113
149
    free(cmdline_filename);
114
150
    if(cl_fd == -1){
115
 
      error(0, errno, "open");
 
151
      if(errno != ENOENT){
 
152
        error_plus(0, errno, "open");
 
153
      }
116
154
      return 0;
117
155
    }
118
156
    
128
166
        if(cmdline_len + blocksize + 1 > cmdline_allocated){
129
167
          tmp = realloc(cmdline, cmdline_allocated + blocksize + 1);
130
168
          if(tmp == NULL){
131
 
            error(0, errno, "realloc");
 
169
            error_plus(0, errno, "realloc");
132
170
            free(cmdline);
133
171
            close(cl_fd);
134
172
            return 0;
141
179
        sret = read(cl_fd, cmdline + cmdline_len,
142
180
                    cmdline_allocated - cmdline_len);
143
181
        if(sret == -1){
144
 
          error(0, errno, "read");
 
182
          error_plus(0, errno, "read");
145
183
          free(cmdline);
146
184
          close(cl_fd);
147
185
          return 0;
150
188
      } while(sret != 0);
151
189
      ret = close(cl_fd);
152
190
      if(ret == -1){
153
 
        error(0, errno, "close");
 
191
        error_plus(0, errno, "close");
154
192
        free(cmdline);
155
193
        return 0;
156
194
      }
166
204
      cmdline_base = cmdline;
167
205
    }
168
206
    
169
 
    if((strcmp(cmdline_base, plymouth_name) != 0)
170
 
       and (strcmp(cmdline_base, plymouth_alt_name) != 0)){
 
207
    if(strcmp(cmdline_base, plymouth_name) != 0){
 
208
      if(debug){
 
209
        fprintf(stderr, "\"%s\" is not \"%s\"\n", cmdline_base,
 
210
                plymouth_name);
 
211
      }
171
212
      free(cmdline);
172
213
      return 0;
173
214
    }
 
215
    if(debug){
 
216
      fprintf(stderr, "\"%s\" equals \"%s\"\n", cmdline_base,
 
217
              plymouth_name);
 
218
    }
174
219
    free(cmdline);
175
220
    return 1;
176
221
  }
177
 
 
178
 
  struct dirent **direntries;
 
222
  
 
223
  struct dirent **direntries = NULL;
179
224
  int ret;
180
225
  ret = scandir("/proc", &direntries, is_plymouth, alphasort);
181
 
  if (ret == -1){
182
 
    error(1, errno, "scandir");
183
 
  }
 
226
  if(ret == -1){
 
227
    error_plus(1, errno, "scandir");
 
228
  }
 
229
  {
 
230
    int i = ret;
 
231
    while(i--){
 
232
      free(direntries[i]);
 
233
    }
 
234
  }
 
235
  free(direntries);
184
236
  return ret > 0;
185
237
}
186
238
 
192
244
  struct termios t_new, t_old;
193
245
  char *buffer = NULL;
194
246
  char *prefix = NULL;
 
247
  char *prompt = NULL;
195
248
  int status = EXIT_SUCCESS;
196
249
  struct sigaction old_action,
197
250
    new_action = { .sa_handler = termination_handler,
201
254
      { .name = "prefix", .key = 'p',
202
255
        .arg = "PREFIX", .flags = 0,
203
256
        .doc = "Prefix shown before the prompt", .group = 2 },
 
257
      { .name = "prompt", .key = 129,
 
258
        .arg = "PROMPT", .flags = 0,
 
259
        .doc = "The prompt to show", .group = 2 },
204
260
      { .name = "debug", .key = 128,
205
261
        .doc = "Debug mode", .group = 3 },
206
262
      /*
215
271
      { .name = NULL }
216
272
    };
217
273
    
 
274
    __attribute__((nonnull(3)))
218
275
    error_t parse_opt (int key, char *arg, struct argp_state *state){
219
276
      errno = 0;
220
277
      switch (key){
221
 
      case 'p':
 
278
      case 'p':                 /* --prefix */
222
279
        prefix = arg;
223
280
        break;
224
 
      case 128:
 
281
      case 128:                 /* --debug */
225
282
        debug = true;
226
283
        break;
 
284
      case 129:                 /* --prompt */
 
285
        prompt = arg;
 
286
        break;
227
287
        /*
228
288
         * These reproduce what we would get without ARGP_NO_HELP
229
289
         */
231
291
        argp_state_help(state, state->out_stream,
232
292
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
233
293
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
294
        __builtin_unreachable();
234
295
      case -3:                  /* --usage */
235
296
        argp_state_help(state, state->out_stream,
236
297
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
298
        __builtin_unreachable();
237
299
      case 'V':                 /* --version */
238
300
        fprintf(state->out_stream, "%s\n", argp_program_version);
239
301
        exit(argp_err_exit_status);
256
318
    case ENOMEM:
257
319
    default:
258
320
      errno = ret;
259
 
      error(0, errno, "argp_parse");
 
321
      error_plus(0, errno, "argp_parse");
260
322
      return EX_OSERR;
261
323
    case EINVAL:
262
324
      return EX_USAGE;
267
329
    fprintf(stderr, "Starting %s\n", argv[0]);
268
330
  }
269
331
 
270
 
  if (conflict_detection()){
 
332
  if(conflict_detection()){
271
333
    if(debug){
272
 
      fprintf(stderr, "Stopping %s because of conflict", argv[0]);
 
334
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
273
335
    }
274
336
    return EXIT_FAILURE;
275
337
  }
280
342
  
281
343
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
282
344
    int e = errno;
283
 
    error(0, errno, "tcgetattr");
 
345
    error_plus(0, errno, "tcgetattr");
284
346
    switch(e){
285
347
    case EBADF:
286
348
    case ENOTTY:
293
355
  sigemptyset(&new_action.sa_mask);
294
356
  ret = sigaddset(&new_action.sa_mask, SIGINT);
295
357
  if(ret == -1){
296
 
    error(0, errno, "sigaddset");
 
358
    error_plus(0, errno, "sigaddset");
297
359
    return EX_OSERR;
298
360
  }
299
361
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
300
362
  if(ret == -1){
301
 
    error(0, errno, "sigaddset");
 
363
    error_plus(0, errno, "sigaddset");
302
364
    return EX_OSERR;
303
365
  }
304
366
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
305
367
  if(ret == -1){
306
 
    error(0, errno, "sigaddset");
 
368
    error_plus(0, errno, "sigaddset");
307
369
    return EX_OSERR;
308
370
  }
309
371
  /* Need to check if the handler is SIG_IGN before handling:
312
374
  */
313
375
  ret = sigaction(SIGINT, NULL, &old_action);
314
376
  if(ret == -1){
315
 
    error(0, errno, "sigaction");
 
377
    error_plus(0, errno, "sigaction");
316
378
    return EX_OSERR;
317
379
  }
318
380
  if(old_action.sa_handler != SIG_IGN){
319
381
    ret = sigaction(SIGINT, &new_action, NULL);
320
382
    if(ret == -1){
321
 
      error(0, errno, "sigaction");
 
383
      error_plus(0, errno, "sigaction");
322
384
      return EX_OSERR;
323
385
    }
324
386
  }
325
387
  ret = sigaction(SIGHUP, NULL, &old_action);
326
388
  if(ret == -1){
327
 
    error(0, errno, "sigaction");
 
389
    error_plus(0, errno, "sigaction");
328
390
    return EX_OSERR;
329
391
  }
330
392
  if(old_action.sa_handler != SIG_IGN){
331
393
    ret = sigaction(SIGHUP, &new_action, NULL);
332
394
    if(ret == -1){
333
 
      error(0, errno, "sigaction");
 
395
      error_plus(0, errno, "sigaction");
334
396
      return EX_OSERR;
335
397
    }
336
398
  }
337
399
  ret = sigaction(SIGTERM, NULL, &old_action);
338
400
  if(ret == -1){
339
 
    error(0, errno, "sigaction");
 
401
    error_plus(0, errno, "sigaction");
340
402
    return EX_OSERR;
341
403
  }
342
404
  if(old_action.sa_handler != SIG_IGN){
343
405
    ret = sigaction(SIGTERM, &new_action, NULL);
344
406
    if(ret == -1){
345
 
      error(0, errno, "sigaction");
 
407
      error_plus(0, errno, "sigaction");
346
408
      return EX_OSERR;
347
409
    }
348
410
  }
356
418
  t_new.c_lflag &= ~(tcflag_t)ECHO;
357
419
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
358
420
    int e = errno;
359
 
    error(0, errno, "tcsetattr-echo");
 
421
    error_plus(0, errno, "tcsetattr-echo");
360
422
    switch(e){
361
423
    case EBADF:
362
424
    case ENOTTY:
382
444
    if(prefix){
383
445
      fprintf(stderr, "%s ", prefix);
384
446
    }
385
 
    {
 
447
    if(prompt != NULL){
 
448
      fprintf(stderr, "%s: ", prompt);
 
449
    } else {
386
450
      const char *cryptsource = getenv("CRYPTTAB_SOURCE");
387
451
      const char *crypttarget = getenv("CRYPTTAB_NAME");
388
452
      /* Before cryptsetup 1.1.0~rc2 */
426
490
        sret = write(STDOUT_FILENO, buffer + written, n - written);
427
491
        if(sret < 0){
428
492
          int e = errno;
429
 
          error(0, errno, "write");
 
493
          error_plus(0, errno, "write");
430
494
          switch(e){
431
495
          case EBADF:
432
496
          case EFAULT:
448
512
      sret = close(STDOUT_FILENO);
449
513
      if(sret == -1){
450
514
        int e = errno;
451
 
        error(0, errno, "close");
 
515
        error_plus(0, errno, "close");
452
516
        switch(e){
453
517
        case EBADF:
454
518
          status = EX_OSFILE;
463
527
    }
464
528
    if(sret < 0){
465
529
      int e = errno;
466
 
      if(errno != EINTR and not feof(stdin)){
467
 
        error(0, errno, "getline");
468
 
        switch(e){
469
 
        case EBADF:
470
 
          status = EX_UNAVAILABLE;
471
 
        case EIO:
472
 
        case EINVAL:
473
 
        default:
474
 
          status = EX_IOERR;
 
530
      if(errno != EINTR){
 
531
        if(not feof(stdin)){
 
532
          error_plus(0, errno, "getline");
 
533
          switch(e){
 
534
          case EBADF:
 
535
            status = EX_UNAVAILABLE;
 
536
            break;
 
537
          case EIO:
 
538
          case EINVAL:
 
539
          default:
 
540
            status = EX_IOERR;
 
541
            break;
 
542
          }
475
543
          break;
 
544
        } else {
 
545
          clearerr(stdin);
476
546
        }
477
 
        break;
478
547
      }
479
548
    }
480
 
    /* if(sret == 0), then the only sensible thing to do is to retry to
481
 
       read from stdin */
 
549
    /* if(sret == 0), then the only sensible thing to do is to retry
 
550
       to read from stdin */
482
551
    fputc('\n', stderr);
483
552
    if(debug and not quit_now){
484
553
      /* If quit_now is nonzero, we were interrupted by a signal, and
493
562
    fprintf(stderr, "Restoring terminal attributes\n");
494
563
  }
495
564
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
496
 
    error(0, errno, "tcsetattr+echo");
 
565
    error_plus(0, errno, "tcsetattr+echo");
497
566
  }
498
567
  
499
568
  if(quit_now){
501
570
    old_action.sa_handler = SIG_DFL;
502
571
    ret = sigaction(signal_received, &old_action, NULL);
503
572
    if(ret == -1){
504
 
      error(0, errno, "sigaction");
 
573
      error_plus(0, errno, "sigaction");
505
574
    }
506
575
    raise(signal_received);
507
576
  }