/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: 2008-08-29 05:53:59 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080829055359-wkdasnyxtylmnxus
* mandos.xml (EXAMPLE): Replaced all occurences of command name with
                        "&COMMANDNAME;".

* plugins.d/password-prompt.c (main): Improved some documentation
                                      strings.  Do perror() of
                                      tcgetattr() fails.  Add debug
                                      output if interrupted by signal.
                                      Loop over write() instead of
                                      using fwrite() when outputting
                                      password.  Add debug output if
                                      getline() returns 0, unless it
                                      was caused by a signal.  Add
                                      exit status code to debug
                                      output.

* plugins.d/password-prompt.xml: Changed all single quotes to double
                                 quotes for consistency.  Removed
                                 <?xml-stylesheet>.
  (ENTITY TIMESTAMP): New.  Automatically updated by Emacs time-stamp
                      by using Emacs local variables.
  (/refentry/refentryinfo/title): Changed to "Mandos Manual".
  (/refentry/refentryinfo/productname): Changed to "Mandos".
  (/refentry/refentryinfo/date): New; set to "&TIMESTAMP;".
  (/refentry/refentryinfo/copyright): Split copyright holders.
  (/refentry/refnamediv/refpurpose): Improved wording.
  (SYNOPSIS): Fix to use correct markup.  Add short options.
  (DESCRIPTION, OPTIONS): Improved wording.
  (OPTIONS): Improved wording.  Use more correct markup.  Document
             short options.
  (EXIT STATUS): Add text.
  (ENVIRONMENT): Document use of "cryptsource" and "crypttarget".
  (FILES): REMOVED.
  (BUGS): Add text.
  (EXAMPLE): Added some examples.
  (SECURITY): Added text.
  (SEE ALSO): Remove reference to mandos(8).  Add reference to
              crypttab(5).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  -*- coding: utf-8; mode: c; mode: orgtbl -*- */
 
1
/*  -*- coding: utf-8 -*- */
2
2
/*
3
 
 * Password-prompt - Read a password from the terminal and print it
4
 
 * 
5
 
 * Copyright © 2008,2009 Teddy Hogeborn
6
 
 * Copyright © 2008,2009 Björn Påhlsson
 
3
 * Passprompt - Read a password from the terminal and print it
 
4
 *
 
5
 * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
7
6
 * 
8
7
 * This program is free software: you can redistribute it and/or
9
8
 * modify it under the terms of the GNU General Public License as
19
18
 * along with this program.  If not, see
20
19
 * <http://www.gnu.org/licenses/>.
21
20
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
21
 * Contact the authors at <https://www.fukt.bsnet.se/~belorn/> and
 
22
 * <https://www.fukt.bsnet.se/~teddy/>.
23
23
 */
24
24
 
25
25
#define _GNU_SOURCE             /* getline() */
32
32
#include <signal.h>             /* sig_atomic_t, raise(), struct
33
33
                                   sigaction, sigemptyset(),
34
34
                                   sigaction(), sigaddset(), SIGINT,
35
 
                                   SIGQUIT, SIGHUP, SIGTERM,
36
 
                                   raise() */
 
35
                                   SIGQUIT, SIGHUP, SIGTERM */
37
36
#include <stddef.h>             /* NULL, size_t, ssize_t */
38
37
#include <sys/types.h>          /* ssize_t */
39
38
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
40
 
                                   getenv() */
 
39
                                   getopt_long, getenv() */
41
40
#include <stdio.h>              /* fprintf(), stderr, getline(),
42
 
                                   stdin, feof(), fputc()
43
 
                                */
44
 
#include <errno.h>              /* errno, EBADF, ENOTTY, EINVAL,
45
 
                                   EFAULT, EFBIG, EIO, ENOSPC, EINTR
46
 
                                */
47
 
#include <error.h>              /* error() */
 
41
                                   stdin, feof(), perror(), fputc(),
 
42
                                   stdout, getopt_long */
 
43
#include <errno.h>              /* errno, EINVAL */
48
44
#include <iso646.h>             /* or, not */
49
45
#include <stdbool.h>            /* bool, false, true */
50
 
#include <string.h>             /* strlen, rindex */
 
