/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 plugbasedclient.c

  • Committer: Teddy Hogeborn
  • Date: 2008-08-02 10:48:24 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080802104824-fx0miwp9o4g9r31e
* plugbasedclient.c (struct process): New fields "eof", "completed",
                                      and "status".
  (handle_sigchld): New function.
  (main): Initialize "dir" to NULL to only closedir() it if necessary.
          Move "process_list" to be a global variable to be accessible
          by "handle_sigchld".  Make "handle_sigchld" handle SIGCHLD.
          Remove redundant check for NULL "dir".  Free "filename" when
          no longer used.  Block SIGCHLD around fork()/exec().
          Restore normal signals in child.  Only loop while running
          processes exist.  Print process buffer when the process is
          done and it has emitted EOF, not when it only emits EOF.
          Remove processes from list which exit non-cleanly.  In
          cleaning up, closedir() if necessary.  Bug fix: set next
          pointer correctly when freeing process list.

* plugins.d/passprompt.c (main): Do not ignore SIGQUIT.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 * Contact the authors at <mandos@fukt.bsnet.se>.
22
22
 */
23
23
 
24
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
25
 
                                   asprintf() */
26
 
#include <stddef.h>             /* size_t, NULL */
27
 
#include <stdlib.h>             /* malloc(), exit(), EXIT_FAILURE,
28
 
                                   EXIT_SUCCESS, realloc() */
29
 
#include <stdbool.h>            /* bool, true, false */
30
 
#include <stdio.h>              /* perror, popen(), fileno(),
31
 
                                   fprintf(), stderr, STDOUT_FILENO */
32
 
#include <sys/types.h>          /* DIR, opendir(), stat(), struct
33
 
                                   stat, waitpid(), WIFEXITED(),
34
 
                                   WEXITSTATUS(), wait(), pid_t,
35
 
                                   uid_t, gid_t, getuid(), getgid(),
36
 
                                   dirfd() */
37
 
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
38
 
                                   FD_SET(), FD_ISSET(), FD_CLR */
39
 
#include <sys/wait.h>           /* wait(), waitpid(), WIFEXITED(),
40
 
                                   WEXITSTATUS() */
41
 
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
 
24
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY() */
 
25
 
 
26
#include <stdio.h>              /* popen(), fileno(), fprintf(),
 
27
                                   stderr, STDOUT_FILENO */
42
28
#include <iso646.h>             /* and, or, not */
 
29
#include <sys/types.h>          /* DIR, opendir(), stat(),
 
30
                                   struct stat, waitpid(),
 
31
                                   WIFEXITED(), WEXITSTATUS(),
 
32
                                   wait() */
 
33
#include <sys/wait.h>           /* wait() */
43
34
#include <dirent.h>             /* DIR, struct dirent, opendir(),
44
 
                                   readdir(), closedir(), dirfd() */
 
35
                                   readdir(), closedir() */
 
36
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
45
37
#include <unistd.h>             /* struct stat, stat(), S_ISREG(),
46
 
                                   fcntl(), setuid(), setgid(),
47
 
                                   F_GETFD, F_SETFD, FD_CLOEXEC,
48
 
                                   access(), pipe(), fork(), close()
49
 
                                   dup2, STDOUT_FILENO, _exit(),
50
 
                                   execv(), write(), read(),
51
 
                                   close() */
52
 
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
53
 
                                   FD_CLOEXEC */
54
 
#include <string.h>             /* strsep, strlen(), asprintf() */
 
38
                                   fcntl() */
 
39
#include <fcntl.h>              /* fcntl() */
 
40
#include <stddef.h>             /* NULL */
 
41
#include <stdlib.h>             /* EXIT_FAILURE */
 
42
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
 
43
                                   FD_SET(), FD_ISSET() */
 
44
#include <string.h>             /* strlen(), strcpy(), strcat() */
 
45
#include <stdbool.h>            /* true */
 
46
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
 
47
                                   WEXITSTATUS() */
55
48
#include <errno.h>              /* errno */
56
 
#include <argp.h>               /* struct argp_option, struct
57
 
                                   argp_state, struct argp,
58
 
                                   argp_parse(), ARGP_ERR_UNKNOWN,
59
 
                                   ARGP_KEY_END, ARGP_KEY_ARG,
60
 
                                   error_t */
61
 
#include <signal.h>             /* struct sigaction, sigemptyset(),
62
 
                                   sigaddset(), sigaction(),
63
 
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
64
 
                                   SIG_UNBLOCK, kill() */
65
 
#include <errno.h>              /* errno, EBADF */
66
 
 
67
 
#define BUFFER_SIZE 256
68
 
 
69
 
#define PDIR "/lib/mandos/plugins.d"
70
 
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
71
 
 
72
 
const char *argp_program_version = "plugin-runner 1.0";
73
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
74
 
 
75
 
struct plugin;
76
 
 
77
 
