/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,
65
65
#include <errno.h>              /* errno, EBADF */
66
66
 
67
67
#define BUFFER_SIZE 256
68
 
#define CONFFILE "/conf/conf.d/mandos/client.conf"
69
 
 
70
 
const char *argp_program_version = "mandos-client 1.0";
 
68
 
 
69
#define PDIR "/lib/mandos/plugins.d"
 
70
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
 
71
 
 
72
const char *argp_program_version = "plugin-runner 1.0";
71
73
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
72
74
 
73
75
struct process;
79
81
  size_t buffer_size;
80
82
  size_t buffer_length;
81
83
  bool eof;
82
 
  bool completed;
83
 
  int status;
 
84
  volatile bool completed;
 
85
  volatile int status;
84
86
  struct process *next;
85
87
} process;
86
88
 
88
90
  char *name;                   /* can be NULL or any plugin name */
89
91
  char **argv;
90
92
  int argc;
 
93
  char **environ;
 
94
  int envc;
91
95
  bool disabled;
92
96
  struct plugin *next;
93
97
} plugin;
102
106
  /* Create a new plugin */
103
107
  plugin *new_plugin = malloc(sizeof(plugin));
104
108
  if (new_plugin == NULL){
105
 
    perror("malloc");
106
 
    exit(EXIT_FAILURE);
107
 
  }
108
 
  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
  
109
125
  new_plugin->argv = malloc(sizeof(char *) * 2);
110
126
  if (new_plugin->argv == NULL){
111
 
    perror("malloc");
112
 
    exit(EXIT_FAILURE);
 
127
    free(copy_name);
 
128
    free(new_plugin);
 
129
    return NULL;
113
130
  }
114
 
  new_plugin->argv[0] = name;
 
131
  new_plugin->argv[0] = copy_name;
115
132
  new_plugin->argv[1] = NULL;
116
 
  new_plugin->argc = 1;
117
 
  new_plugin->disabled = false;
118
 
  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;
119
142
  /* Append the new plugin to the list */
120
143
  *plugin_list = new_plugin;
121
144
  return new_plugin;
122
145
}
123
146
 
124
 
static void addargument(plugin *p, char *arg){
125
 
  p->argv[p->argc] = arg;
126
 
  p->argv = realloc(p->argv, sizeof(char *) * (size_t)(p->argc + 2));
127
 
  if (p->argv == NULL){
128
 
    perror("malloc");
129
 
    exit(EXIT_FAILURE);
130
 
  }
131
 
  p->argc++;
132
 
  p->argv[p->argc] = NULL;
133
 
}
 
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
 
134
186
 
135
187
/*
136
188
 * Based on the example in the GNU LibC manual chapter 13.13 "File
150
202
 
151
203
process *process_list = NULL;
152
204
 
153
 
/* Mark a process as completed when it exits, and save its exit
 
205
/* Mark processes as completed when they exit, and save their exit
154
206
   status. */
155
207
void handle_sigchld(__attribute__((unused)) int sig){
156
 
  process *proc = process_list;
157
 
  int status;
158
 
  pid_t pid = wait(&status);
159
 
  if(pid == -1){
160
 
    perror("wait");
161
 
    return;
162
 
  }
163
 
  while(proc != NULL and proc->pid != pid){
164
 
    proc = proc->next;
165
 
  }
166
 
  if(proc == NULL){
167
 
    /* Process not found in process list */
168
 
    return;
169
 
  }
170
 
  proc->status = status;
171
 
  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
  }
172
235
}
173
236
 
174
237
bool print_out_password(const char *buffer, size_t length){
186
249
  return true;
187
250
}
188
251
 
189
 
