/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: 2015-01-25 00:02:51 UTC
  • mto: (237.7.304 trunk)
  • mto: This revision was merged to the branch mainline in revision 325.
  • Revision ID: teddy@recompile.se-20150125000251-j2bw50gfq9smqyxe
mandos.xml (SEE ALSO): Update links.

Update link to GnuPG home page, change reference from TLS 1.1 to TLS
1.2, and change to latest RFC for using OpenPGP keys with TLS (and use
its correct title).

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,2009 Teddy Hogeborn
6
 
 * Copyright © 2008,2009 Björn Påhlsson
 
5
 * Copyright © 2008-2014 Teddy Hogeborn
 
6
 * Copyright © 2008-2014 Björn Påhlsson
7
7
 * 
8
8
 * This program is free software: you can redistribute it and/or
9
9
 * modify it under the terms of the GNU General Public License as
19
19
 * along with this program.  If not, see
20
20
 * <http://www.gnu.org/licenses/>.
21
21
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
22
 * Contact the authors at <mandos@recompile.se>.
23
23
 */
24
24
 
25
 
#define _GNU_SOURCE             /* getline() */
 
25
#define _GNU_SOURCE             /* getline(), asprintf() */
26
26
 
27
 
#include <termios.h>            /* struct termios, tcsetattr(),
 
27
#include <termios.h>            /* struct termios, tcsetattr(),
28
28
                                   TCSAFLUSH, tcgetattr(), ECHO */
29
29
#include <unistd.h>             /* struct termios, tcsetattr(),
30
30
                                   STDIN_FILENO, TCSAFLUSH,
31
 
                                   tcgetattr(), ECHO */
 
31
                                   tcgetattr(), ECHO, readlink() */
32
32
#include <signal.h>             /* sig_atomic_t, raise(), struct
33
33
                                   sigaction, sigemptyset(),
34
34
                                   sigaction(), sigaddset(), SIGINT,
35
35
                                   SIGQUIT, SIGHUP, SIGTERM,
36
36
                                   raise() */
37
37
#include <stddef.h>             /* NULL, size_t, ssize_t */
38
 
#include <sys/types.h>          /* ssize_t */
 
38
#include <sys/types.h>          /* ssize_t, struct dirent, pid_t,
 
39
                                   ssize_t, open() */
39
40
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
40
 
                                   getopt_long, getenv() */
 
41
                                   getenv(), free() */
 
42
#include <dirent.h>             /* scandir(), alphasort() */
41
43
#include <stdio.h>              /* fprintf(), stderr, getline(),
42
 
                                   stdin, feof(), perror(), fputc(),
43
 
                                   getopt_long */
 
44
                                   stdin, feof(), fputc(), vfprintf(),
 
45
                                   vasprintf() */
44
46
#include <errno.h>              /* errno, EBADF, ENOTTY, EINVAL,
45
47
                                   EFAULT, EFBIG, EIO, ENOSPC, EINTR
46
48
                                */
 
49
#include <error.h>              /* error() */
47
50
#include <iso646.h>             /* or, not */
48
51
#include <stdbool.h>            /* bool, false, true */
49
 
#include <string.h>             /* strlen, rindex, strncmp, strcmp */
 
52
#include <inttypes.h>           /* strtoumax() */
 
53
#include <sys/stat.h>           /* struct stat, lstat(), open() */
 
54
#include <string.h>             /* strlen, rindex, memcmp, strerror()
 
55
                                 */
50
56
#include <argp.h>               /* struct argp_option, struct
51
57
                                   argp_state, struct argp,
52
58
                                   argp_parse(), error_t,
54
60
                                   ARGP_ERR_UNKNOWN */
55
61
#include <sysexits.h>           /* EX_SOFTWARE, EX_OSERR,
56
62
                                   EX_UNAVAILABLE, EX_IOERR, EX_OK */
 
63
#include <fcntl.h>              /* open() */
 
64
#include <stdarg.h>             /* va_list, va_start(), ... */
57
65
 
58
66
volatile sig_atomic_t quit_now = 0;
59
67
int signal_received;
60
68
bool debug = false;
61
69
const char *argp_program_version = "password-prompt " VERSION;
62
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
70
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
71
 
 
72
/* Needed for conflict resolution */
 
73
const char plymouth_name[] = "plymouthd";
 
74
 
 
75
/* Function to use when printing errors */
 