typedef struct plugin{
78
 
  char *name;                   /* can be NULL or any plugin name */
79
 
  char **argv;
80
 
  int argc;
81
 
  char **environ;
82
 
  int envc;
83
 
  bool disabled;
84
 
 
85
 
  /* Variables used for running processes*/
 
49
#include <argp.h>               /* struct argp_option,
 
50
                                   struct argp_state, struct argp,
 
51
                                   argp_parse() */
 
52
 
 
53
struct process;
 
54
 
 
55
typedef struct process{
86
56
  pid_t pid;
87
57
  int fd;
88
58
  char *buffer;
89
59
  size_t buffer_size;
90
60
  size_t buffer_length;
91
61
  bool eof;
92
 
  volatile bool completed;
93
 
  volatile int status;
 
62
  bool completed;
 
63
  int status;
 
64
  struct process *next;
 
65
} process;
 
66
 
 
67
typedef struct plugin{
 
68
  char *name;                   /* can be NULL or any plugin name */
 
69
  char **argv;
 
70
  int argc;
 
71
  bool disabled;
94
72
  struct plugin *next;
95
73
} plugin;
96
74
 
97
 
static plugin *plugin_list = NULL;
98
 
 
99
 
/* Gets an existing plugin based on name,
100
 
   or if none is found, creates a new one */
101
 
static plugin *getplugin(char *name){
102
 
  /* Check for exiting plugin with that name */
103
 
  for (plugin *p = plugin_list; p != NULL; p = p->next){
 
75
plugin *getplugin(char *name, plugin **plugin_list){
 
76
  for (plugin *p = *plugin_list; p != NULL; p = p->next){
104
77
    if ((p->name == name)
105
78
        or (p->name and name and (strcmp(p->name, name) == 0))){
106
79
      return p;
109
82
  /* Create a new plugin */
110
83
  plugin *new_plugin = malloc(sizeof(plugin));
111
84
  if (new_plugin == NULL){
112
 
    return NULL;
113
 
  }
114
 
  char *copy_name = NULL;
115
 
  if(name != NULL){
116
 
    copy_name = strdup(name);
117
 
    if(copy_name == NULL){
118
 
      return NULL;
119
 
    }
120
 
  }
121
 
  
122
 
  *new_plugin = (plugin) { .name = copy_name,
123
 
                           .argc = 1,
124
 
                           .disabled = false,
125
 
                           .next = plugin_list };
126
 
  
 
85
    perror("malloc");
 
86
    exit(EXIT_FAILURE);
 
87
  }
 
88
  new_plugin->name = name;
127
89
  new_plugin->argv = malloc(sizeof(char *) * 2);
128
90
  if (new_plugin->argv == NULL){
129
 
    free(copy_name);
130
 
    free(new_plugin);
131
 
    return NULL;
 
91
    perror("malloc");
 
92
    exit(EXIT_FAILURE);
132
93
  }
133
 
  new_plugin->argv[0] = copy_name;
 
94
  new_plugin->argv[0] = name;
134
95
  new_plugin->argv[1] = NULL;
135
 
  
136
 
  new_plugin->environ = malloc(sizeof(char *));
137
 
  if(new_plugin->environ == NULL){
138
 
    free(copy_name);
139
 
    free(new_plugin->argv);
140
 
    free(new_plugin);
141
 
    return NULL;
142
 
  }
143
 
  new_plugin->environ[0] = NULL;
144
 
  
 
96
  new_plugin->argc = 1;
 
97
  new_plugin->disabled = false;
 
98
  new_plugin->next = *plugin_list;
145
99
  /* Append the new plugin to the list */
146
 
  plugin_list = new_plugin;
 
100
  *plugin_list = new_plugin;
147
101
  return new_plugin;
148
102
}
149
103
 
150
 
/* Helper function for add_argument and add_environment */
151
 
static bool add_to_char_array(const char *new, char ***array,
152
 
                              int *len){
153
 
  /* Resize the pointed-to array to hold one more pointer */
154
 
  *array = realloc(*array, sizeof(char *)
155
 
                   * (size_t) ((*len) + 2));
156
 
  /* Malloc check */
157
 
  if(*array == NULL){
158
 
    return false;
159
 
  }
160
 
  /* Make a copy of the new string */
161
 
  char *copy = strdup(new);
162
 
  if(copy == NULL){
163
 
    return false;
164
 
  }
165
 
  /* Insert the copy */
166
 
  (*array)[*len] = copy;
167
 
  (*len)++;
168
 
  /* Add a new terminating NULL pointer to the last element */
169
 
  (*array)[*len] = NULL;
170
 
  return true;
171
 
}
172
 
 
173
 
/* Add to a plugin's argument vector */
174
 
static bool add_argument(plugin *p, const char *arg){
175
 
  if(p == NULL){
176
 
    return false;
177
 
  }
178
 
  return add_to_char_array(arg, &(p->argv), &(p->argc));
179
 
}
180
 
 
181
 
/* Add to a plugin's environment */
182
 
static bool add_environment(plugin *p, const char *def, bool replace){
183
 
  if(p == NULL){
184
 
    return false;
185
 
  }
186
 
  /* namelen = length of name of environment variable */
187
 
  size_t namelen = (size_t)(strchrnul(def, '=') - def);
188
 
  /* Search for this environment variable */
189
 
  for(char **e = p->environ; *e != NULL; e++){
190
 
    if(strncmp(*e, def, namelen + 1) == 0){
191
 
      /* It already exists */
192
 
      if(replace){
193
 
        char *new = realloc(*e, strlen(def) + 1);
194
 
        if(new == NULL){
195
 
          return false;
196
 
        }
197
 
        *e = new;
198
 
        strcpy(*e, def);
199
 
      }
200
 
      return true;
201
 
    }
202
 
  }
203
 
  return add_to_char_array(def, &(p->environ), &(p->envc));
 
104
void addargument(plugin *p, char *arg){
 
105
  p->argv[p->argc] = arg;
 
106
  p->argv = realloc(p->argv, sizeof(char *) * (size_t)(p->argc + 2));
 
107
  if (p->argv == NULL){
 
108
    perror("malloc");
 
109
    exit(EXIT_FAILURE);
 
110
  }
 
111
  p->argc++;
 
112
  p->argv[p->argc] = NULL;
204
113
}
205
114
 
206
115
/*
208
117
 * Descriptor Flags".
209
118
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
210
119
 */
211
 
static int set_cloexec_flag(int fd)
 
120
int set_cloexec_flag(int fd)
212
121
{
213
122
  int ret = fcntl(fd, F_GETFD, 0);
214
123
  /* If reading the flags failed, return error indication now. */
219
128
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
220
129
}
221
130
 
222
 
 
223
 
/* Mark processes as completed when they exit, and save their exit
 
131
#define BUFFER_SIZE 256
 
132
 
 
133
const char *argp_program_version = "plugbasedclient 0.9";
 
134
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
135
 
 
136
process *process_list = NULL;
 
137
 
 
138
/* Mark a process as completed when it exits, and save its exit
224
139
   status. */
225
140
void handle_sigchld(__attribute__((unused)) int sig){
226
 
  while(true){
227
 
    plugin *proc = plugin_list;
228
 
    int status;
229
 
    pid_t pid = waitpid(-1, &status, WNOHANG);
230
 
    if(pid == 0){
231
 
      /* Only still running child processes */
232
 
      break;
233
 
    }
234
 
    if(pid == -1){
235
 
      if (errno != ECHILD){
236
 
        perror("waitpid");
237
 
      }
238
 
      /* No child processes */
239
 
      break;
240
 
    }
241
 
    
242
 
    /* A child exited, find it in process_list */
243
 
    while(proc != NULL and proc->pid != pid){
244
 
      proc = proc->next;
245
 
    }
246
 
    if(proc == NULL){
247
 
      /* Process not found in process list */
248
 
      continue;
249
 
    }
250
 
    proc->status = status;
251
 
    proc->completed = true;
252
 
  }
253
 
}
254
 
 
255
 
/* Prints out a password to stdout */
256
 
bool print_out_password(const char *buffer, size_t length){
257
 
  ssize_t ret;
258
 
  for(size_t written = 0; written < length; written += (size_t)ret){
259
 
    ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
260
 
                                   length - written));
261
 
    if(ret < 0){
262
 
      return false;
263
 
    }
264
 
  }
265
 
  return true;
266
 
}
267
 
 
268
 
/* Removes and free a plugin from the plugin list */
269
 
static void free_plugin(plugin *plugin_node){
270
 
  
271
 
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
272
 
    free(*arg);
273
 
  }
274
 
  free(plugin_node->argv);
275
 
  for(char **env = plugin_node->environ; *env != NULL; env++){
276
 
    free(*env);
277
 
  }
278
 
  free(plugin_node->environ);
279
 
  free(plugin_node->buffer);
280
 
 
281
 
  /* Removes the plugin from the singly-linked list */
282
 
  if(plugin_node == plugin_list){
283
 
    /* First one - simple */
284
 
    plugin_list = plugin_list->next;
285
 
  } else {
286
 
    /* Second one or later */
287
 
    for(plugin *p = plugin_list; p != NULL; p = p->next){
288
 
      if(p->next == plugin_node){
289
 
        p->next = plugin_node->next;
290
 
        break;
291
 
      }
292
 
    }
293
 
  }
294
 
  
295
 
  free(plugin_node);
296
 
}
297
 
 
298
 
static void free_plugin_list(void){
299
 
  while(plugin_list != NULL){
300
 
    free_plugin(plugin_list);
301
 
  }
 
141
  process *proc = process_list;
 
142
  int status;
 
143
  pid_t pid = wait(&status);
 
144
  while(proc != NULL and proc->pid != pid){
 
145
    proc = proc->next;    
 
146
  }
 
147
  if(proc == NULL){
 
148
    /* Process not found in process list */
 
149
    return;
 
150
  }
 
151
  proc->status = status;
 
152
  proc->completed = true;
302
153
}
303
154
 
304
155
int main(int argc, char *argv[]){
305
 
  char *plugindir = NULL;
306
 
  char *argfile = NULL;
307
 
  FILE *conffp;
 
156
  const char *plugindir = "/conf/conf.d/mandos/plugins.d";
308
157
  size_t d_name_len;
309
158
  DIR *dir = NULL;
310
159
  struct dirent *dirst;
311
160
  struct stat st;
312
161
  fd_set rfds_all;
313
162
  int ret, maxfd = 0;
314
 
  uid_t uid = 65534;
315
 
  gid_t gid = 65534;
316
163
  bool debug = false;
317
164
  int exitstatus = EXIT_SUCCESS;
318
 
  struct sigaction old_sigchld_action;
319
 
  struct sigaction sigchld_action = { .sa_handler = handle_sigchld,
320
 
                                      .sa_flags = SA_NOCLDSTOP };
321
 
  char **custom_argv = NULL;
322
 
  int custom_argc = 0;
323
165
  
324
166
  /* Establish a signal handler */
 
167
  struct sigaction old_sigchld_action,
 
168
    sigchld_action = { .sa_handler = handle_sigchld,
 
169
                       .sa_flags = SA_NOCLDSTOP };
325
170
  sigemptyset(&sigchld_action.sa_mask);
326
171
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
327
 
  if(ret == -1){
 
172
  if(ret < 0){
328
173
    perror("sigaddset");
329
 
    exitstatus = EXIT_FAILURE;
330
 
    goto fallback;
 
174
    exit(EXIT_FAILURE);
331
175
  }
332
176
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
333
 
  if(ret == -1){
 
177
  if(ret < 0){
334
178
    perror("sigaction");
335
 
    exitstatus = EXIT_FAILURE;
336
 
    goto fallback;
 
179
    exit(EXIT_FAILURE);
337
180
  }
338
181
  
339
182
  /* The options we understand. */
341
184
    { .name = "global-options", .key = 'g',
342
185
      .arg = "OPTION[,OPTION[,...]]",
343
186
      .doc = "Options passed to all plugins" },
344
 
    { .name = "global-env", .key = 'G',
345
 
      .arg = "VAR=value",
346
 
      .doc = "Environment variable passed to all plugins" },
347
187
    { .name = "options-for", .key = 'o',
348
188
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
349
189
      .doc = "Options passed only to specified plugin" },
350
 
    { .name = "env-for", .key = 'E',
351
 
      .arg = "PLUGIN:ENV=value",
352
 
      .doc = "Environment variable passed to specified plugin" },
353
190
    { .name = "disable", .key = 'd',
354
191
      .arg = "PLUGIN",
355
192
      .doc = "Disable a specific plugin", .group = 1 },
356
 
    { .name = "enable", .key = 'e',
357
 
      .arg = "PLUGIN",
358
 
      .doc = "Enable a specific plugin", .group = 1 },
359
193
    { .name = "plugin-dir", .key = 128,
360
194
      .arg = "DIRECTORY",
361
195
      .doc = "Specify a different plugin directory", .group = 2 },
362
 
    { .name = "config-file", .key = 129,
363
 
      .arg = "FILE",
364
 
      .doc = "Specify a different configuration file", .group = 2 },
365
 
    { .name = "userid", .key = 130,
366
 
      .arg = "ID", .flags = 0,
367
 
      .doc = "User ID the plugins will run as", .group = 3 },
368
 
    { .name = "groupid", .key = 131,
369
 
      .arg = "ID", .flags = 0,
370
 
      .doc = "Group ID the plugins will run as", .group = 3 },
371
 
    { .name = "debug", .key = 132,
372
 
      .doc = "Debug mode", .group = 4 },
 
196
    { .name = "debug", .key = 129,
 
197
      .doc = "Debug mode", .group = 3 },
373
198
    { .name = NULL }
374
199
  };
375
200
  
376
 
  error_t parse_opt (int key, char *arg, __attribute__((unused))
377
 
                     struct argp_state *state) {
 
201
  error_t parse_opt (int key, char *arg, struct argp_state *state) {
 
202
    /* Get the INPUT argument from `argp_parse', which we know is a
 
203
       pointer to our plugin list pointer. */
 
204
    plugin **plugins = state->input;
378
205
    switch (key) {
379
 
    case 'g':                   /* --global-options */
380
 
      if (arg != NULL){
381
 
        char *p;
382
 
        while((p = strsep(&arg, ",")) != NULL){
383
 
          if(p[0] == '\0'){
384
 
            continue;
385
 
          }
386
 
          if(not add_argument(getplugin(NULL), p)){
387
 
            perror("add_argument");
388
 
            return ARGP_ERR_UNKNOWN;
389
 
          }
390
 
        }
391
 
      }
392
 
      break;
393
 
    case 'G':                   /* --global-env */
394
 
      if(arg == NULL){
395
 
        break;
396
 
      }
397
 
      {
398
 
        char *envdef = strdup(arg);
399
 
        if(envdef == NULL){
400
 
          break;
401
 
        }
402
 
        if(not add_environment(getplugin(NULL), envdef, true)){
403
 
          perror("add_environment");
404
 
        }
405
 
      }
406
 
      break;
407
 
    case 'o':                   /* --options-for */
408
 
      if (arg != NULL){
409
 
        char *p_name = strsep(&arg, ":");
410
 
        if(p_name[0] == '\0' or arg == NULL){
411
 
          break;
412
 
        }
413
 
        char *opt = strsep(&arg, ":");
414
 
        if(opt[0] == '\0' or opt == NULL){
415
 
          break;
416
 
        }
417
 
        char *p;
418
 
        while((p = strsep(&opt, ",")) != NULL){
419
 
          if(p[0] == '\0'){
420
 
            continue;
421
 
          }
422
 
          if(not add_argument(getplugin(p_name), p)){
423
 
            perror("add_argument");
424
 
            return ARGP_ERR_UNKNOWN;
425
 
          }
426
 
        }
427
 
      }
428
 
      break;
429
 
    case 'E':                   /* --env-for */
430
 
      if(arg == NULL){
431
 
        break;
432
 
      }
433
 
      {
434
 
        char *envdef = strchr(arg, ':');
435
 
        if(envdef == NULL){
436
 
          break;
437
 
        }
438
 
        char *p_name = strndup(arg, (size_t) (envdef-arg));
439
 
        if(p_name == NULL){
440
 
          break;
441
 
        }
442
 
        envdef++;
443
 
        if(not add_environment(getplugin(p_name), envdef, true)){
444
 
          perror("add_environment");
445
 
        }
446
 
      }
447
 
      break;
448
 
    case 'd':                   /* --disable */
449
 
      if (arg != NULL){
450
 
        plugin *p = getplugin(arg);
451
 
        if(p == NULL){
452
 
          return ARGP_ERR_UNKNOWN;
453
 
        }
454
 
        p->disabled = true;
455
 
      }
456
 
      break;
457
 
    case 'e':                   /* --enable */
458
 
      if (arg != NULL){
459
 
        plugin *p = getplugin(arg);
460
 
        if(p == NULL){
461
 
          return ARGP_ERR_UNKNOWN;
462
 
        }
463
 
        p->disabled = false;
464
 
      }
465
 
      break;
466
 
    case 128:                   /* --plugin-dir */
467
 
      plugindir = strdup(arg);
468
 
      if(plugindir == NULL){
469
 
        perror("strdup");
470
 
      }      
471
 
      break;
472
 
    case 129:                   /* --config-file */
473
 
      /* This is already done by parse_opt_config_file() */
474
 
      break;
475
 
    case 130:                   /* --userid */
476
 
      uid = (uid_t)strtol(arg, NULL, 10);
477
 
      break;
478
 
    case 131:                   /* --groupid */
479
 
      gid = (gid_t)strtol(arg, NULL, 10);
480
 
      break;
481
 
    case 132:                   /* --debug */
 
206
    case 'g':
 
207
      if (arg != NULL){
 
208
        char *p = strtok(arg, ",");
 
209
        do{
 
210
          addargument(getplugin(NULL, plugins), p);
 
211
          p = strtok(NULL, ",");
 
212
        } while (p);
 
213
      }
 
214
      break;
 
215
    case 'o':
 
216
      if (arg != NULL){
 
217
        char *name = strtok(arg, ":");
 
218
        char *p = strtok(NULL, ":");
 
219
        if(p){
 
220
          p = strtok(p, ",");
 
221
          do{
 
222
            addargument(getplugin(name, plugins), p);
 
223
            p = strtok(NULL, ",");
 
224
          } while (p);
 
225
        }
 
226
      }
 
227
      break;
 
228
    case 'd':
 
229
      if (arg != NULL){
 
230
        getplugin(arg, plugins)->disabled = true;
 
231
      }
 
232
      break;
 
233
    case 128:
 
234
      plugindir = arg;
 
235
      break;
 
236
    case 129:
482
237
      debug = true;
483
238
      break;
484
239
    case ARGP_KEY_ARG:
485
 
      fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
486
 
      break;
487
 
    case ARGP_KEY_END:
488
 
      break;
489
 
    default:
490
 
      return ARGP_ERR_UNKNOWN;
491
 
    }
492
 
    return 0;
493
 
  }
494
 
  
495
 
  /* This option parser is the same as parse_opt() above, except it
496
 
     ignores everything but the --config-file option. */
497
 
  error_t parse_opt_config_file (int key, char *arg,
498
 
                                 __attribute__((unused))
499
 
                                 struct argp_state *state) {
500
 
    switch (key) {
501
 
    case 'g':                   /* --global-options */
502
 
    case 'G':                   /* --global-env */
503
 
    case 'o':                   /* --options-for */
504
 
    case 'E':                   /* --env-for */
505
 
    case 'd':                   /* --disable */
506
 
    case 'e':                   /* --enable */
507
 
    case 128:                   /* --plugin-dir */
508
 
      break;
509
 
    case 129:                   /* --config-file */
510
 
      argfile = strdup(arg);
511
 
      if(argfile == NULL){
512
 
        perror("strdup");
513
 
      }
514
 
      break;      
515
 
    case 130:                   /* --userid */
516
 
    case 131:                   /* --groupid */
517
 
    case 132:                   /* --debug */
518
 
    case ARGP_KEY_ARG:
519
 
    case ARGP_KEY_END:
520
 
      break;
521
 
    default:
522
 
      return ARGP_ERR_UNKNOWN;
523
 
    }
524
 
    return 0;
525
 
  }
526
 
  
527
 
  struct argp argp = { .options = options,
528
 
                       .parser = parse_opt_config_file,
 
240
      argp_usage (state);
 
241
      break;
 
242
    case ARGP_KEY_END:
 
243
      break;
 
244
    default:
 
245
      return ARGP_ERR_UNKNOWN;
 
246
    }
 
247
    return 0;
 
248
  }
 
249
  
 
250
  plugin *plugin_list = NULL;
 
251
  
 
252
  struct argp argp = { .options = options, .parser = parse_opt,
529
253
                       .args_doc = "",
530
254
                       .doc = "Mandos plugin runner -- Run plugins" };
531
255
  
532
 
  /* Parse using the parse_opt_config_file in order to get the custom
533
 
     config file location, if any. */
534
 
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
535
 
  if (ret == ARGP_ERR_UNKNOWN){
536
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
537
 
    exitstatus = EXIT_FAILURE;
538
 
    goto fallback;
539
 
  }
540
 
  
541
 
  /* Reset to the normal argument parser */
542
 
  argp.parser = parse_opt;
543
 
  
544
 
  /* Open the configfile if available */
545
 
  if (argfile == NULL){
546
 
    conffp = fopen(AFILE, "r");
547
 
  } else {
548
 
    conffp = fopen(argfile, "r");
549
 
  }  
550
 
  if(conffp != NULL){
551
 
    char *org_line = NULL;
552
 
    char *p, *arg, *new_arg, *line;
553
 
    size_t size = 0;
554
 
    ssize_t sret;
555
 
    const char whitespace_delims[] = " \r\t\f\v\n";
556
 
    const char comment_delim[] = "#";
557
 
 
558
 
    custom_argc = 1;
559
 
    custom_argv = malloc(sizeof(char*) * 2);
560
 
    if(custom_argv == NULL){
561
 
      perror("malloc");
562
 
      exitstatus = EXIT_FAILURE;
563
 
      goto fallback;
564
 
    }
565
 
    custom_argv[0] = argv[0];
566
 
    custom_argv[1] = NULL;
567
 
 
568
 
    /* for each line in the config file, strip whitespace and ignore
569
 
       commented text */
570
 
    while(true){
571
 
      sret = getline(&org_line, &size, conffp);
572
 
      if(sret == -1){
573
 
        break;
574
 
      }
575
 
 
576
 
      line = org_line;
577
 
      arg = strsep(&line, comment_delim);
578
 
      while((p = strsep(&arg, whitespace_delims)) != NULL){
579
 
        if(p[0] == '\0'){
580
 
          continue;
581
 
        }
582
 
        new_arg = strdup(p);
583
 
        if(new_arg == NULL){
584
 
          perror("strdup");
585
 
          exitstatus = EXIT_FAILURE;
586
 
          free(org_line);
587
 
          goto fallback;
588
 
        }
589
 
        
590
 
        custom_argc += 1;
591
 
        custom_argv = realloc(custom_argv, sizeof(char *)
592
 
                              * ((unsigned int) custom_argc + 1));
593
 
        if(custom_argv == NULL){
594
 
          perror("realloc");
595
 
          exitstatus = EXIT_FAILURE;
596
 
          free(org_line);
597
 
          goto fallback;
598
 
        }
599
 
        custom_argv[custom_argc-1] = new_arg;
600
 
        custom_argv[custom_argc] = NULL;        
601
 
      }
602
 
    }
603
 
    free(org_line);
604
 
  } else {
605
 
    /* Check for harmful errors and go to fallback. Other errors might
606
 
       not affect opening plugins */
607
 
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
608
 
      perror("fopen");
609
 
      exitstatus = EXIT_FAILURE;
610
 
      goto fallback;
611
 
    }
612
 
  }
613
 
  /* If there was any arguments from configuration file,
614
 
     pass them to parser as command arguments */
615
 
  if(custom_argv != NULL){
616
 
    ret = argp_parse (&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
617
 
                      0, NULL);
618
 
    if (ret == ARGP_ERR_UNKNOWN){
619
 
      fprintf(stderr, "Unknown error while parsing arguments\n");
620
 
      exitstatus = EXIT_FAILURE;
621
 
      goto fallback;
622
 
    }
623
 
  }
624
 
  
625
 
  /* Parse actual command line arguments, to let them override the
626
 
     config file */
627
 
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
628
 
  if (ret == ARGP_ERR_UNKNOWN){
629
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
630
 
    exitstatus = EXIT_FAILURE;
631
 
    goto fallback;
632
 
  }
 
256
  argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
633
257
  
634
258
  if(debug){
635
259
    for(plugin *p = plugin_list; p != NULL; p=p->next){
638
262
      for(char **a = p->argv; *a != NULL; a++){
639
263
        fprintf(stderr, "\tArg: %s\n", *a);
640
264
      }
641
 
      fprintf(stderr, "...and %u environment variables\n", p->envc);
642
 
      for(char **a = p->environ; *a != NULL; a++){
643
 
        fprintf(stderr, "\t%s\n", *a);
644
 
      }
645
265
    }
646
266
  }
647
267
  
648
 
  /* Strip permissions down to nobody */
649
 
  ret = setuid(uid);
650
 
  if (ret == -1){
651
 
    perror("setuid");
652
 
  }  
653
 
  setgid(gid);
654
 
  if (ret == -1){
655
 
    perror("setgid");
656
 
  }
657
 
  
658
 
  if (plugindir == NULL){
659
 
    dir = opendir(PDIR);
660
 
  } else {
661
 
    dir = opendir(plugindir);
662
 
  }
663
 
  
 
268
  dir = opendir(plugindir);
664
269
  if(dir == NULL){
665
270
    perror("Could not open plugin dir");
666
271
    exitstatus = EXIT_FAILURE;
667
 
    goto fallback;
 
272
    goto end;
668
273
  }
669
274
  
670
275
  /* Set the FD_CLOEXEC flag on the directory, if possible */
675
280
      if(ret < 0){
676
281
        perror("set_cloexec_flag");
677
282
        exitstatus = EXIT_FAILURE;
678
 
        goto fallback;
 
283
        goto end;
679
284
      }
680
285
    }
681
286
  }
682
287
  
683
288
  FD_ZERO(&rfds_all);
684
289
  
685
 
  /* Read and execute any executable in the plugin directory*/
686
290
  while(true){
687
291
    dirst = readdir(dir);
688
292
    
689
 
    /* All directory entries have been processed */
 
293
    // All directory entries have been processed
690
294
    if(dirst == NULL){
691
 
      if (errno == EBADF){
692
 
        perror("readdir");
693
 
        exitstatus = EXIT_FAILURE;
694
 
        goto fallback;
695
 
      }
696
295
      break;
697
296
    }
698
297
    
699
298
    d_name_len = strlen(dirst->d_name);
700
299
    
701
 
    /* Ignore dotfiles, backup files and other junk */
 
300
    // Ignore dotfiles, backup files and other junk
702
301
    {
703
302
      bool bad_name = false;
704
303
      
719
318
          break;
720
319
        }
721
320
      }
 
321
      
722
322
      if(bad_name){
723
323
        continue;
724
324
      }
 
325
      
725
326
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
726
327
        size_t suf_len = strlen(*suf);
727
328
        if((d_name_len >= suf_len)
740
341
        continue;
741
342
      }
742
343
    }
