/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-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;
874
721
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
875
722
        size_t suf_len = strlen(*suf);
876
723
        if((d_name_len >= suf_len)
877
 
           and (strcmp((dirst->d_name) + d_name_len-suf_len, *suf)
 
724
           and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
878
725
                == 0)){
879
726
          if(debug){
880
727
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
889
736
        continue;
890
737
      }
891
738
    }
892
 
    
 
739
 
893
740
    char *filename;
894
741
    if(plugindir == NULL){
895
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s",
896
 
                                             dirst->d_name));
 
742
      ret = asprintf(&filename, PDIR "/%s", dirst->d_name);
897
743
    } else {
898
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s",
899
 
                                             plugindir,
900
 
                                             dirst->d_name));
 
744
      ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
901
745
    }
902
746
    if(ret < 0){
903
 
      error(0, errno, "asprintf");
 
747
      perror("asprintf");
904
748
      continue;
905
749
    }
906
750
    
907
 
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
908
 
    if(ret == -1){
909
 
      error(0, errno, "stat");
 
751
    ret = stat(filename, &st);
 
752
    if (ret == -1){
 
753
      perror("stat");
910
754
      free(filename);
911
755
      continue;
912
756
    }
913
 
    
 
757
 
914
758
    /* Ignore non-executable files */
915
 
    if(not S_ISREG(st.st_mode)
916
 
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
 
759
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
917
760
      if(debug){
918
761
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
919
762
                " with bad type or mode\n", filename);
924
767
    
925
768
    plugin *p = getplugin(dirst->d_name);
926
769
    if(p == NULL){
927
 
      error(0, errno, "getplugin");
 
770
      perror("getplugin");
928
771
      free(filename);
929
772
      continue;
930
773
    }
942
785
      if(g != NULL){
943
786
        for(char **a = g->argv + 1; *a != NULL; a++){
944
787
          if(not add_argument(p, *a)){
945
 
            error(0, errno, "add_argument");
 
788
            perror("add_argument");
946
789
          }
947
790
        }
948
791
        /* Add global environment variables */
949
792
        for(char **e = g->environ; *e != NULL; e++){
950
793
          if(not add_environment(p, *e, false)){
951
 
            error(0, errno, "add_environment");
 
794
            perror("add_environment");
952
795
          }
953
796
        }
954
797
      }
959
802
    if(p->environ[0] != NULL){
960
803
      for(char **e = environ; *e != NULL; e++){
961
804
        if(not add_environment(p, *e, false)){
962
 
          error(0, errno, "add_environment");
 
805
          perror("add_environment");
963
806
        }
964
807
      }
965
808
    }
966
809
    
967
810
    int pipefd[2];
968
 
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
969
 
    if(ret == -1){
970
 
      error(0, errno, "pipe");
971
 
      exitstatus = EX_OSERR;
 
811
    ret = pipe(pipefd);
 
812
    if (ret == -1){
 
813
      perror("pipe");
 
814
      exitstatus = EXIT_FAILURE;
972
815
      goto fallback;
973
816
    }
974
817
    /* Ask OS to automatic close the pipe on exec */
975
818
    ret = set_cloexec_flag(pipefd[0]);
976
819
    if(ret < 0){
977
 
      error(0, errno, "set_cloexec_flag");
978
 
      exitstatus = EX_OSERR;
 
820
      perror("set_cloexec_flag");
 
821
      exitstatus = EXIT_FAILURE;
979
822
      goto fallback;
980
823
    }
981
824
    ret = set_cloexec_flag(pipefd[1]);
982
825
    if(ret < 0){
983
 
      error(0, errno, "set_cloexec_flag");
984
 
      exitstatus = EX_OSERR;
 
826
      perror("set_cloexec_flag");
 
827
      exitstatus = EXIT_FAILURE;
985
828
      goto fallback;
986
829
    }
987
830
    /* Block SIGCHLD until process is safely in process list */
988
 
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
989
 
                                              &sigchld_action.sa_mask,
990
 
                                              NULL));
 
831
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
991
832
    if(ret < 0){
992
 
      error(0, errno, "sigprocmask");
993
 
      exitstatus = EX_OSERR;
 
833
      perror("sigprocmask");
 
834
      exitstatus = EXIT_FAILURE;
994
835
      goto fallback;
995
836
    }
996
837
    /* Starting a new process to be watched */
997
 
    pid_t pid;
998
 
    do {
999
 
      pid = fork();
1000
 
    } while(pid == -1 and errno == EINTR);
 
838
    pid_t pid = fork();
1001
839
    if(pid == -1){
1002
 
      error(0, errno, "fork");
1003
 
      exitstatus = EX_OSERR;
 
840
      perror("fork");
 
841
      exitstatus = EXIT_FAILURE;
1004
842
      goto fallback;
1005
843
    }
1006
844
    if(pid == 0){
1007
845
      /* this is the child process */
1008
846
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1009
847
      if(ret < 0){
1010
 
        error(0, errno, "sigaction");
1011
 
        _exit(EX_OSERR);
 
848
        perror("sigaction");
 
849
        _exit(EXIT_FAILURE);
1012
850
      }
1013
851
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
1014
852
      if(ret < 0){
1015
 
        error(0, errno, "sigprocmask");
1016
 
        _exit(EX_OSERR);
 
853
        perror("sigprocmask");
 
854
        _exit(EXIT_FAILURE);
1017
855
      }
1018
856
      
1019
857
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
1020
858
      if(ret == -1){
1021
 
        error(0, errno, "dup2");
1022
 
        _exit(EX_OSERR);
 
859
        perror("dup2");
 
860
        _exit(EXIT_FAILURE);
1023
861
      }
1024
862
      
1025
863
      if(dirfd(dir) < 0){
1029
867
      }
1030
868
      if(p->environ[0] == NULL){
1031
869
        if(execv(filename, p->argv) < 0){
1032
 
          error(0, errno, "execv for %s", filename);
1033
 
          _exit(EX_OSERR);
 
870
          perror("execv");
 
871
          _exit(EXIT_FAILURE);
1034
872
        }
1035
873
      } else {
1036
874
        if(execve(filename, p->argv, p->environ) < 0){
1037
 
          error(0, errno, "execve for %s", filename);
1038
 
          _exit(EX_OSERR);
 
875
          perror("execve");
 
876
          _exit(EXIT_FAILURE);
1039
877
        }
1040
878
      }
1041
879
      /* no return */
1042
880
    }
1043
881
    /* Parent process */
1044
 
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1045
 
                                             pipe */
 
882
    close(pipefd[1]);           /* Close unused write end of pipe */
1046
883
    free(filename);
1047
884
    plugin *new_plugin = getplugin(dirst->d_name);
1048
 
    if(new_plugin == NULL){
1049
 
      error(0, errno, "getplugin");
1050
 
      ret = (int)(TEMP_FAILURE_RETRY
1051
 
                  (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
1052
 
                               NULL)));
 
885
    if (new_plugin == NULL){
 
886
      perror("getplugin");
 
887
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
1053
888
      if(ret < 0){
1054
 
        error(0, errno, "sigprocmask");
 
889
        perror("sigprocmask");
1055
890
      }
1056
 
      exitstatus = EX_OSERR;
 
891
      exitstatus = EXIT_FAILURE;
1057
892
      goto fallback;
1058
893
    }
1059
894
    
1062
897
    
1063
898
    /* Unblock SIGCHLD so signal handler can be run if this process
1064
899
       has already completed */
1065
 
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1066
 
                                              &sigchld_action.sa_mask,
1067
 
                                              NULL));
 
