/mandos/trunk

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

« back to all changes in this revision

Viewing changes to plugbasedclient.c

  • Committer: Teddy Hogeborn
  • Date: 2008-08-02 10:48:24 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080802104824-fx0miwp9o4g9r31e
* plugbasedclient.c (struct process): New fields "eof", "completed",
                                      and "status".
  (handle_sigchld): New function.
  (main): Initialize "dir" to NULL to only closedir() it if necessary.
          Move "process_list" to be a global variable to be accessible
          by "handle_sigchld".  Make "handle_sigchld" handle SIGCHLD.
          Remove redundant check for NULL "dir".  Free "filename" when
          no longer used.  Block SIGCHLD around fork()/exec().
          Restore normal signals in child.  Only loop while running
          processes exist.  Print process buffer when the process is
          done and it has emitted EOF, not when it only emits EOF.
          Remove processes from list which exit non-cleanly.  In
          cleaning up, closedir() if necessary.  Bug fix: set next
          pointer correctly when freeing process list.

* plugins.d/passprompt.c (main): Do not ignore SIGQUIT.

Show diffs side-by-side

added added

removed removed

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