/mandos/trunk

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

« back to all changes in this revision

Viewing changes to plugin-runner.c

  • Committer: Teddy Hogeborn
  • Date: 2008-08-29 05:53:59 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080829055359-wkdasnyxtylmnxus
* mandos.xml (EXAMPLE): Replaced all occurences of command name with
                        "&COMMANDNAME;".

* plugins.d/password-prompt.c (main): Improved some documentation
                                      strings.  Do perror() of
                                      tcgetattr() fails.  Add debug
                                      output if interrupted by signal.
                                      Loop over write() instead of
                                      using fwrite() when outputting
                                      password.  Add debug output if
                                      getline() returns 0, unless it
                                      was caused by a signal.  Add
                                      exit status code to debug
                                      output.

* plugins.d/password-prompt.xml: Changed all single quotes to double
                                 quotes for consistency.  Removed
                                 <?xml-stylesheet>.
  (ENTITY TIMESTAMP): New.  Automatically updated by Emacs time-stamp
                      by using Emacs local variables.
  (/refentry/refentryinfo/title): Changed to "Mandos Manual".
  (/refentry/refentryinfo/productname): Changed to "Mandos".
  (/refentry/refentryinfo/date): New; set to "&TIMESTAMP;".
  (/refentry/refentryinfo/copyright): Split copyright holders.
  (/refentry/refnamediv/refpurpose): Improved wording.
  (SYNOPSIS): Fix to use correct markup.  Add short options.
  (DESCRIPTION, OPTIONS): Improved wording.
  (OPTIONS): Improved wording.  Use more correct markup.  Document
             short options.
  (EXIT STATUS): Add text.
  (ENVIRONMENT): Document use of "cryptsource" and "crypttarget".
  (FILES): REMOVED.
  (BUGS): Add text.
  (EXAMPLE): Added some examples.
  (SECURITY): Added text.
  (SEE ALSO): Remove reference to mandos(8).  Add reference to
              crypttab(5).

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 * Contact the authors at <mandos@fukt.bsnet.se>.
22
22
 */
23
23
 
24
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY() */
25
 
 
 
24
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
 
25
                                   asprintf() */
26
26
#include <stddef.h>             /* size_t, NULL */
27
27
#include <stdlib.h>             /* malloc(), exit(), EXIT_FAILURE,
28
28
                                   EXIT_SUCCESS, realloc() */
51
51
                                   close() */
52
52
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
53
53
                                   FD_CLOEXEC */
54
 
#include <string.h>             /* strsep, strlen(), strcpy(),
55
 
                                   strcat() */
 
54
#include <string.h>             /* strsep, strlen(), asprintf() */
56
55
#include <errno.h>              /* errno */
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, error_t */
 
59
                                   ARGP_KEY_END, ARGP_KEY_ARG,
 
60
                                   error_t */
61
61
#include <signal.h>             /* struct sigaction, sigemptyset(),
62
62
                                   sigaddset(), sigaction(),
63
63
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
66
66
 
67
67
#define BUFFER_SIZE 256
68
68
 
 
69
#define PDIR "/lib/mandos/plugins.d"
 
70
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
 
71
 
69
72
const char *argp_program_version = "plugin-runner 1.0";
70
73
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
71
74
 
78
81
  size_t buffer_size;
79
82
  size_t buffer_length;
80
83
  bool eof;
81
 
  bool completed;
82
 
  int status;
 
84
  volatile bool completed;
 
85
  volatile int status;
83
86
  struct process *next;
84
87
} process;
85
88
 
87
90
  char *name;                   /* can be NULL or any plugin name */
88
91
  char **argv;
89
92
  int argc;
 
93
  char **environ;
 
94
  int envc;
90
95
  bool disabled;
91
96
  struct plugin *next;
92
97
} plugin;
101
106
  /* Create a new plugin */
102
107
  plugin *new_plugin = malloc(sizeof(plugin));
103
108
  if (new_plugin == NULL){
104
 
    perror("malloc");
105
 
    exit(EXIT_FAILURE);
106
 
  }
107
 
  new_plugin->name = name;
 
109
    return NULL;
 
110
  }
 
111
  char *copy_name = NULL;
 
