/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 a 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));
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
 
  if(length>0 and buffer[length-1] == '\n'){
259
 
    length--;
260
 
  }
261
 
  for(size_t written = 0; written < length; written += (size_t)ret){
262
 
    ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
263
 
                                   length - written));
264
 
    if(ret < 0){
265
 
      return false;
266
 
    }
267
 
  }
268
 
  return true;
269
 
}
270
 
 
271
 
/* Removes and free a plugin from the plugin list */
272
 
static void free_plugin(plugin *plugin_node){
273
 
  
274
 
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
275
 
    free(*arg);
276
 
  }
277
 
  free(plugin_node->argv);
278
 
  for(char **env = plugin_node->environ; *env != NULL; env++){
279
 
    free(*env);
280
 
  }
281
 
  free(plugin_node->environ);
282
 
  free(plugin_node->buffer);
283
 
 
284
 
  /* Removes the plugin from the singly-linked list */
285
 
  if(plugin_node == plugin_list){
286
 
    /* First one - simple */
287
 
    plugin_list = plugin_list->next;
288
 
  } else {
289
 
    /* Second one or later */
290
 
    for(plugin *p = plugin_list; p != NULL; p = p->next){
291
 
      if(p->next == plugin_node){
292
 
        p->next = plugin_node->next;
293
 
        break;
294
 
      }
295
 
    }
296
 
  }
297
 
  
298
 
  free(plugin_node);
299
 
}
300
 
 
301
 
static void free_plugin_list(void){
302
 
  while(plugin_list != NULL){
303
 
    free_plugin(plugin_list);
304
 
  }
 
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;
305
153
}
306
154
 
