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