743
 
 
744
 
    char *filename;
745
 
    if(plugindir == NULL){
746
 
      ret = asprintf(&filename, PDIR "/%s", dirst->d_name);
747
 
    } else {
748
 
      ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
749
 
    }
750
 
    if(ret < 0){
751
 
      perror("asprintf");
752
 
      continue;
753
 
    }
754
 
    
755
 
    ret = stat(filename, &st);
756
 
    if (ret == -1){
757
 
      perror("stat");
758
 
      free(filename);
759
 
      continue;
760
 
    }
761
 
 
762
 
    /* Ignore non-executable files */
 
344
    
 
345
    char *filename = malloc(d_name_len + strlen(plugindir) + 2);
 
346
    if (filename == NULL){
 
347
      perror("malloc");
 
348
      exitstatus = EXIT_FAILURE;
 
349
      goto end;
 
350
    }
 
351
    strcpy(filename, plugindir); /* Spurious warning */
 
352
    strcat(filename, "/");      /* Spurious warning */
 
353
    strcat(filename, dirst->d_name); /* Spurious warning */
 
354
    
 
355
    stat(filename, &st);
 
356
    
763
357
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
764
358
      if(debug){
765
359
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
768
362
      free(filename);
769
363
      continue;
770
364
    }
771
 
    
772
 
    plugin *p = getplugin(dirst->d_name);