char ** addcustomargument(char **argv, int *argc, char *arg){
190
 
 
191
 
  if (argv == NULL){
192
 
    *argc = 1;
193
 
    argv = malloc(sizeof(char*) * 2);
194
 
    if(argv == NULL){
195
 
      return NULL;
196
 
    }
197
 
    argv[0] = NULL;     /* Will be set to argv[0] in main before parsing */
198
 
    argv[1] = NULL;
199
 
  }
200
 
  *argc += 1;
201
 
  argv = realloc(argv, sizeof(char *)
202
 
                  * ((unsigned int) *argc + 1));
203
 
  if(argv == NULL){
204
 
    return NULL;
205
 
  }
206
 
  argv[*argc-1] = arg;
207
 
  argv[*argc] = NULL;   
208
 
  return argv;
 
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
  }
209
265
}
210
266
 
211
267
int main(int argc, char *argv[]){
212
 
  const char *plugindir = "/conf/conf.d/mandos/plugins.d";
213
 
  const char *conffile = CONFFILE;
 
268
  char *plugindir = NULL;
 
269
  char *argfile = NULL;
214
270
  FILE *conffp;
215
271
  size_t d_name_len;
216
272
  DIR *dir = NULL;
231
287
  /* Establish a signal handler */
232
288
  sigemptyset(&sigchld_action.sa_mask);
233
289
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
234
 
  if(ret < 0){
 
290
  if(ret == -1){
235
291
    perror("sigaddset");
236
 
    exit(EXIT_FAILURE);
 
292
    exitstatus = EXIT_FAILURE;
 
293
    goto fallback;
237
294
  }
238
295
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
239
 
  if(ret < 0){
 
296
  if(ret == -1){
240
297
    perror("sigaction");
241
 
    exit(EXIT_FAILURE);
 
298
    exitstatus = EXIT_FAILURE;
 
299
    goto fallback;
242
300
  }
243
301
  
244
302
  /* The options we understand. */
246
304
    { .name = "global-options", .key = 'g',
247
305
      .arg = "OPTION[,OPTION[,...]]",
248
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" },
249
310
    { .name = "options-for", .key = 'o',
250
311
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
251
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" },
252
316
    { .name = "disable", .key = 'd',
253
317
      .arg = "PLUGIN",
254
318
      .doc = "Disable a specific plugin", .group = 1 },
255
319
    { .name = "plugin-dir", .key = 128,
256
320
      .arg = "DIRECTORY",
257
321
      .doc = "Specify a different plugin directory", .group = 2 },
258
 
    { .name = "userid", .key = 129,
259
 
      .arg = "ID", .flags = 0,
260
 
      .doc = "User ID the plugins will run as", .group = 2 },
261
 
    { .name = "groupid", .key = 130,
262
 
      .arg = "ID", .flags = 0,
263
 
      .doc = "Group ID the plugins will run as", .group = 2 },
264
 
    { .name = "debug", .key = 131,
265
 
      .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 },
266
333
    { .name = NULL }
267
334
  };
268
335
  
278
345
          if(p[0] == '\0'){
279
346
            continue;
280
347
          }
281
 
          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");
282
366
        }
283
367
      }
284
368
      break;
285
369
    case 'o':
286
370
      if (arg != NULL){
287
 
        char *name = strsep(&arg, ":");
288
 
        if(name[0] == '\0'){
 
371
        char *p_name = strsep(&arg, ":");
 
372
        if(p_name[0] == '\0'){
289
373
          break;
290
374
        }
291
375
        char *opt = strsep(&arg, ":");
298
382
            if(p[0] == '\0'){
299
383
              continue;
300
384
            }
301
 
            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
            }
302
389
          }
303
390
        }
304
391
      }
305
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;
306
412
    case 'd':
307
413
      if (arg != NULL){
308
 
        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;
309
419
      }
310
420
      break;
311
421
    case 128:
312
 
      plugindir = arg;
 
422
      plugindir = strdup(arg);
 
423
      if(plugindir == NULL){
 
424
        perror("strdup");
 
425
      }      
313
426
      break;
314
427
    case 129:
 