112
  if(name != NULL){
 
113
    copy_name = strdup(name);
 
114
    if(copy_name == NULL){
 
115
      return NULL;
 
116
    }
 
117
  }
 
118
  
 
119
  *new_plugin = (plugin) { .name = copy_name,
 
120
                           .argc = 1,
 
121
                           .envc = 0,
 
122
                           .disabled = false,
 
123
                           .next = *plugin_list };
 
124
  
108
125
  new_plugin->argv = malloc(sizeof(char *) * 2);
109
126
  if (new_plugin->argv == NULL){
110
 
    perror("malloc");
111
 
    exit(EXIT_FAILURE);
 
127
    free(copy_name);
 
128
    free(new_plugin);
 
129
    return NULL;
112
130
  }
113
 
  new_plugin->argv[0] = name;
 
131
  new_plugin->argv[0] = copy_name;
114
132
  new_plugin->argv[1] = NULL;
115
 
  new_plugin->argc = 1;
116
 
  new_plugin->disabled = false;
117
 
  new_plugin->next = *plugin_list;
 
133
 
 
134
  new_plugin->environ = malloc(sizeof(char *));
 
135
  if(new_plugin->environ == NULL){
 
136
    free(copy_name);
 
137
    free(new_plugin->argv);
 
138
    free(new_plugin);
 
139
    return NULL;
 
140
  }
 
141
  new_plugin->environ[0] = NULL;
118
142
  /* Append the new plugin to the list */
119
143
  *plugin_list = new_plugin;
120
144
  return new_plugin;
121
145
}
122
146
 
123
 
static void addargument(plugin *p, char *arg){
124
 
  p->argv[p->argc] = arg;
125
 
  p->argv = realloc(p->argv, sizeof(char *) * (size_t)(p->argc + 2));
126
 
  if (p->argv == NULL){
127
 
    perror("malloc");
128
 
    exit(EXIT_FAILURE);
129
 
  }
130
 
  p->argc++;
131
 
  p->argv[p->argc] = NULL;
132
 
}
 
147
/* Helper function for add_argument and add_environment */
 
148
static bool add_to_char_array(const char *new, char ***array,
 
149
                              int *len){
 
150
  /* Resize the pointed-to array to hold one more pointer */
 
151
  *array = realloc(*array, sizeof(char *)
 
152
                   * (size_t) ((*len) + 2));
 
153
  /* Malloc check */
 
154
  if(*array == NULL){
 
155
    return false;
 
156
  }
 
157
  /* Make a copy of the new string */
 
158
  char *copy = strdup(new);
 
159
  if(copy == NULL){
 
160
    return false;
 
161
  }
 
162
  /* Insert the copy */
 
163
  (*array)[*len] = copy;
 
164
  (*len)++;
 
165
  /* Add a new terminating NULL pointer to the last element */
 
166
  (*array)[*len] = NULL;
 
167
  return true;
 
168
}
 
169
 
 
170
/* Add to a plugin's argument vector */
 
171
static bool add_argument(plugin *p, const char *arg){
 
172
  if(p == NULL){
 
173
    return false;
 
174
  }
 
175
  return add_to_char_array(arg, &(p->argv), &(p->argc));
 
176
}
 
177
 
 
178
/* Add to a plugin's environment */
 
179
static bool add_environment(plugin *p, const char *def){
 
180
  if(p == NULL){
 
181
    return false;
 
182
  }
 
183
  return add_to_char_array(def, &(p->environ), &(p->envc));
 
184
}
 
185
 
133
186
 
134
187
/*
135
188
 * Based on the example in the GNU LibC manual chapter 13.13 "File
149
202
 
150
203
process *process_list = NULL;
151
204
 
152
 
/* Mark a process as completed when it exits, and save its exit
 
205
/* Mark processes as completed when they exit, and save their exit
153
206
   status. */