307
155
int main(int argc, char *argv[]){
308
 
  char *plugindir = NULL;
309
 
  char *argfile = NULL;
310
 
  FILE *conffp;
 
156
  const char *plugindir = "/conf/conf.d/mandos/plugins.d";
311
157
  size_t d_name_len;
312
158
  DIR *dir = NULL;
313
159
  struct dirent *dirst;
314
160
  struct stat st;
315
161
  fd_set rfds_all;
316
162
  int ret, maxfd = 0;
317
 
  uid_t uid = 65534;
318
 
  gid_t gid = 65534;
319
163
  bool debug = false;
320
164
  int exitstatus = EXIT_SUCCESS;
321
 
  struct sigaction old_sigchld_action;
322
 
  struct sigaction sigchld_action = { .sa_handler = handle_sigchld,
323
 
                                      .sa_flags = SA_NOCLDSTOP };
324
 
  char **custom_argv = NULL;
325
 
  int custom_argc = 0;
326
165
  
327
166
  /* Establish a signal handler */
 
167
  struct sigaction old_sigchld_action,
 
168
    sigchld_action = { .sa_handler = handle_sigchld,
 
169
                       .sa_flags = SA_NOCLDSTOP };
328
170
  sigemptyset(&sigchld_action.sa_mask);
329
171
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
330
 
  if(ret == -1){
 
172
  if(ret < 0){
331
173
    perror("sigaddset");
332
 
    exitstatus = EXIT_FAILURE;
333
 
    goto fallback;
 
174
    exit(EXIT_FAILURE);
334
175
  }
335
176
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
336
 
  if(ret == -1){
 
177
  if(ret < 0){
337
178
    perror("sigaction");
338
 
    exitstatus = EXIT_FAILURE;
339
 
    goto fallback;
 
179
    exit(EXIT_FAILURE);
340
180
  }
341
181
  
342
182
  /* The options we understand. */
344
184
    { .name = "global-options", .key = 'g',
345
185
      .arg = "OPTION[,OPTION[,...]]",
346
186
      .doc = "Options passed to all plugins" },
347
 
    { .name = "global-env", .key = 'G',
348
 
      .arg = "VAR=value",
349
 
      .doc = "Environment variable passed to all plugins" },
350
187
    { .name = "options-for", .key = 'o',
351
188
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
352
189
      .doc = "Options passed only to specified plugin" },
353
 
    { .name = "env-for", .key = 'E',
354
 
      .arg = "PLUGIN:ENV=value",
355
 
      .doc = "Environment variable passed to specified plugin" },
356
190
    { .name = "disable", .key = 'd',
357
191
      .arg = "PLUGIN",
358
192
      .doc = "Disable a specific plugin", .group = 1 },
359
 
    { .name = "enable", .key = 'e',
360
 
      .arg = "PLUGIN",
361
 
      .doc = "Enable a specific plugin", .group = 1 },
362
193
    { .name = "plugin-dir", .key = 128,
363
194
      .arg = "DIRECTORY",
364
195
      .doc = "Specify a different plugin directory", .group = 2 },
365
 
    { .name = "config-file", .key = 129,
366
 
      .arg = "FILE",
367
 
      .doc = "Specify a different configuration file", .group = 2 },
368
 
    { .name = "userid", .key = 130,
369
 
      .arg = "ID", .flags = 0,
370
 
      .doc = "User ID the plugins will run as", .group = 3 },
371
 
    { .name = "groupid", .key = 131,
372
 
      .arg = "ID", .flags = 0,
373
 
      .doc = "Group ID the plugins will run as", .group = 3 },
374
 
    { .name = "debug", .key = 132,
375
 
      .doc = "Debug mode", .group = 4 },
 
196
    { .name = "debug", .key = 129,
 
197
      .doc = "Debug mode", .group = 3 },
376
198
    { .name = NULL }
377
199
  };
378
200
  
379
 
  error_t parse_opt (int key, char *arg, __attribute__((unused))
380
 
                     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;
381
205
    switch (key) {
382
 
    case 'g':                   /* --global-options */
383
 
      if (arg != NULL){
384
 
        char *p;
385
 
        while((p = strsep(&arg, ",")) != NULL){
386
 
          if(p[0] == '\0'){
387
 
            continue;
388
 
          }
389
 
          if(not add_argument(getplugin(NULL), p)){
390
 
            perror("add_argument");
391
 
            return ARGP_ERR_UNKNOWN;
392
 
          }
393
 
        }
394
 
      }
395
 
      break;
396
 
    case 'G':                   /* --global-env */
397
 
      if(arg == NULL){
398
 
        break;
399
 
      }
400
 
      {
401
 
        char *envdef = strdup(arg);
402
 
        if(envdef == NULL){
403
 
          break;
404
 
        }
405
 
        if(not add_environment(getplugin(NULL), envdef, true)){
406
 
          perror("add_environment");
407
 
        }
408
 
      }
409
 
      break;
410
 
    case 'o':                   /* --options-for */
411
 
      if (arg != NULL){
412
 
        char *p_name = strsep(&arg, ":");
413
 
        if(p_name[0] == '\0'){
414
 
          break;
415
 
        }
416
 
        char *opt = strsep(&arg, ":");
417
 
        if(opt[0] == '\0'){
418
 
          break;
419
 
        }
420
 
        if(opt != NULL){
421
 
          char *p;
422
 
          while((p = strsep(&opt, ",")) != NULL){
423
 
            if(p[0] == '\0'){
424
 
              continue;
425
 
            }
426
 
            if(not add_argument(getplugin(p_name), p)){
427
 
              perror("add_argument");
428
 
              return ARGP_ERR_UNKNOWN;
429
 
            }
430
 
          }
431
 
        }
432
 
      }
433
 
      break;
434
 
    case 'E':                   /* --env-for */
435
 
      if(arg == NULL){
436
 
        break;
437
 
      }
438
 
      {
439
 
        char *envdef = strchr(arg, ':');
440
 
        if(envdef == NULL){
441
 
          break;
442
 
        }
443
 
        char *p_name = strndup(arg, (size_t) (envdef-arg));
444
 
        if(p_name == NULL){
445
 
          break;
446
 
        }
447
 
        envdef++;
448
 
        if(not add_environment(getplugin(p_name), envdef, true)){
449
 
          perror("add_environment");
450
 
        }
451
 
      }
452
 
      break;
453
 
    case 'd':                   /* --disable */
454
 
      if (arg != NULL){
455
 
        plugin *p = getplugin(arg);
456
 
        if(p == NULL){
457
 
          return ARGP_ERR_UNKNOWN;
458
 
        }
459
 
        p->disabled = true;
460
 
      }
461
 
      break;
462
 
    case 'e':                   /* --enable */
463
 
      if (arg != NULL){
464
 
        plugin *p = getplugin(arg);
465
 
        if(p == NULL){
466
 
          return ARGP_ERR_UNKNOWN;
467
 
        }
468
 
        p->disabled = false;
469
 
      }
470
 
      break;
471
 
    case 128:                   /* --plugin-dir */
472
 
      plugindir = strdup(arg);
473
 
      if(plugindir == NULL){
474
 
        perror("strdup");
475
 
      }      
476
 
      break;
477
 
    case 129:                   /* --config-file */
478
 
      /* This is already done by parse_opt_config_file() */
479
 
      break;
480
 
    case 130:                   /* --userid */
481
 
      uid = (uid_t)strtol(arg, NULL, 10);
482
 
      break;
483
 
    case 131:                   /* --groupid */
484
 
      gid = (gid_t)strtol(arg, NULL, 10);
485
 
      break;
486
 
    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:
487
237
      debug = true;
488
238
      break;
489
239
    case ARGP_KEY_ARG:
490
 
      fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
491
 
      break;
492
 
    case ARGP_KEY_END:
493
 
      break;
494
 
    default:
495
 
      return ARGP_ERR_UNKNOWN;
496
 
    }
497
 
    return 0;
498
 
  }
499
 
  
500
 
  /* This option parser is the same as parse_opt() above, except it
501
 
     ignores everything but the --config-file option. */
502
 
  error_t parse_opt_config_file (int key, char *arg,
503
 
                                 __attribute__((unused))
504
 
                                 struct argp_state *state) {
505
 
    switch (key) {
506
 
    case 'g':                   /* --global-options */
507
 
    case 'G':                   /* --global-env */
508
 
    case 'o':                   /* --options-for */
509
 
    case 'E':                   /* --env-for */
510
 
    case 'd':                   /* --disable */
511
 
    case 'e':                   /* --enable */
512
 
    case 128:                   /* --plugin-dir */
513
 
      break;
514
 
    case 129:                   /* --config-file */
515
 
      argfile = strdup(arg);
516
 
      if(argfile == NULL){
517
 
        perror("strdup");
518
 
      }
519
 
      break;      
520
 
    case 130:                   /* --userid */
521
 
    case 131:                   /* --groupid */
522
 
    case 132:                   /* --debug */
523
 
    case ARGP_KEY_ARG:
524
 
    case ARGP_KEY_END:
525
 
      break;
526
 
    default:
527
 
      return ARGP_ERR_UNKNOWN;
528
 
    }
529
 
    return 0;
530
 
  }
531
 
  
532
 
  struct argp argp = { .options = options,
533
 
                       .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,
534
253
                       .args_doc = "",
535
254
                       .doc = "Mandos plugin runner -- Run plugins" };
536
255
  
537
 
  /* Parse using the parse_opt_config_file in order to get the custom
538
 
     config file location, if any. */
539
 
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
540
 
  if (ret == ARGP_ERR_UNKNOWN){
541
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
542
 
    exitstatus = EXIT_FAILURE;
543
 
    goto fallback;
544
 
  }
545
 
  
546
 
  /* Reset to the normal argument parser */
547
 
  argp.parser = parse_opt;
548
 
  
549
 
  /* Open the configfile if available */
550
 
  if (argfile == NULL){
551
 
    conffp = fopen(AFILE, "r");
552
 
  } else {
553
 
    conffp = fopen(argfile, "r");
554
 
  }  
555
 
  if(conffp != NULL){
556
 
    char *org_line = NULL;
557
 
    char *p, *arg, *new_arg, *line;
558
 
    size_t size = 0;
559
 
    ssize_t sret;
560
 
    const char whitespace_delims[] = " \r\t\f\v\n";
561
 
    const char comment_delim[] = "#";
562
 
 
563
 
    custom_argc = 1;
564
 
    custom_argv = malloc(sizeof(char*) * 2);
565
 
    if(custom_argv == NULL){
566
 
      perror("malloc");
567
 
      exitstatus = EXIT_FAILURE;
568
 
      goto fallback;
569
 
    }
570
 
    custom_argv[0] = argv[0];
571
 
    custom_argv[1] = NULL;
572
 
 
573
 
    /* for each line in the config file, strip whitespace and ignore
574
 
       commented text */
575
 
    while(true){
576
 
      sret = getline(&org_line, &size, conffp);
577
 
      if(sret == -1){
578
 
        break;
579
 
      }
580
 
 
581
 
      line = org_line;
582
 
      arg = strsep(&line, comment_delim);
583
 
      while((p = strsep(&arg, whitespace_delims)) != NULL){
584
 
        if(p[0] == '\0'){
585
 
          continue;
586
 
        }
587
 
        new_arg = strdup(p);
588
 
        if(new_arg == NULL){
589
 
          perror("strdup");
590
 
          exitstatus = EXIT_FAILURE;
591
 
          free(org_line);
592
 
          goto fallback;
593
 
        }
594
 
        
595
 
        custom_argc += 1;
596
 
        custom_argv = realloc(custom_argv, sizeof(char *)
597
 
                              * ((unsigned int) custom_argc + 1));
598
 
        if(custom_argv == NULL){
599
 
          perror("realloc");
600
 
          exitstatus = EXIT_FAILURE;
601
 
          free(org_line);
602
 
          goto fallback;
603
 
        }
604
 
        custom_argv[custom_argc-1] = new_arg;
605
 
        custom_argv[custom_argc] = NULL;        
606
 
      }
607
 
    }
608
 
    free(org_line);
609
 
  } else {
610
 
    /* Check for harmful errors and go to fallback. Other errors might
611
 
       not affect opening plugins */
612
 
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
613
 
      perror("fopen");
614
 
      exitstatus = EXIT_FAILURE;
615
 
      goto fallback;
616
 
    }
617
 
  }
618
 
  /* If there was any arguments from configuration file,
619
 
     pass them to parser as command arguments */
620
 
  if(custom_argv != NULL){
621
 
    ret = argp_parse (&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
622
 
                      0, NULL);
623
 
    if (ret == ARGP_ERR_UNKNOWN){
624
 
      fprintf(stderr, "Unknown error while parsing arguments\n");
625
 
      exitstatus = EXIT_FAILURE;
626
 
      goto fallback;
627
 
    }
628
 
  }
629
 
  
630
 
  /* Parse actual command line arguments, to let them override the
631
 
     config file */
632
 
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
633
 
  if (ret == ARGP_ERR_UNKNOWN){
634
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
635
 
    exitstatus = EXIT_FAILURE;
636
 
    goto fallback;
637
 
  }
 
256
  argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
638
257
  
639
258
  if(debug){
640
259
    for(plugin *p = plugin_list; p != NULL; p=p->next){
643
262
      for(char **a = p->argv; *a != NULL; a++){
644
263
        fprintf(stderr, "\tArg: %s\n", *a);
645
264
      }
646
 
      fprintf(stderr, "...and %u environment variables\n", p->envc);
647
 
      for(char **a = p->environ; *a != NULL; a++){
648
 
        fprintf(stderr, "\t%s\n", *a);
649
 
      }
650
265
    }
651
266
  }
652
267
  
653
 
  /* Strip permissions down to nobody */
654
 
  ret = setuid(uid);
655
 
  if (ret == -1){
656
 
    perror("setuid");
657
 
  }  
658
 
  setgid(gid);
659
 
  if (ret == -1){
660
 
    perror("setgid");
661
 
  }
662
 
  
663
 
  if (plugindir == NULL){
664
 
    dir = opendir(PDIR);
665
 
  } else {
666
 
    dir = opendir(plugindir);
667
 
  }
668
 
  
 
268
  dir = opendir(plugindir);
669
269
  if(dir == NULL){
670
270
    perror("Could not open plugin dir");
671
271
    exitstatus = EXIT_FAILURE;
672
 
    goto fallback;
 
272
    goto end;
673
273
  }
674
274
  
675
275
  /* Set the FD_CLOEXEC flag on the directory, if possible */
680
280
      if(ret < 0){
681
281
        perror("set_cloexec_flag");
682
282
        exitstatus = EXIT_FAILURE;
683
 
        goto fallback;
 
283
        goto end;
684
284
      }
685
285
    }
