/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-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 ARGFILE "/conf/conf.d/mandos/plugins.conf"
 
68
 
 
69
#define PDIR "/lib/mandos/plugins.d"
 
70
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
69
71
 
70
72
const char *argp_program_version = "plugin-runner 1.0";
71
73
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
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 = "/lib/mandos/plugins.d";
213
 
  const char *argfile = ARGFILE;
 
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(argfile, "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);
381
527
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
382
528
      perror("fopen");
383
529
      exitstatus = EXIT_FAILURE;
384
 
      goto end;
 
530
      goto fallback;
385
531
    }
386
532
  }
387
533
 
388
534
  if(custom_argv != NULL){
389
 
    custom_argv[0] = argv[0];
390
535
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, &plugin_list);
391
536
    if (ret == ARGP_ERR_UNKNOWN){
392
537
      fprintf(stderr, "Unknown error while parsing arguments\n");
393
538
      exitstatus = EXIT_FAILURE;
394
 
      goto end;
 
539
      goto fallback;
395
540
    }
396
541
  }
397
542
  
402
547
      for(char **a = p->argv; *a != NULL; a++){
403
548
        fprintf(stderr, "\tArg: %s\n", *a);
404
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
      }
405
554
    }
406
555
  }
407
556
  
414
563
  if (ret == -1){
415
564
    perror("setgid");
416
565
  }
 
566
 
 
567
  if (plugindir == NULL){
 
568
    dir = opendir(PDIR);
 
569
  } else {
 
570
    dir = opendir(plugindir);
 
571
  }
417
572
  
418
 
  dir = opendir(plugindir);
419
573
  if(dir == NULL){
420
574
    perror("Could not open plugin dir");
421
575
    exitstatus = EXIT_FAILURE;
422
 
    goto end;
 
576
    goto fallback;
423
577
  }
424
578
  
425
579
  /* Set the FD_CLOEXEC flag on the directory, if possible */
430
584
      if(ret < 0){
431
585
        perror("set_cloexec_flag");
432
586
        exitstatus = EXIT_FAILURE;
433
 
        goto end;
 
587
        goto fallback;
434
588
      }
435
589
    }
436
590
  }
445
599
      if (errno == EBADF){
446
600
        perror("readdir");
447
601
        exitstatus = EXIT_FAILURE;
448
 
        goto end;
 
602
        goto fallback;
449
603
      }
450
604
      break;
451
605
    }
496
650
        continue;
497
651
      }
498
652
    }
499
 
    
500
 
    char *filename = malloc(d_name_len + strlen(plugindir) + 2);
501
 
    if (filename == NULL){
502
 
      perror("malloc");
 
653
 
 
654
    char *filename;
 
655
    ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
 
656
    if(ret < 0){
 
657
      perror("asprintf");
503
658
      continue;
504
659
    }
505
 
    strcpy(filename, plugindir); /* Spurious warning */
506
 
    strcat(filename, "/");      /* Spurious warning */
507
 
    strcat(filename, dirst->d_name); /* Spurious warning */
508
660
    
509
661
    ret = stat(filename, &st);
510
662
    if (ret == -1){
521
673
      free(filename);
522
674
      continue;
523
675
    }
524
 
    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){
525
683
      if(debug){
526
684
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
527
685
                dirst->d_name);
529
687
      free(filename);
530
688
      continue;
531
689
    }
532
 
    plugin *p = getplugin(dirst->d_name, &plugin_list);
533
690
    {
534
691
      /* Add global arguments to argument list for this plugin */
535
692
      plugin *g = getplugin(NULL, &plugin_list);
536
 
      for(char **a = g->argv + 1; *a != NULL; a++){
537
 
        addargument(p, *a);
538
 
      }
539
 
    }
540
 
    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];
541
724
    ret = pipe(pipefd);
542
725
    if (ret == -1){
543
726
      perror("pipe");
544
727
      exitstatus = EXIT_FAILURE;
545
 
      goto end;
 
728
      goto fallback;
546
729
    }
547
730
    ret = set_cloexec_flag(pipefd[0]);
548
731
    if(ret < 0){
549
732
      perror("set_cloexec_flag");
550
733
      exitstatus = EXIT_FAILURE;
551
 
      goto end;
 
734
      goto fallback;
552
735
    }
553
736
    ret = set_cloexec_flag(pipefd[1]);
554
737
    if(ret < 0){
555
738
      perror("set_cloexec_flag");
556
739
      exitstatus = EXIT_FAILURE;
557
 
      goto end;
 
740
      goto fallback;
558
741
    }
559
742
    /* Block SIGCHLD until process is safely in process list */