900
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
1068
901
    if(ret < 0){
1069
 
      error(0, errno, "sigprocmask");
1070
 
      exitstatus = EX_OSERR;
 
902
      perror("sigprocmask");
 
903
      exitstatus = EXIT_FAILURE;
1071
904
      goto fallback;
1072
905
    }
1073
906
    
1074
 
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
1075
 
                                          -Wconversion */
 
907
    FD_SET(new_plugin->fd, &rfds_all);
1076
908
    
1077
 
    if(maxfd < new_plugin->fd){
 
909
    if (maxfd < new_plugin->fd){
1078
910
      maxfd = new_plugin->fd;
1079
911
    }
1080
912
  }
1081
913
  
1082
 
  TEMP_FAILURE_RETRY(closedir(dir));
 
914
  closedir(dir);
1083
915
  dir = NULL;
1084
 
  free_plugin(getplugin(NULL));
1085
916
  
1086
917
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1087
918
    if(p->pid != 0){
1098
929
  while(plugin_list){
1099
930
    fd_set rfds = rfds_all;
1100
931
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
1101
 
    if(select_ret == -1 and errno != EINTR){
1102
 
      error(0, errno, "select");
1103
 
      exitstatus = EX_OSERR;
 
932
    if (select_ret == -1){
 
933
      perror("select");
 
934
      exitstatus = EXIT_FAILURE;
1104
935
      goto fallback;
1105
936
    }
1106
937
    /* OK, now either a process completed, or something can be read
1107
938
       from one of them */
1108
939
    for(plugin *proc = plugin_list; proc != NULL;){
1109
940
      /* Is this process completely done? */
1110
 
      if(proc->completed and proc->eof){
 
941
      if(proc->eof and proc->completed){
1111
942
        /* Only accept the plugin output if it exited cleanly */
1112
943
        if(not WIFEXITED(proc->status)
1113
944
           or WEXITSTATUS(proc->status) != 0){
1114
945
          /* Bad exit by plugin */
1115
 
          
 
946
 
1116
947
          if(debug){
1117
948
            if(WIFEXITED(proc->status)){
1118
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
1119
 
                      " status %d\n", proc->name,
1120
 
                      (intmax_t) (proc->pid),
 
949
              fprintf(stderr, "Plugin %u exited with status %d\n",
 
950
                      (unsigned int) (proc->pid),
1121
951
                      WEXITSTATUS(proc->status));
1122
 
            } else if(WIFSIGNALED(proc->status)){
1123
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] killed by"
1124
 
                      " signal %d: %s\n", proc->name,
1125
 
                      (intmax_t) (proc->pid),
1126
 
                      WTERMSIG(proc->status),
1127
 
                      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));
1128
956
            } else if(WCOREDUMP(proc->status)){
1129
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
1130
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
 
957
              fprintf(stderr, "Plugin %d dumped core\n",
 
958
                      (unsigned int) (proc->pid));
1131
959
            }
1132
960
          }
1133
961
          
1134
962
          /* Remove the plugin */
1135
 
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1136
 
                                          -Wconversion */
1137
 
          
 
963
          FD_CLR(proc->fd, &rfds_all);
 
964
 
1138
965
          /* Block signal while modifying process_list */
1139
 
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1140
 
                                        (SIG_BLOCK,
1141
 
                                         &sigchld_action.sa_mask,
1142
 
                                         NULL));
 
966
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
1143
967
          if(ret < 0){
1144
 
            error(0, errno, "sigprocmask");
1145
 
            exitstatus = EX_OSERR;
 
968
            perror("sigprocmask");
 
969
            exitstatus = EXIT_FAILURE;
1146
970
            goto fallback;
1147
971
          }
1148
972
          
1151
975
          proc = next_plugin;
1152
976
          
1153
977
          /* We are done modifying process list, so unblock signal */
1154
 
          ret = (int)(TEMP_FAILURE_RETRY
1155
 
                      (sigprocmask(SIG_UNBLOCK,
1156
 
                                   &sigchld_action.sa_mask, NULL)));
 
978
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
 
979
                             NULL);
1157
980
          if(ret < 0){
1158
 
            error(0, errno, "sigprocmask");
1159
 
            exitstatus = EX_OSERR;
 
981
            perror("sigprocmask");
 
982
            exitstatus = EXIT_FAILURE;
1160
983
            goto fallback;
1161
984
          }
1162
985
          
1172
995
        bool bret = print_out_password(proc->buffer,
1173
996
                                       proc->buffer_length);
1174
997
        if(not bret){
1175
 
          error(0, errno, "print_out_password");
1176
 
          exitstatus = EX_IOERR;
 
998
          perror("print_out_password");
 
999
          exitstatus = EXIT_FAILURE;
1177
1000
        }
1178
1001
        goto fallback;
1179
1002
      }
1180
1003
      
1181
1004
      /* This process has not completed.  Does it have any output? */
1182
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1183
 
                                                         warning from
1184
 
                                                         -Wconversion */
 
1005
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1185
1006
        /* This process had nothing to say at this time */
1186
1007
        proc = proc->next;
1187
1008
        continue;
1190
1011
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1191
1012
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1192
1013
                               + (size_t) BUFFER_SIZE);
