/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 plugin-runner.c

  • Committer: Teddy Hogeborn
  • Date: 2015-07-20 03:03:33 UTC
  • Revision ID: teddy@recompile.se-20150720030333-203m2aeblypcsfte
Bug fix for GnuTLS 3: be compatible with old 2048-bit DSA keys.

The mandos-keygen program in Mandos version 1.6.0 and older generated
2048-bit DSA keys, and when GnuTLS uses these it has trouble
connecting using the Mandos default priority string.  This was
previously fixed in Mandos 1.6.2, but the bug reappeared when using
GnuTLS 3, so the default priority string has to change again; this
time also the Mandos client has to change its default, so now the
server and the client should use the same default priority string:

SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256

* mandos (main/server_defaults): Changed default priority string.
* mandos-options.xml (/section/para[id="priority_compat"]): Removed.
  (/section/para[id="priority"]): Changed default priority string.
* mandos.conf ([DEFAULT]/priority): - '' -
* mandos.conf.xml (OPTIONS/priority): Refer to the id "priority"
                                      instead of "priority_compat".
* mandos.xml (OPTIONS/--priority): - '' -
* plugins.d/mandos-client.c (main): Changed default priority string.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  -*- coding: utf-8 -*- */
 
1
/*  -*- coding: utf-8; mode: c; mode: orgtbl -*- */
2
2
/*
3
3
 * Mandos plugin runner - Run Mandos plugins
4
4
 *
5
 
 * Copyright © 2008,2009 Teddy Hogeborn
6
 
 * Copyright © 2008,2009 Björn Påhlsson
 
5
 * Copyright © 2008-2014 Teddy Hogeborn
 
6
 * Copyright © 2008-2014 Björn Påhlsson
7
7
 * 
8
8
 * This program is free software: you can redistribute it and/or
9
9
 * modify it under the terms of the GNU General Public License as
19
19
 * along with this program.  If not, see
20
20
 * <http://www.gnu.org/licenses/>.
21
21
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
22
 * Contact the authors at <mandos@recompile.se>.
23
23
 */
24
24
 
25
25
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
26
 
                                   asprintf() */
 
26
                                   O_CLOEXEC, pipe2() */
27
27
#include <stddef.h>             /* size_t, NULL */
28
 
#include <stdlib.h>             /* malloc(), exit(), EXIT_FAILURE,
29
 
                                   EXIT_SUCCESS, realloc() */
 
28
#include <stdlib.h>             /* malloc(), exit(), EXIT_SUCCESS,
 
29
                                   realloc() */
30
30
#include <stdbool.h>            /* bool, true, false */
31
 
#include <stdio.h>              /* perror, fileno(), fprintf(),
32
 
                                   stderr, STDOUT_FILENO */
33
 
#include <sys/types.h>          /* DIR, opendir(), stat(), struct
34
 
                                   stat, waitpid(), WIFEXITED(),
35
 
                                   WEXITSTATUS(), wait(), pid_t,
36
 
                                   uid_t, gid_t, getuid(), getgid(),
37
 
                                   dirfd() */
 
31
#include <stdio.h>              /* fileno(), fprintf(),
 
32
                                   stderr, STDOUT_FILENO, fclose() */
 
33
#include <sys/types.h>          /* fstat(), struct stat, waitpid(),
 
34
                                   WIFEXITED(), WEXITSTATUS(), wait(),
 
35
                                   pid_t, uid_t, gid_t, getuid(),
 
36
                                   getgid() */
38
37
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
39
38
                                   FD_SET(), FD_ISSET(), FD_CLR */
40
39
#include <sys/wait.h>           /* wait(), waitpid(), WIFEXITED(),
41
40
                                   WEXITSTATUS(), WTERMSIG(),
42
41
                                   WCOREDUMP() */
43
 
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
 
42
#include <sys/stat.h>           /* struct stat, fstat(), S_ISREG() */
44
43
#include <iso646.h>             /* and, or, not */
45
 
#include <dirent.h>             /* DIR, struct dirent, opendir(),
46
 
                                   readdir(), closedir(), dirfd() */
47
 
#include <unistd.h>             /* struct stat, stat(), S_ISREG(),
48
 
                                   fcntl(), setuid(), setgid(),
49
 
                                   F_GETFD, F_SETFD, FD_CLOEXEC,
50
 
                                   access(), pipe(), fork(), close()
51
 
                                   dup2(), STDOUT_FILENO, _exit(),
52
 
                                   execv(), write(), read(),
53
 
                                   close() */
 
44
#include <dirent.h>             /* struct dirent, scandirat() */
 
45
#include <unistd.h>             /* fcntl(), F_GETFD, F_SETFD,
 
46
                                   FD_CLOEXEC, write(), STDOUT_FILENO,
 
47
                                   struct stat, fstat(), close(),
 
48
                                   setgid(), setuid(), S_ISREG(),
 
49
                                   faccessat() pipe2(), fork(),
 
50
                                   _exit(), dup2(), fexecve(), read()
 
51
                                */
54
52
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
55
 
                                   FD_CLOEXEC */
56
 
#include <string.h>             /* strsep, strlen(), asprintf(),
57
 
                                   strsignal() */
 
53
                                   FD_CLOEXEC, openat(), scandirat(),
 
54
                                   pipe2() */
 
55
#include <string.h>             /* strsep, strlen(), strsignal(),
 
56
                                   strcmp(), strncmp() */
58
57
#include <errno.h>              /* errno */
59
58
#include <argp.h>               /* struct argp_option, struct
60
59
                                   argp_state, struct argp,
68
67
                                */
69
68
#include <errno.h>              /* errno, EBADF */
70
69
#include <inttypes.h>           /* intmax_t, PRIdMAX, strtoimax() */
 
70
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_IOERR,
 
71
                                   EX_CONFIG, EX_UNAVAILABLE, EX_OK */
 
72
#include <errno.h>              /* errno */
 
73
#include <error.h>              /* error() */
 
74
#include <fnmatch.h>            /* fnmatch() */
71
75
 
72
76
#define BUFFER_SIZE 256
73
77
 
74
78
#define PDIR "/lib/mandos/plugins.d"
 
79
#define PHDIR "/lib/mandos/plugin-helpers"
75
80
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
76
81
 
77
82
const char *argp_program_version = "plugin-runner " VERSION;
78
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
83
const char *argp_program_bug_address = "<mandos@recompile.se>";
79
84
 