154
207
void handle_sigchld(__attribute__((unused)) int sig){
155
 
  process *proc = process_list;
156
 
  int status;
157
 
  pid_t pid = wait(&status);
158
 
  if(pid == -1){
159
 
    perror("wait");
160
 
    return;
161
 
  }
162
 
  while(proc != NULL and proc->pid != pid){
163
 
    proc = proc->next;
164
 
  }
165
 
  if(proc == NULL){
166
 
    /* Process not found in process list */
167
 
    return;
168
 
  }
169
 
  proc->status = status;
170
 
  proc->completed = true;
 
208
  while(true){
 
209
    process *proc = process_list;
 
210
    int status;
 
211
    pid_t pid = waitpid(-1, &status, WNOHANG);
 
212
    if(pid == 0){
 
213
      /* Only still running child processes */
 
214
      break;
 
215
    }
 
216
    if(pid == -1){
 
217
      if (errno != ECHILD){
 
218
        perror("waitpid");
 
219
      }
 
220
      /* No child processes */
 
221
      break;
 
222
    }
 
223
 
 
224
    /* A child exited, find it in process_list */
 
225
    while(proc != NULL and proc->pid != pid){
 
226
      proc = proc->next;
 
227
    }
 
228
    if(proc == NULL){
 
229
      /* Process not found in process list */
 
230
      continue;
 
231
    }
 
232
    proc->status = status;
 
233
    proc->completed = true;
 
234
  }
171
235
}
172
236
 
173
237
bool print_out_password(const char *buffer, size_t length){
185
249
  return true;
186
250
}
187
251
 
 
252
static void free_plugin_list(plugin *plugin_list){
 
253
  for(plugin *next; plugin_list != NULL; plugin_list = next){
 
254
    next = plugin_list->next;
 
255
    for(char **arg = plugin_list->argv; *arg != NULL; arg++){
 
256
      free(*arg);
 
257
    }
 
258
    free(plugin_list->argv);
 
259
    for(char **env = plugin_list->environ; *env != NULL; env++){
 
260
      free(*env);
 
261
    }
 
262
    free(plugin_list->environ);
 
263
    free(plugin_list);
 
264
  }
 
265
}
 
266
 
