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