80
85
typedef struct plugin{
81
86
  char *name;                   /* can be NULL or any plugin name */
101
106
 
102
107
/* Gets an existing plugin based on name,
103
108
   or if none is found, creates a new one */
 
109
__attribute__((warn_unused_result))
104
110
static plugin *getplugin(char *name){
105
 
  /* Check for exiting plugin with that name */
 
111
  /* Check for existing plugin with that name */
106
112
  for(plugin *p = plugin_list; p != NULL; p = p->next){
107
113
    if((p->name == name)
108
114
       or (p->name and name and (strcmp(p->name, name) == 0))){
110
116
    }
111
117
  }
112
118
  /* Create a new plugin */
113
 
  plugin *new_plugin = malloc(sizeof(plugin));
 
119
  plugin *new_plugin = NULL;
 
120
  do {
 
121
    new_plugin = malloc(sizeof(plugin));
 
122
  } while(new_plugin == NULL and errno == EINTR);
114
123
  if(new_plugin == NULL){
115
124
    return NULL;
116
125
  }
117
126
  char *copy_name = NULL;
118
127
  if(name != NULL){
119
 
    copy_name = strdup(name);
 
128
    do {
 
129
      copy_name = strdup(name);
 
130
    } while(copy_name == NULL and errno == EINTR);
120
131
    if(copy_name == NULL){
 
132
      int e = errno;
121
133
      free(new_plugin);
 
134
      errno = e;
122
135
      return NULL;
123
136
    }
124
137
  }
128
141
                          .disabled = false,
129
142
                          .next = plugin_list };
130
143
  
131
 
  new_plugin->argv = malloc(sizeof(char *) * 2);
 
144
  do {
 
145
    new_plugin->argv = malloc(sizeof(char *) * 2);
 
146
  } while(new_plugin->argv == NULL and errno == EINTR);
132
147
  if(new_plugin->argv == NULL){
 
148
    int e = errno;
133
149
    free(copy_name);
134
150
    free(new_plugin);
 
151
    errno = e;
135
152
    return NULL;
136
153
  }
137
154
  new_plugin->argv[0] = copy_name;
138
155
  new_plugin->argv[1] = NULL;
139
156
  
140
 
  new_plugin->environ = malloc(sizeof(char *));
 
157
  do {
 
158
    new_plugin->environ = malloc(sizeof(char *));
 
159
  } while(new_plugin->environ == NULL and errno == EINTR);
141
160
  if(new_plugin->environ == NULL){
 
161
    int e = errno;
142
162
    free(copy_name);
143
163
    free(new_plugin->argv);
144
164
    free(new_plugin);
 
165
    errno = e;
145
166
    return NULL;
146
167
  }
147
168
  new_plugin->environ[0] = NULL;
152
173
}
153
174
 
154
175
/* Helper function for add_argument and add_environment */
 