560
743
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
561
744
    if(ret < 0){
562
745
      perror("sigprocmask");
563
746
      exitstatus = EXIT_FAILURE;
564
 
      goto end;
 
747
      goto fallback;
565
748
    }
566
749
    // Starting a new process to be watched
567
750
    pid_t pid = fork();
568
751
    if(pid == -1){
569
752
      perror("fork");
570
753
      exitstatus = EXIT_FAILURE;
571
 
      goto end;
 
754
      goto fallback;
572
755
    }
573
756
    if(pid == 0){
574
757
      /* this is the child process */
594
777
           above and must now close it manually here. */
595
778
        closedir(dir);
596
779
      }
597
 
      if(execv(filename, p->argv) < 0){
598
 
        perror("execv");
599
 
        _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
        }
600
790
      }
601
791
      /* no return */
602
792
    }
611
801
        perror("sigprocmask");
612
802
      }
613
803
      exitstatus = EXIT_FAILURE;
614
 
      goto end;
 
804
      goto fallback;
615
805
    }
616
806
    
617
807
    *new_process = (struct process){ .pid = pid,
625
815
    if(ret < 0){
626
816
      perror("sigprocmask");
627
817
      exitstatus = EXIT_FAILURE;
628
 
      goto end;
 
818
      goto fallback;
629
819
    }
630
820
    
631
821
    FD_SET(new_process->fd, &rfds_all);
635
825
    }
636
826
    
637
827
  }
638
 
  
639
 
  /* Free the plugin list */
640
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
641
 
    next = plugin_list->next;
642
 
    free(plugin_list->argv);
643
 
    free(plugin_list);
644
 
  }
 
828
 
 
829
  free_plugin_list(plugin_list);
 
830
  plugin_list = NULL;
645
831
  
646
832
  closedir(dir);
647
833
  dir = NULL;
657
843
    if (select_ret == -1){
658
844
      perror("select");
659
845
      exitstatus = EXIT_FAILURE;
660
 
      goto end;
 
846
      goto fallback;
661
847
    }
662
848
    /* OK, now either a process completed, or something can be read
663
849
       from one of them */
685
871
          /* Remove the plugin */
686
872
          FD_CLR(proc->fd, &rfds_all);
687
873
          /* Block signal while modifying process_list */
688
 
          ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
 
874
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
689
875
          if(ret < 0){
690
876
            perror("sigprocmask");
691
877
            exitstatus = EXIT_FAILURE;
692
 
            goto end;
 
878
            goto fallback;
693
879
          }
694
880
          /* Delete this process entry from the list */
695
881
          if(process_list == proc){
719
905
        }
720
906
        /* This process exited nicely, so print its buffer */
721
907
 
722
 
        bool bret = print_out_password(proc->buffer, proc->buffer_length);
 
908
        bool bret = print_out_password(proc->buffer,
 
909
                                       proc->buffer_length);
723
910
        if(not bret){
724
911
          perror("print_out_password");
725
912
          exitstatus = EXIT_FAILURE;
726
913
        }
727
 
        goto end;
 
914
        goto fallback;
728
915
      }
729
916
      /* This process has not completed.  Does it have any output? */
730
917
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
738
925
        if (proc->buffer == NULL){
739
926
          perror("malloc");
740
927
          exitstatus = EXIT_FAILURE;
741
 
          goto end;
 
928
          goto fallback;
742
929
        }
743
930
        proc->buffer_size += BUFFER_SIZE;
744
931
      }
759
946
  }
760
947
 
761
948
 
762
 
 end:
 
949
 fallback:
763
950
  
764
951
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
765
 
    /* 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 */
766
954
    bool bret;
767
955
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
768
956
    char *passwordbuffer = getpass("Password: ");
770
958
    if(not bret){
771
959
      perror("print_out_password");
772
960
      exitstatus = EXIT_FAILURE;
773
 
      goto end;
774
961
    }
775
962
  }
776
963
  
777
964
  /* Restore old signal handler */
778
 
  sigaction(SIGCHLD, &old_sigchld_action, NULL);
779
 
  
780
 
  free(custom_argv);
781
 
  
782
 
  /* Free the plugin list */
783
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
784
 
    next = plugin_list->next;
785
 
    free(plugin_list->argv);
786
 
    free(plugin_list);
787
 
  }
 
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);
788
978
  
789
979
  if(dir != NULL){
790
980
    closedir(dir);
810
1000
  if(errno != ECHILD){
811
1001
    perror("wait");
812
1002
  }
 
1003
 
 
1004
  free(plugindir);
 
1005
  free(argfile);
813
1006
  
814
1007
  return exitstatus;
815
1008
}