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