176
__attribute__((nonnull, warn_unused_result))
155
177
static bool add_to_char_array(const char *new, char ***array,
156
178
                              int *len){
157
179
  /* Resize the pointed-to array to hold one more pointer */
158
 
  *array = realloc(*array, sizeof(char *)
159
 
                   * (size_t) ((*len) + 2));
 
180
  char **new_array = NULL;
 
181
  do {
 
182
    new_array = realloc(*array, sizeof(char *)
 
183
                        * (size_t) ((*len) + 2));
 
184
  } while(new_array == NULL and errno == EINTR);
160
185
  /* Malloc check */
161
 
  if(*array == NULL){
 
186
  if(new_array == NULL){
162
187
    return false;
163
188
  }
 
189
  *array = new_array;
164
190
  /* Make a copy of the new string */
165
 
  char *copy = strdup(new);
 
191
  char *copy;
 
192
  do {
 
193
    copy = strdup(new);
 
194
  } while(copy == NULL and errno == EINTR);
166
195
  if(copy == NULL){
167
196
    return false;
168
197
  }
175
204
}
176
205
 
177
206
/* Add to a plugin's argument vector */
 
207
__attribute__((nonnull(2), warn_unused_result))
178
208
static bool add_argument(plugin *p, const char *arg){
179
209
  if(p == NULL){
180
210
    return false;
183
213
}
184
214
 
185
215
/* Add to a plugin's environment */
 
216
__attribute__((nonnull(2), warn_unused_result))
186
217
static bool add_environment(plugin *p, const char *def, bool replace){
187
218
  if(p == NULL){
188
219
    return false;
190
221
  /* namelen = length of name of environment variable */
191
222
  size_t namelen = (size_t)(strchrnul(def, '=') - def);
192
223
  /* Search for this environment variable */
193
 
  for(char **e = p->environ; *e != NULL; e++){
194
 
    if(strncmp(*e, def, namelen + 1) == 0){
 
224
  for(char **envdef = p->environ; *envdef != NULL; envdef++){
 
225
    if(strncmp(*envdef, def, namelen + 1) == 0){
195
226
      /* It already exists */
196
227
      if(replace){
197
 
        char *new = realloc(*e, strlen(def) + 1);
198
 
        if(new == NULL){
 
228
        char *new_envdef;
 
229
        do {
 
230
          new_envdef = realloc(*envdef, strlen(def) + 1);
 
231
        } while(new_envdef == NULL and errno == EINTR);
 
232
        if(new_envdef == NULL){
199
233
          return false;
200
234
        }
201
 
        *e = new;
202
 
        strcpy(*e, def);
 
235
        *envdef = new_envdef;
 
236
        strcpy(*envdef, def);
203
237
      }
204
238
      return true;
205
239
    }
207
241
  return add_to_char_array(def, &(p->environ), &(p->envc));
208
242
}
209
243
 
 
244
#ifndef O_CLOEXEC
210
245
/*
211
246
 * Based on the example in the GNU LibC manual chapter 13.13 "File
212
247
 * Descriptor Flags".
213
 
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
 
248
 | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
214
249
 */
 
250
__attribute__((warn_unused_result))
215
251
static int set_cloexec_flag(int fd){
216
 
  int ret = fcntl(fd, F_GETFD, 0);
 
252
  int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
217
253
  /* If reading the flags failed, return error indication now. */
218
254
  if(ret < 0){
219
255
    return ret;
220
256
  }
221
257
  /* Store modified flag word in the descriptor. */
222
 
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
 
258
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
 
259
                                       ret | FD_CLOEXEC));
223
260
}
 
261
#endif  /* not O_CLOEXEC */
224
262
 
225
263
 
226
264
/* Mark processes as completed when they exit, and save their exit
240
278
        /* No child processes */
241
279
        break;
242
280
      }
243
 
      perror("waitpid");
 
281
      error(0, errno, "waitpid");
244
282
    }
245
283
    
246
284
    /* A child exited, find it in process_list */
258
296
}
259
297
 
260
298
/* Prints out a password to stdout */
 
299
__attribute__((nonnull, warn_unused_result))
261
300
static bool print_out_password(const char *buffer, size_t length){
262
301
  ssize_t ret;
263
302
  for(size_t written = 0; written < length; written += (size_t)ret){
271
310
}
272
311
 
273
312
/* Removes and free a plugin from the plugin list */
 
313
__attribute__((nonnull))
274
314
static void free_plugin(plugin *plugin_node){
275
315
  
276
316
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
308
348
 
309
349
int main(int argc, char *argv[]){
310
350
  char *plugindir = NULL;
 
351
  char *pluginhelperdir = NULL;
311
352
  char *argfile = NULL;
312
353
  FILE *conffp;
313
 
  size_t d_name_len;
314
 
  DIR *dir = NULL;
315
 
  struct dirent *dirst;
 
354
  struct dirent **direntries = NULL;
316
355
  struct stat st;
317
356
  fd_set rfds_all;
318
357
  int ret, maxfd = 0;
319
358
  ssize_t sret;
320
 
  intmax_t tmpmax;
321
359
  uid_t uid = 65534;
322
360
  gid_t gid = 65534;
323
361
  bool debug = false;
327
365
                                      .sa_flags = SA_NOCLDSTOP };
328
366
  char **custom_argv = NULL;
329
367
  int custom_argc = 0;
 
368
  int dir_fd = -1;
330
369
  
331
370
  /* Establish a signal handler */
332
371
  sigemptyset(&sigchld_action.sa_mask);
333
372
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
334
373
  if(ret == -1){
335
 
    perror("sigaddset");
336
 
    exitstatus = EXIT_FAILURE;
 
374
    error(0, errno, "sigaddset");
 
375
    exitstatus = EX_OSERR;
337
376
    goto fallback;
338
377
  }
339
378
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
340
379
  if(ret == -1){
341
 
    perror("sigaction");
342
 
    exitstatus = EXIT_FAILURE;
 
380
    error(0, errno, "sigaction");
 
381
    exitstatus = EX_OSERR;
343
382
    goto fallback;
344
383
  }
345
384
  
377
416
      .doc = "Group ID the plugins will run as", .group = 3 },
378
417
    { .name = "debug", .key = 132,
379
418
      .doc = "Debug mode", .group = 4 },
 
419
    { .name = "plugin-helper-dir", .key = 133,
 
420
      .arg = "DIRECTORY",
 
421
      .doc = "Specify a different plugin helper directory",
 
422
      .group = 2 },
 
423
    /*
 
424
     * These reproduce what we would get without ARGP_NO_HELP
 
425
     */
 
426
    { .name = "help", .key = '?',
 
427
      .doc = "Give this help list", .group = -1 },
 
428
    { .name = "usage", .key = -3,
 
429
      .doc = "Give a short usage message", .group = -1 },
 
430
    { .name = "version", .key = 'V',
 
431
      .doc = "Print program version", .group = -1 },
380
432
    { .name = NULL }
381
433
  };
382
434
  
383
 
  error_t parse_opt(int key, char *arg, __attribute__((unused))
384
 
                    struct argp_state *state){
385
 
    char *tmp;
 
435
  __attribute__((nonnull(3)))
 
436
  error_t parse_opt(int key, char *arg, struct argp_state *state){
 
437
    errno = 0;
386
438
    switch(key){
 
439
      char *tmp;
 
440
      intmax_t tmp_id;
387
441
    case 'g':                   /* --global-options */
388
 
      if(arg != NULL){
 
442
      {
389
443
        char *plugin_option;
390
444
        while((plugin_option = strsep(&arg, ",")) != NULL){
391
 
          if(plugin_option[0] == '\0'){
392
 
            continue;
393
 
          }
394
445
          if(not add_argument(getplugin(NULL), plugin_option)){
395
 
            perror("add_argument");
396
 
            return ARGP_ERR_UNKNOWN;
 
446
            break;
397
447
          }
398
448
        }
 
449
        errno = 0;
399
450
      }
400
451
      break;
401
452
    case 'G':                   /* --global-env */
402
 
      if(arg == NULL){
403
 
        break;
404
 
      }
405
 
      if(not add_environment(getplugin(NULL), arg, true)){
406
 
        perror("add_environment");
 
453
      if(add_environment(getplugin(NULL), arg, true)){
 
454
        errno = 0;
407
455
      }
408
456
      break;
409
457
    case 'o':                   /* --options-for */
410
 
      if(arg != NULL){
411
 
        char *plugin_name = strsep(&arg, ":");
412
 
        if(plugin_name[0] == '\0'){
413
 
          break;
414
 
        }
415
 
        char *plugin_option;
416
 
        while((plugin_option = strsep(&arg, ",")) != NULL){
417
 
          if(not add_argument(getplugin(plugin_name), plugin_option)){
418
 
            perror("add_argument");
419
 
            return ARGP_ERR_UNKNOWN;
 
458
      {
 
459
        char *option_list = strchr(arg, ':');
 
460
        if(option_list == NULL){
 
461
          argp_error(state, "No colon in \"%s\"", arg);
 
462
          errno = EINVAL;
 
463
          break;
 
464
        }
 
465
        *option_list = '\0';
 
466
        option_list++;
 
467
        if(arg[0] == '\0'){
 
468
          argp_error(state, "Empty plugin name");
 
469
          errno = EINVAL;
 
470
          break;
 
471
        }
 
472
        char *option;
 
473
        while((option = strsep(&option_list, ",")) != NULL){
 
474
          if(not add_argument(getplugin(arg), option)){
 
475
            break;
420
476
          }
421
477
        }
 
478
        errno = 0;
422
479
      }
423
480
      break;
424
481
    case 'E':                   /* --env-for */
425
 
      if(arg == NULL){
426
 
        break;
427
 
      }
428
482
      {
429
483
        char *envdef = strchr(arg, ':');
430
484
        if(envdef == NULL){
 
485
          argp_error(state, "No colon in \"%s\"", arg);
 
486
          errno = EINVAL;
431
487
          break;
432
488
        }
433
489
        *envdef = '\0';
434
 
        if(not add_environment(getplugin(arg), envdef+1, true)){
435
 
          perror("add_environment");
 
490
        envdef++;
 
491
        if(arg[0] == '\0'){
 
492
          argp_error(state, "Empty plugin name");
 
493
          errno = EINVAL;
 
494
          break;
 
495
        }
 
496
        if(add_environment(getplugin(arg), envdef, true)){
 
497
          errno = 0;
436
498
        }
437
499
      }
438
500
      break;
439
501
    case 'd':                   /* --disable */
440
 
      if(arg != NULL){
 
502
      {
441
503
        plugin *p = getplugin(arg);
442
 
        if(p == NULL){
443
 
          return ARGP_ERR_UNKNOWN;
 
504
        if(p != NULL){
 
505
          p->disabled = true;
 
506
          errno = 0;
444
507
        }
445
 
        p->disabled = true;
446
508
      }
447
509
      break;
448
510
    case 'e':                   /* --enable */
449
 
      if(arg != NULL){
 
511
      {
450
512
        plugin *p = getplugin(arg);
451
 
        if(p == NULL){
452
 
          return ARGP_ERR_UNKNOWN;
 
513
        if(p != NULL){
 
514
          p->disabled = false;
 
515
          errno = 0;
453
516
        }
454
 
        p->disabled = false;
455
517
      }
456
518
      break;
457
519
    case 128:                   /* --plugin-dir */
458
520
      free(plugindir);
459
521
      plugindir = strdup(arg);
460
 
      if(plugindir == NULL){
461
 
        perror("strdup");
 
522
      if(plugindir != NULL){
 
523
        errno = 0;
462
524
      }
463
525
      break;
464
526
    case 129:                   /* --config-file */
465
527
      /* This is already done by parse_opt_config_file() */
466
528
      break;
467
529
    case 130:                   /* --userid */
468
 
      errno = 0;
469
 
      tmpmax = strtoimax(arg, &tmp, 10);
 
530
      tmp_id = strtoimax(arg, &tmp, 10);
470
531
      if(errno != 0 or tmp == arg or *tmp != '\0'
471
 
         or tmpmax != (uid_t)tmpmax){
472
 
        fprintf(stderr, "Bad user ID number: \"%s\", using %"
473
 
                PRIdMAX "\n", arg, (intmax_t)uid);
474
 
      } else {
475
 
        uid = (uid_t)tmpmax;
 
532
         or tmp_id != (uid_t)tmp_id){
 
533
        argp_error(state, "Bad user ID number: \"%s\", using %"
 
534
                   PRIdMAX, arg, (intmax_t)uid);
 
535
        break;
476
536
      }
 
537
      uid = (uid_t)tmp_id;
 
538
      errno = 0;
477
539
      break;
478
540
    case 131:                   /* --groupid */
479
 
      errno = 0;
480
 
      tmpmax = strtoimax(arg, &tmp, 10);
 
541
      tmp_id = strtoimax(arg, &tmp, 10);
481
542
      if(errno != 0 or tmp == arg or *tmp != '\0'
482
 
         or tmpmax != (gid_t)tmpmax){
483
 
        fprintf(stderr, "Bad group ID number: \"%s\", using %"
484
 
                PRIdMAX "\n", arg, (intmax_t)gid);
485
 
      } else {
486
 
        gid = (gid_t)tmpmax;
 
543
         or tmp_id != (gid_t)tmp_id){
 
544
        argp_error(state, "Bad group ID number: \"%s\", using %"
 
545
                   PRIdMAX, arg, (intmax_t)gid);
 
546
        break;
487
547
      }
 
548
      gid = (gid_t)tmp_id;
 
549
      errno = 0;
488
550
      break;
489
551
    case 132:                   /* --debug */
490
552
      debug = true;
491
553
      break;
 
554
    case 133:                   /* --plugin-helper-dir */
 
555
      free(pluginhelperdir);
 
556
      pluginhelperdir = strdup(arg);
 
557
      if(pluginhelperdir != NULL){
 
558
        errno = 0;
 
559
      }
 
560
      break;
 
561
      /*
 
562
       * These reproduce what we would get without ARGP_NO_HELP
 
563
       */
 
564
    case '?':                   /* --help */
 
565
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
 
566
      argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
 
567
    case -3:                    /* --usage */
 
568
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
 
569
      argp_state_help(state, state->out_stream,
 
570
                      ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
 
571
    case 'V':                   /* --version */
 
572
      fprintf(state->out_stream, "%s\n", argp_program_version);
 
573
      exit(EXIT_SUCCESS);
 
574
      break;
492
575
/*
493
576
 * When adding more options before this line, remember to also add a
494
577
 * "case" to the "parse_opt_config_file" function below.
497
580
      /* Cryptsetup always passes an argument, which is an empty
498
581
         string if "none" was specified in /etc/crypttab.  So if
499
582
         argument was empty, we ignore it silently. */
500
 
      if(arg[0] != '\0'){
501
 
        fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
 
583
      if(arg[0] == '\0'){
 
584
        break;
502
585
      }
503
 
      break;
504
 
    case ARGP_KEY_END:
505
 
      break;
506
586
    default:
507
587
      return ARGP_ERR_UNKNOWN;
508
588
    }
509
 
    return 0;
 
589
    return errno;               /* Set to 0 at start */
510
590
  }
511
591
  
512
592
  /* This option parser is the same as parse_opt() above, except it
514
594
  error_t parse_opt_config_file(int key, char *arg,
515
595
                                __attribute__((unused))
516
596
                                struct argp_state *state){
 
597
    errno = 0;
517
598
    switch(key){
518
599
    case 'g':                   /* --global-options */
519
600
    case 'G':                   /* --global-env */
526
607
    case 129:                   /* --config-file */
527
608
      free(argfile);
528
609
      argfile = strdup(arg);
529
 
      if(argfile == NULL){
530
 
        perror("strdup");
 
610
      if(argfile != NULL){
 
611
        errno = 0;
531
612
      }
532
613
      break;
533
614
    case 130:                   /* --userid */
534
615
    case 131:                   /* --groupid */
535
616
    case 132:                   /* --debug */
 
617
    case 133:                   /* --plugin-helper-dir */
 
618
    case '?':                   /* --help */
 
619
    case -3:                    /* --usage */
 
620
    case 'V':                   /* --version */
536
621
    case ARGP_KEY_ARG:
537
 
    case ARGP_KEY_END:
538
622
      break;
539
623
    default:
540
624
      return ARGP_ERR_UNKNOWN;
541
625
    }
542
 
    return 0;
 
626
    return errno;
543
627
  }
544
628
  
545
629
  struct argp argp = { .options = options,
549
633
  
550
634
  /* Parse using parse_opt_config_file() in order to get the custom
551
635
     config file location, if any. */
552
 
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
553
 
  if(ret == ARGP_ERR_UNKNOWN){
554
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
555
 
    exitstatus = EXIT_FAILURE;
 
636
  ret = argp_parse(&argp, argc, argv,
 
637
                   ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP,
 
638
                   NULL, NULL);
 
639
  switch(ret){
 
640
  case 0:
 
641
    break;
 
642
  case ENOMEM:
 
643
  default:
 
644
    errno = ret;
 
645
    error(0, errno, "argp_parse");
 
646
    exitstatus = EX_OSERR;
 
647
    goto fallback;
 
648
  case EINVAL:
 
649
    exitstatus = EX_USAGE;
556
650
    goto fallback;
557
651
  }
558
652
  
575
669
    custom_argc = 1;
576
670
    custom_argv = malloc(sizeof(char*) * 2);
577
671
    if(custom_argv == NULL){
578
 
      perror("malloc");
579
 
      exitstatus = EXIT_FAILURE;
 
672
      error(0, errno, "malloc");
 
673
      exitstatus = EX_OSERR;
580
674
      goto fallback;
581
675
    }
582
676
    custom_argv[0] = argv[0];
598
692
        }
599
693
        new_arg = strdup(p);
600
694
        if(new_arg == NULL){
601
 
          perror("strdup");
602
 
          exitstatus = EXIT_FAILURE;
 
695
          error(0, errno, "strdup");
 
696
          exitstatus = EX_OSERR;
603
697
          free(org_line);
604
698
          goto fallback;
605
699
        }
606
700
        
607
701
        custom_argc += 1;
608
 
        custom_argv = realloc(custom_argv, sizeof(char *)
609
 
                              * ((unsigned int) custom_argc + 1));
610
 
        if(custom_argv == NULL){
611
 
          perror("realloc");
612
 
          exitstatus = EXIT_FAILURE;
613
 
          free(org_line);
614
 
          goto fallback;
 
702
        {
 
703
          char **new_argv = realloc(custom_argv, sizeof(char *)
 
704
                                    * ((unsigned int)
 
705
                                       custom_argc + 1));
 
706
          if(new_argv == NULL){
 
707
            error(0, errno, "realloc");
 
708
            exitstatus = EX_OSERR;
 
709
            free(new_arg);
 
710
            free(org_line);
 
711
            goto fallback;
 
712
          } else {
 
713
            custom_argv = new_argv;
 
714
          }
615
715
        }
616
716
        custom_argv[custom_argc-1] = new_arg;
617
717
        custom_argv[custom_argc] = NULL;
618
718
      }
619
719
    }
 
720
    do {
 
721
      ret = fclose(conffp);
 
722
    } while(ret == EOF and errno == EINTR);
 
723
    if(ret == EOF){
 
724
      error(0, errno, "fclose");
 
725
      exitstatus = EX_IOERR;
 
726
      goto fallback;
 
727
    }
620
728
    free(org_line);
621
729
  } else {
622
730
    /* Check for harmful errors and go to fallback. Other errors might
623
731
       not affect opening plugins */
624
732
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
625
 
      perror("fopen");
626
 
      exitstatus = EXIT_FAILURE;
 
733
      error(0, errno, "fopen");
 
734
      exitstatus = EX_OSERR;
627
735
      goto fallback;
628
736
    }
629
737
  }
630
 
  /* If there was any arguments from configuration file,
631
 
     pass them to parser as command arguments */
 
738
  /* If there were any arguments from the configuration file, pass
 
739
     them to parser as command line arguments */
632
740
  if(custom_argv != NULL){
633
 
    ret = argp_parse(&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
634
 
                     0, NULL);
635
 
    if(ret == ARGP_ERR_UNKNOWN){
636
 
      fprintf(stderr, "Unknown error while parsing arguments\n");
637
 
      exitstatus = EXIT_FAILURE;
 
741
    ret = argp_parse(&argp, custom_argc, custom_argv,
 
742
                     ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP,
 
743
                     NULL, NULL);
 
744
    switch(ret){
 
745
    case 0:
 
746
      break;
 
747
    case ENOMEM:
 
748
    default:
 
749
      errno = ret;
 
750
      error(0, errno, "argp_parse");
 
751
      exitstatus = EX_OSERR;
 
752
      goto fallback;
 
753
    case EINVAL:
 
754
      exitstatus = EX_CONFIG;
638
755
      goto fallback;
639
756
    }
640
757
  }
641
758
  
642
759
  /* Parse actual command line arguments, to let them override the
643
760
     config file */
644
 
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
645
 
  if(ret == ARGP_ERR_UNKNOWN){
646
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
647
 
    exitstatus = EXIT_FAILURE;
648
 
    goto fallback;
 
761
  ret = argp_parse(&argp, argc, argv,
 
762
                   ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP,
 
763
                   NULL, NULL);
 
764
  switch(ret){
 
765
  case 0:
 
766
    break;
 
767
  case ENOMEM:
 
768
  default:
 
769
    errno = ret;
 
770
    error(0, errno, "argp_parse");
 
771
    exitstatus = EX_OSERR;
 
772
    goto fallback;
 
773
  case EINVAL:
 
774
    exitstatus = EX_USAGE;
 
775
    goto fallback;
 
776
  }
 
777
  
 
778
  {
 
779
    char *pluginhelperenv;
 
780
    bool bret = true;
 
781
    ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s",
 
782
                   pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
783
    if(ret != -1){
 
784
      bret = add_environment(getplugin(NULL), pluginhelperenv, true);
 
785
    }
 
786
    if(ret == -1 or not bret){
 
787
      error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR"
 
788
            " environment variable to \"%s\" for all plugins\n",
 
789
            pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
790
    }
 
791
    if(ret != -1){
 
792
      free(pluginhelperenv);
 
793
    }
649
794
  }
650
795
  
651
796
  if(debug){
662
807
    }
663
808
  }
664
809
  
665
 
  /* Strip permissions down to nobody */
666
 
  setgid(gid);
 
810
  if(getuid() == 0){
 
811
    /* Work around Debian bug #633582:
 
812
       <http://bugs.debian.org/633582> */
 
813
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
 
814
    if(plugindir_fd == -1){
 
815
      if(errno != ENOENT){
 
816
        error(0, errno, "open(\"" PDIR "\")");
 
817
      }
 
818
    } else {
 
819
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
 
820
      if(ret == -1){
 
821
        error(0, errno, "fstat");
 
822
      } else {
 
823
        if(S_ISDIR(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
 
824
          ret = fchown(plugindir_fd, uid, gid);
 
825
          if(ret == -1){
 
826
            error(0, errno, "fchown");
 
827
          }
 
828
        }
 
829
      }
 
830
      close(plugindir_fd);
 
831
    }
 
832
  }
 
833
  
 
834
  /* Lower permissions */
 
835
  ret = setgid(gid);
667
836
  if(ret == -1){
668
 
    perror("setgid");
 
837
    error(0, errno, "setgid");
669
838
  }
670
839
  ret = setuid(uid);
671
840
  if(ret == -1){
672
 
    perror("setuid");
673
 
  }
674
 
  
675
 
  if(plugindir == NULL){
676
 
    dir = opendir(PDIR);
677
 
  } else {
678
 
    dir = opendir(plugindir);
679
 
  }
680
 
  
681
 
  if(dir == NULL){
682
 
    perror("Could not open plugin dir");
683
 
    exitstatus = EXIT_FAILURE;
684
 
    goto fallback;
685
 
  }
686
 
  
687
 
  /* Set the FD_CLOEXEC flag on the directory, if possible */
 
841
    error(0, errno, "setuid");
 
842
  }
 
843
  
 
844
  /* Open plugin directory with close_on_exec flag */
688
845
  {
689
 
    int dir_fd = dirfd(dir);
690
 
    if(dir_fd >= 0){
691
 
      ret = set_cloexec_flag(dir_fd);
692
 
      if(ret < 0){
693
 
        perror("set_cloexec_flag");
694
 
        exitstatus = EXIT_FAILURE;
695
 
        goto fallback;
 
846
    dir_fd = open(plugindir != NULL ? plugindir : PDIR, O_RDONLY |
 
847
#ifdef O_CLOEXEC
 
848
                  O_CLOEXEC
 
849
#else  /* not O_CLOEXEC */
 
850
                  0
 
851
#endif  /* not O_CLOEXEC */
 
852
                  );
 
853
    if(dir_fd == -1){
 
854
      error(0, errno, "Could not open plugin dir");
 
855
      exitstatus = EX_UNAVAILABLE;
 
856
      goto fallback;
 
857
    }
 
858
    
 
859
#ifndef O_CLOEXEC
 
860
  /* Set the FD_CLOEXEC flag on the directory */
 
861
    ret = set_cloexec_flag(dir_fd);
 
862
    if(ret < 0){
 
863
      error(0, errno, "set_cloexec_flag");
 
864
      exitstatus = EX_OSERR;
 
865
      goto fallback;
 
866
    }
 
867
#endif  /* O_CLOEXEC */
 
868
  }
 
869
  
 
870
  int good_name(const struct dirent * const dirent){
 
871
    const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new",
 
872
                                      "*.dpkg-old", "*.dpkg-bak",
 
873
                                      "*.dpkg-divert", NULL };
 
874
#ifdef __GNUC__
 
875
#pragma GCC diagnostic push
 
876
#pragma GCC diagnostic ignored "-Wcast-qual"
 
877
#endif
 
878
    for(const char **pat = (const char **)patterns;
 
879
        *pat != NULL; pat++){
 
880
#ifdef __GNUC__
 
881
#pragma GCC diagnostic pop
 
882
#endif
 
883
      if(fnmatch(*pat, dirent->d_name, FNM_FILE_NAME | FNM_PERIOD)
 
884
         != FNM_NOMATCH){
 
885
        if(debug){
 
886
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
887
                    " matching pattern %s\n", dirent->d_name, *pat);
 
888
        }
 
889
        return 0;
696
890
      }
697
891
    }
 
892
    return 1;
 
893
  }
 
894
  
 
895
#ifdef __GLIBC__
 
896
#if __GLIBC_PREREQ(2, 15)
 
897
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
 
898
                             alphasort);
 
899
#else  /* not __GLIBC_PREREQ(2, 15) */
 
900
  int numplugins = scandir(plugindir != NULL ? plugindir : PDIR,
 
901
                           &direntries, good_name, alphasort);
 
902
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
903
#else   /* not __GLIBC__ */
 
904
  int numplugins = scandir(plugindir != NULL ? plugindir : PDIR,
 
905
                           &direntries, good_name, alphasort);
 
906
#endif  /* not __GLIBC__ */
 
907
  if(numplugins == -1){
 
908
    error(0, errno, "Could not scan plugin dir");
 
909
    direntries = NULL;
 
910
    exitstatus = EX_OSERR;
 
911
    goto fallback;
698
912
  }
699
913
  
700
914
  FD_ZERO(&rfds_all);
701
915
  
702
916
  /* Read and execute any executable in the plugin directory*/
703
 
  while(true){
704
 
    dirst = readdir(dir);
705
 
    
706
 
    /* All directory entries have been processed */
707
 
    if(dirst == NULL){
708
 
      if(errno == EBADF){
709
 
        perror("readdir");
710
 
        exitstatus = EXIT_FAILURE;
711
 
        goto fallback;
712
 
      }
713
 
      break;
714
 
    }
715
 
    
716
 
    d_name_len = strlen(dirst->d_name);
717
 
    
718
 
    /* Ignore dotfiles, backup files and other junk */
719
 
    {
720
 
      bool bad_name = false;
721
 
      
722
 
      const char const *bad_prefixes[] = { ".", "#", NULL };
723
 
      
724
 
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
725
 
                                           ".dpkg-old",
726
 
                                           ".dpkg-bak",
727
 
                                           ".dpkg-divert", NULL };
728
 
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
729
 
        size_t pre_len = strlen(*pre);
730
 
        if((d_name_len >= pre_len)
731
 
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
732
 
          if(debug){
733
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
734
 
                    " with bad prefix %s\n", dirst->d_name, *pre);
735
 
          }
736
 
          bad_name = true;
737
 
          break;
738
 
        }
739
 
      }
740
 
      if(bad_name){
741
 
        continue;
742
 
      }
743
 
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
744
 
        size_t suf_len = strlen(*suf);
745
 
        if((d_name_len >= suf_len)
746
 
           and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
747
 
                == 0)){
748
 
          if(debug){
749
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
750
 
                    " with bad suffix %s\n", dirst->d_name, *suf);
751
 
          }
752
 
          bad_name = true;
753
 
          break;
754
 
        }
755
 
      }
756
 
      
757
 
      if(bad_name){
758
 
        continue;
759
 
      }
760
 
    }
761
 
    
762
 
    char *filename;
763
 
    if(plugindir == NULL){
764
 
      ret = asprintf(&filename, PDIR "/%s", dirst->d_name);
765
 
    } else {
766
 
      ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
767
 
    }
768
 
    if(ret < 0){
769
 
      perror("asprintf");
 
917
  for(int i = 0; i < numplugins; i++){
 
918
    
 
919
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
 
920
    if(plugin_fd == -1){
 
921
      error(0, errno, "Could not open plugin");
 
922
      free(direntries[i]);
770
923
      continue;
771
924
    }
772
 
    
773
 
    ret = stat(filename, &st);
 
925
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
774
926
    if(ret == -1){
775
 
      perror("stat");
776
 
      free(filename);
 
927
      error(0, errno, "stat");
 
928
      close(plugin_fd);
 
929
      free(direntries[i]);
777
930
      continue;
778
931
    }
779
932
    
780
933
    /* Ignore non-executable files */
781
 
    if(not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
 
934
    if(not S_ISREG(st.st_mode)
 
935
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
 
936
                                        X_OK, 0)) != 0)){
782
937
      if(debug){
783
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
784
 
                " with bad type or mode\n", filename);
 
938
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
 
939
                " with bad type or mode\n",
 
940
                plugindir != NULL ? plugindir : PDIR,
 
941
                direntries[i]->d_name);
785
942
      }
786
 
      free(filename);
 
943
      close(plugin_fd);
 
944
      free(direntries[i]);
787
945
      continue;
788
946
    }
789
947
    
790
 
    plugin *p = getplugin(dirst->d_name);
 
948
    plugin *p = getplugin(direntries[i]->d_name);
791
949
    if(p == NULL){
792
 
      perror("getplugin");
793
 
      free(filename);
 
950
      error(0, errno, "getplugin");
 
951
      close(plugin_fd);
 
952
      free(direntries[i]);
794
953
      continue;
795
954
    }
796
955
    if(p->disabled){
797
956
      if(debug){
798
957
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
799
 
                dirst->d_name);
 
958
                direntries[i]->d_name);
800
959
      }
801
 
      free(filename);
 
960
      close(plugin_fd);
 
961
      free(direntries[i]);
802
962
      continue;
803
963
    }
804
964
    {
807
967
      if(g != NULL){
808
968
        for(char **a = g->argv + 1; *a != NULL; a++){
809
969
          if(not add_argument(p, *a)){
810
 
            perror("add_argument");
 
970
            error(0, errno, "add_argument");
811
971
          }
812
972
        }
813
973
        /* Add global environment variables */
814
974
        for(char **e = g->environ; *e != NULL; e++){
815
975
          if(not add_environment(p, *e, false)){
816
 
            perror("add_environment");
 
976
            error(0, errno, "add_environment");
817
977
          }
818
978
        }
819
979
      }
820
980
    }
821
 
    /* If this plugin has any environment variables, we will call
822
 
       using execve and need to duplicate the environment from this
823
 
       process, too. */
 
981
    /* If this plugin has any environment variables, we need to
 
982
       duplicate the environment from this process, too. */
824
983
    if(p->environ[0] != NULL){
825
984
      for(char **e = environ; *e != NULL; e++){
826
985
        if(not add_environment(p, *e, false)){
827
 
          perror("add_environment");
 
986
          error(0, errno, "add_environment");
828
987
        }
829
988
      }
830
989
    }
831
990
    
832
991
    int pipefd[2];
833
 
    ret = pipe(pipefd);
 
992
#ifndef O_CLOEXEC
 
993
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
 
994
#else  /* O_CLOEXEC */
 
995
    ret = (int)TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC));
 
996
#endif  /* O_CLOEXEC */
834
997
    if(ret == -1){
835
 
      perror("pipe");
836
 
      exitstatus = EXIT_FAILURE;
837
 
      goto fallback;
838
 
    }
 
998
      error(0, errno, "pipe");
 
999
      exitstatus = EX_OSERR;
 
1000
      free(direntries[i]);
 
1001
      goto fallback;
 
1002
    }
 
1003
    if(pipefd[0] >= FD_SETSIZE){
 
1004
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
 
1005
              FD_SETSIZE);
 
1006
      close(pipefd[0]);
 
1007
      close(pipefd[1]);
 
1008
      exitstatus = EX_OSERR;
 
1009
      free(direntries[i]);
 
1010
      goto fallback;
 
1011
    }
 