773
 
    if(p == NULL){
774
 
      perror("getplugin");
775
 
      free(filename);
776
 
      continue;
777
 
    }
778
 
    if(p->disabled){
 
365
    if(getplugin(dirst->d_name, &plugin_list)->disabled){
779
366
      if(debug){
780
367
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
781
368
                dirst->d_name);
783
370
      free(filename);
784
371
      continue;
785
372
    }
 
373
    plugin *p = getplugin(dirst->d_name, &plugin_list);
786
374
    {
787
375
      /* Add global arguments to argument list for this plugin */
788
 
      plugin *g = getplugin(NULL);
789
 
      if(g != NULL){
790
 
        for(char **a = g->argv + 1; *a != NULL; a++){
791
 
          if(not add_argument(p, *a)){
792
 
            perror("add_argument");
793
 
          }
794
 
        }
795
 
        /* Add global environment variables */
796
 
        for(char **e = g->environ; *e != NULL; e++){
797
 
          if(not add_environment(p, *e, false)){
798
 
            perror("add_environment");
799
 
          }
800
 
        }
801
 
      }
802
 
    }
803
 
    /* If this plugin has any environment variables, we will call
804
 
       using execve and need to duplicate the environment from this
805
 
       process, too. */
806
 
    if(p->environ[0] != NULL){
807
 
      for(char **e = environ; *e != NULL; e++){
808
 
        if(not add_environment(p, *e, false)){
809
 
          perror("add_environment");
810
 
        }
811
 
      }
812
 
    }
813
 
    
814
 
    int pipefd[2];
 
376
      plugin *g = getplugin(NULL, &plugin_list);
 
377
      for(char **a = g->argv + 1; *a != NULL; a++){
 
378
        addargument(p, *a);
 
379
      }
 
380
    }
 