1193
 
        if(proc->buffer == NULL){
1194
 
          error(0, errno, "malloc");
1195
 
          exitstatus = EX_OSERR;
 
1014
        if (proc->buffer == NULL){
 
1015
          perror("malloc");
 
1016
          exitstatus = EXIT_FAILURE;
1196
1017
          goto fallback;
1197
1018
        }
1198
1019
        proc->buffer_size += BUFFER_SIZE;
1199
1020
      }
1200
1021
      /* Read from the process */
1201
 
      sret = TEMP_FAILURE_RETRY(read(proc->fd,
1202
 
                                     proc->buffer
1203
 
                                     + proc->buffer_length,
1204
 
                                     BUFFER_SIZE));
1205
 
      if(sret < 0){
 
1022
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
 
1023
                 BUFFER_SIZE);
 
1024
      if(ret < 0){
1206
1025
        /* Read error from this process; ignore the error */
1207
1026
        proc = proc->next;
1208
1027
        continue;
1209
1028
      }
1210
 
      if(sret == 0){
 
1029
      if(ret == 0){
1211
1030
        /* got EOF */
1212
1031
        proc->eof = true;
1213
1032
      } else {
1214
 
        proc->buffer_length += (size_t) sret;
 
1033
        proc->buffer_length += (size_t) ret;
1215
1034
      }
1216
1035
    }
