/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: 2019-07-29 16:35:53 UTC
  • Revision ID: teddy@recompile.se-20190729163553-1i442i2cbx64c537
Make tests and man page examples match

Make the tests test_manual_page_example[1-5] match exactly what is
written in the manual page, and add comments to manual page as
reminders to keep tests and manual page examples in sync.

* mandos-ctl (Test_commands_from_options.test_manual_page_example_1):
  Remove "--verbose" option, since the manual does not have it as the
  first example, and change assertion to match.
* mandos-ctl.xml (EXAMPLE): Add comments to all examples documenting
  which test function they correspond to.  Also remove unnecessary
  quotes from option arguments in fourth example, and clarify language
  slightly in fifth example.

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 Teddy Hogeborn
 
6
 * Copyright © 2008-2019 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
26
#define _GNU_SOURCE             /* getline(), asprintf() */
26
27
 
27
28
#include <termios.h>            /* struct termios, tcsetattr(),
28
29
                                   TCSAFLUSH, tcgetattr(), ECHO */
29
 
#include <unistd.h>             /* struct termios, tcsetattr(),
30
 
                                   STDIN_FILENO, TCSAFLUSH,
31
 
                                   tcgetattr(), ECHO, readlink() */
 
30
#include <unistd.h>             /* access(), struct termios,
 
31
                                   tcsetattr(), STDIN_FILENO,
 
32
                                   TCSAFLUSH, tcgetattr(), ECHO,
 
33
                                   readlink() */
32
34
#include <signal.h>             /* sig_atomic_t, raise(), struct
33
35
                                   sigaction, sigemptyset(),
34
36
                                   sigaction(), sigaddset(), SIGINT,
41
43
                                   getenv(), free() */
42
44
#include <dirent.h>             /* scandir(), alphasort() */
43
45
#include <stdio.h>              /* fprintf(), stderr, getline(),
44
 
                                   stdin, feof(), fputc()
45
 
                                */
 
46
                                   stdin, feof(), fputc(), vfprintf(),
 
47
                                   vasprintf() */
46
48
#include <errno.h>              /* errno, EBADF, ENOTTY, EINVAL,
47
49
                                   EFAULT, EFBIG, EIO, ENOSPC, EINTR
48
50
                                */
51
53
#include <stdbool.h>            /* bool, false, true */
52
54
#include <inttypes.h>           /* strtoumax() */
53
55
#include <sys/stat.h>           /* struct stat, lstat(), open() */
54
 
#include <string.h>             /* strlen, rindex, memcmp */
 
56
#include <string.h>             /* strlen, rindex, memcmp, strerror()
 
57
                                 */
55
58
#include <argp.h>               /* struct argp_option, struct
56
59
                                   argp_state, struct argp,
57
60
                                   argp_parse(), error_t,
60
63
#include <sysexits.h>           /* EX_SOFTWARE, EX_OSERR,
61
64
                                   EX_UNAVAILABLE, EX_IOERR, EX_OK */
62
65
#include <fcntl.h>              /* open() */
 
66
#include <stdarg.h>             /* va_list, va_start(), ... */
63
67
 
64
68
volatile sig_atomic_t quit_now = 0;
65
69
int signal_received;
66
70
bool debug = false;
67
71
const char *argp_program_version = "password-prompt " VERSION;
68
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
72
const char *argp_program_bug_address = "<mandos@recompile.se>";
69
73
 
70
74
/* Needed for conflict resolution */
71
75
const char plymouth_name[] = "plymouthd";
72
76
 
 
77
/* Function to use when printing errors */
 
78
__attribute__((format (gnu_printf, 3, 4)))
 
79
void error_plus(int status, int errnum, const char *formatstring,
 
80
                ...){
 
81
  va_list ap;
 
82
  char *text;
 
83
  int ret;
 
84
  
 
85
  va_start(ap, formatstring);
 
86
  ret = vasprintf(&text, formatstring, ap);
 
87
  if(ret == -1){
 
88
    fprintf(stderr, "Mandos plugin %s: ",
 
89
            program_invocation_short_name);
 
90
    vfprintf(stderr, formatstring, ap);
 
91
    fprintf(stderr, ": %s\n", strerror(errnum));
 
92
    error(status, errno, "vasprintf while printing error");
 
93
    return;
 
94
  }
 
95
  fprintf(stderr, "Mandos plugin ");
 
96
  error(status, errnum, "%s", text);
 
97
  free(text);
 
98
}
 