188
267
int main(int argc, char *argv[]){
189
 
  const char *plugindir = "/lib/mandos/plugins.d";
 
268
  char *plugindir = NULL;
 
269
  char *argfile = NULL;
 
270
  FILE *conffp;
190
271
  size_t d_name_len;
191
272
  DIR *dir = NULL;
192
273
  struct dirent *dirst;
200
281
  struct sigaction old_sigchld_action;
201
282
  struct sigaction sigchld_action = { .sa_handler = handle_sigchld,
202
283
                                      .sa_flags = SA_NOCLDSTOP };
203
 
  char *plus_options = NULL;
204
 
  char **plus_argv = NULL;
205
 
 
 
284
  char **custom_argv = NULL;
 
285
  int custom_argc = 0;
 
286
  
206
287
  /* Establish a signal handler */
207
288
  sigemptyset(&sigchld_action.sa_mask);
208
289
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
209
 
  if(ret < 0){
 
290
  if(ret == -1){
210
291
    perror("sigaddset");
211
 
    exit(EXIT_FAILURE);
 
292
    exitstatus = EXIT_FAILURE;
 
293
    goto fallback;
212
294
  }
213
295
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
214
 
  if(ret < 0){
 
296
  if(ret == -1){
215
297
    perror("sigaction");
216
 
    exit(EXIT_FAILURE);
 
298
    exitstatus = EXIT_FAILURE;
 
299
    goto fallback;
217
300
  }
218
301
  
219
302
  /* The options we understand. */
221
304
    { .name = "global-options", .key = 'g',
222
305
      .arg = "OPTION[,OPTION[,...]]",
223
306
      .doc = "Options passed to all plugins" },
 
307
    { .name = "global-envs", .key = 'e',
 
308
      .arg = "VAR=value",
 
309
      .doc = "Environment variable passed to all plugins" },
224
310
    { .name = "options-for", .key = 'o',
225
311
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
226
312
      .doc = "Options passed only to specified plugin" },
 
313
    { .name = "envs-for", .key = 'f',
 
314
      .arg = "PLUGIN:ENV=value",
 
315
      .doc = "Environment variable passed to specified plugin" },
227
316
    { .name = "disable", .key = 'd',
228
317
      .arg = "PLUGIN",
229
318
      .doc = "Disable a specific plugin", .group = 1 },
230
319
    { .name = "plugin-dir", .key = 128,
231
320
      .arg = "DIRECTORY",
232
321
      .doc = "Specify a different plugin directory", .group = 2 },
233
 
    { .name = "userid", .key = 129,
234
 
      .arg = "ID", .flags = 0,
235
 
      .doc = "User ID the plugins will run as", .group = 2 },
236
 
    { .name = "groupid", .key = 130,
237
 
      .arg = "ID", .flags = 0,
238
 
      .doc = "Group ID the plugins will run as", .group = 2 },
239
 
    { .name = "debug", .key = 131,
240
 
      .doc = "Debug mode", .group = 3 },
 
322
    { .name = "config-file", .key = 129,
 
323
      .arg = "FILE",
 
324
      .doc = "Specify a different configuration file", .group = 2 },
 
325
    { .name = "userid", .key = 130,
 
326
      .arg = "ID", .flags = 0,
 
327
      .doc = "User ID the plugins will run as", .group = 3 },
 
328
    { .name = "groupid", .key = 131,
 
329
      .arg = "ID", .flags = 0,
 
330
      .doc = "Group ID the plugins will run as", .group = 3 },
 
331
    { .name = "debug", .key = 132,
 
332
      .doc = "Debug mode", .group = 4 },
241
333
    { .name = NULL }
242
334
  };
243
335
  
250
342
      if (arg != NULL){
251
343
        char *p;
252
344
        while((p = strsep(&arg, ",")) != NULL){
253
 
          if(strcmp(p, "") == 0){
 
345
          if(p[0] == '\0'){
254
346
            continue;
255
347
          }
256
 
          addargument(getplugin(NULL, plugins), p);
 
348
          if(not add_argument(getplugin(NULL, plugins), p)){
 
349
            perror("add_argument");
 
350
            return ARGP_ERR_UNKNOWN;
 
351
          }
 
352
        }
 
353
      }
 
354
      break;
 
355
    case 'e':
 
356
      if(arg == NULL){
 
357
        break;
 
358
      }
 
359
      {
 
360
        char *envdef = strdup(arg);
 
361
        if(envdef == NULL){
 
362
          break;
 
363
        }
 
364
        if(not add_environment(getplugin(NULL, plugins), envdef)){
 
365
          perror("add_environment");
257
366
        }
258
367
      }
259
368
      break;
260
369
    case 'o':
261
370
      if (arg != NULL){
262
 
        char *name = strsep(&arg, ":");
263
 
        if(strcmp(name, "") == 0){
 
371
        char *p_name = strsep(&arg, ":");
 
372
        if(p_name[0] == '\0'){
264
373
          break;
265
374
        }
266
375
        char *opt = strsep(&arg, ":");
267
 
        if(strcmp(opt, "") == 0){
 
376
        if(opt[0] == '\0'){
268
377
          break;
269
378
        }
270
379
        if(opt != NULL){
271
380
          char *p;
272
381
          while((p = strsep(&opt, ",")) != NULL){
273
 
            if(strcmp(p, "") == 0){
 
382
            if(p[0] == '\0'){
274
383
              continue;
275
384
            }
276
 
            addargument(getplugin(name, plugins), p);
 
385
            if(not add_argument(getplugin(p_name, plugins), p)){
 
386
              perror("add_argument");
 
387
              return ARGP_ERR_UNKNOWN;
 
388
            }
277
389
          }
278
390
        }
279
391
      }
280
392
      break;
 
393
    case 'f':
 
394
      if(arg == NULL){
 
395
        break;
 
396
      }
 
397
      {
 
398
        char *envdef = strchr(arg, ':');
 
399
        if(envdef == NULL){
 
400
          break;
 
401
        }
 
402
        char *p_name = strndup(arg, (size_t) (envdef-arg));
 
403
        if(p_name == NULL){
 
404
          break;
 
405
        }
 
406
        envdef++;
 
407
        if(not add_environment(getplugin(p_name, plugins), envdef)){
 
408
          perror("add_environment");
 
409
        }
 
410
      }
 
411
      break;
281
412
    case 'd':
282
413
      if (arg != NULL){
283
 
        getplugin(arg, plugins)->disabled = true;
 
414
        plugin *p = getplugin(arg, plugins);
 
415
        if(p == NULL){
 
416
          return ARGP_ERR_UNKNOWN;
 
417
        }
 
418
        p->disabled = true;
284
419
      }
285
420
      break;
286
421
    case 128:
287
 
      plugindir = arg;
 
422
      plugindir = strdup(arg);
 
423
      if(plugindir == NULL){
 
424
        perror("strdup");
 
425
      }      
288
426
      break;
289
427
    case 129:
 
428
      argfile = strdup(arg);
 
429
      if(argfile == NULL){
 
430
        perror("strdup");
 
431
      }
 
432
      break;      
 
433
    case 130:
290
434
      uid = (uid_t)strtol(arg, NULL, 10);
291
435
      break;
292
 
    case 130:
 
436
    case 131:
293
437
      gid = (gid_t)strtol(arg, NULL, 10);
294
438
      break;
295
 
    case 131:
 
439
    case 132:
296
440
      debug = true;
297
441
      break;
298
442
    case ARGP_KEY_ARG:
299
 
      if(plus_options != NULL or arg == NULL or arg[0] != '+'){
300
 
        argp_usage (state);
301
 
      }
302
 
      plus_options = arg;
 
443
      fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
303
444
      break;
304
445
    case ARGP_KEY_END:
305
446
      break;
319
460
  if (ret == ARGP_ERR_UNKNOWN){
320
461
    fprintf(stderr, "Unknown error while parsing arguments\n");
321
462
    exitstatus = EXIT_FAILURE;
322
 
    goto end;
 
463
    goto fallback;
 
464
  }
 
465
 
 
466
  if (argfile == NULL){
 
467
    conffp = fopen(AFILE, "r");
 
468
  } else {
 
469
    conffp = fopen(argfile, "r");
323
470
  }
324
471
  
325
 
  if(plus_options){
326
 
    /* This is a mangled argument in the form of
327
 
     "+--option+--other-option=parameter+--yet-another-option", etc */
328
 
    /* Make new argc and argv vars, and call argp_parse() again. */
329
 
    plus_options++;             /* skip the first '+' character */
330
 
    const char delims[] = "+";
331
 
    char *arg;
332
 
    int new_argc = 1;
333
 
    plus_argv = malloc(sizeof(char*) * 2);
334
 
    if(plus_argv == NULL){
 
472
  if(conffp != NULL){
 
473
    char *org_line = NULL;
 
474
    char *p, *arg, *new_arg, *line;
 
475
    size_t size = 0;
 
476
    ssize_t sret;
 
477
    const char whitespace_delims[] = " \r\t\f\v\n";
 
478
    const char comment_delim[] = "#";
 
479
 
 
480
    custom_argc = 1;
 
481
    custom_argv = malloc(sizeof(char*) * 2);
 
482
    if(custom_argv == NULL){
335
483
      perror("malloc");
336
484
      exitstatus = EXIT_FAILURE;
337
 
      goto end;
338
 
    }
339
 
    plus_argv[0] = argv[0];
340
 
    plus_argv[1] = NULL;
341
 
 
342
 
    while((arg = strsep(&plus_options, delims)) != NULL){
343
 
      new_argc++;
344
 
      plus_argv = realloc(plus_argv, sizeof(char *)
345
 
                         * ((unsigned int) new_argc + 1));
346
 
      if(plus_argv == NULL){
347
 
        perror("realloc");
348
 
        exitstatus = EXIT_FAILURE;
349
 
        goto end;
350
 
      }
351
 
      plus_argv[new_argc-1] = arg;
352
 
      plus_argv[new_argc] = NULL;
353
 
    }
 
485
      goto fallback;
 
486
    }
 
487
    custom_argv[0] = argv[0];
 
488
    custom_argv[1] = NULL;
354
489
    
355
 
    ret = argp_parse (&argp, new_argc, plus_argv, 0, 0, &plugin_list);
 
490
    while(true){
 
491
      sret = getline(&org_line, &size, conffp);
 
492
      if(sret == -1){
 
493
        break;
 
494
      }
 
495
 
 
496
      line = org_line;
 
497
      arg = strsep(&line, comment_delim);
 
498
      while((p = strsep(&arg, whitespace_delims)) != NULL){
 
499
        if(p[0] == '\0'){
 
500
          continue;
 
501
        }
 
502
        new_arg = strdup(p);
 
503
        if(new_arg == NULL){
 
504
          perror("strdup");
 
505
          exitstatus = EXIT_FAILURE;
 
506
          free(org_line);
 
507
          goto fallback;
 
508
        }
 
509
        
 
510
        custom_argc += 1;
 
511
        custom_argv = realloc(custom_argv, sizeof(char *)
 
512
                              * ((unsigned int) custom_argc + 1));
 
513
        if(custom_argv == NULL){
 
514
          perror("realloc");
 
515
          exitstatus = EXIT_FAILURE;
 
516
          free(org_line);
 
517
          goto fallback;
 
518
        }
 
519
        custom_argv[custom_argc-1] = new_arg;
 
520
        custom_argv[custom_argc] = NULL;        
 
521
      }
 
522
    }
 
523
    free(org_line);
 
524
  } else{
 
525
    /* Check for harmful errors and go to fallback. Other errors might
 
526
       not affect opening plugins */
 
527
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
 
528
      perror("fopen");
 
529
      exitstatus = EXIT_FAILURE;
 
530
      goto fallback;
 
531
    }
 
532
  }
 
533
 
 
534
  if(custom_argv != NULL){
 
535
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, &plugin_list);
356
536
    if (ret == ARGP_ERR_UNKNOWN){
357
537
      fprintf(stderr, "Unknown error while parsing arguments\n");
358
538
      exitstatus = EXIT_FAILURE;
359
 
      goto end;
 
539
      goto fallback;
360
540
    }
361
541
  }
362
542
  
367
547
      for(char **a = p->argv; *a != NULL; a++){
368
548
        fprintf(stderr, "\tArg: %s\n", *a);
369
549
      }
 
550
      fprintf(stderr, "...and %u environment variables\n", p->envc);
 
551
      for(char **a = p->environ; *a != NULL; a++){
 
552
        fprintf(stderr, "\t%s\n", *a);
 
553
      }
370
554
    }
371
555
  }
372
556
  
379
563
  if (ret == -1){
380
564
    perror("setgid");
381
565
  }
 
566
 
 
567
  if (plugindir == NULL){
 
568
    dir = opendir(PDIR);
 
569
  } else {
 
570
    dir = opendir(plugindir);
 
571
  }
382
572
  
383
 
  dir = opendir(plugindir);
384
573
  if(dir == NULL){
385
574
    perror("Could not open plugin dir");
386
575
    exitstatus = EXIT_FAILURE;
387
 
    goto end;
 
576
    goto fallback;
388
577
  }
389
578
  
390
579
  /* Set the FD_CLOEXEC flag on the directory, if possible */
395
584
      if(ret < 0){
396
585
        perror("set_cloexec_flag");
397
586
        exitstatus = EXIT_FAILURE;
398
 
        goto end;
 
587
        goto fallback;
399
588
      }
400
589
    }
401
590
  }
410
599
      if (errno == EBADF){
411
600
        perror("readdir");
412
601
        exitstatus = EXIT_FAILURE;
413
 
        goto end;
 
602
        goto fallback;
414
603
      }
415
604
      break;
416
605
    }
