/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,
67
69
int signal_received;
68
70
bool debug = false;
69
71
const char *argp_program_version = "password-prompt " VERSION;
70
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
72
const char *argp_program_bug_address = "<mandos@recompile.se>";
71
73
 
72
74
/* Needed for conflict resolution */
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
      }
208
215
    return 1;
209
216
  }
210
217
  
211
 
  struct dirent **direntries;
 
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");
216
 
  }
 
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);
217
231
  return ret > 0;
218
232
}
219
233
 
225
239
  struct termios t_new, t_old;
226
240
  char *buffer = NULL;
227
241
  char *prefix = NULL;
 
242
  char *prompt = NULL;
228
243
  int status = EXIT_SUCCESS;
229
244
  struct sigaction old_action,
230
245
    new_action = { .sa_handler = termination_handler,
234
249
      { .name = "prefix", .key = 'p',
235
250
        .arg = "PREFIX", .flags = 0,
236
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 },
237
255
      { .name = "debug", .key = 128,
238
256
        .doc = "Debug mode", .group = 3 },
239
257
      /*
248
266
      { .name = NULL }
249
267
    };
250
268
    
 
269
    __attribute__((nonnull(3)))
251
270
    error_t parse_opt (int key, char *arg, struct argp_state *state){
252
271
      errno = 0;
253
272
      switch (key){
254
 
      case 'p':
 
273
      case 'p':                 /* --prefix */
255
274
        prefix = arg;
256
275
        break;
257
 
      case 128:
 
276
      case 128:                 /* --debug */
258
277
        debug = true;
259
278
        break;
 
279
      case 129:                 /* --prompt */
 
280
        prompt = arg;
 
281
        break;
260
282
        /*
261
283
         * These reproduce what we would get without ARGP_NO_HELP
262
284
         */
264
286
        argp_state_help(state, state->out_stream,
265
287
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
266
288
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
289
        __builtin_unreachable();
267
290
      case -3:                  /* --usage */
268
291
        argp_state_help(state, state->out_stream,
269
292
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
293
        __builtin_unreachable();
270
294
      case 'V':                 /* --version */
271
295
        fprintf(state->out_stream, "%s\n", argp_program_version);
272
296
        exit(argp_err_exit_status);
289
313
    case ENOMEM:
290
314
    default:
291
315
      errno = ret;
292
 
      error(0, errno, "argp_parse");
 
316
      error_plus(0, errno, "argp_parse");
293
317
      return EX_OSERR;
294
318
    case EINVAL:
295
319
      return EX_USAGE;
300
324
    fprintf(stderr, "Starting %s\n", argv[0]);
301
325
  }
302
326
 
303
 
  if (conflict_detection()){
 
327
  if(conflict_detection()){
304
328
    if(debug){
305
329
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
306
330
    }
313
337
  
314
338
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
315
339
    int e = errno;
316
 
    error(0, errno, "tcgetattr");
 
340
    error_plus(0, errno, "tcgetattr");
317
341
    switch(e){
318
342
    case EBADF:
319
343
    case ENOTTY:
326
350
  sigemptyset(&new_action.sa_mask);
327
351
  ret = sigaddset(&new_action.sa_mask, SIGINT);
328
352
  if(ret == -1){
329
 
    error(0, errno, "sigaddset");
 
353
    error_plus(0, errno, "sigaddset");
330
354
    return EX_OSERR;
331
355
  }
332
356
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
333
357
  if(ret == -1){
334
 
    error(0, errno, "sigaddset");
 
358
    error_plus(0, errno, "sigaddset");
335
359
    return EX_OSERR;
336
360
  }
337
361
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
338
362
  if(ret == -1){
339
 
    error(0, errno, "sigaddset");
 
363
    error_plus(0, errno, "sigaddset");
340
364
    return EX_OSERR;
341
365
  }
342
366
  /* Need to check if the handler is SIG_IGN before handling:
345
369
  */
346
370
  ret = sigaction(SIGINT, NULL, &old_action);
347
371
  if(ret == -1){
348
 
    error(0, errno, "sigaction");
 
372
    error_plus(0, errno, "sigaction");
349
373
    return EX_OSERR;
350
374
  }
351
375
  if(old_action.sa_handler != SIG_IGN){
352
376
    ret = sigaction(SIGINT, &new_action, NULL);
353
377
    if(ret == -1){
354
 
      error(0, errno, "sigaction");
 
378
      error_plus(0, errno, "sigaction");
355
379
      return EX_OSERR;
356
380
    }
357
381
  }
358
382
  ret = sigaction(SIGHUP, NULL, &old_action);
359
383
  if(ret == -1){
360
 
    error(0, errno, "sigaction");
 
384
    error_plus(0, errno, "sigaction");
361
385
    return EX_OSERR;
362
386
  }
363
387
  if(old_action.sa_handler != SIG_IGN){
364
388
    ret = sigaction(SIGHUP, &new_action, NULL);
365
389
    if(ret == -1){
366
 
      error(0, errno, "sigaction");
 
390
      error_plus(0, errno, "sigaction");
367
391
      return EX_OSERR;
368
392
    }
369
393
  }
370
394
  ret = sigaction(SIGTERM, NULL, &old_action);
371
395
  if(ret == -1){
372
 
    error(0, errno, "sigaction");
 
396
    error_plus(0, errno, "sigaction");
373
397
    return EX_OSERR;
374
398
  }
375
399
  if(old_action.sa_handler != SIG_IGN){
376
400
    ret = sigaction(SIGTERM, &new_action, NULL);
377
401
    if(ret == -1){
378
 
      error(0, errno, "sigaction");
 
402
      error_plus(0, errno, "sigaction");
379
403
      return EX_OSERR;
380
404
    }
381
405
  }
389
413
  t_new.c_lflag &= ~(tcflag_t)ECHO;
390
414
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
391
415
    int e = errno;
392
 
    error(0, errno, "tcsetattr-echo");
 
416
    error_plus(0, errno, "tcsetattr-echo");
393
417
    switch(e){
394
418
    case EBADF:
395
419
    case ENOTTY:
415
439
    if(prefix){
416
440
      fprintf(stderr, "%s ", prefix);
417
441
    }
418
 
    {
 
442
    if(prompt != NULL){
 
443
      fprintf(stderr, "%s: ", prompt);
 
444
    } else {
419
445
      const char *cryptsource = getenv("CRYPTTAB_SOURCE");
420
446
      const char *crypttarget = getenv("CRYPTTAB_NAME");
421
447
      /* Before cryptsetup 1.1.0~rc2 */
459
485
        sret = write(STDOUT_FILENO, buffer + written, n - written);
460
486
        if(sret < 0){
461
487
          int e = errno;
462
 
          error(0, errno, "write");
 
488
          error_plus(0, errno, "write");
463
489
          switch(e){
464
490
          case EBADF:
465
491
          case EFAULT:
481
507
      sret = close(STDOUT_FILENO);
482
508
      if(sret == -1){
483
509
        int e = errno;
484
 
        error(0, errno, "close");
 
510
        error_plus(0, errno, "close");
485
511
        switch(e){
486
512
        case EBADF:
487
513
          status = EX_OSFILE;
496
522
    }
497
523
    if(sret < 0){
498
524
      int e = errno;
499
 
      if(errno != EINTR and not feof(stdin)){
500
 
        error(0, errno, "getline");
501
 
        switch(e){
502
 
        case EBADF:
503
 
          status = EX_UNAVAILABLE;
504
 
        case EIO:
505
 
        case EINVAL:
506
 
        default:
507
 
          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
          }
508
538
          break;
 
539
        } else {
 
540
          clearerr(stdin);
509
541
        }
510
 
        break;
511
542
      }
512
543
    }
513
544
    /* if(sret == 0), then the only sensible thing to do is to retry
526
557
    fprintf(stderr, "Restoring terminal attributes\n");
527
558
  }
528
559
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
529
 
    error(0, errno, "tcsetattr+echo");
 
560
    error_plus(0, errno, "tcsetattr+echo");
530
561
  }
531
562
  
532
563
  if(quit_now){
534
565
    old_action.sa_handler = SIG_DFL;
535
566
    ret = sigaction(signal_received, &old_action, NULL);
536
567
    if(ret == -1){
537
 
      error(0, errno, "sigaction");
 
568
      error_plus(0, errno, "sigaction");
538
569
    }
539
570
    raise(signal_received);
540
571
  }