428
      argfile = strdup(arg);
 
429
      if(argfile == NULL){
 
430
        perror("strdup");
 
431
      }
 
432
      break;      
 
433
    case 130:
315
434
      uid = (uid_t)strtol(arg, NULL, 10);
316
435
      break;
317
 
    case 130:
 
436
    case 131:
318
437
      gid = (gid_t)strtol(arg, NULL, 10);
319
438
      break;
320
 
    case 131:
 
439
    case 132:
321
440
      debug = true;
322
441
      break;
323
442
    case ARGP_KEY_ARG:
324
 
      fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg); 
 
443
      fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
325
444
      break;
326
445
    case ARGP_KEY_END:
327
446
      break;
341
460
  if (ret == ARGP_ERR_UNKNOWN){
342
461
    fprintf(stderr, "Unknown error while parsing arguments\n");
343
462
    exitstatus = EXIT_FAILURE;
344
 
    goto end;
 
463
    goto fallback;
345
464
  }
346
465
 
347
 
  conffp = fopen(conffile, "r");
 
466
  if (argfile == NULL){
 
467
    conffp = fopen(AFILE, "r");
 
468
  } else {
 
469
    conffp = fopen(argfile, "r");
 
470
  }
 
471
  
348
472
  if(conffp != NULL){
349
473
    char *org_line = NULL;
 
474
    char *p, *arg, *new_arg, *line;
350
475
    size_t size = 0;
351
476
    ssize_t sret;
352
 
    char *p, *arg, *new_arg, *line;
353
477
    const char whitespace_delims[] = " \r\t\f\v\n";
354
478
    const char comment_delim[] = "#";
355
479
 
 
480
    custom_argc = 1;
 
481
    custom_argv = malloc(sizeof(char*) * 2);
 
482
    if(custom_argv == NULL){
 
483
      perror("malloc");
 
484
      exitstatus = EXIT_FAILURE;
 
485
      goto fallback;
 
486
    }
 
487
    custom_argv[0] = argv[0];
 
488
    custom_argv[1] = NULL;
 
489
    
356
490
    while(true){
357
491
      sret = getline(&org_line, &size, conffp);
358
492
      if(sret == -1){
366
500
          continue;
367
501
        }
368
502
        new_arg = strdup(p);
369
 
        custom_argv = addcustomargument(custom_argv, &custom_argc, new_arg);
370
 
        if (custom_argv == NULL){
371
 
          perror("addcustomargument");
372
 
          exitstatus = EXIT_FAILURE;
373
 
          goto end;
374
 
        }
 
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;        
375
521
      }
376
522
    }
377
523
    free(org_line);
378
524
  } else{
379
 
    /* check for harmfull errors */
 
525
    /* Check for harmful errors and go to fallback. Other errors might
 
526
       not affect opening plugins */
380
527
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
381
528
      perror("fopen");
382
529
      exitstatus = EXIT_FAILURE;
383
 
      goto end;
 
530
      goto fallback;
384
531
    }
385
532
  }
386
533
 
387
534
  if(custom_argv != NULL){
388
 
    custom_argv[0] = argv[0];
389
535
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, &plugin_list);
390
536
    if (ret == ARGP_ERR_UNKNOWN){
391
537
      fprintf(stderr, "Unknown error while parsing arguments\n");
392
538
      exitstatus = EXIT_FAILURE;
393
 
      goto end;
 
539
      goto fallback;
394
540
    }
395
541
  }
396
542
  
401
547
      for(char **a = p->argv; *a != NULL; a++){
402
548
        fprintf(stderr, "\tArg: %s\n", *a);
403
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
      }
404
554
    }
405
555
  }
406
556
  
413
563
  if (ret == -1){
414
564
    perror("setgid");
415
565
  }
 
566
 
 
567
  if (plugindir == NULL){
 
568
    dir = opendir(PDIR);
 
569
  } else {
 
570
    dir = opendir(plugindir);
 
571
  }