461
650
        continue;
462
651
      }
463
652
    }
464
 
    
465
 
    char *filename = malloc(d_name_len + strlen(plugindir) + 2);
466
 
    if (filename == NULL){
467
 
      perror("malloc");
 
653
 
 
654
    char *filename;
 
655
    ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
 
656
    if(ret < 0){
 
657
      perror("asprintf");
468
658
      continue;
469
659
    }
470
 
    strcpy(filename, plugindir); /* Spurious warning */
471
 
    strcat(filename, "/");      /* Spurious warning */
472
 
    strcat(filename, dirst->d_name); /* Spurious warning */
473
660
    
474
661
    ret = stat(filename, &st);
475
662
    if (ret == -1){
486
673
      free(filename);
487
674
      continue;
488
675
    }
489
 
    if(getplugin(dirst->d_name, &plugin_list)->disabled){
 
676
    plugin *p = getplugin(dirst->d_name, &plugin_list);
 
677
    if(p == NULL){
 
678
      perror("getplugin");
 
679
      free(filename);
 
680
      continue;
 
681
    }
 
682
    if(p->disabled){
490
683
      if(debug){
491
684
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
492
685
                dirst->d_name);
494
687
      free(filename);
495
688
      continue;
496
689
    }
497
 
    plugin *p = getplugin(dirst->d_name, &plugin_list);
498
690
    {
499
691
      /* Add global arguments to argument list for this plugin */
500
692
      plugin *g = getplugin(NULL, &plugin_list);
501
 
      for(char **a = g->argv + 1; *a != NULL; a++){
502
 
        addargument(p, *a);
503
 
      }
504
 
    }
505
 
    int pipefd[2]; 
 
693
      if(g != NULL){
 
694
        for(char **a = g->argv + 1; *a != NULL; a++){
 
695
          if(not add_argument(p, *a)){
 
696
            perror("add_argument");
 
697
          }
 
698
        }
 
699
        /* Add global environment variables */
 
700
        for(char **e = g->environ; *e != NULL; e++){
 
701
          if(not add_environment(p, *e)){
 
702
            perror("add_environment");
 
703
          }
 
704
        }
 
705
      }
 
706
    }
 
707
    /* If this plugin has any environment variables, we will call
 
708
       using execve and need to duplicate the environment from this
 
709
       process, too. */
 
710
    if(p->environ[0] != NULL){
 
711
      for(char **e = environ; *e != NULL; e++){
 
712
        char *copy = strdup(*e);
 
713
        if(copy == NULL){
 
714
          perror("strdup");
 
715
          continue;
 
716
        }
 
717
        if(not add_environment(p, copy)){
 
718
          perror("add_environment");
 
719
        }
 
720
      }
 
721
    }
 
722
    
 
723
    int pipefd[2];
506
724
    ret = pipe(pipefd);
507
725
    if (ret == -1){
508
726
      perror("pipe");
509
727
      exitstatus = EXIT_FAILURE;
510
 
      goto end;
 
728
      goto fallback;
511
729
    }
512
730
    ret = set_cloexec_flag(pipefd[0]);
513
731
    if(ret < 0){
514
732
      perror("set_cloexec_flag");
515
733
      exitstatus = EXIT_FAILURE;
516
 
      goto end;
 
734
      goto fallback;
517
735
    }
518
736
    ret = set_cloexec_flag(pipefd[1]);
519
737
    if(ret < 0){
520
738
      perror("set_cloexec_flag");
521
739
      exitstatus = EXIT_FAILURE;
522
 
      goto end;
 
740
      goto fallback;
523
741
    }
524
742
    /* Block SIGCHLD until process is safely in process list */
525
743
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
526
744
    if(ret < 0){
527
745
      perror("sigprocmask");
528
746
      exitstatus = EXIT_FAILURE;
529
 
      goto end;
 
747
      goto fallback;
530
748
    }
531
749
    // Starting a new process to be watched
532
750
    pid_t pid = fork();
533
751
    if(pid == -1){
534
752
      perror("fork");
535
753
      exitstatus = EXIT_FAILURE;
536
 
      goto end;
 
754
      goto fallback;
537
755
    }
538
756
    if(pid == 0){
539
757
      /* this is the child process */
559
777
           above and must now close it manually here. */
560
778
        closedir(dir);
561
779
      }
562
 
      if(execv(filename, p->argv) < 0){
563
 
        perror("execv");
564
 
        _exit(EXIT_FAILURE);
 
780
      if(p->environ[0] == NULL){
 
781
        if(execv(filename, p->argv) < 0){
 
782
          perror("execv");
 
783
          _exit(EXIT_FAILURE);
 
784
        }
 
785
      } else {
 
786
        if(execve(filename, p->argv, p->environ) < 0){
 
787
          perror("execve");
 
788
          _exit(EXIT_FAILURE);
 
789
        }
565
790
      }
566
791
      /* no return */
567
792
    }
576
801
        perror("sigprocmask");
577
802
      }