1012
#ifndef O_CLOEXEC
839
1013
    /* Ask OS to automatic close the pipe on exec */
840
1014
    ret = set_cloexec_flag(pipefd[0]);
841
1015
    if(ret < 0){
842
 
      perror("set_cloexec_flag");
843
 
      exitstatus = EXIT_FAILURE;
 
1016
      error(0, errno, "set_cloexec_flag");
 
1017
      close(pipefd[0]);
 
1018
      close(pipefd[1]);
 
1019
      exitstatus = EX_OSERR;
 
1020
      free(direntries[i]);
844
1021
      goto fallback;
845
1022
    }
846
1023
    ret = set_cloexec_flag(pipefd[1]);
847
1024
    if(ret < 0){
848
 
      perror("set_cloexec_flag");
849
 
      exitstatus = EXIT_FAILURE;
 
1025
      error(0, errno, "set_cloexec_flag");
 
1026
      close(pipefd[0]);
 
1027
      close(pipefd[1]);
 
1028
      exitstatus = EX_OSERR;
 
1029
      free(direntries[i]);
850
1030
      goto fallback;
851
1031
    }
 
1032
#endif  /* not O_CLOEXEC */
852
1033
    /* Block SIGCHLD until process is safely in process list */
853
 
    ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
 
1034
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
 
