/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/passprompt.c

  • Committer: Björn Påhlsson
  • Date: 2008-07-20 02:52:20 UTC
  • Revision ID: belorn@braxen-20080720025220-r5u0388uy9iu23h6
Added following support:
Pluginbased client handler
rewritten Mandos client
       Avahi instead of udp server discovery
       openpgp encrypted key support
Passprompt stand alone application for direct console input
Added logging for Mandos server

Show diffs side-by-side

added added

removed removed

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