578
803
      exitstatus = EXIT_FAILURE;
579
 
      goto end;
 
804
      goto fallback;
580
805
    }
581
806
    
582
807
    *new_process = (struct process){ .pid = pid,
590
815
    if(ret < 0){
591
816
      perror("sigprocmask");
592
817
      exitstatus = EXIT_FAILURE;
593
 
      goto end;
 
818
      goto fallback;
594
819
    }
595
820
    
596
821
    FD_SET(new_process->fd, &rfds_all);
600
825
    }
601
826
    
602
827
  }
603
 
  
604
 
  /* Free the plugin list */
605
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
606
 
    next = plugin_list->next;
607
 
    free(plugin_list->argv);
608
 
    free(plugin_list);
609
 
  }
 
828
 
 
829
  free_plugin_list(plugin_list);
 
830
  plugin_list = NULL;
610
831
  
611
832
  closedir(dir);
612
833
  dir = NULL;
622
843
    if (select_ret == -1){
623
844
      perror("select");
624
845
      exitstatus = EXIT_FAILURE;
625
 
      goto end;
 
846
      goto fallback;
626
847
    }
627
848
    /* OK, now either a process completed, or something can be read
628
849
       from one of them */
650
871
          /* Remove the plugin */
651
872
          FD_CLR(proc->fd, &rfds_all);