1035
                                              &sigchld_action.sa_mask,
 
1036
                                              NULL));
854
1037
    if(ret < 0){
855
 
      perror("sigprocmask");
856
 
      exitstatus = EXIT_FAILURE;
 
1038
      error(0, errno, "sigprocmask");
 
1039
      exitstatus = EX_OSERR;
 
1040
      free(direntries[i]);
857
1041
      goto fallback;
858
1042
    }
859
1043
    /* Starting a new process to be watched */
860
 
    pid_t pid = fork();
 
1044
    pid_t pid;
 
1045
    do {
 
1046
      pid = fork();
 
1047
    } while(pid == -1 and errno == EINTR);
861
1048
    if(pid == -1){
862
 
      perror("fork");
863
 
      exitstatus = EXIT_FAILURE;
 
1049
      error(0, errno, "fork");
 
1050
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
 
1051
                                     &sigchld_action.sa_mask, NULL));
 
1052
      close(pipefd[0]);
 
1053
      close(pipefd[1]);
 
1054
      exitstatus = EX_OSERR;
 
1055
      free(direntries[i]);
864
1056
      goto fallback;
865
1057
    }
866
1058
    if(pid == 0){
867
1059
      /* this is the child process */
868
1060
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
869
1061
      if(ret < 0){
870
 
        perror("sigaction");
871
 
        _exit(EXIT_FAILURE);
 
1062
        error(0, errno, "sigaction");
 
1063
        _exit(EX_OSERR);
872
1064
      }
873
1065
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
874
1066
      if(ret < 0){
875
 
        perror("sigprocmask");
876
 
        _exit(EXIT_FAILURE);
 
1067
        error(0, errno, "sigprocmask");
 
1068
        _exit(EX_OSERR);
877
1069
      }
878
1070
      
879
1071
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
880
1072
      if(ret == -1){
881
 
        perror("dup2");
882
 
        _exit(EXIT_FAILURE);
 
1073
        error(0, errno, "dup2");
 
1074
        _exit(EX_OSERR);
883
1075
      }
884
1076
      
885
 
      if(dirfd(dir) < 0){
886
 
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
887
 
           above and must now close it manually here. */
888
 
        closedir(dir);
889
 
      }
890
 
      if(p->environ[0] == NULL){
891
 
        if(execv(filename, p->argv) < 0){
892
 
          perror("execv");
893
 
          _exit(EXIT_FAILURE);
894
 
        }
895
 
      } else {
896
 
        if(execve(filename, p->argv, p->environ) < 0){
897
 
          perror("execve");
898
 
          _exit(EXIT_FAILURE);
899
 
        }
 
1077
      if(fexecve(plugin_fd, p->argv,
 
1078
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
 
1079
        error(0, errno, "fexecve for %s/%s",
 
1080
              plugindir != NULL ? plugindir : PDIR,
 
1081
              direntries[i]->d_name);
 
1082
        _exit(EX_OSERR);
900
1083
      }
901
1084
      /* no return */
902
1085
    }
903
1086
    /* Parent process */
904
1087
    close(pipefd[1]);           /* Close unused write end of pipe */
905
 
    free(filename);
906
 
    plugin *new_plugin = getplugin(dirst->d_name);
 
1088
    close(plugin_fd);
 
1089
    plugin *new_plugin = getplugin(direntries[i]->d_name);
907
1090
    if(new_plugin == NULL){
908
 
      perror("getplugin");
909
 
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
1091
      error(0, errno, "getplugin");
 
1092
      ret = (int)(TEMP_FAILURE_RETRY
 
1093
                  (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
 
1094
                               NULL)));
910
1095
      if(ret < 0){
911
 
        perror("sigprocmask");
 
1096
        error(0, errno, "sigprocmask");
912
1097
      }
913
 
      exitstatus = EXIT_FAILURE;
 
1098
      exitstatus = EX_OSERR;
 
1099
      free(direntries[i]);
914
1100
      goto fallback;
915
1101
    }
 
1102
    free(direntries[i]);
916
1103
    
917
1104
    new_plugin->pid = pid;
918
1105
    new_plugin->fd = pipefd[0];
919
1106
    
920
1107
    /* Unblock SIGCHLD so signal handler can be run if this process
921
1108
       has already completed */
922
 
    ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
1109
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
 
1110
                                              &sigchld_action.sa_mask,
 
1111
                                              NULL));