46
#include <string.h>             /* strlen, rindex, strncmp, strcmp */
51
47
#include <argp.h>               /* struct argp_option, struct
52
48
                                   argp_state, struct argp,
53
49
                                   argp_parse(), error_t,
54
50
                                   ARGP_KEY_ARG, ARGP_KEY_END,
55
51
                                   ARGP_ERR_UNKNOWN */
56
 
#include <sysexits.h>           /* EX_SOFTWARE, EX_OSERR,
57
 
                                   EX_UNAVAILABLE, EX_IOERR, EX_OK */
58
52
 
59
 
volatile sig_atomic_t quit_now = 0;
60
 
int signal_received;
 
53
volatile bool quit_now = false;
61
54
bool debug = false;
62
 
const char *argp_program_version = "password-prompt " VERSION;
 
55
const char *argp_program_version = "password-prompt 1.0";
63
56
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
64
57
 
65
 
static void termination_handler(int signum){
66
 
  if(quit_now){
67
 
    return;
68
 
  }
69
 
  quit_now = 1;
70
 
  signal_received = signum;
 
58
static void termination_handler(__attribute__((unused))int signum){
 
59
  quit_now = true;
71
60
}
72
61
 
73
62
int main(int argc, char **argv){
74
 
  ssize_t sret;
75
 
  int ret;
 
63
  ssize_t ret;
76
64
  size_t n;
77
65
  struct termios t_new, t_old;
78
66
  char *buffer = NULL;
88
76
        .doc = "Prefix shown before the prompt", .group = 2 },
89
77
      { .name = "debug", .key = 128,
90
78
        .doc = "Debug mode", .group = 3 },
91
 
      /*
92
 
       * These reproduce what we would get without ARGP_NO_HELP
93
 
       */
94
 
      { .name = "help", .key = '?',
95
 
        .doc = "Give this help list", .group = -1 },
96
 
      { .name = "usage", .key = -3,
97
 
        .doc = "Give a short usage message", .group = -1 },
98
 
      { .name = "version", .key = 'V',
99
 
        .doc = "Print program version", .group = -1 },
100
79
      { .name = NULL }
101
80
    };
