/mandos/release

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

« back to all changes in this revision

Viewing changes to plugin-runner.c

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

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

Show diffs side-by-side

added added

removed removed

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