76
__attribute__((format (gnu_printf, 3, 4)))
 
77
void error_plus(int status, int errnum, const char *formatstring,
 
78
                ...){
 
79
  va_list ap;
 
80
  char *text;
 
81
  int ret;
 
82
  
 
83
  va_start(ap, formatstring);
 
84
  ret = vasprintf(&text, formatstring, ap);
 
85
  if(ret == -1){
 
86
    fprintf(stderr, "Mandos plugin %s: ",
 
87
            program_invocation_short_name);
 
88
    vfprintf(stderr, formatstring, ap);
 
89
    fprintf(stderr, ": %s\n", strerror(errnum));
 
90
    error(status, errno, "vasprintf while printing error");
 
91
    return;
 
92
  }
 
93
  fprintf(stderr, "Mandos plugin ");
 
94
  error(status, errnum, "%s", text);
 
95
  free(text);
 
96
}
63
97
 
64
98
static void termination_handler(int signum){
65
99
  if(quit_now){
69
103
  signal_received = signum;
70
104
}
71
105
 
 
106
bool conflict_detection(void){
 
107
 
 
108
  /* plymouth conflicts with password-prompt since both want to read
 
109
     from the terminal.  Password-prompt will exit if it detects
 
110
     plymouth since plymouth performs the same functionality.
 
111
   */
 
112
  __attribute__((nonnull))
 
113
  int is_plymouth(const struct dirent *proc_entry){
 
114
    int ret;
 
115
    int cl_fd;
 
116
    {
 
117
      uintmax_t proc_id;
 
118
      char *tmp;
 
119
      errno = 0;
 
120
      proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
 
121
      
 
122
      if(errno != 0 or *tmp != '\0'
 
123
         or proc_id != (uintmax_t)((pid_t)proc_id)){
 
124
        return 0;
 
125
      }
 
126
    }
 
127
    
 
128
    char *cmdline_filename;
 
129
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
 
130
                   proc_entry->d_name);
 
131
    if(ret == -1){
 
132
      error_plus(0, errno, "asprintf");
 
133
      return 0;
 
134
    }
 
135
    
 
136
    /* Open /proc/<pid>/cmdline */
 
137
    cl_fd = open(cmdline_filename, O_RDONLY);
 
138
    free(cmdline_filename);
 
139
    if(cl_fd == -1){
 
140
      if(errno != ENOENT){
 
141
        error_plus(0, errno, "open");
 
142
      }
 
143
      return 0;
 
144
    }
 
145
    
 
146
    char *cmdline = NULL;
 
147
    {
 
148
      size_t cmdline_len = 0;
 
149
      size_t cmdline_allocated = 0;
 
150
      char *tmp;
 
151
      const size_t blocksize = 1024;
 
152
      ssize_t sret;
 
153
      do {
 
154
        /* Allocate more space? */
 
155
        if(cmdline_len + blocksize + 1 > cmdline_allocated){
 
156
          tmp = realloc(cmdline, cmdline_allocated + blocksize + 1);
 
157
          if(tmp == NULL){
 
158
            error_plus(0, errno, "realloc");
 
159
            free(cmdline);
 
160
            close(cl_fd);
 
161
            return 0;
 
162
          }
 
163
          cmdline = tmp;
 
164
          cmdline_allocated += blocksize;
 
165
        }
 
166
        
 
167
        /* Read data */
 
168
        sret = read(cl_fd, cmdline + cmdline_len,
 
169
                    cmdline_allocated - cmdline_len);
 
170
        if(sret == -1){
 
171
          error_plus(0, errno, "read");
 
172
          free(cmdline);
 
173
          close(cl_fd);
 
174
          return 0;
 
175
        }
 
176
        cmdline_len += (size_t)sret;
 
177
      } while(sret != 0);
 
178
      ret = close(cl_fd);
 
179
      if(ret == -1){
 
180
        error_plus(0, errno, "close");
 
181
        free(cmdline);
 
182
        return 0;
 
183
      }
 
184
      cmdline[cmdline_len] = '\0'; /* Make sure it is terminated */
 
185
    }
 
186
    /* we now have cmdline */
 
187
    
 
188
    /* get basename */
 
189
    char *cmdline_base = strrchr(cmdline, '/');
 
190
    if(cmdline_base != NULL){
 
191
      cmdline_base += 1;                /* skip the slash */
 
192
    } else {
 
193
      cmdline_base = cmdline;
 
194
    }
 
195
    
 
196
    if(strcmp(cmdline_base, plymouth_name) != 0){
 
197
      if(debug){
 
198
        fprintf(stderr, "\"%s\" is not \"%s\"\n", cmdline_base,
 
199
                plymouth_name);
 
200
      }
 
201
      free(cmdline);
 
202
      return 0;
 
203
    }
 
204
    if(debug){
 
205
      fprintf(stderr, "\"%s\" equals \"%s\"\n", cmdline_base,
 
206
              plymouth_name);
 
207
    }
 
208
    free(cmdline);
 
209
    return 1;
 
210
  }
 
211
  
 
212
  struct dirent **direntries = NULL;
 
213
  int ret;
 
214
  ret = scandir("/proc", &direntries, is_plymouth, alphasort);
 
215
  if(ret == -1){
 
216
    error_plus(1, errno, "scandir");
 
217
  }
 
218
  free(direntries);
 
219
  return ret > 0;
 
220
}
 