416
572
  
417
 
  dir = opendir(plugindir);
418
573
  if(dir == NULL){
419
574
    perror("Could not open plugin dir");
420
575
    exitstatus = EXIT_FAILURE;
421
 
    goto end;
 
576
    goto fallback;
422
577
  }
423
578
  
424
579
  /* Set the FD_CLOEXEC flag on the directory, if possible */
429
584
      if(ret < 0){
430
585
        perror("set_cloexec_flag");
431
586
        exitstatus = EXIT_FAILURE;
432
 
        goto end;
 
587
        goto fallback;
433
588
      }
434
589
    }
435
590
  }
444
599
      if (errno == EBADF){
445
600
        perror("readdir");
446
601
        exitstatus = EXIT_FAILURE;
447
 
        goto end;
 
602
        goto fallback;
448
603
      }
449
604
      break;
450
605
    }
495
650
        continue;
496
651
      }
497
652
    }
498
 
    
499
 
    char *filename = malloc(d_name_len + strlen(plugindir) + 2);
500
 
    if (filename == NULL){
501
 
      perror("malloc");
 
653
 
 
654
    char *filename;
 
655
    ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
 
656
    if(ret < 0){
 
657
      perror("asprintf");
502
658
      continue;
503
659
    }
504
 
    strcpy(filename, plugindir); /* Spurious warning */
505
 
    strcat(filename, "/");      /* Spurious warning */
506
 
    strcat(filename, dirst->d_name); /* Spurious warning */
507
660
    
508
661
    ret = stat(filename, &st);
509
662
    if (ret == -1){
520
673
      free(filename);
521
674
      continue;
522
675
    }
523
 
    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){
524
683
      if(debug){
525
684
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
526
685
                dirst->d_name);
528
687
      free(filename);
529
688
      continue;
530
689
    }
531
 
    plugin *p = getplugin(dirst->d_name, &plugin_list);
532
690
    {
533
691
      /* Add global arguments to argument list for this plugin */
534
692
      plugin *g = getplugin(NULL, &plugin_list);
535
 
      for(char **a = g->argv + 1; *a != NULL; a++){
536
 
        addargument(p, *a);
537
 
      }
538
 
    }
539
 
    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];
540
724
    ret = pipe(pipefd);
541
725
    if (ret == -1){
542
726
      perror("pipe");
543
727
      exitstatus = EXIT_FAILURE;
544
 
      goto end;
 
728
      goto fallback;
545
729
    }
546
730
    ret = set_cloexec_flag(pipefd[0]);
547
731
    if(ret < 0){
548
732
      perror("set_cloexec_flag");
549
733
      exitstatus = EXIT_FAILURE;
550
 
      goto end;
 
734
      goto fallback;
551
735
    }
552
736
    ret = set_cloexec_flag(pipefd[1]);
553
737
    if(ret < 0){
554
738
      perror("set_cloexec_flag");
555
739
      exitstatus = EXIT_FAILURE;
556
 
      goto end;
 
740
      goto fallback;
557
741
    }
558
742
    /* Block SIGCHLD until process is safely in process list */
559
743
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
560
744
    if(ret < 0){
561
745
      perror("sigprocmask");
562
746
      exitstatus = EXIT_FAILURE;
563
 
      goto end;
 
747
      goto fallback;
564
748
    }
565
749
    // Starting a new process to be watched
566
750
    pid_t pid = fork();
567
751
    if(pid == -1){
568
752
      perror("fork");
569
753
      exitstatus = EXIT_FAILURE;
570
 
      goto end;
 
754
      goto fallback;
571
755
    }
572
756
    if(pid == 0){
573
757
      /* this is the child process */
593
777
           above and must now close it manually here. */
594
778
        closedir(dir);
595
779
      }
596
 
      if(execv(filename, p->argv) < 0){
597
 
        perror("execv");
598
 
        _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
        }
