/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
 
 
27
 
#include <termios.h>            /* struct termios, tcsetattr(),
 
1
#define _GNU_SOURCE             /* getline() */
 
2
#define _FORTIFY_SOURCE 2
 
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 conflict resolution */
71
 
const char plymouth_name[] = "plymouthd";
72
 
 
73
 
static void termination_handler(int signum){
74
 
  if(quit_now){
75
 
    return;
76
 
  }
77
 
  quit_now = 1;
78
 
  signal_received = signum;
79
 
}
80
 
 
81
 
bool conflict_detection(void){
82
 
 
83
 
  /* plymouth conflicts with password-prompt since both want to read
84
 
     from the terminal.  Password-prompt will exit if it detects
85
 
     plymouth since plymouth performs the same functionality.
86
 
   */
87
 
  int is_plymouth(const struct dirent *proc_entry){
88
 
    int ret;
89
 
    int cl_fd;
90
 
    {
91
 
      uintmax_t maxvalue;
92
 
      char *tmp;
93
 
      errno = 0;
94
 
      maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
95
 
      
96
 
      if(errno != 0 or *tmp != '\0'
97
 
         or maxvalue != (uintmax_t)((pid_t)maxvalue)){
98
 
        return 0;
99
 
      }
100
 
    }
101
 
    
102
 
    char *cmdline_filename;
103
 
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
104
 
                   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, plymouth_name) != 0){
169
 
      if(debug){
170
 
        fprintf(stderr, "\"%s\" is not \"%s\"\n", cmdline_base,
171
 
                plymouth_name);
172
 
      }
173
 
      free(cmdline);
174
 
      return 0;
175
 
    }
176
 
    if(debug){
177
 
      fprintf(stderr, "\"%s\" equals \"%s\"\n", cmdline_base,
178
 
              plymouth_name);
179
 
    }
180
 
    free(cmdline);
181
 
    return 1;
182
 
  }
183
 
  
184
 
  struct dirent **direntries;
185
 
  int ret;
186
 
  ret = scandir("/proc", &direntries, is_plymouth, alphasort);
187
 
  if (ret == -1){
188
 
    error(1, errno, "scandir");
189
 
  }
190
 
  return ret > 0;
191
 
}
192
 
 
 
21
 
 
22
volatile bool quit_now = false;
 
23
 
 
24
void termination_handler(int signum){
 
25
  quit_now = true;
 
26
}
193
27
 