923
1112
    if(ret < 0){
924
 
      perror("sigprocmask");
925
 
      exitstatus = EXIT_FAILURE;
 
1113
      error(0, errno, "sigprocmask");
 
1114
      exitstatus = EX_OSERR;
926
1115
      goto fallback;
927
1116
    }
928
1117
    
929
 
    FD_SET(new_plugin->fd, &rfds_all);
 
1118
#if defined (__GNUC__) and defined (__GLIBC__)
 
1119
#if not __GLIBC_PREREQ(2, 16)
 
1120
#pragma GCC diagnostic push
 
1121
#pragma GCC diagnostic ignored "-Wsign-conversion"
 
1122
#endif
 
1123
#endif
 
1124
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
 
1125
                                          -Wconversion in GNU libc
 
1126
                                          before 2.16 */
 
1127
#if defined (__GNUC__) and defined (__GLIBC__)
 
1128
#if not __GLIBC_PREREQ(2, 16)
 
1129
#pragma GCC diagnostic pop
 
1130
#endif
 
1131
#endif
930
1132
    
931
1133
    if(maxfd < new_plugin->fd){
932
1134
      maxfd = new_plugin->fd;
933
1135
    }
934
1136
  }
935
1137
  
936
 
  closedir(dir);
937
 
  dir = NULL;
 