102
 
    
103
 
    error_t parse_opt (int key, char *arg, struct argp_state *state){
104
 
      errno = 0;
105
 
      switch (key){
 
81
  
 
82
    error_t parse_opt (int key, char *arg, struct argp_state *state) {
 
83
      /* Get the INPUT argument from `argp_parse', which we know is a
 
84
         pointer to our plugin list pointer. */
 
85
      switch (key) {
106
86
      case 'p':
107
87
        prefix = arg;
108
88
        break;
109
89
      case 128:
110
90
        debug = true;
111
91
        break;
112
 
        /*
113
 
         * These reproduce what we would get without ARGP_NO_HELP
114
 
         */
115
 
      case '?':                 /* --help */
116
 
        argp_state_help(state, state->out_stream,
117
 
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
118
 
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
119
 
      case -3:                  /* --usage */
120
 
        argp_state_help(state, state->out_stream,
121
 
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
122
 
      case 'V':                 /* --version */
123
 
        fprintf(state->out_stream, "%s\n", argp_program_version);
124
 
        exit(argp_err_exit_status);
 
92
      case ARGP_KEY_ARG:
 
93
        argp_usage (state);
 
94
        break;
 
95
      case ARGP_KEY_END:
125
96
        break;
126
97
      default:
127
98
        return ARGP_ERR_UNKNOWN;
128
99
      }
129
 
      return errno;
 
100
      return 0;
130
101
    }
131
 
    
 
102
  
132
103
    struct argp argp = { .options = options, .parser = parse_opt,
133
104
                         .args_doc = "",
134
105
                         .doc = "Mandos password-prompt -- Read and"
135
106
                         " output a password" };
136
 
    ret = argp_parse(&argp, argc, argv,
137
 
                     ARGP_IN_ORDER | ARGP_NO_HELP, NULL, NULL);
138
 
    switch(ret){
139
 
    case 0:
140
 
      break;
141
 
    case ENOMEM:
142
 
    default:
143
 
      errno = ret;
144
 
      error(0, errno, "argp_parse");
145
 
      return EX_OSERR;
146
 
    case EINVAL:
147
 
      return EX_USAGE;
 
107
    ret = argp_parse (&argp, argc, argv, 0, 0, NULL);
 
108
    if (ret == ARGP_ERR_UNKNOWN){
 
109
      fprintf(stderr, "Unknown error while parsing arguments\n");
 
110
      return EXIT_FAILURE;
148
111
    }
149
112
  }
150
 
  
151
 
  if(debug){
 
113
    
 
114
  if (debug){
152
115
    fprintf(stderr, "Starting %s\n", argv[0]);
153
116
  }
154
 
  if(debug){
 
117
  if (debug){
155
118
    fprintf(stderr, "Storing current terminal attributes\n");
156
119
  }
157
120
  
158
 
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
159
 
    int e = errno;
160
 
    error(0, errno, "tcgetattr");
161
 
    switch(e){
162
 
    case EBADF:
163
 
    case ENOTTY:
164
 
      return EX_UNAVAILABLE;
165
 
    default:
166
 
      return EX_OSERR;
167
 
    }
 
121
  if (tcgetattr(STDIN_FILENO, &t_old) != 0){
 
122
    perror("tcgetattr");
 
123
    return EXIT_FAILURE;
168
124
  }
169
125
  
170
126
  sigemptyset(&new_action.sa_mask);
171
 
  ret = sigaddset(&new_action.sa_mask, SIGINT);
172
 
  if(ret == -1){
173
 
    error(0, errno, "sigaddset");
174
 
    return EX_OSERR;
175
 
  }
176
 
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
177
 
  if(ret == -1){
178
 
    error(0, errno, "sigaddset");
179
 
    return EX_OSERR;
180
 
  }
181
 
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
182
 
  if(ret == -1){
183
 
    error(0, errno, "sigaddset");
184
 
    return EX_OSERR;
185
 
  }
186
 
  /* Need to check if the handler is SIG_IGN before handling:
187
 
     | [[info:libc:Initial Signal Actions]] |
188
 
     | [[info:libc:Basic Signal Handling]]  |
189
 
  */
 
127
  sigaddset(&new_action.sa_mask, SIGINT);
 
128
  sigaddset(&new_action.sa_mask, SIGHUP);
 
129
  sigaddset(&new_action.sa_mask, SIGTERM);
190
130
  ret = sigaction(SIGINT, NULL, &old_action);
191
131
  if(ret == -1){
192
 
    error(0, errno, "sigaction");
193
 
    return EX_OSERR;
 
132
    perror("sigaction");
 
133
    return EXIT_FAILURE;
194
134
  }
195
 
  if(old_action.sa_handler != SIG_IGN){
 
135
  if (old_action.sa_handler != SIG_IGN){
196
136
    ret = sigaction(SIGINT, &new_action, NULL);
197
137
    if(ret == -1){
198
 
      error(0, errno, "sigaction");
199
 
      return EX_OSERR;
 
138
      perror("sigaction");
 
139
      return EXIT_FAILURE;
200
140
    }
201
141
  }
202
142
  ret = sigaction(SIGHUP, NULL, &old_action);
203
143
  if(ret == -1){
204
 
    error(0, errno, "sigaction");
205
 
    return EX_OSERR;
 
144
    perror("sigaction");
 
145
    return EXIT_FAILURE;
206
146
  }
207
 
  if(old_action.sa_handler != SIG_IGN){
 
147
  if (old_action.sa_handler != SIG_IGN){
208
148
    ret = sigaction(SIGHUP, &new_action, NULL);
209
149
    if(ret == -1){
210
 
      error(0, errno, "sigaction");
211
 
      return EX_OSERR;
 
150
      perror("sigaction");
 
151
      return EXIT_FAILURE;
212
152
    }
213
153
  }
214
154
  ret = sigaction(SIGTERM, NULL, &old_action);
215
155
  if(ret == -1){
216
 
    error(0, errno, "sigaction");
217
 
    return EX_OSERR;
 
156
    perror("sigaction");
 
157
    return EXIT_FAILURE;
218
158
  }
219
 
  if(old_action.sa_handler != SIG_IGN){
 
159
  if (old_action.sa_handler != SIG_IGN){
220
160
    ret = sigaction(SIGTERM, &new_action, NULL);
221
161
    if(ret == -1){
222
 
      error(0, errno, "sigaction");
223
 
      return EX_OSERR;
 
162
      perror("sigaction");
 
163
      return EXIT_FAILURE;
224
164
    }
225
165
  }
226
166
  
227
167
  
228
 
  if(debug){
 
168
  if (debug){
229
169
    fprintf(stderr, "Removing echo flag from terminal attributes\n");
230
170
  }
231
171
  
232
172
  t_new = t_old;
233
 
  t_new.c_lflag &= ~(tcflag_t)ECHO;
234
 
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
235
 
    int e = errno;
236
 
    error(0, errno, "tcsetattr-echo");
237
 
    switch(e){
238
 
    case EBADF:
239
 
    case ENOTTY:
240
 
      return EX_UNAVAILABLE;
241
 
    case EINVAL:
242
 
    default:
243
 
      return EX_OSERR;
244
 
    }
 
173
  t_new.c_lflag &= ~ECHO;
 
174
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
 
175
    perror("tcsetattr-echo");
 
176
    return EXIT_FAILURE;
245
177
  }
246
 
  
247
 
  if(debug){
 
178
 
 
179
  if (debug){
248
180
    fprintf(stderr, "Waiting for input from stdin \n");
249
181
  }
250
182
  while(true){
251
 
    if(quit_now){
 
183
    if (quit_now){
252
184
      if(debug){
253
185
        fprintf(stderr, "Interrupted by signal, exiting.\n");
254
186
      }
260
192
      fprintf(stderr, "%s ", prefix);
261
193
    }
262
194
    {
263
 
      const char *cryptsource = getenv("CRYPTTAB_SOURCE");
264
 
      const char *crypttarget = getenv("CRYPTTAB_NAME");
265
 
      /* Before cryptsetup 1.1.0~rc2 */
266
 
      if(cryptsource == NULL){
267
 
        cryptsource = getenv("cryptsource");
268
 
      }
269
 
      if(crypttarget == NULL){
270
 
        crypttarget = getenv("crypttarget");
271
 
      }
272
 
      const char *const prompt1 = "Unlocking the disk";
273
 
      const char *const prompt2 = "Enter passphrase";
 
195
      const char *cryptsource = getenv("cryptsource");
 
196
      const char *crypttarget = getenv("crypttarget");
 
197
      const char *const prompt
 
198
        = "Enter passphrase to unlock the disk";
274
199
      if(cryptsource == NULL){
275
200
        if(crypttarget == NULL){
276
 
          fprintf(stderr, "%s to unlock the disk: ", prompt2);
 
201
          fprintf(stderr, "%s: ", prompt);
277
202
        } else {
278
 
          fprintf(stderr, "%s (%s)\n%s: ", prompt1, crypttarget,
279
 
                  prompt2);
 
203
          fprintf(stderr, "%s (%s): ", prompt, crypttarget);
280
204
        }
281
205
      } else {
282
206
        if(crypttarget == NULL){
283
 
          fprintf(stderr, "%s %s\n%s: ", prompt1, cryptsource,
284
 
                  prompt2);
 
207
          fprintf(stderr, "%s %s: ", prompt, cryptsource);
285
208
        } else {
286
 
          fprintf(stderr, "%s %s (%s)\n%s: ", prompt1, cryptsource,
287
 
                  crypttarget, prompt2);
 
209
          fprintf(stderr, "%s %s (%s): ", prompt, cryptsource,
 
210
                  crypttarget);
288
211
        }
289
212
      }
290
213
    }
291
 
    sret = getline(&buffer, &n, stdin);
292
 
    if(sret > 0){
 
214
    ret = getline(&buffer, &n, stdin);
 
215
    if (ret > 0){
293
216
      status = EXIT_SUCCESS;
294
217
      /* Make n = data size instead of allocated buffer size */
295
 
      n = (size_t)sret;
296
 
      /* Strip final newline */
297
 
      if(n > 0 and buffer[n-1] == '\n'){
298
 
        buffer[n-1] = '\0';     /* not strictly necessary */
299
 
        n--;
300
 
      }
 
218
      n = (size_t)ret;
301
219
      size_t written = 0;
302
220
      while(written < n){
303
 
        sret = write(STDOUT_FILENO, buffer + written, n - written);
304
 
        if(sret < 0){
305
 
          int e = errno;
306
 
          error(0, errno, "write");
307
 
          switch(e){
308
 
          case EBADF:
309
 
          case EFAULT:
310
 
          case EINVAL:
311
 
          case EFBIG:
312
 
          case EIO:
313
 
          case ENOSPC:
314
 
          default:
315
 
            status = EX_IOERR;
316
 
            break;
317
 
          case EINTR:
318
 
            status = EXIT_FAILURE;
319
 
            break;
320
 
          }
321
 
          break;
322
 
        }
323
 
        written += (size_t)sret;
324
 
      }
325
 
      sret = close(STDOUT_FILENO);
326
 
      if(sret == -1){
327
 
        int e = errno;
328
 
        error(0, errno, "close");
329
 
        switch(e){
330
 
        case EBADF:
331
 
          status = EX_OSFILE;
332
 
          break;
333
 
        case EIO:
334
 
        default:
335
 
          status = EX_IOERR;
336
 
          break;
337
 
        }
 
221
        ret = write(STDOUT_FILENO, buffer + written, n - written);
 
222
        if(ret < 0){
 
223
          perror("write");
 
224
          status = EXIT_FAILURE;
 
225
          break;
 
226
        }
 
227
        written += (size_t)ret;
338
228
      }
339
229
      break;
340
230
    }
341
 
    if(sret < 0){
342
 
      int e = errno;
343
 
      if(errno != EINTR and not feof(stdin)){
344
 
        error(0, errno, "getline");
345
 
        switch(e){
346
 
        case EBADF:
347
 
          status = EX_UNAVAILABLE;
348
 
        case EIO:
349
 
        case EINVAL:
350
 
        default:
351
 
          status = EX_IOERR;
352
 
          break;
353
 
        }
 
231
    if (ret < 0){
 
232
      if (errno != EINTR and not feof(stdin)){
 
233
        perror("getline");
 
234
        status = EXIT_FAILURE;
354
235
        break;
355
236
      }
356
237
    }
357
 
    /* if(sret == 0), then the only sensible thing to do is to retry to
 
238
    /* if(ret == 0), then the only sensible thing to do is to retry to
358
239
       read from stdin */
359
240
    fputc('\n', stderr);
360
241
    if(debug and not quit_now){
361
 
      /* If quit_now is nonzero, we were interrupted by a signal, and
 
242
      /* If quit_now is true, we were interrupted by a signal, and
362
243
         will print that later, so no need to show this too. */
363
244
      fprintf(stderr, "getline() returned 0, retrying.\n");
364
245
    }
365
246
  }
366
247
  
367
 
  free(buffer);
368
 
  
369
 
  if(debug){
 
248
  if (debug){
370
249
    fprintf(stderr, "Restoring terminal attributes\n");
371
250
  }
372
 
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
373
 
    error(0, errno, "tcsetattr+echo");
374
 
  }
375
 
  
376
 
  if(quit_now){
377
 
    sigemptyset(&old_action.sa_mask);
378
 
    old_action.sa_handler = SIG_DFL;
379
 
    ret = sigaction(signal_received, &old_action, NULL);
380
 
    if(ret == -1){
381
 
      error(0, errno, "sigaction");
382
 
    }
383
 
    raise(signal_received);
384
 
  }
385
 
  
386
 
  if(debug){
 
251
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
 
252
    perror("tcsetattr+echo");
 
253
  }
 
254
  
 
255
  if (debug){
387
256
    fprintf(stderr, "%s is exiting with status %d\n", argv[0],
388
257
            status);
389
258
  }
390
 
  if(status == EXIT_SUCCESS or status == EX_OK){
391
 
    fputc('\n', stderr);
392
 
  }
393
259
  
394
260
  return status;
395
261
}