194
28
int main(int argc, char **argv){
195
 
  ssize_t sret;
196
 
  int ret;
 
29
  ssize_t ret = -1;
197
30
  size_t n;
198
31
  struct termios t_new, t_old;
199
32
  char *buffer = NULL;
200
 
  char *prefix = NULL;
201
33
  int status = EXIT_SUCCESS;
202
34
  struct sigaction old_action,
203
35
    new_action = { .sa_handler = termination_handler,
204
36
                   .sa_flags = 0 };
205
 
  {
206
 
    struct argp_option options[] = {
207
 
      { .name = "prefix", .key = 'p',
208
 
        .arg = "PREFIX", .flags = 0,
209
 
        .doc = "Prefix shown before the prompt", .group = 2 },
210
 
      { .name = "debug", .key = 128,
211
 
        .doc = "Debug mode", .group = 3 },
212
 
      /*
213
 
       * These reproduce what we would get without ARGP_NO_HELP
214
 
       */
215
 
      { .name = "help", .key = '?',
216
 
        .doc = "Give this help list", .group = -1 },
217
 
      { .name = "usage", .key = -3,
218
 
        .doc = "Give a short usage message", .group = -1 },
219
 
      { .name = "version", .key = 'V',
220
 
        .doc = "Print program version", .group = -1 },
221
 
      { .name = NULL }
222
 
    };
223
 
    
224
 
    error_t parse_opt (int key, char *arg, struct argp_state *state){
225
 
      errno = 0;
226
 
      switch (key){
227
 
      case 'p':
228
 
        prefix = arg;
229
 
        break;
230
 
      case 128:
231
 
        debug = true;
232
 
        break;
233
 
        /*
234
 
         * These reproduce what we would get without ARGP_NO_HELP
235
 
         */
236
 
      case '?':                 /* --help */
237
 
        argp_state_help(state, state->out_stream,
238
 
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
239
 
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
240
 
      case -3:                  /* --usage */
241
 
        argp_state_help(state, state->out_stream,
242
 
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
243
 
      case 'V':                 /* --version */
244
 
        fprintf(state->out_stream, "%s\n", argp_program_version);
245
 
        exit(argp_err_exit_status);
246
 
        break;
247
 
      default:
248
 
        return ARGP_ERR_UNKNOWN;
249
 
      }
250
 
      return errno;
251
 
    }
252
 
    
253
 
    struct argp argp = { .options = options, .parser = parse_opt,
254
 
                         .args_doc = "",
255
 
                         .doc = "Mandos password-prompt -- Read and"
256
 
                         " output a password" };
257
 
    ret = argp_parse(&argp, argc, argv,
258
 
                     ARGP_IN_ORDER | ARGP_NO_HELP, NULL, NULL);
259
 
    switch(ret){
260
 
    case 0:
261
 
      break;
262
 
    case ENOMEM:
263
 
    default:
264
 
      errno = ret;
265
 
      error(0, errno, "argp_parse");
266
 
      return EX_OSERR;
267
 
    case EINVAL:
268
 
      return EX_USAGE;
269
 
    }
270
 
  }
271
37
  
272
 
  if(debug){
273
 
    fprintf(stderr, "Starting %s\n", argv[0]);
274
 
  }
275
 
 
276
 
  if (conflict_detection()){
277
 
    if(debug){
278
 
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
279
 
    }
 
38
  if (tcgetattr(STDIN_FILENO, &t_old) != 0){
280
39
    return EXIT_FAILURE;
281
40
  }
282
41
  
283
 
  if(debug){
284
 
    fprintf(stderr, "Storing current terminal attributes\n");
285
 
  }
286
 
  
287
 
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
288
 
    int e = errno;
289
 
    error(0, errno, "tcgetattr");
290
 
    switch(e){
291
 
    case EBADF:
292
 
    case ENOTTY:
293
 
      return EX_UNAVAILABLE;
294
 
    default:
295
 
      return EX_OSERR;
296
 
    }
297
 
  }
298
 
  
299
42
  sigemptyset(&new_action.sa_mask);
300
 
  ret = sigaddset(&new_action.sa_mask, SIGINT);
301
 
  if(ret == -1){
302
 
    error(0, errno, "sigaddset");
303
 
    return EX_OSERR;
304
 
  }
305
 
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
306
 
  if(ret == -1){
307
 
    error(0, errno, "sigaddset");
308
 
    return EX_OSERR;
309
 
  }
310
 
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
311
 
  if(ret == -1){
312
 
    error(0, errno, "sigaddset");
313
 
    return EX_OSERR;
314
 
  }
315
 
  /* Need to check if the handler is SIG_IGN before handling:
316
 
     | [[info:libc:Initial Signal Actions]] |
317
 
     | [[info:libc:Basic Signal Handling]]  |
318
 
  */
319
 
  ret = sigaction(SIGINT, NULL, &old_action);
320
 
  if(ret == -1){
321
 
    error(0, errno, "sigaction");
322
 
    return EX_OSERR;
323
 
  }
324
 
  if(old_action.sa_handler != SIG_IGN){
325
 
    ret = sigaction(SIGINT, &new_action, NULL);
326
 
    if(ret == -1){
327
 
      error(0, errno, "sigaction");
328
 
      return EX_OSERR;
329
 
    }
330
 
  }
331
 
  ret = sigaction(SIGHUP, NULL, &old_action);
332
 
  if(ret == -1){
333
 
    error(0, errno, "sigaction");
334
 
    return EX_OSERR;
335
 
  }
336
 
  if(old_action.sa_handler != SIG_IGN){
337
 
    ret = sigaction(SIGHUP, &new_action, NULL);
338
 
    if(ret == -1){
339
 
      error(0, errno, "sigaction");
340
 
      return EX_OSERR;
341
 
    }
342
 
  }
343
 
  ret = sigaction(SIGTERM, NULL, &old_action);
344
 
  if(ret == -1){
345
 
    error(0, errno, "sigaction");
346
 
    return EX_OSERR;
347
 
  }
348
 
  if(old_action.sa_handler != SIG_IGN){
349
 
    ret = sigaction(SIGTERM, &new_action, NULL);
350
 
    if(ret == -1){
351
 
      error(0, errno, "sigaction");
352
 
      return EX_OSERR;
353
 
    }
354
 
  }
355
 
  
356
 
  
357
 
  if(debug){
358
 
    fprintf(stderr, "Removing echo flag from terminal attributes\n");
359
 
  }
 
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);
360
59
  
361
60
  t_new = t_old;
362
 
  t_new.c_lflag &= ~(tcflag_t)ECHO;
363
 
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
364
 
    int e = errno;
365
 
    error(0, errno, "tcsetattr-echo");
366
 
    switch(e){
367
 
    case EBADF:
368
 
    case ENOTTY:
369
 
      return EX_UNAVAILABLE;
370
 
    case EINVAL:
371
 
    default:
372
 
      return EX_OSERR;
373
 
    }
 
61
  t_new.c_lflag &= ~ECHO;
 
62
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
 
63
    perror("tcsetattr-echo");
 
64
    return EXIT_FAILURE;
374
65
  }
375
66
  
376
 
  if(debug){
377
 
    fprintf(stderr, "Waiting for input from stdin \n");
378
 
  }
379
67
  while(true){
380
 
    if(quit_now){
381
 
      if(debug){
382
 
        fprintf(stderr, "Interrupted by signal, exiting.\n");
383
 
      }
 
68
    if (quit_now){
384
69
      status = EXIT_FAILURE;
385
70
      break;
386
71
    }
387
 
 
388
 
    if(prefix){
389
 
      fprintf(stderr, "%s ", prefix);
390
 
    }
391
 
    {
392
 
      const char *cryptsource = getenv("CRYPTTAB_SOURCE");
393
 
      const char *crypttarget = getenv("CRYPTTAB_NAME");
394
 
      /* Before cryptsetup 1.1.0~rc2 */
395
 
      if(cryptsource == NULL){
396
 
        cryptsource = getenv("cryptsource");
397
 
      }
398
 
      if(crypttarget == NULL){
399
 
        crypttarget = getenv("crypttarget");
400
 
      }
401
 
      const char *const prompt1 = "Unlocking the disk";
402
 
      const char *const prompt2 = "Enter passphrase";
403
 
      if(cryptsource == NULL){
404
 
        if(crypttarget == NULL){
405
 
          fprintf(stderr, "%s to unlock the disk: ", prompt2);
406
 
        } else {
407
 
          fprintf(stderr, "%s (%s)\n%s: ", prompt1, crypttarget,
408
 
                  prompt2);
409
 
        }
410
 
      } else {
411
 
        if(crypttarget == NULL){
412
 
          fprintf(stderr, "%s %s\n%s: ", prompt1, cryptsource,
413
 
                  prompt2);
414
 
        } else {
415
 
          fprintf(stderr, "%s %s (%s)\n%s: ", prompt1, cryptsource,
416
 
                  crypttarget, prompt2);
417
 
        }
418
 
      }
419
 
    }
420
 
    sret = getline(&buffer, &n, stdin);
421
 
    if(sret > 0){
 
72
    fprintf(stderr, "Password: ");
 
73
    ret = getline(&buffer, &n, stdin);
 
74
    if (ret > 0){
 
75
      fprintf(stdout, "%s", buffer);
422
76
      status = EXIT_SUCCESS;
423
 
      /* Make n = data size instead of allocated buffer size */
424
 
      n = (size_t)sret;
425
 
      /* Strip final newline */
426
 
      if(n > 0 and buffer[n-1] == '\n'){
427
 
        buffer[n-1] = '\0';     /* not strictly necessary */
428
 
        n--;
429
 
      }
430
 
      size_t written = 0;
431
 
      while(written < n){
432
 
        sret = write(STDOUT_FILENO, buffer + written, n - written);
433
 
        if(sret < 0){
434
 
          int e = errno;
435
 
          error(0, errno, "write");
436
 
          switch(e){
437
 
          case EBADF:
438
 
          case EFAULT:
439
 
          case EINVAL:
440
 
          case EFBIG:
441
 
          case EIO:
442
 
          case ENOSPC:
443
 
          default:
444
 
            status = EX_IOERR;
445
 
            break;
446
 
          case EINTR:
447
 
            status = EXIT_FAILURE;
448
 
            break;
449
 
          }
450
 
          break;
451
 
        }
452
 
        written += (size_t)sret;
453
 
      }
454
 
      sret = close(STDOUT_FILENO);
455
 
      if(sret == -1){
456
 
        int e = errno;
457
 
        error(0, errno, "close");
458
 
        switch(e){
459
 
        case EBADF:
460
 
          status = EX_OSFILE;
461
 
          break;
462
 
        case EIO:
463
 
        default:
464
 
          status = EX_IOERR;
465
 
          break;
466
 
        }
467
 
      }
468
77
      break;
469
78
    }
470
 
    if(sret < 0){
471
 
      int e = errno;
472
 
      if(errno != EINTR and not feof(stdin)){
473
 
        error(0, errno, "getline");
474
 
        switch(e){
475
 
        case EBADF:
476
 
          status = EX_UNAVAILABLE;
477
 
        case EIO:
478
 
        case EINVAL:
479
 
        default:
480
 
          status = EX_IOERR;
481
 
          break;
482
 
        }
 
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;
483
84
        break;
484
85
      }
485
86
    }
486
 
    /* if(sret == 0), then the only sensible thing to do is to retry
487
 
       to read from stdin */
488
 
    fputc('\n', stderr);
489
 
    if(debug and not quit_now){
490
 
      /* If quit_now is nonzero, we were interrupted by a signal, and
491
 
         will print that later, so no need to show this too. */
492
 
      fprintf(stderr, "getline() returned 0, retrying.\n");
493
 
    }
494
 
  }
495
 
  
496
 
  free(buffer);
497
 
  
498
 
  if(debug){
499
 
    fprintf(stderr, "Restoring terminal attributes\n");
500
 
  }
501
 
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
502
 
    error(0, errno, "tcsetattr+echo");
503
 
  }
504
 
  
505
 
  if(quit_now){
506
 
    sigemptyset(&old_action.sa_mask);
507
 
    old_action.sa_handler = SIG_DFL;
508
 
    ret = sigaction(signal_received, &old_action, NULL);
509
 
    if(ret == -1){
510
 
      error(0, errno, "sigaction");
511
 
    }
512
 
    raise(signal_received);
513
 
  }
514
 
  
515
 
  if(debug){
516
 
    fprintf(stderr, "%s is exiting with status %d\n", argv[0],
517
 
            status);
518
 
  }
519
 
  if(status == EXIT_SUCCESS or status == EX_OK){
520
 
    fputc('\n', stderr);
 
87
    fputc('\n', stderr);
 
88
  }
 
89
 
 
90
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
 
91
    perror("tcsetattr+echo");
521
92
  }
522
93
  
523
94
  return status;