381
    int pipefd[2]; 
815
382
    ret = pipe(pipefd);
816
383
    if (ret == -1){
817
384
      perror("pipe");
818
385
      exitstatus = EXIT_FAILURE;
819
 
      goto fallback;
 
386
      goto end;
820
387
    }
821
 
    /* Ask OS to automatic close the pipe on exec */
822
388
    ret = set_cloexec_flag(pipefd[0]);
823
389
    if(ret < 0){
824
390
      perror("set_cloexec_flag");
825
391
      exitstatus = EXIT_FAILURE;
826
 
      goto fallback;
 
392
      goto end;
827
393
    }
828
394
    ret = set_cloexec_flag(pipefd[1]);
829
395
    if(ret < 0){
830
396
      perror("set_cloexec_flag");
831
397
      exitstatus = EXIT_FAILURE;
832
 
      goto fallback;
 
398
      goto end;
833
399
    }
834
400
    /* Block SIGCHLD until process is safely in process list */
835
401
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
836
402
    if(ret < 0){
837
403
      perror("sigprocmask");
838
404
      exitstatus = EXIT_FAILURE;
839
 
      goto fallback;
 
405
      goto end;
840
406
    }
841
 
    /* Starting a new process to be watched */
 
407
    // Starting a new process to be watched
842
408
    pid_t pid = fork();
