/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/password-prompt.c

  • Committer: Teddy Hogeborn
  • Date: 2019-07-29 16:35:53 UTC
  • mto: This revision was merged to the branch mainline in revision 384.
  • 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
 
const char plymouth_alt_name[] = "plymouthd";
73
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
}
74
99
 
75
100
static void termination_handler(int signum){
76
101
  if(quit_now){
86
111
     from the terminal.  Password-prompt will exit if it detects
87
112
     plymouth since plymouth performs the same functionality.
88
113
   */
 
114
  if(access("/run/plymouth/pid", R_OK) == 0){
 
115
    return true;
 
116
  }
 
117
  
 
118
  __attribute__((nonnull))
89
119
  int is_plymouth(const struct dirent *proc_entry){
90
120
    int ret;
91
121
    int cl_fd;
92
122
    {
93
 
      uintmax_t maxvalue;
 
123
      uintmax_t proc_id;
94
124
      char *tmp;
95
125
      errno = 0;
96
 
      maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
126
      proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
97
127
      
98
128
      if(errno != 0 or *tmp != '\0'
99
 
         or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
129
         or proc_id != (uintmax_t)((pid_t)proc_id)){
100
130
        return 0;
101
131
      }
102
132
    }
105
135
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
106
136
                   proc_entry->d_name);
107
137
    if(ret == -1){
108
 
      error(0, errno, "asprintf");
 
138
      error_plus(0, errno, "asprintf");
109
139
      return 0;
110
140
    }
111
141
    
113
143
    cl_fd = open(cmdline_filename, O_RDONLY);
114
144
    free(cmdline_filename);
115
145
    if(cl_fd == -1){
116
 
      error(0, errno, "open");
 
146
      if(errno != ENOENT){
 
147
        error_plus(0, errno, "open");
 
148
      }
117
149
      return 0;
118
150
    }
119
151
    
129
161
        if(cmdline_len + blocksize + 1 > cmdline_allocated){
130
162
          tmp = realloc(cmdline, cmdline_allocated + blocksize + 1);
131
163
          if(tmp == NULL){
132
 
            error(0, errno, "realloc");
 
164
            error_plus(0, errno, "realloc");
133
165
            free(cmdline);
134
166
            close(cl_fd);
135
167
            return 0;
142
174
        sret = read(cl_fd, cmdline + cmdline_len,
143
175
                    cmdline_allocated - cmdline_len);
144
176
        if(sret == -1){
145
 
          error(0, errno, "read");
 
177
          error_plus(0, errno, "read");
146
178
          free(cmdline);
147
179
          close(cl_fd);
148
180
          return 0;
151
183
      } while(sret != 0);
152
184
      ret = close(cl_fd);
153
185
      if(ret == -1){
154
 
        error(0, errno, "close");
 
186
        error_plus(0, errno, "close");
155
187
        free(cmdline);
156
188
        return 0;
157
189
      }
167
199
      cmdline_base = cmdline;
168
200
    }
169
201
    
170
 
    if((strcmp(cmdline_base, plymouth_name) != 0)
171
 
       and (strcmp(cmdline_base, plymouth_alt_name) != 0)){
 
202
    if(strcmp(cmdline_base, plymouth_name) != 0){
172
203
      if(debug){
173
 
        fprintf(stderr, "\"%s\" is not \"%s\" or \"%s\"\n",
174
 
                cmdline_base, plymouth_name, plymouth_alt_name);
 
204
        fprintf(stderr, "\"%s\" is not \"%s\"\n", cmdline_base,
 
205
                plymouth_name);
175
206
      }
176
207
      free(cmdline);
177
208
      return 0;
178
209
    }
179
 
    fprintf(stderr, "\"%s\" equals \"%s\" or \"%s\"\n",
180
 
            cmdline_base, plymouth_name, plymouth_alt_name);
 
210
    if(debug){
 
211
      fprintf(stderr, "\"%s\" equals \"%s\"\n", cmdline_base,
 
212
              plymouth_name);
 
213
    }
181
214
    free(cmdline);
182
215
    return 1;
183
216
  }
184
217
  
185
 
  struct dirent **direntries;
 
218
  struct dirent **direntries = NULL;
186
219
  int ret;
187
220
  ret = scandir("/proc", &direntries, is_plymouth, alphasort);
188
 
  if (ret == -1){
189
 
    error(1, errno, "scandir");
190
 
  }
 
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);
191
231
  return ret > 0;
192
232
}
193
233
 
199
239
  struct termios t_new, t_old;
200
240
  char *buffer = NULL;
201
241
  char *prefix = NULL;
 
242
  char *prompt = NULL;
202
243
  int status = EXIT_SUCCESS;
203
244
  struct sigaction old_action,
204
245
    new_action = { .sa_handler = termination_handler,
208
249
      { .name = "prefix", .key = 'p',
209
250
        .arg = "PREFIX", .flags = 0,
210
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 },
211
255
      { .name = "debug", .key = 128,
212
256
        .doc = "Debug mode", .group = 3 },
213
257
      /*
222
266
      { .name = NULL }
223
267
    };
224
268
    
 
269
    __attribute__((nonnull(3)))
225
270
    error_t parse_opt (int key, char *arg, struct argp_state *state){
226
271
      errno = 0;
227
272
      switch (key){
228
 
      case 'p':
 
273
      case 'p':                 /* --prefix */
229
274
        prefix = arg;
230
275
        break;
231
 
      case 128:
 
276
      case 128:                 /* --debug */
232
277
        debug = true;
233
278
        break;
 
279
      case 129:                 /* --prompt */
 
280
        prompt = arg;
 
281
        break;
234
282
        /*
235
283
         * These reproduce what we would get without ARGP_NO_HELP
236
284
         */
238
286
        argp_state_help(state, state->out_stream,
239
287
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
240
288
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
289
        __builtin_unreachable();
241
290
      case -3:                  /* --usage */
242
291
        argp_state_help(state, state->out_stream,
243
292
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
293
        __builtin_unreachable();
244
294
      case 'V':                 /* --version */
245
295
        fprintf(state->out_stream, "%s\n", argp_program_version);
246
296
        exit(argp_err_exit_status);
263
313
    case ENOMEM:
264
314
    default:
265
315
      errno = ret;
266
 
      error(0, errno, "argp_parse");
 
316
      error_plus(0, errno, "argp_parse");
267
317
      return EX_OSERR;
268
318
    case EINVAL:
269
319
      return EX_USAGE;
274
324
    fprintf(stderr, "Starting %s\n", argv[0]);
275
325
  }
276
326
 
277
 
  if (conflict_detection()){
 
327
  if(conflict_detection()){
278
328
    if(debug){
279
329
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
280
330
    }
287
337
  
288
338
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
289
339
    int e = errno;
290
 
    error(0, errno, "tcgetattr");
 
340
    error_plus(0, errno, "tcgetattr");
291
341
    switch(e){
292
342
    case EBADF:
293
343
    case ENOTTY:
300
350
  sigemptyset(&new_action.sa_mask);
301
351
  ret = sigaddset(&new_action.sa_mask, SIGINT);
302
352
  if(ret == -1){
303
 
    error(0, errno, "sigaddset");
 
353
    error_plus(0, errno, "sigaddset");
304
354
    return EX_OSERR;
305
355
  }
306
356
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
307
357
  if(ret == -1){
308
 
    error(0, errno, "sigaddset");
 
358
    error_plus(0, errno, "sigaddset");
309
359
    return EX_OSERR;
310
360
  }
311
361
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
312
362
  if(ret == -1){
313
 
    error(0, errno, "sigaddset");
 
363
    error_plus(0, errno, "sigaddset");
314
364
    return EX_OSERR;
315
365
  }
316
366
  /* Need to check if the handler is SIG_IGN before handling:
319
369
  */
320
370
  ret = sigaction(SIGINT, NULL, &old_action);
321
371
  if(ret == -1){
322
 
    error(0, errno, "sigaction");
 
372
    error_plus(0, errno, "sigaction");
323
373
    return EX_OSERR;
324
374
  }
325
375
  if(old_action.sa_handler != SIG_IGN){
326
376
    ret = sigaction(SIGINT, &new_action, NULL);
327
377
    if(ret == -1){
328
 
      error(0, errno, "sigaction");
 
378
      error_plus(0, errno, "sigaction");
329
379
      return EX_OSERR;
330
380
    }
331
381
  }
332
382
  ret = sigaction(SIGHUP, NULL, &old_action);
333
383
  if(ret == -1){
334
 
    error(0, errno, "sigaction");
 
384
    error_plus(0, errno, "sigaction");
335
385
    return EX_OSERR;
336
386
  }
337
387
  if(old_action.sa_handler != SIG_IGN){
338
388
    ret = sigaction(SIGHUP, &new_action, NULL);
339
389
    if(ret == -1){
340
 
      error(0, errno, "sigaction");
 
390
      error_plus(0, errno, "sigaction");
341
391
      return EX_OSERR;
342
392
    }
343
393
  }
344
394
  ret = sigaction(SIGTERM, NULL, &old_action);
345
395
  if(ret == -1){
346
 
    error(0, errno, "sigaction");
 
396
    error_plus(0, errno, "sigaction");
347
397
    return EX_OSERR;
348
398
  }
349
399
  if(old_action.sa_handler != SIG_IGN){
350
400
    ret = sigaction(SIGTERM, &new_action, NULL);
351
401
    if(ret == -1){
352
 
      error(0, errno, "sigaction");
 
402
      error_plus(0, errno, "sigaction");
353
403
      return EX_OSERR;
354
404
    }
355
405
  }
363
413
  t_new.c_lflag &= ~(tcflag_t)ECHO;
364
414
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
365
415
    int e = errno;
366
 
    error(0, errno, "tcsetattr-echo");
 
416
    error_plus(0, errno, "tcsetattr-echo");
367
417
    switch(e){
368
418
    case EBADF:
369
419
    case ENOTTY:
389
439
    if(prefix){
390
440
      fprintf(stderr, "%s ", prefix);
391
441
    }
392
 
    {
 
442
    if(prompt != NULL){
 
443
      fprintf(stderr, "%s: ", prompt);
 
444
    } else {
393
445
      const char *cryptsource = getenv("CRYPTTAB_SOURCE");
394
446
      const char *crypttarget = getenv("CRYPTTAB_NAME");
395
447
      /* Before cryptsetup 1.1.0~rc2 */
433
485
        sret = write(STDOUT_FILENO, buffer + written, n - written);
434
486
        if(sret < 0){
435
487
          int e = errno;
436
 
          error(0, errno, "write");
 
488
          error_plus(0, errno, "write");
437
489
          switch(e){
438
490
          case EBADF:
439
491
          case EFAULT:
455
507
      sret = close(STDOUT_FILENO);
456
508
      if(sret == -1){
457
509
        int e = errno;
458
 
        error(0, errno, "close");
 
510
        error_plus(0, errno, "close");
459
511
        switch(e){
460
512
        case EBADF:
461
513
          status = EX_OSFILE;
470
522
    }
471
523
    if(sret < 0){
472
524
      int e = errno;
473
 
      if(errno != EINTR and not feof(stdin)){
474
 
        error(0, errno, "getline");
475
 
        switch(e){
476
 
        case EBADF:
477
 
          status = EX_UNAVAILABLE;
478
 
        case EIO:
479
 
        case EINVAL:
480
 
        default:
481
 
          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
          }
482
538
          break;
 
539
        } else {
 
540
          clearerr(stdin);
483
541
        }
484
 
        break;
485
542
      }
486
543
    }
487
544
    /* if(sret == 0), then the only sensible thing to do is to retry
500
557
    fprintf(stderr, "Restoring terminal attributes\n");
501
558
  }
502
559
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
503
 
    error(0, errno, "tcsetattr+echo");
 
560
    error_plus(0, errno, "tcsetattr+echo");
504
561
  }
505
562
  
506
563
  if(quit_now){
508
565
    old_action.sa_handler = SIG_DFL;
509
566
    ret = sigaction(signal_received, &old_action, NULL);
510
567
    if(ret == -1){
511
 
      error(0, errno, "sigaction");
 
568
      error_plus(0, errno, "sigaction");
512
569
    }
513
570
    raise(signal_received);
514
571
  }