652
873
          /* Block signal while modifying process_list */
653
 
          ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
 
874
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
654
875
          if(ret < 0){
655
876
            perror("sigprocmask");
656
877
            exitstatus = EXIT_FAILURE;
657
 
            goto end;
 
878
            goto fallback;
658
879
          }
659
880
          /* Delete this process entry from the list */
660
881
          if(process_list == proc){
684
905
        }
685
906
        /* This process exited nicely, so print its buffer */
686
907
 
687
 
        bool bret = print_out_password(proc->buffer, proc->buffer_length);
 
908
        bool bret = print_out_password(proc->buffer,
 
909
                                       proc->buffer_length);
688
910
        if(not bret){
689
911
          perror("print_out_password");
690
912
          exitstatus = EXIT_FAILURE;
691
913
        }
692
 
        goto end;
 
914
        goto fallback;
693
915
      }
694
916
      /* This process has not completed.  Does it have any output? */
695
917
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
703
925
        if (proc->buffer == NULL){
704
926
          perror("malloc");
705
927
          exitstatus = EXIT_FAILURE;
706
 
          goto end;
 
928
          goto fallback;
707
929
        }
708
930
        proc->buffer_size += BUFFER_SIZE;
709
931
      }
724
946
  }
725
947
 
726
948
 
727
 
 end:
 