843
 
    if(pid == -1){
844
 
      perror("fork");
845
 
      exitstatus = EXIT_FAILURE;
846
 
      goto fallback;
847
 
    }
848
409
    if(pid == 0){
849
410
      /* this is the child process */
850
411
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
857
418
        perror("sigprocmask");
858
419
        _exit(EXIT_FAILURE);
859
420
      }
860
 
 
861
 
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
862
 
      if(ret == -1){
863
 
        perror("dup2");
864
 
        _exit(EXIT_FAILURE);
865
 
      }
 
421
      dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
866
422
      
867
423
      if(dirfd(dir) < 0){
868
424
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
869
425
           above and must now close it manually here. */
870
426
        closedir(dir);
871
427
      }
872
 
      if(p->environ[0] == NULL){
873
 
        if(execv(filename, p->argv) < 0){
874
 
          perror("execv");
875
 
          _exit(EXIT_FAILURE);
876
 
        }
877
 
      } else {
878
 
        if(execve(filename, p->argv, p->environ) < 0){
879
 
          perror("execve");
880
 
          _exit(EXIT_FAILURE);
881
 
        }
 
428
      if(execv(filename, p->argv) < 0){
 
429
        perror("execv");
 
430
        _exit(EXIT_FAILURE);
882
431
      }
883
432
      /* no return */
884
433
    }
