/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-16 03:29:08 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080816032908-ihw7c05r2mnyk389
Add feature to specify custom environment variables for plugins.

* plugin-runner.c (plugin): New members "environ" and "envc" to
                            contain possible custom environment.
  (getplugin): Return NULL on failure instead of doing exit(); all
               callers changed.
  (add_to_char_array): New helper function for "add_argument" and
                       "add_environment".
  (addargument): Renamed to "add_argument".  Return bool.  Call
                 "add_to_char_array" to actually do things.
  (add_environment): New; analogous to "add_argument".
  (addcustomargument): Renamed to "add_to_argv" to avoid confusion
                       with "add_argument".
  (main): New options "--global-envs" and "--envs-for" to specify
          custom environment for plugins.  Print environment for
          plugins in debug mode.  Use asprintf instead of strcpy and
          strcat.  Use execve() for plugins with custom environments.
          Free environment for plugin when freeing plugin list.

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 */
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
 
#include <string.h>             /* strlen, rindex */
 
46
#include <string.h>             /* strlen, rindex, strncmp, strcmp */
50
47
#include <argp.h>               /* struct argp_option, struct
51
48
                                   argp_state, struct argp,
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){
83
73
    struct argp_option options[] = {
84
74
      { .name = "prefix", .key = 'p',
85
75
        .arg = "PREFIX", .flags = 0,
86
 
        .doc = "Prefix shown before the prompt", .group = 2 },
 
76
        .doc = "Prefix used before the passprompt", .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
 
                         .doc = "Mandos password-prompt -- Read and"
133
 
                         " 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;
 
105
                         .doc = "Mandos Passprompt -- Provides a passprompt" };
 
106
    ret = argp_parse (&argp, argc, argv, 0, 0, NULL);
 
107
    if (ret == ARGP_ERR_UNKNOWN){
 
108
      fprintf(stderr, "Unknown error while parsing arguments\n");
 
109
      return EXIT_FAILURE;
146
110
    }
147
111
  }
