/mandos/trunk

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

« back to all changes in this revision

Viewing changes to plugin-runner.c

  • Committer: Teddy Hogeborn
  • Date: 2008-08-16 03:29:08 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080816032908-ihw7c05r2mnyk389
Add feature to specify custom environment variables for plugins.

* plugin-runner.c (plugin): New members "environ" and "envc" to
                            contain possible custom environment.
  (getplugin): Return NULL on failure instead of doing exit(); all
               callers changed.
  (add_to_char_array): New helper function for "add_argument" and
                       "add_environment".
  (addargument): Renamed to "add_argument".  Return bool.  Call
                 "add_to_char_array" to actually do things.
  (add_environment): New; analogous to "add_argument".
  (addcustomargument): Renamed to "add_to_argv" to avoid confusion
                       with "add_argument".
  (main): New options "--global-envs" and "--envs-for" to specify
          custom environment for plugins.  Print environment for
          plugins in debug mode.  Use asprintf instead of strcpy and
          strcat.  Use execve() for plugins with custom environments.
          Free environment for plugin when freeing plugin list.

Show diffs side-by-side

added added

removed removed

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