885
 
    /* Parent process */
886
 
    close(pipefd[1]);           /* Close unused write end of pipe */
 
434
    /* parent process */
887
435
    free(filename);
888
 
    plugin *new_plugin = getplugin(dirst->d_name);
889
 
    if (new_plugin == NULL){
890
 
      perror("getplugin");
 
436
    close(pipefd[1]);           /* close unused write end of pipe */
 
437
    process *new_process = malloc(sizeof(process));
 
438
    if (new_process == NULL){
 
439
      perror("malloc");
891
440
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
892
441
      if(ret < 0){
893
 
        perror("sigprocmask");
 
442
        perror("sigprocmask");
894
443
      }
895
444
      exitstatus = EXIT_FAILURE;
896
 
      goto fallback;
 
445
      goto end;
897
446
    }
898
447
    
899
 
    new_plugin->pid = pid;
900
 
    new_plugin->fd = pipefd[0];
901
 
    
 
448
    *new_process = (struct process){ .pid = pid,
 
449
                                     .fd = pipefd[0],
 
450
                                     .next = process_list };
 
451
    // List handling
 
452
    process_list = new_process;
902
453
    /* Unblock SIGCHLD so signal handler can be run if this process
903
454
       has already completed */
904
455
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
905
456
    if(ret < 0){
906
457
      perror("sigprocmask");
907
458
      exitstatus = EXIT_FAILURE;
908
 
      goto fallback;
909
 
    }
910
 
    
911
 
    FD_SET(new_plugin->fd, &rfds_all);
912
 
    
913
 
    if (maxfd < new_plugin->fd){
914
 
      maxfd = new_plugin->fd;
915
 
    }
916
 
    
 
459
      goto end;
 
460
    }
 
461
    
 
462
    FD_SET(new_process->fd, &rfds_all);
 
463
    
 
464
    if (maxfd < new_process->fd){
 
465
      maxfd = new_process->fd;
 
466
    }
 
467
    
 
468
  }
 
469
  
 
470
  /* Free the plugin list */
 
471
  for(plugin *next; plugin_list != NULL; plugin_list = next){
 
472
    next = plugin_list->next;
 
473
    free(plugin_list->argv);
 
474
    free(plugin_list);
917
475
  }
918
476
  
919
477
  closedir(dir);
920
478
  dir = NULL;
921
 
 
922
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
923
 
    if(p->pid != 0){
924
 
      break;
925
 
    }
926
 
    if(p->next == NULL){
927
 
      fprintf(stderr, "No plugin processes started. Incorrect plugin"
928
 
              " directory?\n");
929
 
      free_plugin_list();
930
 
    }
 
479
  
 
480
  if (process_list == NULL){
 
481
    fprintf(stderr, "No plugin processes started, exiting\n");
 
482
    exitstatus = EXIT_FAILURE;
 
483
    goto end;
931
484
  }
932
 
 
933
 
  /* Main loop while running plugins exist */
