/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to plugin-runner.c

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-2011 Teddy Hogeborn
6
 
 * Copyright © 2008-2011 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;
174
150
static bool add_to_char_array(const char *new, char ***array,
175
151
                              int *len){
176
152
  /* Resize the pointed-to array to hold one more pointer */
177
 
  do {
178
 
    *array = realloc(*array, sizeof(char *)
179
 
                     * (size_t) ((*len) + 2));
180
 
  } while(*array == NULL and errno == EINTR);
 
153
  *array = realloc(*array, sizeof(char *)
 
154
                   * (size_t) ((*len) + 2));
181
155
  /* Malloc check */
182
156
  if(*array == NULL){
183
157
    return false;
184
158
  }
185
159
  /* Make a copy of the new string */
186
 
  char *copy;
187
 
  do {
188
 
    copy = strdup(new);
189
 
  } while(copy == NULL and errno == EINTR);
 
160
  char *copy = strdup(new);
190
161
  if(copy == NULL){
191
162
    return false;
192
163
  }
218
189
    if(strncmp(*e, def, namelen + 1) == 0){
219
190
      /* It already exists */
220
191
      if(replace){
221
 
        char *new;
222
 
        do {
223
 
          new = realloc(*e, strlen(def) + 1);
224
 
        } while(new == NULL and errno == EINTR);
 
192
        char *new = realloc(*e, strlen(def) + 1);
225
193
        if(new == NULL){
226
194
          return false;
227
195
        }
237
205
/*
238
206
 * Based on the example in the GNU LibC manual chapter 13.13 "File
239
207
 * Descriptor Flags".
240
 
 | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
 
208
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
241
209
 */
242
210
static int set_cloexec_flag(int fd){
243
 
  int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
 
211
  int ret = fcntl(fd, F_GETFD, 0);
244
212
  /* If reading the flags failed, return error indication now. */
245
213
  if(ret < 0){
246
214
    return ret;
247
215
  }
248
216
  /* Store modified flag word in the descriptor. */
249
 
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
250
 
                                       ret | FD_CLOEXEC));
 
217
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
251
218
}
252
219
 
253
220
 
254
221
/* Mark processes as completed when they exit, and save their exit
255
222
   status. */
256
223
static void handle_sigchld(__attribute__((unused)) int sig){
257
 
  int old_errno = errno;
258
224
  while(true){
259
225
    plugin *proc = plugin_list;
260
226
    int status;
264
230
      break;
265
231
    }
266
232
    if(pid == -1){
267
 
      if(errno == ECHILD){
268
 
        /* No child processes */
269
 
        break;
 
233
      if (errno != ECHILD){
 
234
        perror("waitpid");
270
235
      }
271
 
      error(0, errno, "waitpid");
 
236
      /* No child processes */
 
237
      break;
272
238
    }
273
239
    
274
240
    /* A child exited, find it in process_list */
280
246
      continue;
281
247
    }
282
248
    proc->status = status;
283
 
    proc->completed = 1;
 
249
    proc->completed = true;
284
250
  }
285
 
  errno = old_errno;
286
251
}
287
252
 
288
253
/* Prints out a password to stdout */
310
275
  }
311
276
  free(plugin_node->environ);
312
277
  free(plugin_node->buffer);
313
 
  
 
278
 
314
279
  /* Removes the plugin from the singly-linked list */
315
280
  if(plugin_node == plugin_list){
316
281
    /* First one - simple */
344
309
  struct stat st;
345
310
  fd_set rfds_all;
346
311
  int ret, maxfd = 0;
347
 
  ssize_t sret;
348
312
  uid_t uid = 65534;
349
313
  gid_t gid = 65534;
350
314
  bool debug = false;
359
323
  sigemptyset(&sigchld_action.sa_mask);
360
324
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
361
325
  if(ret == -1){
362
 
    error(0, errno, "sigaddset");
363
 
    exitstatus = EX_OSERR;
 
326
    perror("sigaddset");
 
327
    exitstatus = EXIT_FAILURE;
364
328
    goto fallback;
365
329
  }
366
330
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
367
331
  if(ret == -1){
368
 
    error(0, errno, "sigaction");
369
 
    exitstatus = EX_OSERR;
 
332
    perror("sigaction");
 
333
    exitstatus = EXIT_FAILURE;
370
334
    goto fallback;
371
335
  }
372
336
  
404
368
      .doc = "Group ID the plugins will run as", .group = 3 },
405
369
    { .name = "debug", .key = 132,
406
370
      .doc = "Debug mode", .group = 4 },
407
 
    /*
408
 
     * These reproduce what we would get without ARGP_NO_HELP
409
 
     */
410
 
    { .name = "help", .key = '?',
411
 
      .doc = "Give this help list", .group = -1 },
412
 
    { .name = "usage", .key = -3,
413
 
      .doc = "Give a short usage message", .group = -1 },