221
 
 
222
 
72
223
int main(int argc, char **argv){
73
 
  ssize_t ret;
 
224
  ssize_t sret;
 
225
  int ret;
74
226
  size_t n;
75
227
  struct termios t_new, t_old;
76
228
  char *buffer = NULL;
86
238
        .doc = "Prefix shown before the prompt", .group = 2 },
87
239
      { .name = "debug", .key = 128,
88
240
        .doc = "Debug mode", .group = 3 },
 
241
      /*
 
242
       * These reproduce what we would get without ARGP_NO_HELP
 
243
       */
 
244
      { .name = "help", .key = '?',
 
245
        .doc = "Give this help list", .group = -1 },
 
246
      { .name = "usage", .key = -3,
 
247
        .doc = "Give a short usage message", .group = -1 },
 
248
      { .name = "version", .key = 'V',
 
249
        .doc = "Print program version", .group = -1 },
89
250
      { .name = NULL }
90
251
    };
91
252
    
 
253
    __attribute__((nonnull(3)))
92
254
    error_t parse_opt (int key, char *arg, struct argp_state *state){
 
255
      errno = 0;
93
256
      switch (key){
94
257
      case 'p':
95
258
        prefix = arg;
97
260
      case 128:
98
261
        debug = true;
99
262
        break;
100
 
      case ARGP_KEY_ARG:
101
 
        argp_usage(state);
102
 
        break;
103
 
      case ARGP_KEY_END:
 
263
        /*
 
264
         * These reproduce what we would get without ARGP_NO_HELP
 
265
         */
 
266
      case '?':                 /* --help */
 
267
        argp_state_help(state, state->out_stream,
 
268
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
 
269
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
270
      case -3:                  /* --usage */
 
271
        argp_state_help(state, state->out_stream,
 
272
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
273
      case 'V':                 /* --version */
 
274
        fprintf(state->out_stream, "%s\n", argp_program_version);
 
275
        exit(argp_err_exit_status);
104
276
        break;
105
277
      default:
106
278
        return ARGP_ERR_UNKNOWN;
107
279
      }
108
 
      return 0;
 
280
      return errno;
109
281
    }
110
282
    
111
283
    struct argp argp = { .options = options, .parser = parse_opt,
112
284
                         .args_doc = "",
113
285
                         .doc = "Mandos password-prompt -- Read and"
114
286
                         " output a password" };
115
 
    ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
116
 
    if(ret == ARGP_ERR_UNKNOWN){
117
 
      fprintf(stderr, "Unknown error while parsing arguments\n");
118
 
      return EX_SOFTWARE;
 
287
    ret = argp_parse(&argp, argc, argv,
 
288
                     ARGP_IN_ORDER | ARGP_NO_HELP, NULL, NULL);
 
289
    switch(ret){
 
290
    case 0:
 
291
      break;
 
292
    case ENOMEM:
 
293
    default:
 
294
      errno = ret;
 
295
      error_plus(0, errno, "argp_parse");
 
296
      return EX_OSERR;
 
297
    case EINVAL:
 
298
      return EX_USAGE;
119
299
    }
120
300
  }
121
301
  
122
302
  if(debug){
123
303
    fprintf(stderr, "Starting %s\n", argv[0]);
124
304
  }
 
305
 
 
306
  if(conflict_detection()){
 
307
    if(debug){
 
308
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
 
309
    }
 
310
    return EXIT_FAILURE;
 
311
  }
 
312
  
125
313
  if(debug){
126
314
    fprintf(stderr, "Storing current terminal attributes\n");
127
315
  }
128
316
  
129
317
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
130
318
    int e = errno;
131
 
    perror("tcgetattr");
 
319
    error_plus(0, errno, "tcgetattr");
132
320
    switch(e){
133
321
    case EBADF:
134
322
    case ENOTTY:
141
329
  sigemptyset(&new_action.sa_mask);
142
330
  ret = sigaddset(&new_action.sa_mask, SIGINT);
143
331
  if(ret == -1){
144
 
    perror("sigaddset");
 
332
    error_plus(0, errno, "sigaddset");
145
333
    return EX_OSERR;
146
334
  }
147
335
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
148
336
  if(ret == -1){
149
 
    perror("sigaddset");
 
337
    error_plus(0, errno, "sigaddset");
150
338
    return EX_OSERR;
151
339
  }
152
340
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
153
341
  if(ret == -1){
154
 
    perror("sigaddset");
 
342
    error_plus(0, errno, "sigaddset");
155
343
    return EX_OSERR;
156
344
  }
157
345
  /* Need to check if the handler is SIG_IGN before handling:
160
348
  */
161
349
  ret = sigaction(SIGINT, NULL, &old_action);
162
350
  if(ret == -1){
163
 
    perror("sigaction");
 
351
    error_plus(0, errno, "sigaction");
164
352
    return EX_OSERR;
165
353
  }
166
354
  if(old_action.sa_handler != SIG_IGN){
167
355
    ret = sigaction(SIGINT, &new_action, NULL);
168
356
    if(ret == -1){
169
 
      perror("sigaction");
 
357
      error_plus(0, errno, "sigaction");
170
358
      return EX_OSERR;
171
359
    }
172
360
  }
173
361
  ret = sigaction(SIGHUP, NULL, &old_action);
174
362
  if(ret == -1){
175
 
    perror("sigaction");
 
363
    error_plus(0, errno, "sigaction");
176
364
    return EX_OSERR;
177
365
  }
178
366
  if(old_action.sa_handler != SIG_IGN){
179
367
    ret = sigaction(SIGHUP, &new_action, NULL);
180
368
    if(ret == -1){
181
 
      perror("sigaction");
 
369
      error_plus(0, errno, "sigaction");
182
370
      return EX_OSERR;
183
371
    }
184
372
  }
185
373
  ret = sigaction(SIGTERM, NULL, &old_action);
186
374
  if(ret == -1){
187
 
    perror("sigaction");
 
375
    error_plus(0, errno, "sigaction");
188
376
    return EX_OSERR;
189
377
  }
190
378
  if(old_action.sa_handler != SIG_IGN){
191
379
    ret = sigaction(SIGTERM, &new_action, NULL);
192
380
    if(ret == -1){
193
 
      perror("sigaction");
 
381
      error_plus(0, errno, "sigaction");
194
382
      return EX_OSERR;
195
383
    }
196
384
  }
204
392
  t_new.c_lflag &= ~(tcflag_t)ECHO;
205
393
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
206
394
    int e = errno;
207
 
    perror("tcsetattr-echo");
 
395
    error_plus(0, errno, "tcsetattr-echo");
208
396
    switch(e){
209
397
    case EBADF:
210
398
    case ENOTTY:
231
419
      fprintf(stderr, "%s ", prefix);
232
420
    }
233
421
    {
234
 
      const char *cryptsource = getenv("cryptsource");
235
 
      const char *crypttarget = getenv("crypttarget");
236
 
      const char *const prompt
237
 
        = "Enter passphrase to unlock the disk";
 
422
      const char *cryptsource = getenv("CRYPTTAB_SOURCE");
 
423
      const char *crypttarget = getenv("CRYPTTAB_NAME");
 
424
      /* Before cryptsetup 1.1.0~rc2 */
 
425
      if(cryptsource == NULL){
 
426
        cryptsource = getenv("cryptsource");
 
427
      }
 
428
      if(crypttarget == NULL){
 
429
        crypttarget = getenv("crypttarget");
 
430
      }
 
431
      const char *const prompt1 = "Unlocking the disk";
 
432
      const char *const prompt2 = "Enter passphrase";
238
433
      if(cryptsource == NULL){
239
434
        if(crypttarget == NULL){
240
 
          fprintf(stderr, "%s: ", prompt);
 
435
          fprintf(stderr, "%s to unlock the disk: ", prompt2);
241
436
        } else {
242
 
          fprintf(stderr, "%s (%s): ", prompt, crypttarget);
 
437
          fprintf(stderr, "%s (%s)\n%s: ", prompt1, crypttarget,
 
438
                  prompt2);
243
439
        }
244
440
      } else {
245
441
        if(crypttarget == NULL){
246
 
          fprintf(stderr, "%s %s: ", prompt, cryptsource);
 
442
          fprintf(stderr, "%s %s\n%s: ", prompt1, cryptsource,
 
443
                  prompt2);
247
444
        } else {
248
 
          fprintf(stderr, "%s %s (%s): ", prompt, cryptsource,
249
 
                  crypttarget);
 
445
          fprintf(stderr, "%s %s (%s)\n%s: ", prompt1, cryptsource,
 
446
                  crypttarget, prompt2);
250
447
        }
251
448
      }
252
449
    }
253
 
    ret = getline(&buffer, &n, stdin);
254
 
    if(ret > 0){
 
450
    sret = getline(&buffer, &n, stdin);
 
451
    if(sret > 0){
255
452
      status = EXIT_SUCCESS;
256
453
      /* Make n = data size instead of allocated buffer size */
257
 
      n = (size_t)ret;
 
454
      n = (size_t)sret;
258
455
      /* Strip final newline */
259
456
      if(n > 0 and buffer[n-1] == '\n'){
260
457
        buffer[n-1] = '\0';     /* not strictly necessary */
262
459
      }
263
460
      size_t written = 0;
264
461
      while(written < n){
265
 
        ret = write(STDOUT_FILENO, buffer + written, n - written);
266
 
        if(ret < 0){
 
462
        sret = write(STDOUT_FILENO, buffer + written, n - written);
 
463
        if(sret < 0){
267
464
          int e = errno;
268
 
          perror("write");
 
465
          error_plus(0, errno, "write");
269
466
          switch(e){
270
467
          case EBADF:
271
468
          case EFAULT:
282
479
          }
283
480
          break;
284
481
        }
285
 
        written += (size_t)ret;
 
482
        written += (size_t)sret;
286
483
      }
287
 
      ret = close(STDOUT_FILENO);
288
 
      if(ret == -1){
 
484
      sret = close(STDOUT_FILENO);
 
485
      if(sret == -1){
289
486
        int e = errno;
290
 
        perror("close");
 
487
        error_plus(0, errno, "close");
291
488
        switch(e){
292
489
        case EBADF:
293
490
          status = EX_OSFILE;
300
497
      }
301
498
      break;
302
499
    }
303
 
    if(ret < 0){
 
500
    if(sret < 0){
304
501
      int e = errno;
305
502
      if(errno != EINTR and not feof(stdin)){
306
 
        perror("getline");
 
503
        error_plus(0, errno, "getline");
307
504
        switch(e){
308
505
        case EBADF:
309
506
          status = EX_UNAVAILABLE;
 
507
          break;
310
508
        case EIO:
311
509
        case EINVAL:
312
510
        default:
316
514
        break;
317
515
      }
318
516
    }
319
 
    /* if(ret == 0), then the only sensible thing to do is to retry to
320
 
       read from stdin */
 
517
    /* if(sret == 0), then the only sensible thing to do is to retry
 
518
       to read from stdin */
321
519
    fputc('\n', stderr);
322
520
    if(debug and not quit_now){
323
521
      /* If quit_now is nonzero, we were interrupted by a signal, and
332
530
    fprintf(stderr, "Restoring terminal attributes\n");
333
531
  }
334
532
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
335
 
    perror("tcsetattr+echo");
 
533
    error_plus(0, errno, "tcsetattr+echo");
336
534
  }
337
535
  
338
536
  if(quit_now){
340
538
    old_action.sa_handler = SIG_DFL;
341
539
    ret = sigaction(signal_received, &old_action, NULL);
342
540
    if(ret == -1){
343
 
      perror("sigaction");
 
541
      error_plus(0, errno, "sigaction");
344
542
    }
345
543
    raise(signal_received);
346
544
  }