/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: 2011-12-31 20:07:11 UTC
  • mfrom: (535.1.9 wireless-network-hook)
  • Revision ID: teddy@recompile.se-20111231200711-6dli3r8drftem57r
Merge new wireless network hook.  Fix bridge network hook to use
hardware addresses instead of interface names.  Implement and document
new "CONNECT" environment variable for network hooks.

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-2011 Teddy Hogeborn
 
6
 * Copyright © 2008-2011 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
__attribute__((format (gnu_printf, 2, 3), nonnull(1)))
 
76
int fprintf_plus(FILE *stream, const char *format, ...){
 
77
  va_list ap;
 
78
  va_start (ap, format);
 
79
  
 
80
  TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
 
81
                             program_invocation_short_name));
 
82
  return TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
 
83
}
 
84
 
 
85
/* Function to use when printing errors */
 
86
__attribute__((format (gnu_printf, 3, 4)))
 
87
void error_plus(int status, int errnum, const char *formatstring,
 
88
                ...){
 
89
  va_list ap;
 
90
  char *text;
 
91
  int ret;
 
92
  
 
93
  va_start(ap, formatstring);
 
94
  ret = vasprintf(&text, formatstring, ap);
 
95
  if (ret == -1){
 
96
    fprintf(stderr, "Mandos plugin %s: ",
 
97
            program_invocation_short_name);
 
98
    vfprintf(stderr, formatstring, ap);
 
99
    fprintf(stderr, ": %s\n", strerror(errnum));
 
100
    error(status, errno, "vasprintf while printing error");
 
101
    return;
 
102
  }
 
103
  fprintf(stderr, "Mandos plugin ");
 
104
  error(status, errnum, "%s", text);
 
105
  free(text);
 
106
}
63
107
 
64
108
static void termination_handler(int signum){
65
109
  if(quit_now){
69
113
  signal_received = signum;
70
114
}
71
115
 
 
116
bool conflict_detection(void){
 
117
 
 
118
  /* plymouth conflicts with password-prompt since both want to read
 
119
     from the terminal.  Password-prompt will exit if it detects
 
120
     plymouth since plymouth performs the same functionality.
 
121
   */
 
122
  __attribute__((nonnull))
 
123
  int is_plymouth(const struct dirent *proc_entry){
 
124
    int ret;
 
125
    int cl_fd;
 
126
    {
 
127
      uintmax_t proc_id;
 
128
      char *tmp;
 
129
      errno = 0;
 
130
      proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
 
131
      
 
132
      if(errno != 0 or *tmp != '\0'
 
133
         or proc_id != (uintmax_t)((pid_t)proc_id)){
 
134
        return 0;
 
135
      }
 
136
    }
 
137
    
 
138
    char *cmdline_filename;
 
139
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
 
140
                   proc_entry->d_name);
 
141
    if(ret == -1){
 
142
      error_plus(0, errno, "asprintf");
 
143
      return 0;
 
144
    }
 
145
    
 
146
    /* Open /proc/<pid>/cmdline */
 
147
    cl_fd = open(cmdline_filename, O_RDONLY);
 
148
    free(cmdline_filename);
 
149
    if(cl_fd == -1){
 
150
      if(errno != ENOENT){
 
151
        error_plus(0, errno, "open");
 
152
      }
 
153
      return 0;
 
154
    }
 
155
    
 
156
    char *cmdline = NULL;
 
157
    {
 
158
      size_t cmdline_len = 0;
 
159
      size_t cmdline_allocated = 0;
 
160
      char *tmp;
 
161
      const size_t blocksize = 1024;
 
162
      ssize_t sret;
 
163
      do {
 
164
        /* Allocate more space? */
 
165
        if(cmdline_len + blocksize + 1 > cmdline_allocated){
 
166
          tmp = realloc(cmdline, cmdline_allocated + blocksize + 1);
 
167
          if(tmp == NULL){
 
168
            error_plus(0, errno, "realloc");
 
169
            free(cmdline);
 
170
            close(cl_fd);
 
171
            return 0;
 
172
          }
 
173
          cmdline = tmp;
 
174
          cmdline_allocated += blocksize;
 
175
        }
 
176
        
 
177
        /* Read data */
 
178
        sret = read(cl_fd, cmdline + cmdline_len,
 
179
                    cmdline_allocated - cmdline_len);
 
180
        if(sret == -1){
 
181
          error_plus(0, errno, "read");
 
182
          free(cmdline);
 
183
          close(cl_fd);
 
184
          return 0;
 
185
        }
 
186
        cmdline_len += (size_t)sret;
 
187
      } while(sret != 0);
 
188
      ret = close(cl_fd);
 
189
      if(ret == -1){
 
190
        error_plus(0, errno, "close");
 
191
        free(cmdline);
 
192
        return 0;
 
193
      }
 
194
      cmdline[cmdline_len] = '\0'; /* Make sure it is terminated */
 
195
    }
 
196
    /* we now have cmdline */
 
197
    
 
198
    /* get basename */
 
199
    char *cmdline_base = strrchr(cmdline, '/');
 
200
    if(cmdline_base != NULL){
 
201
      cmdline_base += 1;                /* skip the slash */
 
202
    } else {
 
203
      cmdline_base = cmdline;
 
204
    }
 
205
    
 
206
    if(strcmp(cmdline_base, plymouth_name) != 0){
 
207
      if(debug){
 
208
        fprintf(stderr, "\"%s\" is not \"%s\"\n", cmdline_base,
 
209
                plymouth_name);
 
210
      }
 
211
      free(cmdline);
 
212
      return 0;
 
213
    }
 
214
    if(debug){
 
215
      fprintf(stderr, "\"%s\" equals \"%s\"\n", cmdline_base,
 
216
              plymouth_name);
 
217
    }
 
218
    free(cmdline);
 
219
    return 1;
 
220
  }
 
221
  
 
222
  struct dirent **direntries = NULL;
 
223
  int ret;
 
224
  ret = scandir("/proc", &direntries, is_plymouth, alphasort);
 
225
  if (ret == -1){
 
226
    error_plus(1, errno, "scandir");
 
227
  }
 
228
  free(direntries);
 
229
  return ret > 0;
 
230
}
 
