/mandos/trunk

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

« back to all changes in this revision

Viewing changes to plugin-runner.c

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

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

Show diffs side-by-side

added added

removed removed

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