/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(), perror(), fputc()
43
 
                                */
44
 
#include <errno.h>              /* errno, EBADF, ENOTTY, EINVAL,
45
 
                                   EFAULT, EFBIG, EIO, ENOSPC, EINTR
46
 
                                */
 
41
                                   stdin, feof(), perror(), fputc(),
 
42
                                   stdout, getopt_long */
 
43
#include <errno.h>              /* errno, EINVAL */
47
44
#include <iso646.h>             /* or, not */
48
45
#include <stdbool.h>            /* bool, false, true */
49
46
#include <string.h>             /* strlen, rindex, strncmp, strcmp */
52
49
                                   argp_parse(), error_t,
53
50
                                   ARGP_KEY_ARG, ARGP_KEY_END,
54
51
                                   ARGP_ERR_UNKNOWN */
55
 
#include <sysexits.h>           /* EX_SOFTWARE, EX_OSERR,
56
 
                                   EX_UNAVAILABLE, EX_IOERR, EX_OK */
57
52
 
58
 
volatile sig_atomic_t quit_now = 0;
59
 
int signal_received;
 
53
volatile bool quit_now = false;
60
54
bool debug = false;
61
 
const char *argp_program_version = "password-prompt " VERSION;
 
55
const char *argp_program_version = "password-prompt 1.0";
62
56
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
63
57
 
64
 
static void termination_handler(int signum){
65
 
  if(quit_now){
66
 
    return;
67
 
  }
68
 
  quit_now = 1;
69
 
  signal_received = signum;
 
58
static void termination_handler(__attribute__((unused))int signum){
 
59
  quit_now = true;
70
60
}
71
61
 
72
62
int main(int argc, char **argv){
86
76
        .doc = "Prefix shown before the prompt", .group = 2 },
87
77
      { .name = "debug", .key = 128,
88
78
        .doc = "Debug mode", .group = 3 },
89
 
      /*
90
 
       * These reproduce what we would get without ARGP_NO_HELP
91
 
       */
92
 
      { .name = "help", .key = '?',
93
 
        .doc = "Give this help list", .group = -1 },
94
 
      { .name = "usage", .key = -3,
95
 
        .doc = "Give a short usage message", .group = -1 },
96
 
      { .name = "version", .key = 'V',
97
 
        .doc = "Print program version", .group = -1 },
98
79
      { .name = NULL }
99
80
    };
100
 
    
101
 
    error_t parse_opt (int key, char *arg, struct argp_state *state){
102
 
      errno = 0;
103
 
      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) {
104
86
      case 'p':
105
87
        prefix = arg;
106
88
        break;
107
89
      case 128:
108
90
        debug = true;
109
91
        break;
110
 
        /*
111
 
         * These reproduce what we would get without ARGP_NO_HELP
112
 
         */
113
 
      case '?':                 /* --help */
114
 
        argp_state_help(state, state->out_stream,
115
 
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
116
 
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
117
 
      case -3:                  /* --usage */
118
 
        argp_state_help(state, state->out_stream,
119
 
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
120
 
      case 'V':                 /* --version */
121
 
        fprintf(state->out_stream, "%s\n", argp_program_version);
122
 
        exit(argp_err_exit_status);
 
92
      case ARGP_KEY_ARG:
 
93
        argp_usage (state);
 
94
        break;
 
95
      case ARGP_KEY_END:
123
96
        break;
124
97
      default:
125
98
        return ARGP_ERR_UNKNOWN;
126
99
      }
127
 
      return errno;
 
100
      return 0;
128
101
    }
129
 
    
 
102
  
130
103
    struct argp argp = { .options = options, .parser = parse_opt,
131
104
                         .args_doc = "",
132
105
                         .doc = "Mandos password-prompt -- Read and"
133
106
                         " output a password" };
134
 
    ret = argp_parse(&argp, argc, argv,
135
 
                     ARGP_IN_ORDER | ARGP_NO_HELP, NULL, NULL);
136
 
    switch(ret){
137
 
    case 0:
138
 
      break;
139
 
    case ENOMEM:
140
 
    default:
141
 
      errno = ret;
142
 
      perror("argp_parse");
143
 
      return EX_OSERR;
144
 
    case EINVAL:
145
 
      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;
146
111
    }
147
112
  }