148
 
  
149
 
  if(debug){
 
112
    
 
113
  if (debug){
150
114
    fprintf(stderr, "Starting %s\n", argv[0]);
151
115
  }
152
 
  if(debug){
 
116
  if (debug){
153
117
    fprintf(stderr, "Storing current terminal attributes\n");
154
118
  }
155
119
  
156
 
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
157
 
    int e = errno;
158
 
    perror("tcgetattr");
159
 
    switch(e){
160
 
    case EBADF:
161
 
    case ENOTTY:
162
 
      return EX_UNAVAILABLE;
163
 
    default:
164
 
      return EX_OSERR;
165
 
    }
 
120
  if (tcgetattr(STDIN_FILENO, &t_old) != 0){
 
121
    return EXIT_FAILURE;
166
122
  }
167
123
  
168
124
  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
 
  */
 
125
  sigaddset(&new_action.sa_mask, SIGINT);
 
126
  sigaddset(&new_action.sa_mask, SIGHUP);
 
127
  sigaddset(&new_action.sa_mask, SIGTERM);
188
128
  ret = sigaction(SIGINT, NULL, &old_action);
189
129
  if(ret == -1){
190
130
    perror("sigaction");
191
 
    return EX_OSERR;
 
131
    return EXIT_FAILURE;
192
132
  }
193
 
  if(old_action.sa_handler != SIG_IGN){
 
133
  if (old_action.sa_handler != SIG_IGN){
194
134
    ret = sigaction(SIGINT, &new_action, NULL);
195
135
    if(ret == -1){
196
136
      perror("sigaction");
197
 
      return EX_OSERR;
 
137
      return EXIT_FAILURE;
198
138
    }
199
139
  }
200
140
  ret = sigaction(SIGHUP, NULL, &old_action);
201
141
  if(ret == -1){
202
142
    perror("sigaction");
203
 
    return EX_OSERR;
 
143
    return EXIT_FAILURE;
204
144
  }
205
 
  if(old_action.sa_handler != SIG_IGN){
 
145
  if (old_action.sa_handler != SIG_IGN){
206
146
    ret = sigaction(SIGHUP, &new_action, NULL);
207
147
    if(ret == -1){
208
148
      perror("sigaction");
209
 
      return EX_OSERR;
 
149
      return EXIT_FAILURE;
210
150
    }
211
151
  }
212
152
  ret = sigaction(SIGTERM, NULL, &old_action);
213
153
  if(ret == -1){
214
154
    perror("sigaction");
215
 
    return EX_OSERR;
 
155
    return EXIT_FAILURE;
216
156
  }
217
 
  if(old_action.sa_handler != SIG_IGN){
 
157
  if (old_action.sa_handler != SIG_IGN){
218
158
    ret = sigaction(SIGTERM, &new_action, NULL);
219
159
    if(ret == -1){
220
160
      perror("sigaction");
221
 
      return EX_OSERR;
 
161
      return EXIT_FAILURE;
222
162
    }
223
163
  }
224
164
  
225
165
  
226
 
  if(debug){
 
166
  if (debug){
227
167
    fprintf(stderr, "Removing echo flag from terminal attributes\n");
228
168
  }
229
169
  
230
170
  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;
 
171
  t_new.c_lflag &= ~ECHO;
 
172
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
234
173
    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
 
    }
 
174
    return EXIT_FAILURE;
243
175
  }
244
 
  
245
 
  if(debug){
 
176
 
 
177
  if (debug){
246
178
    fprintf(stderr, "Waiting for input from stdin \n");
247
179
  }
248
180
  while(true){
249
 
    if(quit_now){
250
 
      if(debug){
251
 
        fprintf(stderr, "Interrupted by signal, exiting.\n");
252
 
      }
 
181
    if (quit_now){
253
182
      status = EXIT_FAILURE;
254
183
      break;
255
184
    }
256
185
 
257
186
    if(prefix){
258
 
      fprintf(stderr, "%s ", prefix);
259
 
    }
260
 
    {
261
 
      const char *cryptsource = getenv("CRYPTTAB_SOURCE");
262
 
      const char *crypttarget = getenv("CRYPTTAB_NAME");
263
 
      /* Before cryptsetup 1.1.0~rc2 */
264
 
      if(cryptsource == NULL){
265
 
        cryptsource = getenv("cryptsource");
266
 
      }
267
 
      if(crypttarget == NULL){
268
 
        crypttarget = getenv("crypttarget");
269
 
      }
270
 
      const char *const prompt1 = "Unlocking the disk";
271
 
      const char *const prompt2 = "Enter passphrase";
272
 
      if(cryptsource == NULL){
273
 
        if(crypttarget == NULL){
274
 
          fprintf(stderr, "%s to unlock the disk: ", prompt2);
275
 
        } else {
276
 
          fprintf(stderr, "%s (%s)\n%s: ", prompt1, crypttarget,
277
 
                  prompt2);
278
 
        }
279
 
      } else {
280
 
        if(crypttarget == NULL){
281
 
          fprintf(stderr, "%s %s\n%s: ", prompt1, cryptsource,
282
 
                  prompt2);
283
 
        } else {
284
 
          fprintf(stderr, "%s %s (%s)\n%s: ", prompt1, cryptsource,
285
 
                  crypttarget, prompt2);
286
 
        }
287
 
      }
288
 
    }
 
187
      fprintf(stderr, "%s Password: ", prefix);
 
188
    } else {
 
189
      fprintf(stderr, "Password: ");
 
190
    }      
289
191
    ret = getline(&buffer, &n, stdin);
290
 
    if(ret > 0){
 
192
    if (ret > 0){
 
193
      fprintf(stdout, "%s", buffer);
291
194
      status = EXIT_SUCCESS;
292
 
      /* Make n = data size instead of allocated buffer size */
293
 
      n = (size_t)ret;
294
 
      /* Strip final newline */
295
 
      if(n > 0 and buffer[n-1] == '\n'){
296
 
        buffer[n-1] = '\0';     /* not strictly necessary */
297
 
        n--;
298
 
      }
299
 
      size_t written = 0;
300
 
      while(written < n){
301
 
        ret = write(STDOUT_FILENO, buffer + written, n - written);
302
 
        if(ret < 0){
303
 
          int e = errno;
304
 
          perror("write");
305
 
          switch(e){
306
 
          case EBADF:
307
 
          case EFAULT:
308
 
          case EINVAL:
309
 
          case EFBIG:
310
 
          case EIO:
311
 
          case ENOSPC:
312
 
          default:
313
 
            status = EX_IOERR;
314
 
            break;
315
 
          case EINTR:
316
 
            status = EXIT_FAILURE;
317
 
            break;
318
 
          }
319
 
          break;
320
 
        }
321
 
        written += (size_t)ret;
322
 
      }
323
 
      ret = close(STDOUT_FILENO);
324
 
      if(ret == -1){
325
 
        int e = errno;
326
 
        perror("close");
327
 
        switch(e){
328
 
        case EBADF:
329
 
          status = EX_OSFILE;
330
 
          break;
331
 
        case EIO:
332
 
        default:
333
 
          status = EX_IOERR;
334
 
          break;
335
 
        }
336
 
      }
337
195
      break;
338
196
    }
339
 
    if(ret < 0){
340
 
      int e = errno;
341
 
      if(errno != EINTR and not feof(stdin)){
 
197
    if (ret < 0){
 
198
      if (errno != EINTR and not feof(stdin)){
342
199
        perror("getline");
343
 
        switch(e){
344
 
        case EBADF:
345
 
          status = EX_UNAVAILABLE;
346
 
        case EIO:
347
 
        case EINVAL:
348
 
        default:
349
 
          status = EX_IOERR;
350
 
          break;
351
 
        }
 
200
        status = EXIT_FAILURE;
352
201
        break;
353
202
      }
354
203
    }
355
204
    /* if(ret == 0), then the only sensible thing to do is to retry to
356
205
       read from stdin */
357
206
    fputc('\n', stderr);
358
 
    if(debug and not quit_now){
359
 
      /* If quit_now is nonzero, we were interrupted by a signal, and
360
 
         will print that later, so no need to show this too. */
361
 
      fprintf(stderr, "getline() returned 0, retrying.\n");
362
 
    }
363
207
  }
364
208
  
365
 
  free(buffer);
366
 
  
367
 
  if(debug){
 
209
  if (debug){
368
210
    fprintf(stderr, "Restoring terminal attributes\n");
369
211
  }
370
 
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
 
212
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
371
213
    perror("tcsetattr+echo");
372
214
  }
373
215
  
374
 
  if(quit_now){
375
 
    sigemptyset(&old_action.sa_mask);
376
 
    old_action.sa_handler = SIG_DFL;
377
 
    ret = sigaction(signal_received, &old_action, NULL);
378
 
    if(ret == -1){
379
 
      perror("sigaction");
380
 
    }
381
 
    raise(signal_received);
382
 
  }
383
 
  
384
 
  if(debug){
385
 
    fprintf(stderr, "%s is exiting with status %d\n", argv[0],
386
 
            status);
387
 
  }
388
 
  if(status == EXIT_SUCCESS or status == EX_OK){
389
 
    fputc('\n', stderr);
 
216
  if (debug){
 
217
    fprintf(stderr, "%s is exiting\n", argv[0]);
390
218
  }
391
219
  
392
220
  return status;