99
 
73
100
static void termination_handler(int signum){
74
101
  if(quit_now){
75
102
    return;
84
111
     from the terminal.  Password-prompt will exit if it detects
85
112
     plymouth since plymouth performs the same functionality.
86
113
   */
 
114
  if(access("/run/plymouth/pid", R_OK) == 0){
 
115
    return true;
 
116
  }
 
117
  
 
118
  __attribute__((nonnull))
87
119
  int is_plymouth(const struct dirent *proc_entry){
88
120
    int ret;
89
121
    int cl_fd;
90
122
    {
91
 
      uintmax_t maxvalue;
 
123
      uintmax_t proc_id;
92
124
      char *tmp;
93
125
      errno = 0;
94
 
      maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
126
      proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
95
127
      
96
128
      if(errno != 0 or *tmp != '\0'
97
 
         or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
129
         or proc_id != (uintmax_t)((pid_t)proc_id)){
98
130
        return 0;
99
131
      }
100
132
    }
103
135
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
104
136
                   proc_entry->d_name);
105
137
    if(ret == -1){
106
 
      error(0, errno, "asprintf");
 
138
      error_plus(0, errno, "asprintf");
107
139
      return 0;
108
140
    }
109
141
    
111
143
    cl_fd = open(cmdline_filename, O_RDONLY);
112
144
    free(cmdline_filename);
113
145
    if(cl_fd == -1){
114
 
      error(0, errno, "open");
 
146
      if(errno != ENOENT){
 
147
        error_plus(0, errno, "open");
 
148
      }
115
149
      return 0;
116
150
    }
117
151
    
127
161
        if(cmdline_len + blocksize + 1 > cmdline_allocated){
128
162
          tmp = realloc(cmdline, cmdline_allocated + blocksize + 1);
129
163
          if(tmp == NULL){
130
 
            error(0, errno, "realloc");
 
164
            error_plus(0, errno, "realloc");
131
165
            free(cmdline);
132
166
            close(cl_fd);
133
167
            return 0;
140
174
        sret = read(cl_fd, cmdline + cmdline_len,
141
175
                    cmdline_allocated - cmdline_len);
142
176
        if(sret == -1){
143
 
          error(0, errno, "read");
 
177
          error_plus(0, errno, "read");
144
178
          free(cmdline);
145
179
          close(cl_fd);
146
180
          return 0;
149
183
      } while(sret != 0);
150
184
      ret = close(cl_fd);
151
185
      if(ret == -1){
152
 
        error(0, errno, "close");
 
186
        error_plus(0, errno, "close");
153
187
        free(cmdline);
154
188
        return 0;
155
189
      }
181
215
    return 1;
182
216
  }
183
217
  
184
 
  struct dirent **direntries;
 
218
  struct dirent **direntries = NULL;
185
219
  int ret;
186
220
  ret = scandir("/proc", &direntries, is_plymouth, alphasort);
187
 
  if (ret == -1){
188
 
    error(1, errno, "scandir");
189
 
  }
 
221
  if(ret == -1){
 
222
    error_plus(1, errno, "scandir");
 
223
  }
 
224
  {
 
225
    int i = ret;
 
226
    while(i--){
 
227
      free(direntries[i]);
 
228
    }
 
229
  }
 
230
  free(direntries);
190
231
  return ret > 0;
191
232
}
192
233
 
198
239
  struct termios t_new, t_old;
199
240
  char *buffer = NULL;
200
241
  char *prefix = NULL;
 
242
  char *prompt = NULL;
201
243
  int status = EXIT_SUCCESS;
202
244
  struct sigaction old_action,
203
245
    new_action = { .sa_handler = termination_handler,
207
249
      { .name = "prefix", .key = 'p',
208
250
        .arg = "PREFIX", .flags = 0,
209
251
        .doc = "Prefix shown before the prompt", .group = 2 },
 
252
      { .name = "prompt", .key = 129,
 
253
        .arg = "PROMPT", .flags = 0,
 
254
        .doc = "The prompt to show", .group = 2 },
210
255
      { .name = "debug", .key = 128,
211
256
        .doc = "Debug mode", .group = 3 },
212
257
      /*
221
266
      { .name = NULL }
222
267
    };
223
268
    
 
269
    __attribute__((nonnull(3)))
224
270
    error_t parse_opt (int key, char *arg, struct argp_state *state){
225
271
      errno = 0;
226
272
      switch (key){
227
 
      case 'p':
 
273
      case 'p':                 /* --prefix */
228
274
        prefix = arg;
229
275
        break;
230
 
      case 128:
 
276
      case 128:                 /* --debug */
231
277
        debug = true;
232
278
        break;
 
279
      case 129:                 /* --prompt */
 
280
        prompt = arg;
 
281
        break;
233
282
        /*
234
283
         * These reproduce what we would get without ARGP_NO_HELP
235
284
         */
237
286
        argp_state_help(state, state->out_stream,
238
287
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
239
288
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
289
        __builtin_unreachable();
240
290
      case -3:                  /* --usage */
241
291
        argp_state_help(state, state->out_stream,
242
292
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
293
        __builtin_unreachable();
243
294
      case 'V':                 /* --version */
244
295
        fprintf(state->out_stream, "%s\n", argp_program_version);
245
296
        exit(argp_err_exit_status);
262
313
    case ENOMEM:
263
314
    default:
264
315
      errno = ret;
265
 
      error(0, errno, "argp_parse");
 
316
      error_plus(0, errno, "argp_parse");
266
317
      return EX_OSERR;
267
318
    case EINVAL:
268
319
      return EX_USAGE;
273
324
    fprintf(stderr, "Starting %s\n", argv[0]);
274
325
  }
275
326
 
276
 
  if (conflict_detection()){
 
327
  if(conflict_detection()){
277
328
    if(debug){
278
329
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
279
330
    }
286
337
  
287
338
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
288
339
    int e = errno;
289
 
    error(0, errno, "tcgetattr");
 
340
    error_plus(0, errno, "tcgetattr");
290
341
    switch(e){
291
342
    case EBADF:
292
343
    case ENOTTY:
299
350
  sigemptyset(&new_action.sa_mask);
300
351
  ret = sigaddset(&new_action.sa_mask, SIGINT);
301
352
  if(ret == -1){
302
 
    error(0, errno, "sigaddset");
 
353
    error_plus(0, errno, "sigaddset");
303
354
    return EX_OSERR;
304
355
  }
305
356
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
306
357
  if(ret == -1){
307
 
    error(0, errno, "sigaddset");
 
358
    error_plus(0, errno, "sigaddset");
308
359
    return EX_OSERR;
309
360
  }
310
361
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
311
362
  if(ret == -1){
312
 
    error(0, errno, "sigaddset");
 
363
    error_plus(0, errno, "sigaddset");
313
364
    return EX_OSERR;
314
365
  }
315
366
  /* Need to check if the handler is SIG_IGN before handling:
318
369
  */
319
370
  ret = sigaction(SIGINT, NULL, &old_action);
320
371
  if(ret == -1){
321
 
    error(0, errno, "sigaction");
 
372
    error_plus(0, errno, "sigaction");
322
373
    return EX_OSERR;
323
374
  }
324
375
  if(old_action.sa_handler != SIG_IGN){
325
376
    ret = sigaction(SIGINT, &new_action, NULL);
326
377
    if(ret == -1){
327
 
      error(0, errno, "sigaction");
 
378
      error_plus(0, errno, "sigaction");
328
379
      return EX_OSERR;
329
380
    }
330
381
  }
331
382
  ret = sigaction(SIGHUP, NULL, &old_action);
332
383
  if(ret == -1){
333
 
    error(0, errno, "sigaction");
 
384
    error_plus(0, errno, "sigaction");
334
385
    return EX_OSERR;
335
386
  }
336
387
  if(old_action.sa_handler != SIG_IGN){
337
388
    ret = sigaction(SIGHUP, &new_action, NULL);
338
389
    if(ret == -1){
339
 
      error(0, errno, "sigaction");
 
390
      error_plus(0, errno, "sigaction");
340
391
      return EX_OSERR;
341
392
    }
342
393
  }
343
394
  ret = sigaction(SIGTERM, NULL, &old_action);
344
395
  if(ret == -1){
345
 
    error(0, errno, "sigaction");
 
396
    error_plus(0, errno, "sigaction");
346
397
    return EX_OSERR;
347
398
  }
348
399
  if(old_action.sa_handler != SIG_IGN){
349
400
    ret = sigaction(SIGTERM, &new_action, NULL);
350
401
    if(ret == -1){
351
 
      error(0, errno, "sigaction");
 
402
      error_plus(0, errno, "sigaction");
352
403
      return EX_OSERR;
353
404
    }
354
405
  }
362
413
  t_new.c_lflag &= ~(tcflag_t)ECHO;
363
414
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
364
415
    int e = errno;
365
 
    error(0, errno, "tcsetattr-echo");
 
416
    error_plus(0, errno, "tcsetattr-echo");
366
417
    switch(e){
367
418
    case EBADF:
368
419
    case ENOTTY:
388
439
    if(prefix){
389
440
      fprintf(stderr, "%s ", prefix);
390
441
    }
391
 
    {
 
442
    if(prompt != NULL){
 
443
      fprintf(stderr, "%s: ", prompt);
 
444
    } else {
392
445
      const char *cryptsource = getenv("CRYPTTAB_SOURCE");
393
446
      const char *crypttarget = getenv("CRYPTTAB_NAME");
394
447
      /* Before cryptsetup 1.1.0~rc2 */
432
485
        sret = write(STDOUT_FILENO, buffer + written, n - written);
433
486
        if(sret < 0){
434
487
          int e = errno;
435
 
          error(0, errno, "write");
 
488
          error_plus(0, errno, "write");
436
489
          switch(e){
437
490
          case EBADF:
438
491
          case EFAULT:
454
507
      sret = close(STDOUT_FILENO);
455
508
      if(sret == -1){
456
509
        int e = errno;
457
 
        error(0, errno, "close");
 
510
        error_plus(0, errno, "close");
458
511
        switch(e){
459
512
        case EBADF:
460
513
          status = EX_OSFILE;
469
522
    }
470
523
    if(sret < 0){
471
524
      int e = errno;
472
 
      if(errno != EINTR and not feof(stdin)){
473
 
        error(0, errno, "getline");
474
 
        switch(e){
475
 
        case EBADF:
476
 
          status = EX_UNAVAILABLE;
477
 
        case EIO:
478
 
        case EINVAL:
479
 
        default:
480
 
          status = EX_IOERR;
 
525
      if(errno != EINTR){
 
526
        if(not feof(stdin)){
 
527
          error_plus(0, errno, "getline");
 
528
          switch(e){
 
529
          case EBADF:
 
530
            status = EX_UNAVAILABLE;
 
531
            break;
 
532
          case EIO:
 
533
          case EINVAL:
 
534
          default:
 
535
            status = EX_IOERR;
 
536
            break;
 
537
          }
481
538
          break;
 
539
        } else {
 
540
          clearerr(stdin);
482
541
        }
483
 
        break;
484
542
      }
485
543
    }
486
544
    /* if(sret == 0), then the only sensible thing to do is to retry
499
557
    fprintf(stderr, "Restoring terminal attributes\n");
500
558
  }
501
559
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
502
 
    error(0, errno, "tcsetattr+echo");
 
560
    error_plus(0, errno, "tcsetattr+echo");
503
561
  }
504
562
  
505
563
  if(quit_now){
507
565
    old_action.sa_handler = SIG_DFL;
508
566
    ret = sigaction(signal_received, &old_action, NULL);
509
567
    if(ret == -1){
510
 
      error(0, errno, "sigaction");
 
568
      error_plus(0, errno, "sigaction");
511
569
    }
512
570
    raise(signal_received);
513
571
  }