/mandos/trunk

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

« back to all changes in this revision

Viewing changes to plugin-runner.c

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

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

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

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

Show diffs side-by-side

added added

removed removed

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