148
 
  
149
 
  if(debug){
 
113
    
 
114
  if (debug){
150
115
    fprintf(stderr, "Starting %s\n", argv[0]);
151
116
  }
152
 
  if(debug){
 
117
  if (debug){
153
118
    fprintf(stderr, "Storing current terminal attributes\n");
154
119
  }
155
120
  
156
 
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
157
 
    int e = errno;
 
121
  if (tcgetattr(STDIN_FILENO, &t_old) != 0){
158
122
    perror("tcgetattr");
159
 
    switch(e){
160
 
    case EBADF:
161
 
    case ENOTTY:
162
 
      return EX_UNAVAILABLE;
163
 
    default:
164
 
      return EX_OSERR;
165
 
    }
 
123
    return EXIT_FAILURE;
166
124
  }
167
125
  
168
126
  sigemptyset(&new_action.sa_mask);
169
 
  ret = sigaddset(&new_action.sa_mask, SIGINT);
170
 
  if(ret == -1){
171
 
    perror("sigaddset");
172
 
    return EX_OSERR;
173
 
  }
174
 
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
175
 
  if(ret == -1){
176
 
    perror("sigaddset");
177
 
    return EX_OSERR;
178
 
  }
179
 
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
180
 
  if(ret == -1){
181
 
    perror("sigaddset");
182
 
    return EX_OSERR;
183
 
  }
184
 
  /* Need to check if the handler is SIG_IGN before handling:
185
 
     | [[info:libc:Initial Signal Actions]] |
186
 
     | [[info:libc:Basic Signal Handling]]  |
187
 
  */
 
127
  sigaddset(&new_action.sa_mask, SIGINT);
 
128
  sigaddset(&new_action.sa_mask, SIGHUP);
 
129
  sigaddset(&new_action.sa_mask, SIGTERM);
188
130
  ret = sigaction(SIGINT, NULL, &old_action);
189
131
  if(ret == -1){
190
132
    perror("sigaction");
191
 
    return EX_OSERR;
 
133
    return EXIT_FAILURE;
192
134
  }
193
 
  if(old_action.sa_handler != SIG_IGN){
 
135
  if (old_action.sa_handler != SIG_IGN){
194
136
    ret = sigaction(SIGINT, &new_action, NULL);
195
137
    if(ret == -1){
196
138
      perror("sigaction");
197
 
      return EX_OSERR;
 
139
      return EXIT_FAILURE;
198
140
    }
199
141
  }
200
142
  ret = sigaction(SIGHUP, NULL, &old_action);
201
143
  if(ret == -1){
202
144
    perror("sigaction");
203
 
    return EX_OSERR;
 
145
    return EXIT_FAILURE;
204
146
  }
205
 
  if(old_action.sa_handler != SIG_IGN){
 
147
  if (old_action.sa_handler != SIG_IGN){
206
148
    ret = sigaction(SIGHUP, &new_action, NULL);
207
149
    if(ret == -1){
208
150
      perror("sigaction");
209
 
      return EX_OSERR;
 
151
      return EXIT_FAILURE;
210
152
    }
211
153
  }
212
154
  ret = sigaction(SIGTERM, NULL, &old_action);
213
155
  if(ret == -1){
214
156
    perror("sigaction");
215
 
    return EX_OSERR;
 
157
    return EXIT_FAILURE;
216
158
  }
217
 
  if(old_action.sa_handler != SIG_IGN){
 
159
  if (old_action.sa_handler != SIG_IGN){
218
160
    ret = sigaction(SIGTERM, &new_action, NULL);
219
161
    if(ret == -1){
220
162
      perror("sigaction");
221
 
      return EX_OSERR;
 
163
      return EXIT_FAILURE;
222
164
    }
223
165
  }
224
166
  
225
167
  
226
 
  if(debug){
 
168
  if (debug){
227
169
    fprintf(stderr, "Removing echo flag from terminal attributes\n");
228
170
  }
229
171
  
230
172
  t_new = t_old;
231
 
  t_new.c_lflag &= ~(tcflag_t)ECHO;
232
 
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
233
 
    int e = errno;
 
173
  t_new.c_lflag &= ~ECHO;
 
174
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
234
175
    perror("tcsetattr-echo");
235
 
    switch(e){
236
 
    case EBADF:
237
 
    case ENOTTY:
238
 
      return EX_UNAVAILABLE;
239
 
    case EINVAL:
240
 
    default:
241
 
      return EX_OSERR;
242
 
    }
 
176
    return EXIT_FAILURE;
243
177
  }
244
 
  
245
 
  if(debug){
 
178
 
 
179
  if (debug){
246
180
    fprintf(stderr, "Waiting for input from stdin \n");
247
181
  }
248
182
  while(true){
249
 
    if(quit_now){
 
183
    if (quit_now){
250
184
      if(debug){
251
185
        fprintf(stderr, "Interrupted by signal, exiting.\n");
252
186
      }
278
212
      }
279
213
    }
280
214
    ret = getline(&buffer, &n, stdin);
281
 
    if(ret > 0){
 
215
    if (ret > 0){
282
216
      status = EXIT_SUCCESS;
283
217
      /* Make n = data size instead of allocated buffer size */
284
218
      n = (size_t)ret;
285
 
      /* Strip final newline */
286
 
      if(n > 0 and buffer[n-1] == '\n'){
287
 
        buffer[n-1] = '\0';     /* not strictly necessary */
288
 
        n--;
289
 
      }
290
219
      size_t written = 0;
291
220
      while(written < n){
292
221
        ret = write(STDOUT_FILENO, buffer + written, n - written);
293
222
        if(ret < 0){
294
 
          int e = errno;
295
223
          perror("write");
296
 
          switch(e){
297
 
          case EBADF:
298
 
          case EFAULT:
299
 
          case EINVAL:
300
 
          case EFBIG:
301
 
          case EIO:
302
 
          case ENOSPC:
303
 
          default:
304
 
            status = EX_IOERR;
305
 
            break;
306
 
          case EINTR:
307
 
            status = EXIT_FAILURE;
308
 
            break;
309
 
          }
 
224
          status = EXIT_FAILURE;
310
225
          break;
311
226
        }
312
227
        written += (size_t)ret;
313
228
      }
314
 
      ret = close(STDOUT_FILENO);
315
 
      if(ret == -1){
316
 
        int e = errno;
317
 
        perror("close");
318
 
        switch(e){
319
 
        case EBADF:
320
 
          status = EX_OSFILE;
321
 
          break;
322
 
        case EIO:
323
 
        default:
324
 
          status = EX_IOERR;
325
 
          break;
326
 
        }
327
 
      }
328
229
      break;
329
230
    }
330
 
    if(ret < 0){
331
 
      int e = errno;
332
 
      if(errno != EINTR and not feof(stdin)){
 
231
    if (ret < 0){
 
232
      if (errno != EINTR and not feof(stdin)){
333
233
        perror("getline");
334
 
        switch(e){
335
 
        case EBADF:
336
 
          status = EX_UNAVAILABLE;
337
 
        case EIO:
338
 
        case EINVAL:
339
 
        default:
340
 
          status = EX_IOERR;
341
 
          break;
342
 
        }
 
234
        status = EXIT_FAILURE;
343
235
        break;
344
236
      }
345
237
    }
347
239
       read from stdin */
348
240
    fputc('\n', stderr);
349
241
    if(debug and not quit_now){
350
 
      /* 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
351
243
         will print that later, so no need to show this too. */
352
244
      fprintf(stderr, "getline() returned 0, retrying.\n");
353
245
    }
354
246
  }
355
247
  
356
 
  free(buffer);
357
 
  
358
 
  if(debug){
 
248
  if (debug){
359
249
    fprintf(stderr, "Restoring terminal attributes\n");
360
250
  }
361
 
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
 
251
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
362
252
    perror("tcsetattr+echo");
363
253
  }
364
254
  
365
 
  if(quit_now){
366
 
    sigemptyset(&old_action.sa_mask);
367
 
    old_action.sa_handler = SIG_DFL;
368
 
    ret = sigaction(signal_received, &old_action, NULL);
369
 
    if(ret == -1){
370
 
      perror("sigaction");
371
 
    }
372
 
    raise(signal_received);
373
 
  }
374
 
  
375
 
  if(debug){
 
255
  if (debug){
376
256
    fprintf(stderr, "%s is exiting with status %d\n", argv[0],
377
257
            status);
378
258
  }
379
 
  if(status == EXIT_SUCCESS or status == EX_OK){
380
 
    fputc('\n', stderr);
381
 
  }
382
259
  
383
260
  return status;
384
261
}