686
286
  }
687
287
  
688
288
  FD_ZERO(&rfds_all);
689
289
  
690
 
  /* Read and execute any executable in the plugin directory*/
691
290
  while(true){
692
291
    dirst = readdir(dir);
693
292
    
694
 
    /* All directory entries have been processed */
 
293
    // All directory entries have been processed
695
294
    if(dirst == NULL){
696
 
      if (errno == EBADF){
697
 
        perror("readdir");
698
 
        exitstatus = EXIT_FAILURE;
699
 
        goto fallback;
700
 
      }
701
295
      break;
702
296
    }
703
297
    
704
298
    d_name_len = strlen(dirst->d_name);
705
299
    
706
 
    /* Ignore dotfiles, backup files and other junk */
 
300
    // Ignore dotfiles, backup files and other junk
707
301
    {
708
302
      bool bad_name = false;
709
303
      
724
318
          break;
725
319
        }
726
320
      }
 
321
      
727
322
      if(bad_name){
728
323
        continue;
729
324
      }
 
325
      
730
326
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
731
327
        size_t suf_len = strlen(*suf);
732
328
        if((d_name_len >= suf_len)
745
341
        continue;
746
342
      }
747
343
    }
748
 
 
749
 
    char *filename;
750
 
    ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
751
 
    if(ret < 0){
752
 
      perror("asprintf");
753
 
      continue;
754
 
    }
