/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(), vfprintf(), vasprintf()
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, strerror() */
 
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,
66
69
int signal_received;
67
70
bool debug = false;
68
71
const char *argp_program_version = "password-prompt " VERSION;
69
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
72
const char *argp_program_bug_address = "<mandos@recompile.se>";
70
73
 
71
74
/* Needed for conflict resolution */
72
75
const char plymouth_name[] = "plymouthd";
73
76
 
74
77
/* Function to use when printing errors */
75
 
void error_plus(int status, int errnum, const char *formatstring, ...){
 
78
__attribute__((format (gnu_printf, 3, 4)))
 
79
void error_plus(int status, int errnum, const char *formatstring,
 
80
                ...){
76
81
  va_list ap;
77
82
  char *text;
78
83
  int ret;
79
84
  
80
85
  va_start(ap, formatstring);
81
86
  ret = vasprintf(&text, formatstring, ap);
82
 
  if (ret == -1){
83
 
    fprintf(stderr, "Mandos plugin %s: ", program_invocation_short_name);
 
87
  if(ret == -1){
 
88
    fprintf(stderr, "Mandos plugin %s: ",
 
89
            program_invocation_short_name);
84
90
    vfprintf(stderr, formatstring, ap);
85
 
    fprintf(stderr, ": ");
86
 
    fprintf(stderr, "%s\n", strerror(errnum));
 
91
    fprintf(stderr, ": %s\n", strerror(errnum));
87
92
    error(status, errno, "vasprintf while printing error");
88
93
    return;
89
94
  }
106
111
     from the terminal.  Password-prompt will exit if it detects
107
112
     plymouth since plymouth performs the same functionality.
108
113
   */
 
114
  if(access("/run/plymouth/pid", R_OK) == 0){
 
115
    return true;
 
116
  }
 
117
  
 
118
  __attribute__((nonnull))
109
119
  int is_plymouth(const struct dirent *proc_entry){
110
120
    int ret;
111
121
    int cl_fd;
112
122
    {
113
 
      uintmax_t maxvalue;
 
123
      uintmax_t proc_id;
114
124
      char *tmp;
115
125
      errno = 0;
116
 
      maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
126
      proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
117
127
      
118
128
      if(errno != 0 or *tmp != '\0'
119
 
         or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
129
         or proc_id != (uintmax_t)((pid_t)proc_id)){
120
130
        return 0;
121
131
      }
122
132
    }
125
135
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
126
136
                   proc_entry->d_name);
127
137
    if(ret == -1){
128
 
      error(0, errno, "asprintf");
 
138
      error_plus(0, errno, "asprintf");
129
139
      return 0;
130
140
    }
131
141
    
134
144
    free(cmdline_filename);
135
145
    if(cl_fd == -1){
136
146
      if(errno != ENOENT){
137
 
        error(0, errno, "open");
 
147
        error_plus(0, errno, "open");
138
148
      }
139
149
      return 0;
140
150
    }
151
161
        if(cmdline_len + blocksize + 1 > cmdline_allocated){
152
162
          tmp = realloc(cmdline, cmdline_allocated + blocksize + 1);
153
163
          if(tmp == NULL){
154
 
            error(0, errno, "realloc");
 
164
            error_plus(0, errno, "realloc");
155
165
            free(cmdline);
156
166
            close(cl_fd);
157
167
            return 0;
164
174
        sret = read(cl_fd, cmdline + cmdline_len,
165
175
                    cmdline_allocated - cmdline_len);
166
176
        if(sret == -1){
167
 
          error(0, errno, "read");
 
177
          error_plus(0, errno, "read");
168
178
          free(cmdline);
169
179
          close(cl_fd);
170
180
          return 0;
173
183
      } while(sret != 0);
174
184
      ret = close(cl_fd);
175
185
      if(ret == -1){
176
 
        error(0, errno, "close");
 
186
        error_plus(0, errno, "close");
177
187
        free(cmdline);
178
188
        return 0;
179
189
      }
205
215
    return 1;
206
216
  }
207
217
  
208
 
  struct dirent **direntries;
 
218
  struct dirent **direntries = NULL;
209
219
  int ret;
210
220
  ret = scandir("/proc", &direntries, is_plymouth, alphasort);
211
 
  if (ret == -1){
212
 
    error(1, errno, "scandir");
213
 
  }
 
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);
214
231
  return ret > 0;
215
232
}
216
233
 
222
239
  struct termios t_new, t_old;
223
240
  char *buffer = NULL;
224
241
  char *prefix = NULL;
 
242
  char *prompt = NULL;
225
243
  int status = EXIT_SUCCESS;
226
244
  struct sigaction old_action,
227
245
    new_action = { .sa_handler = termination_handler,
231
249
      { .name = "prefix", .key = 'p',
232
250
        .arg = "PREFIX", .flags = 0,
233
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 },
234
255
      { .name = "debug", .key = 128,
235
256
        .doc = "Debug mode", .group = 3 },
236
257
      /*
245
266
      { .name = NULL }
246
267
    };
247
268
    
 
269
    __attribute__((nonnull(3)))
248
270
    error_t parse_opt (int key, char *arg, struct argp_state *state){
249
271
      errno = 0;
250
272
      switch (key){
251
 
      case 'p':
 
273
      case 'p':                 /* --prefix */
252
274
        prefix = arg;
253
275
        break;
254
 
      case 128:
 
276
      case 128:                 /* --debug */
255
277
        debug = true;
256
278
        break;
 
279
      case 129:                 /* --prompt */
 
280
        prompt = arg;
 
281
        break;
257
282
        /*
258
283
         * These reproduce what we would get without ARGP_NO_HELP
259
284
         */
261
286
        argp_state_help(state, state->out_stream,
262
287
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
263
288
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
289
        __builtin_unreachable();
264
290
      case -3:                  /* --usage */
265
291
        argp_state_help(state, state->out_stream,
266
292
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
293
        __builtin_unreachable();
267
294
      case 'V':                 /* --version */
268
295
        fprintf(state->out_stream, "%s\n", argp_program_version);
269
296
        exit(argp_err_exit_status);
286
313
    case ENOMEM:
287
314
    default:
288
315
      errno = ret;
289
 
      error(0, errno, "argp_parse");
 
316
      error_plus(0, errno, "argp_parse");
290
317
      return EX_OSERR;
291
318
    case EINVAL:
292
319
      return EX_USAGE;
297
324
    fprintf(stderr, "Starting %s\n", argv[0]);
298
325
  }
299
326
 
300
 
  if (conflict_detection()){
 
327
  if(conflict_detection()){
301
328
    if(debug){
302
329
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
303
330
    }
310
337
  
311
338
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
312
339
    int e = errno;
313
 
    error(0, errno, "tcgetattr");
 
340
    error_plus(0, errno, "tcgetattr");
314
341
    switch(e){
315
342
    case EBADF:
316
343
    case ENOTTY:
323
350
  sigemptyset(&new_action.sa_mask);
324
351
  ret = sigaddset(&new_action.sa_mask, SIGINT);
325
352
  if(ret == -1){
326
 
    error(0, errno, "sigaddset");
 
353
    error_plus(0, errno, "sigaddset");
327
354
    return EX_OSERR;
328
355
  }
329
356
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
330
357
  if(ret == -1){
331
 
    error(0, errno, "sigaddset");
 
358
    error_plus(0, errno, "sigaddset");
332
359
    return EX_OSERR;
333
360
  }
334
361
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
335
362
  if(ret == -1){
336
 
    error(0, errno, "sigaddset");
 
363
    error_plus(0, errno, "sigaddset");
337
364
    return EX_OSERR;
338
365
  }
339
366
  /* Need to check if the handler is SIG_IGN before handling:
342
369
  */
343
370
  ret = sigaction(SIGINT, NULL, &old_action);
344
371
  if(ret == -1){
345
 
    error(0, errno, "sigaction");
 
372
    error_plus(0, errno, "sigaction");
346
373
    return EX_OSERR;
347
374
  }
348
375
  if(old_action.sa_handler != SIG_IGN){
349
376
    ret = sigaction(SIGINT, &new_action, NULL);
350
377
    if(ret == -1){
351
 
      error(0, errno, "sigaction");
 
378
      error_plus(0, errno, "sigaction");
352
379
      return EX_OSERR;
353
380
    }
354
381
  }
355
382
  ret = sigaction(SIGHUP, NULL, &old_action);
356
383
  if(ret == -1){
357
 
    error(0, errno, "sigaction");
 
384
    error_plus(0, errno, "sigaction");
358
385
    return EX_OSERR;
359
386
  }
360
387
  if(old_action.sa_handler != SIG_IGN){
361
388
    ret = sigaction(SIGHUP, &new_action, NULL);
362
389
    if(ret == -1){
363
 
      error(0, errno, "sigaction");
 
390
      error_plus(0, errno, "sigaction");
364
391
      return EX_OSERR;
365
392
    }
366
393
  }
367
394
  ret = sigaction(SIGTERM, NULL, &old_action);
368
395
  if(ret == -1){
369
 
    error(0, errno, "sigaction");
 
396
    error_plus(0, errno, "sigaction");
370
397
    return EX_OSERR;
371
398
  }
372
399
  if(old_action.sa_handler != SIG_IGN){
373
400
    ret = sigaction(SIGTERM, &new_action, NULL);
374
401
    if(ret == -1){
375
 
      error(0, errno, "sigaction");
 
402
      error_plus(0, errno, "sigaction");
376
403
      return EX_OSERR;
377
404
    }
378
405
  }
386
413
  t_new.c_lflag &= ~(tcflag_t)ECHO;
387
414
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
388
415
    int e = errno;
389
 
    error(0, errno, "tcsetattr-echo");
 
416
    error_plus(0, errno, "tcsetattr-echo");
390
417
    switch(e){
391
418
    case EBADF:
392
419
    case ENOTTY:
412
439
    if(prefix){
413
440
      fprintf(stderr, "%s ", prefix);
414
441
    }
415
 
    {
 
442
    if(prompt != NULL){
 
443
      fprintf(stderr, "%s: ", prompt);
 
444
    } else {
416
445
      const char *cryptsource = getenv("CRYPTTAB_SOURCE");
417
446
      const char *crypttarget = getenv("CRYPTTAB_NAME");
418
447
      /* Before cryptsetup 1.1.0~rc2 */
456
485
        sret = write(STDOUT_FILENO, buffer + written, n - written);
457
486
        if(sret < 0){
458
487
          int e = errno;
459
 
          error(0, errno, "write");
 
488
          error_plus(0, errno, "write");
460
489
          switch(e){
461
490
          case EBADF:
462
491
          case EFAULT:
478
507
      sret = close(STDOUT_FILENO);
479
508
      if(sret == -1){
480
509
        int e = errno;
481
 
        error(0, errno, "close");
 
510
        error_plus(0, errno, "close");
482
511
        switch(e){
483
512
        case EBADF:
484
513
          status = EX_OSFILE;
493
522
    }
494
523
    if(sret < 0){
495
524
      int e = errno;
496
 
      if(errno != EINTR and not feof(stdin)){
497
 
        error(0, errno, "getline");
498
 
        switch(e){
499
 
        case EBADF:
500
 
          status = EX_UNAVAILABLE;
501
 
        case EIO:
502
 
        case EINVAL:
503
 
        default:
504
 
          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
          }
505
538
          break;
 
539
        } else {
 
540
          clearerr(stdin);
506
541
        }
507
 
        break;
508
542
      }
509
543
    }
510
544
    /* if(sret == 0), then the only sensible thing to do is to retry
523
557
    fprintf(stderr, "Restoring terminal attributes\n");
524
558
  }
525
559
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
526
 
    error(0, errno, "tcsetattr+echo");
 
560
    error_plus(0, errno, "tcsetattr+echo");
527
561
  }
528
562
  
529
563
  if(quit_now){
531
565
    old_action.sa_handler = SIG_DFL;
532
566
    ret = sigaction(signal_received, &old_action, NULL);
533
567
    if(ret == -1){
534
 
      error(0, errno, "sigaction");
 
568
      error_plus(0, errno, "sigaction");
535
569
    }
536
570
    raise(signal_received);
537
571
  }