231
 
 
232
 
72
233
int main(int argc, char **argv){
73
 
  ssize_t ret;
 
234
  ssize_t sret;
 
235
  int ret;
74
236
  size_t n;
75
237
  struct termios t_new, t_old;
76
238
  char *buffer = NULL;
86
248
        .doc = "Prefix shown before the prompt", .group = 2 },
87
249
      { .name = "debug", .key = 128,
88
250
        .doc = "Debug mode", .group = 3 },
 
251
      /*
 
252
       * These reproduce what we would get without ARGP_NO_HELP
 
253
       */
 
254
      { .name = "help", .key = '?',
 
255
        .doc = "Give this help list", .group = -1 },
 
256
      { .name = "usage", .key = -3,
 
257
        .doc = "Give a short usage message", .group = -1 },
 
258
      { .name = "version", .key = 'V',
 
259
        .doc = "Print program version", .group = -1 },
89
260
      { .name = NULL }
90
261
    };
91
262
    
 
263
    __attribute__((nonnull(3)))
92
264
    error_t parse_opt (int key, char *arg, struct argp_state *state){
 
265
      errno = 0;
93
266
      switch (key){
94
267
      case 'p':
95
268
        prefix = arg;
97
270
      case 128:
98
271
        debug = true;
99
272
        break;
100
 
      case ARGP_KEY_ARG:
101
 
        argp_usage(state);
102
 
        break;
103
 
      case ARGP_KEY_END:
 
273
        /*
 
274
         * These reproduce what we would get without ARGP_NO_HELP
 
275
         */
 
276
      case '?':                 /* --help */
 
277
        argp_state_help(state, state->out_stream,
 
278
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
 
279
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
280
      case -3:                  /* --usage */
 
281
        argp_state_help(state, state->out_stream,
 
282
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
283
      case 'V':                 /* --version */
 
284
        fprintf(state->out_stream, "%s\n", argp_program_version);
 
285
        exit(argp_err_exit_status);
104
286
        break;
105
287
      default:
106
288
        return ARGP_ERR_UNKNOWN;
107
289
      }
108
 
      return 0;
 
290
      return errno;
109
291
    }
110
292
    
111
293
    struct argp argp = { .options = options, .parser = parse_opt,
112
294
                         .args_doc = "",
113
295
                         .doc = "Mandos password-prompt -- Read and"
114
296
                         " 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;
 
297
    ret = argp_parse(&argp, argc, argv,
 
298
                     ARGP_IN_ORDER | ARGP_NO_HELP, NULL, NULL);
 
299
    switch(ret){
 
300
    case 0:
 
301
      break;
 
302
    case ENOMEM:
 
303
    default:
 
304
      errno = ret;
 
305
      error_plus(0, errno, "argp_parse");
 
306
      return EX_OSERR;
 
307
    case EINVAL:
 
308
      return EX_USAGE;
119
309
    }
120
310
  }
121
311
  
122
312
  if(debug){
123
313
    fprintf(stderr, "Starting %s\n", argv[0]);
124
314
  }
 
315
 
 
316
  if (conflict_detection()){
 
317
    if(debug){
 
318
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
 
319
    }
 
320
    return EXIT_FAILURE;
 
321
  }
 
322
  
125
323
  if(debug){
126
324
    fprintf(stderr, "Storing current terminal attributes\n");
127
325
  }
128
326
  
129
327
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
130
328
    int e = errno;
131
 
    perror("tcgetattr");
 
329
    error_plus(0, errno, "tcgetattr");
132
330
    switch(e){
133
331
    case EBADF:
134
332
    case ENOTTY:
141
339
  sigemptyset(&new_action.sa_mask);
142
340
  ret = sigaddset(&new_action.sa_mask, SIGINT);
143
341
  if(ret == -1){
144
 
    perror("sigaddset");
 
342
    error_plus(0, errno, "sigaddset");
145
343
    return EX_OSERR;
146
344
  }
147
345
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
148
346
  if(ret == -1){
149
 
    perror("sigaddset");
 
347
    error_plus(0, errno, "sigaddset");
150
348
    return EX_OSERR;
151
349
  }
152
350
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
153
351
  if(ret == -1){
154
 
    perror("sigaddset");
 
352
    error_plus(0, errno, "sigaddset");
155
353
    return EX_OSERR;
156
354
  }
157
355
  /* Need to check if the handler is SIG_IGN before handling:
160
358
  */
161
359
  ret = sigaction(SIGINT, NULL, &old_action);
162
360
  if(ret == -1){
163
 
    perror("sigaction");
 
361
    error_plus(0, errno, "sigaction");
164
362
    return EX_OSERR;
165
363
  }
166
364
  if(old_action.sa_handler != SIG_IGN){
167
365
    ret = sigaction(SIGINT, &new_action, NULL);
168
366
    if(ret == -1){
169
 
      perror("sigaction");
 
367
      error_plus(0, errno, "sigaction");
170
368
      return EX_OSERR;
171
369
    }
172
370
  }
173
371
  ret = sigaction(SIGHUP, NULL, &old_action);
174
372
  if(ret == -1){
175
 
    perror("sigaction");
 
373
    error_plus(0, errno, "sigaction");
176
374
    return EX_OSERR;
177
375
  }
178
376
  if(old_action.sa_handler != SIG_IGN){
179
377
    ret = sigaction(SIGHUP, &new_action, NULL);
180
378
    if(ret == -1){
181
 
      perror("sigaction");
 
379
      error_plus(0, errno, "sigaction");
182
380
      return EX_OSERR;
183
381
    }
184
382
  }
185
383
  ret = sigaction(SIGTERM, NULL, &old_action);
186
384
  if(ret == -1){
187
 
    perror("sigaction");
 
385
    error_plus(0, errno, "sigaction");
188
386
    return EX_OSERR;
189
387
  }
190
388
  if(old_action.sa_handler != SIG_IGN){
191
389
    ret = sigaction(SIGTERM, &new_action, NULL);
192
390
    if(ret == -1){
193
 
      perror("sigaction");
 
391
      error_plus(0, errno, "sigaction");
194
392
      return EX_OSERR;
195
393
    }
196
394
  }
204
402
  t_new.c_lflag &= ~(tcflag_t)ECHO;
205
403
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
206
404
    int e = errno;
207
 
    perror("tcsetattr-echo");
 
405
    error_plus(0, errno, "tcsetattr-echo");
208
406
    switch(e){
209
407
    case EBADF:
210
408
    case ENOTTY:
231
429
      fprintf(stderr, "%s ", prefix);
232
430
    }
233
431
    {
234
 
      const char *cryptsource = getenv("cryptsource");
235
 
      const char *crypttarget = getenv("crypttarget");
236
 
      const char *const prompt
237
 
        = "Enter passphrase to unlock the disk";
 
432
      const char *cryptsource = getenv("CRYPTTAB_SOURCE");
 
433
      const char *crypttarget = getenv("CRYPTTAB_NAME");
 
434
      /* Before cryptsetup 1.1.0~rc2 */
 
435
      if(cryptsource == NULL){
 
436
        cryptsource = getenv("cryptsource");
 
437
      }
 
438
      if(crypttarget == NULL){
 
439
        crypttarget = getenv("crypttarget");
 
440
      }
 
441
      const char *const prompt1 = "Unlocking the disk";
 
442
      const char *const prompt2 = "Enter passphrase";
238
443
      if(cryptsource == NULL){
239
444
        if(crypttarget == NULL){
240
 
          fprintf(stderr, "%s: ", prompt);
 
445
          fprintf(stderr, "%s to unlock the disk: ", prompt2);
241
446
        } else {
242
 
          fprintf(stderr, "%s (%s): ", prompt, crypttarget);
 
447
          fprintf(stderr, "%s (%s)\n%s: ", prompt1, crypttarget,
 
448
                  prompt2);
243
449
        }
244
450
      } else {
245
451
        if(crypttarget == NULL){
246
 
          fprintf(stderr, "%s %s: ", prompt, cryptsource);
 
452
          fprintf(stderr, "%s %s\n%s: ", prompt1, cryptsource,
 
453
                  prompt2);
247
454
        } else {
248
 
          fprintf(stderr, "%s %s (%s): ", prompt, cryptsource,
249
 
                  crypttarget);
 
455
          fprintf(stderr, "%s %s (%s)\n%s: ", prompt1, cryptsource,
 
456
                  crypttarget, prompt2);
250
457
        }
251
458
      }
252
459
    }
253
 
    ret = getline(&buffer, &n, stdin);
254
 
    if(ret > 0){
 
460
    sret = getline(&buffer, &n, stdin);
 
461
    if(sret > 0){
255
462
      status = EXIT_SUCCESS;
256
463
      /* Make n = data size instead of allocated buffer size */
257
 
      n = (size_t)ret;
 
464
      n = (size_t)sret;
258
465
      /* Strip final newline */
259
466
      if(n > 0 and buffer[n-1] == '\n'){
260
467
        buffer[n-1] = '\0';     /* not strictly necessary */
262
469
      }
263
470
      size_t written = 0;
264
471
      while(written < n){
265
 
        ret = write(STDOUT_FILENO, buffer + written, n - written);
266
 
        if(ret < 0){
 
472
        sret = write(STDOUT_FILENO, buffer + written, n - written);
 
473
        if(sret < 0){
267
474
          int e = errno;
268
 
          perror("write");
 
475
          error_plus(0, errno, "write");
269
476
          switch(e){
270
477
          case EBADF:
271
478
          case EFAULT:
282
489
          }
283
490
          break;
284
491
        }
285
 
        written += (size_t)ret;
 
492
        written += (size_t)sret;
286
493
      }
287
 
      ret = close(STDOUT_FILENO);
288
 
      if(ret == -1){
 
494
      sret = close(STDOUT_FILENO);
 
495
      if(sret == -1){
289
496
        int e = errno;
290
 
        perror("close");
 
497
        error_plus(0, errno, "close");
291
498
        switch(e){
292
499
        case EBADF:
293
500
          status = EX_OSFILE;
300
507
      }
301
508
      break;
302
509
    }
303
 
    if(ret < 0){
 
510
    if(sret < 0){
304
511
      int e = errno;
305
512
      if(errno != EINTR and not feof(stdin)){
306
 
        perror("getline");
 
513
        error_plus(0, errno, "getline");
307
514
        switch(e){
308
515
        case EBADF:
309
516
          status = EX_UNAVAILABLE;
 
517
          break;
310
518
        case EIO:
311
519
        case EINVAL:
312
520
        default:
316
524
        break;
317
525
      }
318
526
    }
319
 
    /* if(ret == 0), then the only sensible thing to do is to retry to
320
 
       read from stdin */
 
527
    /* if(sret == 0), then the only sensible thing to do is to retry
 
528
       to read from stdin */
321
529
    fputc('\n', stderr);
322
530
    if(debug and not quit_now){
323
531
      /* If quit_now is nonzero, we were interrupted by a signal, and
332
540
    fprintf(stderr, "Restoring terminal attributes\n");
333
541
  }
334
542
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
335
 
    perror("tcsetattr+echo");
 
543
    error_plus(0, errno, "tcsetattr+echo");
336
544
  }
337
545
  
338
546
  if(quit_now){
340
548
    old_action.sa_handler = SIG_DFL;
341
549
    ret = sigaction(signal_received, &old_action, NULL);
342
550
    if(ret == -1){
343
 
      perror("sigaction");
 
551
      error_plus(0, errno, "sigaction");
344
552
    }
345
553
    raise(signal_received);
346
554
  }