934
 
  while(plugin_list){
 
485
  while(process_list){
935
486
    fd_set rfds = rfds_all;
936
487
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
937
488
    if (select_ret == -1){
938
489
      perror("select");
939
490
      exitstatus = EXIT_FAILURE;
940
 
      goto fallback;
 
491
      goto end;
941
492
    }
942
493
    /* OK, now either a process completed, or something can be read
943
494
       from one of them */
944
 
    for(plugin *proc = plugin_list; proc != NULL; proc = proc->next){
 
495
    for(process *proc = process_list; proc ; proc = proc->next){
945
496
      /* Is this process completely done? */
946
497
      if(proc->eof and proc->completed){
947
498
        /* Only accept the plugin output if it exited cleanly */
948
499
        if(not WIFEXITED(proc->status)
949
500
           or WEXITSTATUS(proc->status) != 0){
950
501
          /* Bad exit by plugin */
951
 
 
952
502
          if(debug){
953
503
            if(WIFEXITED(proc->status)){
954
 
              fprintf(stderr, "Plugin %u exited with status %d\n",
955
 
                      (unsigned int) (proc->pid),
956
 
                      WEXITSTATUS(proc->status));
 
504
              fprintf(stderr, "Plugin %d exited with status %d\n",
 
505
                      proc->pid, WEXITSTATUS(proc->status));
957
506
            } else if(WIFSIGNALED(proc->status)) {
958
 
              fprintf(stderr, "Plugin %u killed by signal %d\n",
959
 
                      (unsigned int) (proc->pid),
960
 
                      WTERMSIG(proc->status));
 
507
              fprintf(stderr, "Plugin %d killed by signal %d\n",
 
508
                      proc->pid, WTERMSIG(proc->status));
961
509
            } else if(WCOREDUMP(proc->status)){
962
 
              fprintf(stderr, "Plugin %d dumped core\n",
963
 
                      (unsigned int) (proc->pid));
 
510
              fprintf(stderr, "Plugin %d dumped core\n", proc->pid);
964
511
            }
965
512
          }
966
 
          
967
513
          /* Remove the plugin */
968
514
          FD_CLR(proc->fd, &rfds_all);
969
 
 
970
515
          /* Block signal while modifying process_list */
971
 
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
 
516
          ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
972
517
          if(ret < 0){
973
518
            perror("sigprocmask");
974
519
            exitstatus = EXIT_FAILURE;
975
 
            goto fallback;
976
 
          }
977
 
          free_plugin(proc);
 
520
            goto end;
 
521
          }
 
522
          /* Delete this process entry from the list */
 
523
          if(process_list == proc){
 
524
            /* First one - simple */
 
525
            process_list = proc->next;
 
526
          } else {
 
527
            /* Second one or later */
 
528
            for(process *p = process_list; p != NULL; p = p->next){
 
529
              if(p->next == proc){
 
530
                p->next = proc->next;
 
531
                break;
 
532
              }
 
533
            }
 
534
          }
978
535
          /* We are done modifying process list, so unblock signal */
979
536
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
980
537
                             NULL);
981
538
          if(ret < 0){
982
539
            perror("sigprocmask");
983
 
            exitstatus = EXIT_FAILURE;
984
 
            goto fallback;
985
 
          }
986
 
          
987
 
          if(plugin_list == NULL){
988
 
            break;
989
 
          }
990
 
          continue;
 
540
          }
 
541
          free(proc->buffer);
 
542
          free(proc);
 
543
          /* We deleted this process from the list, so we can't go
 
544
             proc->next.  Therefore, start over from the beginning of
 
545
             the process list */
 
546
          break;
991
547
        }
992
 
        
993
548
        /* This process exited nicely, so print its buffer */
994
 
 
995
 
        bool bret = print_out_password(proc->buffer,
996
 
                                       proc->buffer_length);
997
 
        if(not bret){
998
 
          perror("print_out_password");
999
 
          exitstatus = EXIT_FAILURE;
 
549
        for(size_t written = 0; written < proc->buffer_length;
 
550
            written += (size_t)ret){
 
551
          ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO,
 
552
                                         proc->buffer + written,
 
553
                                         proc->buffer_length
 
554
                                         - written));
 
555
          if(ret < 0){
 
556
            perror("write");
 
557
            exitstatus = EXIT_FAILURE;
 
558
            goto end;
 
559
          }
1000
560
        }
1001
 
        goto fallback;
 
561
        goto end;
1002
562
      }
1003
 
      
1004
563
      /* This process has not completed.  Does it have any output? */
1005
564
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1006
565
        /* This process had nothing to say at this time */
1013
572
        if (proc->buffer == NULL){
1014
573
          perror("malloc");
1015
574
          exitstatus = EXIT_FAILURE;
1016
 
          goto fallback;
 
575
          goto end;
1017
576
        }
1018
577
        proc->buffer_size += BUFFER_SIZE;
1019
578
      }
1032
591
      }
1033
592
    }
1034
593
  }
1035
 
 
1036
 
 
1037
 
 fallback:
1038
 
  
1039
 
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
1040
 
    /* Fallback if all plugins failed, none are found or an error
1041
 
       occured */
1042
 
    bool bret;
1043
 
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
1044
 
    char *passwordbuffer = getpass("Password: ");
1045
 
    size_t len = strlen(passwordbuffer);
1046
 
    /* Strip trailing newline */
1047
 
    if(len > 0 and passwordbuffer[len-1] == '\n'){
1048
 
      passwordbuffer[len-1] = '\0'; /* not strictly necessary */
1049
 
      len--;
1050
 
    }
1051
 
    bret = print_out_password(passwordbuffer, len);
1052
 
    if(not bret){
1053
 
      perror("print_out_password");
1054
 
      exitstatus = EXIT_FAILURE;
1055
 
    }
 
594
  if(process_list == NULL){
 
595
    fprintf(stderr, "All plugin processes failed, exiting\n");
 
596
    exitstatus = EXIT_FAILURE;
1056
597
  }
1057
598
  
 
599
 end:
1058
600
  /* Restore old signal handler */
1059
 
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1060
 
  if(ret == -1){
1061
 
    perror("sigaction");
1062
 
    exitstatus = EXIT_FAILURE;
1063
 
  }
1064
 
 
1065
 
  if(custom_argv != NULL){
1066
 
    for(char **arg = custom_argv+1; *arg != NULL; arg++){
1067
 
      free(*arg);
1068
 
    }
1069
 
    free(custom_argv);
 
601
  sigaction(SIGCHLD, &old_sigchld_action, NULL);
 
602
  
 
603
  /* Free the plugin list */
 
604
  for(plugin *next; plugin_list != NULL; plugin_list = next){
 
605
    next = plugin_list->next;
 
606
    free(plugin_list->argv);
 
607
    free(plugin_list);
1070
608
  }
1071
609
  
1072
610
  if(dir != NULL){
1074
612
  }
1075
613
  
1076
614
  /* Free the process list and kill the processes */
1077
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1078
 
    if(p->pid != 0){
1079
 
      close(p->fd);
1080
 
      ret = kill(p->pid, SIGTERM);
1081
 
      if(ret == -1 and errno != ESRCH){
1082
 
        /* Set-uid proccesses might not get closed */
1083
 
        perror("kill");
1084
 
      }
1085
 
    }
 
615
  for(process *next; process_list != NULL; process_list = next){
 
616
    next = process_list->next;
 
617
    close(process_list->fd);
 
618
    kill(process_list->pid, SIGTERM);
 
619
    free(process_list->buffer);
 
620
    free(process_list);
1086
621
  }
1087
622
  
1088
623
  /* Wait for any remaining child processes to terminate */
1092
627
  if(errno != ECHILD){
1093
628
    perror("wait");
1094
629
  }
1095
 
 
1096
 
  free_plugin_list();
1097
 
  
1098
 
  free(plugindir);
1099
 
  free(argfile);
1100
630
  
1101
631
  return exitstatus;
1102
632
}