/mandos/release

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

« back to all changes in this revision

Viewing changes to plugin-runner.c

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