/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: 2016-06-03 17:27:03 UTC
  • Revision ID: teddy@recompile.se-20160603172703-mc6tjor6rhq4xy74
mandos: Bug fix: Do multiprocessing cleanup correctly on exit

* mandos (main): Save module "multiprocessing" and open file "wnull"
                 as scope variables accessible by function cleanup(),
                 since the module and global variable may not be
                 accessible when the cleanup() function is run as
                 scheduled by atexit().

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