/mandos/release

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

« back to all changes in this revision

Viewing changes to plugin-runner.c

First version of a somewhat complete D-Bus server interface.  Also
change user/group name to "_mandos".

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

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

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

Show diffs side-by-side

added added

removed removed

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