755
 
    
756
 
    ret = stat(filename, &st);
757
 
    if (ret == -1){
758
 
      perror("stat");
759
 
      free(filename);
760
 
      continue;
761
 
    }
762
 
 
763
 
    /* 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
    
764
357
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
765
358
      if(debug){
766
359
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
769
362
      free(filename);
770
363
      continue;
771
364
    }
772
 
    
773
 
    plugin *p = getplugin(dirst->d_name);
774
 
    if(p == NULL){
775
 
      perror("getplugin");
776
 
      free(filename);
777
 
      continue;
778
 
    }
779
 
    if(p->disabled){
 
365
    if(getplugin(dirst->d_name, &plugin_list)->disabled){
780
366
      if(debug){
781
367
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
782
368
                dirst->d_name);
784
370
      free(filename);
785
371
      continue;
786
372
    }
 
373
    plugin *p = getplugin(dirst->d_name, &plugin_list);
787
374
    {
788
375
      /* Add global arguments to argument list for this plugin */
789
 
      plugin *g = getplugin(NULL);
790
 
      if(g != NULL){
791
 
        for(char **a = g->argv + 1; *a != NULL; a++){
792
 
          if(not add_argument(p, *a)){
793
 
            perror("add_argument");
794
 
          }
795
 
        }
796
 
        /* Add global environment variables */
797
 
        for(char **e = g->environ; *e != NULL; e++){
798
 
          if(not add_environment(p, *e, false)){
799
 
            perror("add_environment");
800
 
          }
801
 
        }
802
 
      }
