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