599
790
      }
600
791
      /* no return */
601
792
    }
610
801
        perror("sigprocmask");
611
802
      }
612
803
      exitstatus = EXIT_FAILURE;
613
 
      goto end;
 
804
      goto fallback;
614
805
    }
615
806
    
616
807
    *new_process = (struct process){ .pid = pid,
624
815
    if(ret < 0){
625
816
      perror("sigprocmask");
626
817
      exitstatus = EXIT_FAILURE;
627
 
      goto end;
 
818
      goto fallback;
628
819
    }
629
820
    
630
821
    FD_SET(new_process->fd, &rfds_all);
634
825
    }
635
826
    
636
827
  }
637
 
  
638
 
  /* Free the plugin list */
639
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
640
 
    next = plugin_list->next;
641
 
    free(plugin_list->argv);
642
 
    free(plugin_list);
643
 
  }
 
828
 
 
829
  free_plugin_list(plugin_list);
 
830
  plugin_list = NULL;
644
831
  
645
832
  closedir(dir);
646
833
  dir = NULL;
656
843
    if (select_ret == -1){
657
844
      perror("select");
658
845
      exitstatus = EXIT_FAILURE;
659
 
      goto end;
 
846
      goto fallback;
660
847
    }
661
848
    /* OK, now either a process completed, or something can be read
662
849
       from one of them */
684
871
          /* Remove the plugin */
685
872
          FD_CLR(proc->fd, &rfds_all);
686
873
          /* Block signal while modifying process_list */
687
 
          ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
 
874
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
688
875
          if(ret < 0){
689
876
            perror("sigprocmask");
690
877
            exitstatus = EXIT_FAILURE;
691
 
            goto end;
 
878
            goto fallback;
692
879
          }
693
880
          /* Delete this process entry from the list */
694
881
          if(process_list == proc){
718
905
        }
719
906
        /* This process exited nicely, so print its buffer */
720
907
 
721
 
        bool bret = print_out_password(proc->buffer, proc->buffer_length);
 
908
        bool bret = print_out_password(proc->buffer,
 
909
                                       proc->buffer_length);
722
910
        if(not bret){
723
911
          perror("print_out_password");
724
912
          exitstatus = EXIT_FAILURE;
725
913
        }
726
 
        goto end;
 
914
        goto fallback;
727
915
      }
728
916
      /* This process has not completed.  Does it have any output? */
729
917
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
737
925
        if (proc->buffer == NULL){
738
926
          perror("malloc");
739
927
          exitstatus = EXIT_FAILURE;
740
 
          goto end;
 
928
          goto fallback;
741
929
        }
742
930
        proc->buffer_size += BUFFER_SIZE;
743
931
      }
758
946
  }
759
947
 
760
948
 
761
 
 end:
 
949
 fallback:
762
950
  
763
951
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
764
 
    /* 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 */
765
954
    bool bret;
766
955
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
767
956
    char *passwordbuffer = getpass("Password: ");
769
958
    if(not bret){
770
959
      perror("print_out_password");
771
960
      exitstatus = EXIT_FAILURE;
772
 
      goto end;
773
961
    }
774
962
  }
775
963
  
776
964
  /* Restore old signal handler */
777
 
  sigaction(SIGCHLD, &old_sigchld_action, NULL);
778
 
  
779
 
  free(custom_argv);
780
 
  
781
 
  /* Free the plugin list */
782
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
783
 
    next = plugin_list->next;
784
 
    free(plugin_list->argv);
785
 
    free(plugin_list);
786
 
  }
 
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);
787
978
  
788
979
  if(dir != NULL){
789
980
    closedir(dir);
809
1000
  if(errno != ECHILD){
810
1001
    perror("wait");
811
1002
  }
 
1003
 
 
1004
  free(plugindir);
 
1005
  free(argfile);
812
1006
  
813
1007
  return exitstatus;
814
1008
}