803
 
    }
804
 
    /* If this plugin has any environment variables, we will call
805
 
       using execve and need to duplicate the environment from this
806
 
       process, too. */
807
 
    if(p->environ[0] != NULL){
808
 
      for(char **e = environ; *e != NULL; e++){
809
 
        if(not add_environment(p, *e, false)){
810
 
          perror("add_environment");
811
 
        }
812
 
      }
813
 
    }
814
 
    
815
 
    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]; 
816
382
    ret = pipe(pipefd);
817
383
    if (ret == -1){
818
384
      perror("pipe");
819
385
      exitstatus = EXIT_FAILURE;
820
 
      goto fallback;
 
386
      goto end;
821
387
    }
822
 
    /* Ask OS to automatic close the pipe on exec */
823
388
    ret = set_cloexec_flag(pipefd[0]);
824
389
    if(ret < 0){
825
390
      perror("set_cloexec_flag");
826
391
      exitstatus = EXIT_FAILURE;
827
 
      goto fallback;
 
392
      goto end;
828
393
    }
829
394
    ret = set_cloexec_flag(pipefd[1]);
830
395
    if(ret < 0){
831
396
      perror("set_cloexec_flag");
832
397
      exitstatus = EXIT_FAILURE;
833
 
      goto fallback;
 
398
      goto end;
834
399
    }
835
400
    /* Block SIGCHLD until process is safely in process list */
836
401
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
837
402
    if(ret < 0){
838
403
      perror("sigprocmask");
839
404
      exitstatus = EXIT_FAILURE;
840
 
      goto fallback;
 
405
      goto end;
841
406
    }
842
 
    /* Starting a new process to be watched */
 
407
    // Starting a new process to be watched
843
408
    pid_t pid = fork();
844
 
    if(pid == -1){
845
 
      perror("fork");
846
 
      exitstatus = EXIT_FAILURE;
847
 
      goto fallback;
848
 
    }
849
409
    if(pid == 0){
850
410
      /* this is the child process */
851
411
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
858
418
        perror("sigprocmask");
859
419
        _exit(EXIT_FAILURE);
860
420
      }
861
 
 
862
 
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
863
 
      if(ret == -1){
864
 
        perror("dup2");
865
 
        _exit(EXIT_FAILURE);
866
 
      }
 
421
      dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
867
422
      