1138
  free(direntries);
 
1139
  direntries = NULL;
 
1140
  close(dir_fd);
 
1141
  dir_fd = -1;
938
1142
  free_plugin(getplugin(NULL));
939
1143
  
940
1144
  for(plugin *p = plugin_list; p != NULL; p = p->next){
952
1156
  while(plugin_list){
953
1157
    fd_set rfds = rfds_all;
954
1158
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
955
 
    if(select_ret == -1){
956
 
      perror("select");
957
 
      exitstatus = EXIT_FAILURE;
 
1159
    if(select_ret == -1 and errno != EINTR){
 
1160
      error(0, errno, "select");
 
1161
      exitstatus = EX_OSERR;
958
1162
      goto fallback;
959
1163
    }
960
1164
    /* OK, now either a process completed, or something can be read
986
1190
          }
987
1191
          
988
1192
          /* Remove the plugin */
989
 
          FD_CLR(proc->fd, &rfds_all);
 
1193
#if defined (__GNUC__) and defined (__GLIBC__)
 
1194
#if not __GLIBC_PREREQ(2, 16)
 
1195
#pragma GCC diagnostic push
 
1196
#pragma GCC diagnostic ignored "-Wsign-conversion"
 
1197
#endif
 
1198
#endif
 
1199
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
 
1200
                                          -Wconversion in GNU libc
 
1201
                                          before 2.16 */
 
1202
#if defined (__GNUC__) and defined (__GLIBC__)
 
1203
#if not __GLIBC_PREREQ(2, 16)
 
1204
#pragma GCC diagnostic pop
 
1205
#endif
 
1206
#endif
990
1207
          
991
1208
          /* Block signal while modifying process_list */
992
 
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
 
1209
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
 
1210
                                        (SIG_BLOCK,
 
1211
                                         &sigchld_action.sa_mask,
 
1212
                                         NULL));
993
1213
          if(ret < 0){
994
 
            perror("sigprocmask");
995
 
            exitstatus = EXIT_FAILURE;
 
1214
            error(0, errno, "sigprocmask");
 
1215
            exitstatus = EX_OSERR;
996
1216
            goto fallback;
997
1217
          }
998
1218
          
1001
1221
          proc = next_plugin;
1002
1222
          
1003
1223
          /* We are done modifying process list, so unblock signal */
1004
 
          ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
1005
 
                            NULL);
 
1224
          ret = (int)(TEMP_FAILURE_RETRY
 
1225
                      (sigprocmask(SIG_UNBLOCK,
 
1226
                                   &sigchld_action.sa_mask, NULL)));
1006
1227
          if(ret < 0){
1007
 
            perror("sigprocmask");
1008
 
            exitstatus = EXIT_FAILURE;
 
1228
            error(0, errno, "sigprocmask");
 
1229
            exitstatus = EX_OSERR;
1009
1230
            goto fallback;
1010
1231
          }
1011
1232
          
1021
1242
        bool bret = print_out_password(proc->buffer,
1022
1243
                                       proc->buffer_length);
1023
1244
        if(not bret){
1024
 
          perror("print_out_password");
1025
 
          exitstatus = EXIT_FAILURE;
 
1245
          error(0, errno, "print_out_password");
 
1246
          exitstatus = EX_IOERR;
1026
1247
        }
1027
1248
        goto fallback;
1028
1249
      }