1217
1036
  }
1218
 
  
1219
 
  
 
1037
 
 
1038
 
1220
1039
 fallback:
1221
1040
  
1222
 
  if(plugin_list == NULL or (exitstatus != EXIT_SUCCESS
1223
 
                             and exitstatus != EX_OK)){
 
1041
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
1224
1042
    /* Fallback if all plugins failed, none are found or an error
1225
1043
       occured */
1226
1044
    bool bret;
1234
1052
    }
1235
1053
    bret = print_out_password(passwordbuffer, len);
1236
1054
    if(not bret){
1237
 
      error(0, errno, "print_out_password");
1238
 
      exitstatus = EX_IOERR;
 
1055
      perror("print_out_password");
 
1056
      exitstatus = EXIT_FAILURE;
1239
1057
    }
1240
1058
  }
1241
1059
  
1242
1060
  /* Restore old signal handler */
1243
1061
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1244
1062
  if(ret == -1){
1245
 
    error(0, errno, "sigaction");
1246
 
    exitstatus = EX_OSERR;
 
1063
    perror("sigaction");
 
1064
    exitstatus = EXIT_FAILURE;
1247
1065
  }
1248
1066
  
1249
1067
  if(custom_argv != NULL){
1264
1082
      ret = kill(p->pid, SIGTERM);
1265
1083
      if(ret == -1 and errno != ESRCH){
1266
1084
        /* Set-uid proccesses might not get closed */
1267
 
        error(0, errno, "kill");
 
1085
        perror("kill");
1268
1086
      }
1269
1087
    }
1270
1088
  }
1271
1089
  
1272
1090
  /* Wait for any remaining child processes to terminate */
1273
 
  do {
 
1091
  do{
1274
1092
    ret = wait(NULL);
1275
1093
  } while(ret >= 0);
1276
1094
  if(errno != ECHILD){
1277
 
    error(0, errno, "wait");
 
1095
    perror("wait");
1278
1096
  }
1279
1097
  
1280
1098
  free_plugin_list();