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