414
 
    { .name = "version", .key = 'V',
415
 
      .doc = "Print program version", .group = -1 },
416
371
    { .name = NULL }
417
372
  };
418
373
  
419
 
  error_t parse_opt(int key, char *arg, struct argp_state *state){
420
 
    errno = 0;
421
 
    switch(key){
422
 
      char *tmp;
423
 
      intmax_t tmpmax;
 
374
  error_t parse_opt (int key, char *arg, __attribute__((unused))
 
375
                     struct argp_state *state) {
 
376
    switch (key) {
424
377
    case 'g':                   /* --global-options */
425
 
      {
426
 
        char *plugin_option;
427
 
        while((plugin_option = strsep(&arg, ",")) != NULL){
428
 
          if(not add_argument(getplugin(NULL), plugin_option)){
429
 
            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;
430
387
          }
431
388
        }
432
389
      }
433
390
      break;
434
391
    case 'G':                   /* --global-env */
435
 
      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
      }
436
398
      break;
437
399
    case 'o':                   /* --options-for */
438
 
      {
439
 
        char *option_list = strchr(arg, ':');
440
 
        if(option_list == NULL){
441
 
          argp_error(state, "No colon in \"%s\"", arg);
442
 
          errno = EINVAL;
443
 
          break;
444
 
        }
445
 
        *option_list = '\0';
446
 
        option_list++;
447
 
        if(arg[0] == '\0'){
448
 
          argp_error(state, "Empty plugin name");
449
 
          errno = EINVAL;
450
 
          break;
451
 
        }
452
 
        char *option;
453
 
        while((option = strsep(&option_list, ",")) != NULL){
454
 
          if(not add_argument(getplugin(arg), option)){
455
 
            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;
456
417
          }
457
418
        }
458
419
      }
459
420
      break;
460
421
    case 'E':                   /* --env-for */
 
422
      if(arg == NULL){
 
423
        break;
 
424
      }
461
425
      {
462
426
        char *envdef = strchr(arg, ':');
463
427
        if(envdef == NULL){
464
 
          argp_error(state, "No colon in \"%s\"", arg);
465
 
          errno = EINVAL;
466
428
          break;
467
429
        }
468
430
        *envdef = '\0';
469
 
        envdef++;
470
 
        if(arg[0] == '\0'){
471
 
          argp_error(state, "Empty plugin name");
472
 
          errno = EINVAL;
473
 
          break;
 
431
        if(not add_environment(getplugin(arg), envdef+1, true)){
 
432
          perror("add_environment");
474
433
        }
475
 
        add_environment(getplugin(arg), envdef, true);
476
434
      }
477
435
      break;
478
436
    case 'd':                   /* --disable */
479
 
      {
 
437
      if (arg != NULL){
480
438
        plugin *p = getplugin(arg);
481
 
        if(p != NULL){
482
 
          p->disabled = true;
 
439
        if(p == NULL){
 
440
          return ARGP_ERR_UNKNOWN;
483
441
        }
 
442
        p->disabled = true;
484
443
      }
485
444
      break;
486
445
    case 'e':                   /* --enable */
487
 
      {
 
446
      if (arg != NULL){
488
447
        plugin *p = getplugin(arg);
489
 
        if(p != NULL){
490
 
          p->disabled = false;
 
448
        if(p == NULL){
 
449
          return ARGP_ERR_UNKNOWN;
491
450
        }
 
451
        p->disabled = false;
492
452
      }
493
453
      break;
494
454
    case 128:                   /* --plugin-dir */
495
455
      free(plugindir);
496
456
      plugindir = strdup(arg);
 
457
      if(plugindir == NULL){
 
458
        perror("strdup");
 
459
      }      
497
460
      break;
498
461
    case 129:                   /* --config-file */
499
462
      /* This is already done by parse_opt_config_file() */
500
463
      break;
501
464
    case 130:                   /* --userid */
502
 
      tmpmax = strtoimax(arg, &tmp, 10);
503
 
      if(errno != 0 or tmp == arg or *tmp != '\0'
504
 
         or tmpmax != (uid_t)tmpmax){
505
 
        argp_error(state, "Bad user ID number: \"%s\", using %"
506
 
                   PRIdMAX, arg, (intmax_t)uid);
507
 
        break;
508
 
      }
509
 
      uid = (uid_t)tmpmax;
 
465
      uid = (uid_t)strtol(arg, NULL, 10);
510
466
      break;
511
467
    case 131:                   /* --groupid */
512
 
      tmpmax = strtoimax(arg, &tmp, 10);
513
 
      if(errno != 0 or tmp == arg or *tmp != '\0'
514
 
         or tmpmax != (gid_t)tmpmax){
515
 
        argp_error(state, "Bad group ID number: \"%s\", using %"
516
 
                   PRIdMAX, arg, (intmax_t)gid);
517
 
        break;
518
 
      }
519
 
      gid = (gid_t)tmpmax;
 
468
      gid = (gid_t)strtol(arg, NULL, 10);
520
469
      break;
521
470
    case 132:                   /* --debug */
522
471
      debug = true;
523
472
      break;
524
 
      /*
525
 
       * These reproduce what we would get without ARGP_NO_HELP
526
 
       */
527
 
    case '?':                   /* --help */
528
 
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
529
 
      argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
530
 
    case -3:                    /* --usage */
531
 
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
532
 
      argp_state_help(state, state->out_stream,
533
 
                      ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
534
 
    case 'V':                   /* --version */
535
 
      fprintf(state->out_stream, "%s\n", argp_program_version);
536
 
      exit(EXIT_SUCCESS);
537
 
      break;
538
 
/*
539
 
 * When adding more options before this line, remember to also add a
540
 
 * "case" to the "parse_opt_config_file" function below.
541
 
 */
542
473
    case ARGP_KEY_ARG:
543
474
      /* Cryptsetup always passes an argument, which is an empty
544
475
         string if "none" was specified in /etc/crypttab.  So if
545
476
         argument was empty, we ignore it silently. */
546
 
      if(arg[0] == '\0'){
547
 
        break;
 
477
      if(arg[0] != '\0'){
 
478
        fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
548
479
      }
 
480
      break;
 
481
    case ARGP_KEY_END:
 
482
      break;
549
483
    default:
550
484
      return ARGP_ERR_UNKNOWN;
551
485
    }
552
 
    return errno;               /* Set to 0 at start */
 
486
    return 0;
553
487
  }
554
488
  
555
489
  /* This option parser is the same as parse_opt() above, except it
556
490
     ignores everything but the --config-file option. */
557
 
  error_t parse_opt_config_file(int key, char *arg,
558
 
                                __attribute__((unused))
559
 
                                struct argp_state *state){
560
 
    errno = 0;
561
 
    switch(key){
 
491
  error_t parse_opt_config_file (int key, char *arg,
 
492
                                 __attribute__((unused))
 
493
                                 struct argp_state *state) {
 
494
    switch (key) {
562
495
    case 'g':                   /* --global-options */
563
496
    case 'G':                   /* --global-env */
564
497
    case 'o':                   /* --options-for */
570
503
    case 129:                   /* --config-file */
571
504
      free(argfile);
572
505
      argfile = strdup(arg);
573
 
      break;
 
506
      if(argfile == NULL){
 
507
        perror("strdup");
 
508
      }
 
509
      break;      
574
510
    case 130:                   /* --userid */
575
511
    case 131:                   /* --groupid */
576
512
    case 132:                   /* --debug */
577
 
    case '?':                   /* --help */
578
 
    case -3:                    /* --usage */
579
 
    case 'V':                   /* --version */
580
513
    case ARGP_KEY_ARG:
 
514
    case ARGP_KEY_END:
581
515
      break;
582
516
    default:
583
517
      return ARGP_ERR_UNKNOWN;
584
518
    }
585
 
    return errno;
 
519
    return 0;
586
520
  }
587
521
  
588
522
  struct argp argp = { .options = options,
590
524
                       .args_doc = "",
591
525
                       .doc = "Mandos plugin runner -- Run plugins" };
592
526
  
593
 
  /* 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
594
528
     config file location, if any. */
595
 
  ret = argp_parse(&argp, argc, argv,
596
 
                   ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP,
597
 
                   NULL, NULL);
598
 
  switch(ret){
599
 
  case 0:
600
 
    break;
601
 
  case ENOMEM:
602
 
  default:
603
 
    errno = ret;
604
 
    error(0, errno, "argp_parse");
605
 
    exitstatus = EX_OSERR;
606
 
    goto fallback;
607
 
  case EINVAL:
608
 
    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;
609
533
    goto fallback;
610
534
  }
611
535
  
613
537
  argp.parser = parse_opt;
614
538
  
615
539
  /* Open the configfile if available */
616
 
  if(argfile == NULL){
 
540
  if (argfile == NULL){
617
541
    conffp = fopen(AFILE, "r");
618
542
  } else {
619
543
    conffp = fopen(argfile, "r");
620
 
  }
 
544
  }  
621
545
  if(conffp != NULL){
622
546
    char *org_line = NULL;
623
547
    char *p, *arg, *new_arg, *line;
624
548
    size_t size = 0;
 
549
    ssize_t sret;
625
550
    const char whitespace_delims[] = " \r\t\f\v\n";
626
551
    const char comment_delim[] = "#";
627
 
    
 
552
 
628
553
    custom_argc = 1;
629
554
    custom_argv = malloc(sizeof(char*) * 2);
630
555
    if(custom_argv == NULL){
631
 
      error(0, errno, "malloc");
632
 
      exitstatus = EX_OSERR;
 
556
      perror("malloc");
 
557
      exitstatus = EXIT_FAILURE;
633
558
      goto fallback;
634
559
    }
635
560
    custom_argv[0] = argv[0];
636
561
    custom_argv[1] = NULL;
637
 
    
 
562
 
638
563
    /* for each line in the config file, strip whitespace and ignore
639
564
       commented text */
640
565
    while(true){
642
567
      if(sret == -1){
643
568
        break;
644
569
      }
645
 
      
 
570
 
646
571
      line = org_line;
647
572
      arg = strsep(&line, comment_delim);
648
573
      while((p = strsep(&arg, whitespace_delims)) != NULL){
651
576
        }
652
577
        new_arg = strdup(p);
653
578
        if(new_arg == NULL){
654
 
          error(0, errno, "strdup");
655
 
          exitstatus = EX_OSERR;
 
579
          perror("strdup");
 
580
          exitstatus = EXIT_FAILURE;
656
581
          free(org_line);
657
582
          goto fallback;
658
583
        }
661
586
        custom_argv = realloc(custom_argv, sizeof(char *)
662
587
                              * ((unsigned int) custom_argc + 1));
663
588
        if(custom_argv == NULL){
664
 
          error(0, errno, "realloc");
665
 
          exitstatus = EX_OSERR;
 
589
          perror("realloc");
 
590
          exitstatus = EXIT_FAILURE;
666
591
          free(org_line);
667
592
          goto fallback;
668
593
        }
669
594
        custom_argv[custom_argc-1] = new_arg;
670
 
        custom_argv[custom_argc] = NULL;
 
595
        custom_argv[custom_argc] = NULL;        
671
596
      }
672
597
    }
673
 
    do {
674
 
      ret = fclose(conffp);
675
 
    } while(ret == EOF and errno == EINTR);
676
 
    if(ret == EOF){
677
 
      error(0, errno, "fclose");
678
 
      exitstatus = EX_IOERR;
679
 
      goto fallback;
680
 
    }
681
598
    free(org_line);
682
599
  } else {
683
600
    /* Check for harmful errors and go to fallback. Other errors might
684
601
       not affect opening plugins */
685
 
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
686
 
      error(0, errno, "fopen");
687
 
      exitstatus = EX_OSERR;
 
602
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
 
603
      perror("fopen");
 
604
      exitstatus = EXIT_FAILURE;
688
605
      goto fallback;
689
606
    }
690
607
  }
691
 
  /* If there were any arguments from the configuration file, pass
692
 
     them to parser as command line arguments */
 
608
  /* If there was any arguments from configuration file,
 
609
     pass them to parser as command arguments */
693
610
  if(custom_argv != NULL){
694
 
    ret = argp_parse(&argp, custom_argc, custom_argv,
695
 
                     ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP,
696
 
                     NULL, NULL);
697
 
    switch(ret){
698
 
    case 0:
699
 
      break;
700
 
    case ENOMEM:
701
 
    default:
702
 
      errno = ret;
703
 
      error(0, errno, "argp_parse");
704
 
      exitstatus = EX_OSERR;
705
 
      goto fallback;
706
 
    case EINVAL:
707
 
      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;
708
616
      goto fallback;
709
617
    }
710
618
  }
711
619
  
712
620
  /* Parse actual command line arguments, to let them override the
713
621
     config file */
714
 
  ret = argp_parse(&argp, argc, argv,
715
 
                   ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP,
716
 
                   NULL, NULL);
717
 
  switch(ret){
718
 
  case 0:
719
 
    break;
720
 
  case ENOMEM:
721
 
  default:
722
 
    errno = ret;
723
 
    error(0, errno, "argp_parse");
724
 
    exitstatus = EX_OSERR;
725
 
    goto fallback;
726
 
  case EINVAL:
727
 
    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;
728
626
    goto fallback;
729
627
  }
730
628
  
735
633
      for(char **a = p->argv; *a != NULL; a++){
736
634
        fprintf(stderr, "\tArg: %s\n", *a);
737
635
      }
738
 
      fprintf(stderr, "...and %d environment variables\n", p->envc);
 
636
      fprintf(stderr, "...and %u environment variables\n", p->envc);
739
637
      for(char **a = p->environ; *a != NULL; a++){
740
638
        fprintf(stderr, "\t%s\n", *a);
741
639
      }
742
640
    }
743
641
  }
744
642
  
745
 
  if(getuid() == 0){
746
 
    /* Work around Debian bug #633582:
747
 
       <http://bugs.debian.org/633582> */
748
 
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
749
 
    if(plugindir_fd == -1){
750
 
      error(0, errno, "open");
751
 
    } else {
752
 
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
753
 
      if(ret == -1){
754
 
        error(0, errno, "fstat");
755
 
      } else {
756
 
        if(S_ISDIR(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
757
 
          ret = fchown(plugindir_fd, uid, gid);
758
 
          if(ret == -1){
759
 
            error(0, errno, "fchown");
760
 
          }
761
 
        }
762
 
      }
763
 
      TEMP_FAILURE_RETRY(close(plugindir_fd));
764
 
    }
765
 
  }
766
 
  
767
 
  /* Lower permissions */
 
643
  /* Strip permissions down to nobody */
 
644
  ret = setuid(uid);
 
645
  if (ret == -1){
 
646
    perror("setuid");
 
647
  }  
768
648
  setgid(gid);
769
 
  if(ret == -1){
770
 
    error(0, errno, "setgid");
771
 
  }
772
 
  ret = setuid(uid);
773
 
  if(ret == -1){
774
 
    error(0, errno, "setuid");
775
 
  }
776
 
  
777
 
  /* Open plugin directory with close_on_exec flag */
 
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 */
778
666
  {
779
 
    int dir_fd = -1;
780
 
    if(plugindir == NULL){
781
 
      dir_fd = open(PDIR, O_RDONLY |
782
 
#ifdef O_CLOEXEC
783
 
                    O_CLOEXEC
784
 
#else  /* not O_CLOEXEC */
785
 
                    0
786
 
#endif  /* not O_CLOEXEC */
787
 
                    );
788
 
    } else {
789
 
      dir_fd = open(plugindir, O_RDONLY |
790
 
#ifdef O_CLOEXEC
791
 
                    O_CLOEXEC
792
 
#else  /* not O_CLOEXEC */
793
 
                    0
794
 
#endif  /* not O_CLOEXEC */
795
 
                    );
796
 
    }
797
 
    if(dir_fd == -1){
798
 
      error(0, errno, "Could not open plugin dir");
799
 
      exitstatus = EX_UNAVAILABLE;
800
 
      goto fallback;
801
 
    }
802
 
    
803
 
#ifndef O_CLOEXEC
804
 
  /* Set the FD_CLOEXEC flag on the directory */
805
 
    ret = set_cloexec_flag(dir_fd);
806
 
    if(ret < 0){
807
 
      error(0, errno, "set_cloexec_flag");
808
 
      TEMP_FAILURE_RETRY(close(dir_fd));
809
 
      exitstatus = EX_OSERR;
810
 
      goto fallback;
811
 
    }
812
 
#endif  /* O_CLOEXEC */
813
 
    
814
 
    dir = fdopendir(dir_fd);
815
 
    if(dir == NULL){
816
 
      error(0, errno, "Could not open plugin dir");
817
 
      TEMP_FAILURE_RETRY(close(dir_fd));
818
 
      exitstatus = EX_OSERR;
819
 
      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
      }
820
675
    }
821
676
  }
822
677
  
824
679
  
825
680
  /* Read and execute any executable in the plugin directory*/
826
681
  while(true){
827
 
    do {
828
 
      dirst = readdir(dir);
829
 
    } while(dirst == NULL and errno == EINTR);
 
682
    dirst = readdir(dir);
830
683
    
831
684
    /* All directory entries have been processed */
832
685
    if(dirst == NULL){
833
 
      if(errno == EBADF){
834
 
        error(0, errno, "readdir");
835
 
        exitstatus = EX_IOERR;
 
686
      if (errno == EBADF){
 
687
        perror("readdir");
 
688
        exitstatus = EXIT_FAILURE;
836
689
        goto fallback;
837
690
      }
838
691
      break;
868
721
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
869
722
        size_t suf_len = strlen(*suf);
870
723
        if((d_name_len >= suf_len)
871
 
           and (strcmp((dirst->d_name) + d_name_len-suf_len, *suf)
 
724
           and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
872
725
                == 0)){
873
726
          if(debug){
874
727
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
883
736
        continue;
884
737
      }
885
738
    }
886
 
    
 
739
 
887
740
    char *filename;
888
741
    if(plugindir == NULL){
889
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s",
890
 
                                             dirst->d_name));
 
742
      ret = asprintf(&filename, PDIR "/%s", dirst->d_name);
891
743
    } else {
892
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s",
893
 
                                             plugindir,
894
 
                                             dirst->d_name));
 
744
      ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
895
745
    }
896
746
    if(ret < 0){
897
 
      error(0, errno, "asprintf");
 
747
      perror("asprintf");
898
748
      continue;
899
749
    }
900
750
    
901
 
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
902
 
    if(ret == -1){
903
 
      error(0, errno, "stat");
 
751
    ret = stat(filename, &st);
 
752
    if (ret == -1){
 
753
      perror("stat");
904
754
      free(filename);
905
755
      continue;
906
756
    }
907
 
    
 
757
 
908
758
    /* Ignore non-executable files */
909
 
    if(not S_ISREG(st.st_mode)
910
 
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
 
759
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
911
760
      if(debug){
912
761
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
913
762
                " with bad type or mode\n", filename);
918
767
    
919
768
    plugin *p = getplugin(dirst->d_name);
920
769
    if(p == NULL){
921
 
      error(0, errno, "getplugin");
 
770
      perror("getplugin");
922
771
      free(filename);
923
772
      continue;
924
773
    }
936
785
      if(g != NULL){
937
786
        for(char **a = g->argv + 1; *a != NULL; a++){
938
787
          if(not add_argument(p, *a)){
939
 
            error(0, errno, "add_argument");
 
788
            perror("add_argument");
940
789
          }
941
790
        }
942
791
        /* Add global environment variables */
943
792
        for(char **e = g->environ; *e != NULL; e++){
944
793
          if(not add_environment(p, *e, false)){
945
 
            error(0, errno, "add_environment");
 
794
            perror("add_environment");
946
795
          }
947
796
        }
948
797
      }
953
802
    if(p->environ[0] != NULL){
954
803
      for(char **e = environ; *e != NULL; e++){
955
804
        if(not add_environment(p, *e, false)){
956
 
          error(0, errno, "add_environment");
 
805
          perror("add_environment");
957
806
        }
958
807
      }
959
808
    }
960
809
    
961
810
    int pipefd[2];
962
 
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
963
 
    if(ret == -1){
964
 
      error(0, errno, "pipe");
965
 
      exitstatus = EX_OSERR;
 
811
    ret = pipe(pipefd);
 
812
    if (ret == -1){
 
813
      perror("pipe");
 
814
      exitstatus = EXIT_FAILURE;
966
815
      goto fallback;
967
816
    }
968
817
    /* Ask OS to automatic close the pipe on exec */
969
818
    ret = set_cloexec_flag(pipefd[0]);
970
819
    if(ret < 0){
971
 
      error(0, errno, "set_cloexec_flag");
972
 
      exitstatus = EX_OSERR;
 
820
      perror("set_cloexec_flag");
 
821
      exitstatus = EXIT_FAILURE;
973
822
      goto fallback;
974
823
    }
975
824
    ret = set_cloexec_flag(pipefd[1]);
976
825
    if(ret < 0){
977
 
      error(0, errno, "set_cloexec_flag");
978
 
      exitstatus = EX_OSERR;
 
826
      perror("set_cloexec_flag");
 
827
      exitstatus = EXIT_FAILURE;
979
828
      goto fallback;
980
829
    }
981
830
    /* Block SIGCHLD until process is safely in process list */
982
 
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
983
 
                                              &sigchld_action.sa_mask,
984
 
                                              NULL));
 
831
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
985
832
    if(ret < 0){
986
 
      error(0, errno, "sigprocmask");
987
 
      exitstatus = EX_OSERR;
 
833
      perror("sigprocmask");
 
834
      exitstatus = EXIT_FAILURE;
988
835
      goto fallback;
989
836
    }
990
837
    /* Starting a new process to be watched */
991
 
    pid_t pid;
992
 
    do {
993
 
      pid = fork();
994
 
    } while(pid == -1 and errno == EINTR);
 
838
    pid_t pid = fork();
995
839
    if(pid == -1){
996
 
      error(0, errno, "fork");
997
 
      exitstatus = EX_OSERR;
 
840
      perror("fork");
 
841
      exitstatus = EXIT_FAILURE;
998
842
      goto fallback;
999
843
    }
1000
844
    if(pid == 0){
1001
845
      /* this is the child process */
1002
846
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1003
847
      if(ret < 0){
1004
 
        error(0, errno, "sigaction");
1005
 
        _exit(EX_OSERR);
 
848
        perror("sigaction");
 
849
        _exit(EXIT_FAILURE);
1006
850
      }
1007
851
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
1008
852
      if(ret < 0){
1009
 
        error(0, errno, "sigprocmask");
1010
 
        _exit(EX_OSERR);
 
853
        perror("sigprocmask");
 
854
        _exit(EXIT_FAILURE);
1011
855
      }
1012
856
      
1013
857
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
1014
858
      if(ret == -1){
1015
 
        error(0, errno, "dup2");
1016
 
        _exit(EX_OSERR);
 
859
        perror("dup2");
 
860
        _exit(EXIT_FAILURE);
1017
861
      }
1018
862
      
1019
863
      if(dirfd(dir) < 0){
1023
867
      }
1024
868
      if(p->environ[0] == NULL){
1025
869
        if(execv(filename, p->argv) < 0){
1026
 
          error(0, errno, "execv for %s", filename);
1027
 
          _exit(EX_OSERR);
 
870
          perror("execv");
 
871
          _exit(EXIT_FAILURE);
1028
872
        }
1029
873
      } else {
1030
874
        if(execve(filename, p->argv, p->environ) < 0){
1031
 
          error(0, errno, "execve for %s", filename);
1032
 
          _exit(EX_OSERR);
 
875
          perror("execve");
 
876
          _exit(EXIT_FAILURE);
1033
877
        }
1034
878
      }
1035
879
      /* no return */
1036
880
    }
1037
881
    /* Parent process */
1038
 
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1039
 
                                             pipe */
 
882
    close(pipefd[1]);           /* Close unused write end of pipe */
1040
883
    free(filename);
1041
884
    plugin *new_plugin = getplugin(dirst->d_name);
1042
 
    if(new_plugin == NULL){
1043
 
      error(0, errno, "getplugin");
1044
 
      ret = (int)(TEMP_FAILURE_RETRY
1045
 
                  (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
1046
 
                               NULL)));
 
885
    if (new_plugin == NULL){
 
886
      perror("getplugin");
 
887
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
1047
888
      if(ret < 0){
1048
 
        error(0, errno, "sigprocmask");
 
889
        perror("sigprocmask");
1049
890
      }
1050
 
      exitstatus = EX_OSERR;
 
891
      exitstatus = EXIT_FAILURE;
1051
892
      goto fallback;
1052
893
    }
1053
894
    
1056
897
    
1057
898
    /* Unblock SIGCHLD so signal handler can be run if this process
1058
899
       has already completed */
1059
 
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1060
 
                                              &sigchld_action.sa_mask,
1061
 
                                              NULL));
 
900
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
1062
901
    if(ret < 0){
1063
 
      error(0, errno, "sigprocmask");
1064
 
      exitstatus = EX_OSERR;
 
902
      perror("sigprocmask");
 
903
      exitstatus = EXIT_FAILURE;
1065
904
      goto fallback;
1066
905
    }
1067
906
    
1068
 
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
1069
 
                                          -Wconversion */
 
907
    FD_SET(new_plugin->fd, &rfds_all);
1070
908
    
1071
 
    if(maxfd < new_plugin->fd){
 
909
    if (maxfd < new_plugin->fd){
1072
910
      maxfd = new_plugin->fd;
1073
911
    }
1074
912
  }
1075
913
  
1076
 
  TEMP_FAILURE_RETRY(closedir(dir));
 
914
  closedir(dir);
1077
915
  dir = NULL;
1078
 
  free_plugin(getplugin(NULL));
1079
916
  
1080
917
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1081
918
    if(p->pid != 0){
1092
929
  while(plugin_list){
1093
930
    fd_set rfds = rfds_all;
1094
931
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
1095
 
    if(select_ret == -1 and errno != EINTR){
1096
 
      error(0, errno, "select");
1097
 
      exitstatus = EX_OSERR;
 
932
    if (select_ret == -1){
 
933
      perror("select");
 
934
      exitstatus = EXIT_FAILURE;
1098
935
      goto fallback;
1099
936
    }
1100
937
    /* OK, now either a process completed, or something can be read
1101
938
       from one of them */
1102
939
    for(plugin *proc = plugin_list; proc != NULL;){
1103
940
      /* Is this process completely done? */
1104
 
      if(proc->completed and proc->eof){
 
941
      if(proc->eof and proc->completed){
1105
942
        /* Only accept the plugin output if it exited cleanly */
1106
943
        if(not WIFEXITED(proc->status)
1107
944
           or WEXITSTATUS(proc->status) != 0){
1108
945
          /* Bad exit by plugin */
1109
 
          
 
946
 
1110
947
          if(debug){
1111
948
            if(WIFEXITED(proc->status)){
1112
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
1113
 
                      " status %d\n", proc->name,
1114
 
                      (intmax_t) (proc->pid),
 
949
              fprintf(stderr, "Plugin %u exited with status %d\n",
 
950
                      (unsigned int) (proc->pid),
1115
951
                      WEXITSTATUS(proc->status));
1116
 
            } else if(WIFSIGNALED(proc->status)){
1117
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] killed by"
1118
 
                      " signal %d: %s\n", proc->name,
1119
 
                      (intmax_t) (proc->pid),
1120
 
                      WTERMSIG(proc->status),
1121
 
                      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));
1122
956
            } else if(WCOREDUMP(proc->status)){
1123
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
1124
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
 
957
              fprintf(stderr, "Plugin %d dumped core\n",
 
958
                      (unsigned int) (proc->pid));
1125
959
            }
1126
960
          }
1127
961
          
1128
962
          /* Remove the plugin */
1129
 
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1130
 
                                          -Wconversion */
1131
 
          
 
963
          FD_CLR(proc->fd, &rfds_all);
 
964
 
1132
965
          /* Block signal while modifying process_list */
1133
 
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1134
 
                                        (SIG_BLOCK,
1135
 
                                         &sigchld_action.sa_mask,
1136
 
                                         NULL));
 
966
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
1137
967
          if(ret < 0){
1138
 
            error(0, errno, "sigprocmask");
1139
 
            exitstatus = EX_OSERR;
 
968
            perror("sigprocmask");
 
969
            exitstatus = EXIT_FAILURE;
1140
970
            goto fallback;
1141
971
          }
1142
972
          
1145
975
          proc = next_plugin;
1146
976
          
1147
977
          /* We are done modifying process list, so unblock signal */
1148
 
          ret = (int)(TEMP_FAILURE_RETRY
1149
 
                      (sigprocmask(SIG_UNBLOCK,
1150
 
                                   &sigchld_action.sa_mask, NULL)));
 
978
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
 
979
                             NULL);
1151
980
          if(ret < 0){
1152
 
            error(0, errno, "sigprocmask");
1153
 
            exitstatus = EX_OSERR;
 
981
            perror("sigprocmask");
 
982
            exitstatus = EXIT_FAILURE;
1154
983
            goto fallback;
1155
984
          }
1156
985
          
1166
995
        bool bret = print_out_password(proc->buffer,
1167
996
                                       proc->buffer_length);
1168
997
        if(not bret){
1169
 
          error(0, errno, "print_out_password");
1170
 
          exitstatus = EX_IOERR;
 
998
          perror("print_out_password");
 
999
          exitstatus = EXIT_FAILURE;
1171
1000
        }
1172
1001
        goto fallback;
1173
1002
      }
1174
1003
      
1175
1004
      /* This process has not completed.  Does it have any output? */
1176
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1177
 
                                                         warning from
1178
 
                                                         -Wconversion */
 
1005
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1179
1006
        /* This process had nothing to say at this time */
1180
1007
        proc = proc->next;
1181
1008
        continue;
1184
1011
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1185
1012
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1186
1013
                               + (size_t) BUFFER_SIZE);
1187
 
        if(proc->buffer == NULL){
1188
 
          error(0, errno, "malloc");
1189
 
          exitstatus = EX_OSERR;
 
1014
        if (proc->buffer == NULL){
 
1015
          perror("malloc");
 
1016
          exitstatus = EXIT_FAILURE;
1190
1017
          goto fallback;
1191
1018
        }
1192
1019
        proc->buffer_size += BUFFER_SIZE;
1193
1020
      }
1194
1021
      /* Read from the process */
1195
 
      sret = TEMP_FAILURE_RETRY(read(proc->fd,
1196
 
                                     proc->buffer
1197
 
                                     + proc->buffer_length,
1198
 
                                     BUFFER_SIZE));
1199
 
      if(sret < 0){
 
1022
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
 
1023
                 BUFFER_SIZE);
 
1024
      if(ret < 0){
1200
1025
        /* Read error from this process; ignore the error */
1201
1026
        proc = proc->next;
1202
1027
        continue;
1203
1028
      }
1204
 
      if(sret == 0){
 
1029
      if(ret == 0){
1205
1030
        /* got EOF */
1206
1031
        proc->eof = true;
1207
1032
      } else {
1208
 
        proc->buffer_length += (size_t) sret;
 
1033
        proc->buffer_length += (size_t) ret;
1209
1034
      }
1210
1035
    }
1211
1036
  }
1212
 
  
1213
 
  
 
1037
 
 
1038
 
1214
1039
 fallback:
1215
1040
  
1216
 
  if(plugin_list == NULL or (exitstatus != EXIT_SUCCESS
1217
 
                             and exitstatus != EX_OK)){
 
1041
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
1218
1042
    /* Fallback if all plugins failed, none are found or an error
1219
1043
       occured */
1220
1044
    bool bret;
1228
1052
    }
1229
1053
    bret = print_out_password(passwordbuffer, len);
1230
1054
    if(not bret){
1231
 
      error(0, errno, "print_out_password");
1232
 
      exitstatus = EX_IOERR;
 
1055
      perror("print_out_password");
 
1056
      exitstatus = EXIT_FAILURE;
1233
1057
    }
1234
1058
  }
1235
1059
  
1236
1060
  /* Restore old signal handler */
1237
1061
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1238
1062
  if(ret == -1){
1239
 
    error(0, errno, "sigaction");
1240
 
    exitstatus = EX_OSERR;
 
1063
    perror("sigaction");
 
1064
    exitstatus = EXIT_FAILURE;
1241
1065
  }
1242
1066
  
1243
1067
  if(custom_argv != NULL){
1258
1082
      ret = kill(p->pid, SIGTERM);
1259
1083
      if(ret == -1 and errno != ESRCH){
1260
1084
        /* Set-uid proccesses might not get closed */
1261
 
        error(0, errno, "kill");
 
1085
        perror("kill");
1262
1086
      }
1263
1087
    }
1264
1088
  }
1265
1089
  
1266
1090
  /* Wait for any remaining child processes to terminate */
1267
 
  do {
 
1091
  do{
1268
1092
    ret = wait(NULL);
1269
1093
  } while(ret >= 0);
1270
1094
  if(errno != ECHILD){
1271
 
    error(0, errno, "wait");
 
1095
    perror("wait");
1272
1096
  }
1273
1097
  
1274
1098
  free_plugin_list();