1029
1250
      
1030
1251
      /* This process has not completed.  Does it have any output? */
1031
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
 
1252
#if defined (__GNUC__) and defined (__GLIBC__)
 
1253
#if not __GLIBC_PREREQ(2, 16)
 
1254
#pragma GCC diagnostic push
 
1255
#pragma GCC diagnostic ignored "-Wsign-conversion"
 
1256
#endif
 
1257
#endif
 
1258
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
 
1259
                                                         warning from
 
1260
                                                         -Wconversion
 
1261
                                                         in GNU libc
 
1262
                                                         before
 
1263
                                                         2.16 */
 
1264
#if defined (__GNUC__) and defined (__GLIBC__)
 
1265
#if not __GLIBC_PREREQ(2, 16)
 
1266
#pragma GCC diagnostic pop
 
1267
#endif
 
1268
#endif
1032
1269
        /* This process had nothing to say at this time */
1033
1270
        proc = proc->next;
1034
1271
        continue;
1035
1272
      }
1036
1273
      /* Before reading, make the process' data buffer large enough */
1037
1274
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1038
 
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1039
 
                               + (size_t) BUFFER_SIZE);
1040
 
        if(proc->buffer == NULL){
1041
 
          perror("malloc");
1042
 
          exitstatus = EXIT_FAILURE;
 
1275
        char *new_buffer = realloc(proc->buffer, proc->buffer_size
 
1276
                                   + (size_t) BUFFER_SIZE);
 
1277
        if(new_buffer == NULL){
 
1278
          error(0, errno, "malloc");
 
1279
          exitstatus = EX_OSERR;
1043
1280
          goto fallback;
1044
1281
        }
 
1282
        proc->buffer = new_buffer;
1045
1283
        proc->buffer_size += BUFFER_SIZE;
1046
1284
      }
1047
1285
      /* Read from the process */
1048
 
      sret = read(proc->fd, proc->buffer + proc->buffer_length,
1049
 
                  BUFFER_SIZE);
 
1286
      sret = TEMP_FAILURE_RETRY(read(proc->fd,
 
1287
                                     proc->buffer
 
1288
                                     + proc->buffer_length,
 
1289
                                     BUFFER_SIZE));
1050
1290
      if(sret < 0){
1051
1291
        /* Read error from this process; ignore the error */
1052
1292
        proc = proc->next;
1064
1304
  
1065
1305
 fallback:
1066
1306
  
1067
 
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
 
1307
  if(plugin_list == NULL or (exitstatus != EXIT_SUCCESS
 
1308
                             and exitstatus != EX_OK)){
1068
1309
    /* Fallback if all plugins failed, none are found or an error
1069
1310
       occured */
1070
1311
    bool bret;
1078
1319
    }
1079
1320
    bret = print_out_password(passwordbuffer, len);
1080
1321
    if(not bret){
1081
 
      perror("print_out_password");
1082
 
      exitstatus = EXIT_FAILURE;
 
1322
      error(0, errno, "print_out_password");
 
1323
      exitstatus = EX_IOERR;
1083
1324
    }
1084
1325
  }
1085
1326
  
1086
1327
  /* Restore old signal handler */
1087
1328
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1088
1329
  if(ret == -1){
1089
 
    perror("sigaction");
1090
 
    exitstatus = EXIT_FAILURE;
 
1330
    error(0, errno, "sigaction");
 
1331
    exitstatus = EX_OSERR;
1091
1332
  }
1092
1333
  
1093
1334
  if(custom_argv != NULL){
1097
1338
    free(custom_argv);
1098
1339
  }
1099
1340
  
1100
 
  if(dir != NULL){
1101
 
    closedir(dir);
 
1341
  free(direntries);
 
1342
  
 
1343
  if(dir_fd != -1){
 
1344
    close(dir_fd);
1102
1345
  }
1103
1346
  
1104
1347
  /* Kill the processes */
1108
1351
      ret = kill(p->pid, SIGTERM);
1109
1352
      if(ret == -1 and errno != ESRCH){
1110
1353
        /* Set-uid proccesses might not get closed */
1111
 
        perror("kill");
 
1354
        error(0, errno, "kill");
1112
1355
      }
1113
1356
    }
1114
1357
  }
1115
1358
  
1116
1359
  /* Wait for any remaining child processes to terminate */
1117
 
  do{
 
1360
  do {
1118
1361
    ret = wait(NULL);
1119
1362
  } while(ret >= 0);
1120
1363
  if(errno != ECHILD){
1121
 
    perror("wait");
 
1364
    error(0, errno, "wait");
1122
1365
  }
1123
1366
  
1124
1367
  free_plugin_list();
1125
1368
  
1126
1369
  free(plugindir);
 
1370
  free(pluginhelperdir);
1127
1371
  free(argfile);
1128
1372
  
1129
1373
  return exitstatus;