949
 fallback:
728
950
  
729
951
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
730
 
    /* Fallback if all plugins failed, none are found or an error occured */
 
952
    /* Fallback if all plugins failed, none are found or an error
 
953
       occured */
731
954
    bool bret;
732
955
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
733
956
    char *passwordbuffer = getpass("Password: ");
735
958
    if(not bret){
736
959
      perror("print_out_password");
737
960
      exitstatus = EXIT_FAILURE;
738
 
      goto end;
739
961
    }
740
962
  }
741
963
  
742
964
  /* Restore old signal handler */
743
 
  sigaction(SIGCHLD, &old_sigchld_action, NULL);
744
 
  
745
 
  free(plus_argv);
746
 
  
747
 
  /* Free the plugin list */
748
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
749
 
    next = plugin_list->next;
750
 
    free(plugin_list->argv);
751
 
    free(plugin_list);
752
 
  }
 
965
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
 
966
  if(ret == -1){
 
967
    perror("sigaction");
 
968
    exitstatus = EXIT_FAILURE;
 
969
  }
 
970
 
 
971
  if(custom_argv != NULL){
 
972
    for(char **arg = custom_argv+1; *arg != NULL; arg++){
 
973
      free(*arg);
 
974
    }
 
975
    free(custom_argv);
 
976
  }
 
977
  free_plugin_list(plugin_list);
753
978
  
754
979
  if(dir != NULL){
755
980
    closedir(dir);
775
1000
  if(errno != ECHILD){
776
1001
    perror("wait");
777
1002
  }
 
1003
 
 
1004
  free(plugindir);
 
1005
  free(argfile);
778
1006
  
779
1007
  return exitstatus;
780
1008
}