/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: 2008-12-10 01:26:02 UTC
  • mfrom: (237.1.2 mandos)
  • Revision ID: teddy@fukt.bsnet.se-20081210012602-vhz3h75xkj24t340
First version of a somewhat complete D-Bus server interface.  Also
change user/group name to "_mandos".

* debian/mandos.postinst: Rename old "mandos" user and group to
                          "_mandos"; create "_mandos" user and group
                          if none exist.
* debian/mandos-client.postinst: - '' -

* initramfs-tools-hook: Try "_mandos" before "mandos" as user and
                        group name.

* mandos (_datetime_to_dbus_struct): New; was previously local.
  (Client.started): Renamed to "last_started".  All users changed.
  (Client.started): New; boolean.
  (Client.dbus_object_path): New.
  (Client.check_command): Renamed to "checker_command".  All users
                          changed.
  (Client.__init__): Set and use "self.dbus_object_path".  Set
                     "self.started".
  (Client.start): Update "self.started".  Emit "self.PropertyChanged"
                  signals for both "started" and "last_started".
  (Client.stop): Update "self.started".  Emit "self.PropertyChanged"
                 signal for "started".
  (Client.checker_callback): Take additional "command" argument.  All
                             callers changed. Emit
                             "self.PropertyChanged" signal.
  (Client.bump_timeout): Emit "self.PropertyChanged" signal for
                         "last_checked_ok".
  (Client.start_checker): Emit "self.PropertyChanged" signal for
                          "checker_running".
  (Client.stop_checker): Emit "self.PropertyChanged" signal for
                         "checker_running".
  (Client.still_valid): Bug fix: use "getattr(self, started, False)"
                        instead of "self.started" in case this client
                        object is so new that the "started" attribute
                        has not been created yet.
  (Client.IntervalChanged, Client.CheckerIsRunning, Client.GetChecker,
  Client.GetCreated, Client.GetFingerprint, Client.GetHost,
  Client.GetInterval, Client.GetName, Client.GetStarted,
  Client.GetTimeout, Client.StateChanged, Client.TimeoutChanged):
  Removed; all callers changed.
  (Client.CheckerCompleted): Add "condition" and "command" arguments.
                             All callers changed.
  (Client.GetAllProperties, Client.PropertyChanged): New.
  (Client.StillValid): Renamed to "IsStillValid".
  (Client.StartChecker): Changed to its own function to avoid the
                         return value from "Client.start_checker()".
  (Client.Stop): Changed to its own function to avoid the return value
                 from "Client.stop()".
  (main): Try "_mandos" before "mandos" as user and group name.
          Removed inner function "remove_from_clients".  New inner
          class "MandosServer".

Show diffs side-by-side

added added

removed removed

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