868
423
      if(dirfd(dir) < 0){
869
424
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
870
425
           above and must now close it manually here. */
871
426
        closedir(dir);
872
427
      }
873
 
      if(p->environ[0] == NULL){
874
 
        if(execv(filename, p->argv) < 0){
875
 
          perror("execv");
876
 
          _exit(EXIT_FAILURE);
877
 
        }
878
 
      } else {
879
 
        if(execve(filename, p->argv, p->environ) < 0){
880
 
          perror("execve");
881
 
          _exit(EXIT_FAILURE);
882
 
        }
 
428
      if(execv(filename, p->argv) < 0){
 
429
        perror("execv");
 
430
        _exit(EXIT_FAILURE);
883
431
      }
884
432
      /* no return */
885
433
    }
886
 
    /* Parent process */
887
 
    close(pipefd[1]);           /* Close unused write end of pipe */
 
434
    /* parent process */
888
435
    free(filename);
889
 
    plugin *new_plugin = getplugin(dirst->d_name);
890
 
    if (new_plugin == NULL){
891
 
      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");
892
440
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
893
441
      if(ret < 0){
894
 
        perror("sigprocmask");
 
442
        perror("sigprocmask");
895
443
      }
896
444
      exitstatus = EXIT_FAILURE;
897
 
      goto fallback;
 
445
      goto end;
898
446
    }
899
447
    
900
 
    new_plugin->pid = pid;
901
 
    new_plugin->fd = pipefd[0];
902
 
    
 
448
    *new_process = (struct process){ .pid = pid,
 
449
                                     .fd = pipefd[0],
 
450
                                     .next = process_list };
 
451
    // List handling
 
452
    process_list = new_process;
903
453
    /* Unblock SIGCHLD so signal handler can be run if this process
904
454
       has already completed */
905
455
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
906
456
    if(ret < 0){
907
457
      perror("sigprocmask");
908
458
      exitstatus = EXIT_FAILURE;
909
 
      goto fallback;
910
 
    }
911
 
    
912
 
    FD_SET(new_plugin->fd, &rfds_all);
913
 
    
914
 
    if (maxfd < new_plugin->fd){
915
 
      maxfd = new_plugin->fd;
916
 
    }
917
 
    
 
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);
918
475
  }
919
476
  
920
477
  closedir(dir);
921
478
  dir = NULL;
922
 
 
923
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
924
 
    if(p->pid != 0){
925
 
      break;
926
 
    }
927
 
    if(p->next == NULL){
928
 
      fprintf(stderr, "No plugin processes started. Incorrect plugin"
929
 
              " directory?\n");
930
 
      free_plugin_list();
931
 
    }
 
479
  
 
480
  if (process_list == NULL){
 
481
    fprintf(stderr, "No plugin processes started, exiting\n");
 
482
    exitstatus = EXIT_FAILURE;
 
483
    goto end;
932
484
  }
933
 
 
934
 
  /* Main loop while running plugins exist */
935
 
  while(plugin_list){
 
485
  while(process_list){
936
486
    fd_set rfds = rfds_all;
937
487
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
938
488
    if (select_ret == -1){
939
489
      perror("select");
940
490
      exitstatus = EXIT_FAILURE;
941
 
      goto fallback;
 
491
      goto end;
942
492
    }
943
493
    /* OK, now either a process completed, or something can be read
944
494
       from one of them */
945
 
    for(plugin *proc = plugin_list; proc != NULL; proc = proc->next){
 
495
    for(process *proc = process_list; proc ; proc = proc->next){
946
496
      /* Is this process completely done? */
947
497
      if(proc->eof and proc->completed){
948
498
        /* Only accept the plugin output if it exited cleanly */
949
499
        if(not WIFEXITED(proc->status)
950
500
           or WEXITSTATUS(proc->status) != 0){
951
501
          /* Bad exit by plugin */
952
 
 
953
502
          if(debug){
954
503
            if(WIFEXITED(proc->status)){
955
 
              fprintf(stderr, "Plugin %u exited with status %d\n",
956
 
                      (unsigned int) (proc->pid),
957
 
                      WEXITSTATUS(proc->status));
 
504
              fprintf(stderr, "Plugin %d exited with status %d\n",
 
505
                      proc->pid, WEXITSTATUS(proc->status));
958
506
            } else if(WIFSIGNALED(proc->status)) {
959
 
              fprintf(stderr, "Plugin %u killed by signal %d\n",
960
 
                      (unsigned int) (proc->pid),
961
 
                      WTERMSIG(proc->status));
 
507
              fprintf(stderr, "Plugin %d killed by signal %d\n",
 
508
                      proc->pid, WTERMSIG(proc->status));
962
509
            } else if(WCOREDUMP(proc->status)){
963
 
              fprintf(stderr, "Plugin %d dumped core\n",
964
 
                      (unsigned int) (proc->pid));
 
510
              fprintf(stderr, "Plugin %d dumped core\n", proc->pid);
965
511
            }
966
512
          }
967
 
          
968
513
          /* Remove the plugin */
969
514
          FD_CLR(proc->fd, &rfds_all);
970
 
 
971
515
          /* Block signal while modifying process_list */
972
 
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
 
516
          ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
973
517
          if(ret < 0){
974
518
            perror("sigprocmask");
975
519
            exitstatus = EXIT_FAILURE;
976
 
            goto fallback;
977
 
          }
978
 
          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
          }
979
535
          /* We are done modifying process list, so unblock signal */
980
536
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
981
537
                             NULL);
982
538
          if(ret < 0){
983
539
            perror("sigprocmask");
984
 
            exitstatus = EXIT_FAILURE;
985
 
            goto fallback;
986
 
          }
987
 
          
988
 
          if(plugin_list == NULL){
989
 
            break;
990
 
          }
991
 
          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;
992
547
        }
993
 
        
994
548
        /* This process exited nicely, so print its buffer */
995
 
 
996
 
        bool bret = print_out_password(proc->buffer,
997
 
                                       proc->buffer_length);
998
 
        if(not bret){
999
 
          perror("print_out_password");
1000
 
          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
          }
1001
560
        }
1002
 
        goto fallback;
 
561
        goto end;
1003
562
      }
1004
 
      
1005
563
      /* This process has not completed.  Does it have any output? */
1006
564
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1007
565
        /* This process had nothing to say at this time */
1014
572
        if (proc->buffer == NULL){
1015
573
          perror("malloc");
1016
574
          exitstatus = EXIT_FAILURE;
1017
 
          goto fallback;
 
575
          goto end;
1018
576
        }
1019
577
        proc->buffer_size += BUFFER_SIZE;
1020
578
      }
1033
591
      }
1034
592
    }
1035
593
  }
1036
 
 
1037
 
 
1038
 
 fallback:
1039
 
  
1040
 
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
1041
 
    /* Fallback if all plugins failed, none are found or an error
1042
 
       occured */
1043
 
    bool bret;
1044
 
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
1045
 
    char *passwordbuffer = getpass("Password: ");
1046
 
    bret = print_out_password(passwordbuffer, strlen(passwordbuffer));
1047
 
    if(not bret){
1048
 
      perror("print_out_password");
1049
 
      exitstatus = EXIT_FAILURE;
1050
 
    }
 
594
  if(process_list == NULL){
 
595
    fprintf(stderr, "All plugin processes failed, exiting\n");
 
596
    exitstatus = EXIT_FAILURE;
1051
597
  }
1052
598
  
 
599
 end:
1053
600
  /* Restore old signal handler */
1054
 
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1055
 
  if(ret == -1){
1056
 
    perror("sigaction");
1057
 
    exitstatus = EXIT_FAILURE;
1058
 
  }
1059
 
 
1060
 
  if(custom_argv != NULL){
1061
 
    for(char **arg = custom_argv+1; *arg != NULL; arg++){
1062
 
      free(*arg);
1063
 
    }
1064
 
    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);
1065
608
  }
1066
609
  
1067
610
  if(dir != NULL){
1069
612
  }
1070
613
  
1071
614
  /* Free the process list and kill the processes */
1072
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1073
 
    if(p->pid != 0){
1074
 
      close(p->fd);
1075
 
      ret = kill(p->pid, SIGTERM);
1076
 
      if(ret == -1 and errno != ESRCH){
1077
 
        /* Set-uid proccesses might not get closed */
1078
 
        perror("kill");
1079
 
      }
1080
 
    }
 
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);
1081
621
  }
1082
622
  
1083
623
  /* Wait for any remaining child processes to terminate */
1087
627
  if(errno != ECHILD){
1088
628
    perror("wait");
1089
629
  }
1090
 
 
1091
 
  free_plugin_list();
1092
 
  
1093
 
  free(plugindir);
1094
 
  free(argfile);
1095
630
  
1096
631
  return exitstatus;
1097
632
}