/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>             /* strtok, 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
 
const char *argp_program_version = "mandos-client 1.0";
 
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";
70
73
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
71
74
 
72
75
struct process;
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){
174
 
  size_t ret;
175
 
  for(size_t written = 0; written < length; written += ret){
 
238
  ssize_t ret;
 
239
  if(length>0 and buffer[length-1] == '\n'){
 
240
    length--;
 
241
  }
 
242
  for(size_t written = 0; written < length; written += (size_t)ret){
176
243
    ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
177
244
                                   length - written));
178
245
    if(ret < 0){
182
249
  return true;
183
250
}
184
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
 
185
267
int main(int argc, char *argv[]){
186
 
  const char *plugindir = "/conf/conf.d/mandos/plugins.d";
 
268
  char *plugindir = NULL;
 
269
  char *argfile = NULL;
 
270
  FILE *conffp;
187
271
  size_t d_name_len;
188
272
  DIR *dir = NULL;
189
273
  struct dirent *dirst;
197
281
  struct sigaction old_sigchld_action;
198
282
  struct sigaction sigchld_action = { .sa_handler = handle_sigchld,
199
283
                                      .sa_flags = SA_NOCLDSTOP };
200
 
  char *plus_options = NULL;
201
 
  char **plus_argv = NULL;
 
284
  char **custom_argv = NULL;
 
285
  int custom_argc = 0;
202
286
  
203
287
  /* Establish a signal handler */
204
288
  sigemptyset(&sigchld_action.sa_mask);
205
289
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
206
 
  if(ret < 0){
 
290
  if(ret == -1){
207
291
    perror("sigaddset");
208
 
    exit(EXIT_FAILURE);
 
292
    exitstatus = EXIT_FAILURE;
 
293
    goto fallback;
209
294
  }
210
295
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
211
 
  if(ret < 0){
 
296
  if(ret == -1){
212
297
    perror("sigaction");
213
 
    exit(EXIT_FAILURE);
 
298
    exitstatus = EXIT_FAILURE;
 
299
    goto fallback;
214
300
  }
215
301
  
216
302
  /* The options we understand. */
218
304
    { .name = "global-options", .key = 'g',
219
305
      .arg = "OPTION[,OPTION[,...]]",
220
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" },
221
310
    { .name = "options-for", .key = 'o',
222
311
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
223
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" },
224
316
    { .name = "disable", .key = 'd',
225
317
      .arg = "PLUGIN",
226
318
      .doc = "Disable a specific plugin", .group = 1 },
227
319
    { .name = "plugin-dir", .key = 128,
228
320
      .arg = "DIRECTORY",
229
321
      .doc = "Specify a different plugin directory", .group = 2 },
230
 
    { .name = "userid", .key = 129,
231
 
      .arg = "ID", .flags = 0,
232
 
      .doc = "User ID the plugins will run as", .group = 2 },
233
 
    { .name = "groupid", .key = 130,
234
 
      .arg = "ID", .flags = 0,
235
 
      .doc = "Group ID the plugins will run as", .group = 2 },
236
 
    { .name = "debug", .key = 131,
237
 
      .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 },
238
333
    { .name = NULL }
239
334
  };
240
335
  
245
340
    switch (key) {
246
341
    case 'g':
247
342
      if (arg != NULL){
248
 
        char *p = strtok(arg, ",");
249
 
        do{
250
 
          addargument(getplugin(NULL, plugins), p);
251
 
          p = strtok(NULL, ",");
252
 
        } while (p != NULL);
 
343
        char *p;
 
344
        while((p = strsep(&arg, ",")) != NULL){
 
345
          if(p[0] == '\0'){
 
346
            continue;
 
347
          }
 
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");
 
366
        }
253
367
      }
254
368
      break;
255
369
    case 'o':
256
370
      if (arg != NULL){
257
 
        char *name = strtok(arg, ":");
258
 
        char *p = strtok(NULL, ":");
259
 
        if(p != NULL){
260
 
          p = strtok(p, ",");
261
 
          do{
262
 
            addargument(getplugin(name, plugins), p);
263
 
            p = strtok(NULL, ",");
264
 
          } while (p != NULL);
 
371
        char *p_name = strsep(&arg, ":");
 
372
        if(p_name[0] == '\0'){
 
373
          break;
 
374
        }
 
375
        char *opt = strsep(&arg, ":");
 
376
        if(opt[0] == '\0'){
 
377
          break;
 
378
        }
 
379
        if(opt != NULL){
 
380
          char *p;
 
381
          while((p = strsep(&opt, ",")) != NULL){
 
382
            if(p[0] == '\0'){
 
383
              continue;
 
384
            }
 
385
            if(not add_argument(getplugin(p_name, plugins), p)){
 
386
              perror("add_argument");
 
387
              return ARGP_ERR_UNKNOWN;
 
388
            }
 
389
          }
 
390
        }
 
391
      }
 
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");
265
409
        }
266
410
      }
267
411
      break;
268
412
    case 'd':
269
413
      if (arg != NULL){
270
 
        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;
271
419
      }
272
420
      break;
273
421
    case 128:
274
 
      plugindir = arg;
 
422
      plugindir = strdup(arg);
 
423
      if(plugindir == NULL){
 
424
        perror("strdup");
 
425
      }      
275
426
      break;
276
427
    case 129:
 
428
      argfile = strdup(arg);
 
429
      if(argfile == NULL){
 
430
        perror("strdup");
 
431
      }
 
432
      break;      
 
433
    case 130:
277
434
      uid = (uid_t)strtol(arg, NULL, 10);
278
435
      break;
279
 
    case 130:
 
436
    case 131:
280
437
      gid = (gid_t)strtol(arg, NULL, 10);
281
438
      break;
282
 
    case 131:
 
439
    case 132:
283
440
      debug = true;
284
441
      break;
285
442
    case ARGP_KEY_ARG:
286
 
      if(plus_options != NULL or arg == NULL or arg[0] != '+'){
287
 
        argp_usage (state);
288
 
      }
289
 
      plus_options = arg;
 
443
      fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
290
444
      break;
291
445
    case ARGP_KEY_END:
292
446
      break;
304
458
  
305
459
  ret = argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
306
460
  if (ret == ARGP_ERR_UNKNOWN){
307
 
    fprintf(stderr, "Unkown error while parsing arguments\n");
 
461
    fprintf(stderr, "Unknown error while parsing arguments\n");
308
462
    exitstatus = EXIT_FAILURE;
309
 
    goto end;
 
463
    goto fallback;
 
464
  }
 
465
 
 
466
  if (argfile == NULL){
 
467
    conffp = fopen(AFILE, "r");
 
468
  } else {
 
469
    conffp = fopen(argfile, "r");
310
470
  }
311
471
  
312
 
  if(plus_options){
313
 
    /* This is a mangled argument in the form of
314
 
     "+--option+--other-option=parameter+--yet-another-option", etc */
315
 
    /* Make new argc and argv vars, and call argp_parse() again. */
316
 
    plus_options++;             /* skip the first '+' character */
317
 
    const char delims[] = "+";
318
 
    char *arg;
319
 
    int new_argc = 1;
320
 
    plus_argv = malloc(sizeof(char*) * 2);
321
 
    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){
322
483
      perror("malloc");
323
484
      exitstatus = EXIT_FAILURE;
324
 
      goto end;
325
 
    }
326
 
    plus_argv[0] = argv[0];
327
 
    plus_argv[1] = NULL;
328
 
    arg = strtok(plus_options, delims); /* Get first argument */
329
 
    while(arg != NULL){
330
 
      new_argc++;
331
 
      plus_argv = realloc(plus_argv, sizeof(char *)
332
 
                         * ((unsigned int) new_argc + 1));
333
 
      if(plus_argv == NULL){
334
 
        perror("realloc");
335
 
        exitstatus = EXIT_FAILURE;
336
 
        goto end;
337
 
      }
338
 
      plus_argv[new_argc-1] = arg;
339
 
      plus_argv[new_argc] = NULL;
340
 
      arg = strtok(NULL, delims); /* Get next argument */
341
 
    }
342
 
    ret = argp_parse (&argp, new_argc, plus_argv, 0, 0, &plugin_list);
 
485
      goto fallback;
 
486
    }
 
487
    custom_argv[0] = argv[0];
 
488
    custom_argv[1] = NULL;
 
489
    
 
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);
343
536
    if (ret == ARGP_ERR_UNKNOWN){
344
 
      fprintf(stderr, "Unkown error while parsing arguments\n");
 
537
      fprintf(stderr, "Unknown error while parsing arguments\n");
345
538
      exitstatus = EXIT_FAILURE;
346
 
      goto end;
 
539
      goto fallback;
347
540
    }
348
541
  }
349
542
  
354
547
      for(char **a = p->argv; *a != NULL; a++){
355
548
        fprintf(stderr, "\tArg: %s\n", *a);
356
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
      }
357
554
    }
358
555
  }
359
556
  
366
563
  if (ret == -1){
367
564
    perror("setgid");
368
565
  }
 
566
 
 
567
  if (plugindir == NULL){
 
568
    dir = opendir(PDIR);
 
569
  } else {
 
570
    dir = opendir(plugindir);
 
571
  }
369
572
  
370
 
  dir = opendir(plugindir);
371
573
  if(dir == NULL){
372
574
    perror("Could not open plugin dir");
373
575
    exitstatus = EXIT_FAILURE;
374
 
    goto end;
 
576
    goto fallback;
375
577
  }
376
578
  
377
579
  /* Set the FD_CLOEXEC flag on the directory, if possible */
382
584
      if(ret < 0){
383
585
        perror("set_cloexec_flag");
384
586
        exitstatus = EXIT_FAILURE;
385
 
        goto end;
 
587
        goto fallback;
386
588
      }
387
589
    }
388
590
  }
397
599
      if (errno == EBADF){
398
600
        perror("readdir");
399
601
        exitstatus = EXIT_FAILURE;
400
 
        goto end;
 
602
        goto fallback;
401
603
      }
402
604
      break;
403
605
    }
448
650
        continue;
449
651
      }
450
652
    }
451
 
    
452
 
    char *filename = malloc(d_name_len + strlen(plugindir) + 2);
453
 
    if (filename == NULL){
454
 
      perror("malloc");
455
 
      exitstatus = EXIT_FAILURE;
456
 
      goto end;
 
653
 
 
654
    char *filename;
 
655
    ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
 
656
    if(ret < 0){
 
657
      perror("asprintf");
 
658
      continue;
457
659
    }
458
 
    strcpy(filename, plugindir); /* Spurious warning */
459
 
    strcat(filename, "/");      /* Spurious warning */
460
 
    strcat(filename, dirst->d_name); /* Spurious warning */
461
660
    
462
661
    ret = stat(filename, &st);
463
662
    if (ret == -1){
464
663
      perror("stat");
465
 
      exitstatus = EXIT_FAILURE;
466
 
      goto end;
 
664
      free(filename);
 
665
      continue;
467
666
    }
468
667
    
469
668
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
474
673
      free(filename);
475
674
      continue;
476
675
    }
477
 
    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){
478
683
      if(debug){
479
684
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
480
685
                dirst->d_name);
482
687
      free(filename);
483
688
      continue;
484
689
    }
485
 
    plugin *p = getplugin(dirst->d_name, &plugin_list);
486
690
    {
487
691
      /* Add global arguments to argument list for this plugin */
488
692
      plugin *g = getplugin(NULL, &plugin_list);
489
 
      for(char **a = g->argv + 1; *a != NULL; a++){
490
 
        addargument(p, *a);
491
 
      }
492
 
    }
493
 
    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];
494
724
    ret = pipe(pipefd);
495
725
    if (ret == -1){
496
726
      perror("pipe");
497
727
      exitstatus = EXIT_FAILURE;
498
 
      goto end;
 
728
      goto fallback;
499
729
    }
500
730
    ret = set_cloexec_flag(pipefd[0]);
501
731
    if(ret < 0){
502
732
      perror("set_cloexec_flag");
503
733
      exitstatus = EXIT_FAILURE;
504
 
      goto end;
 
734
      goto fallback;
505
735
    }
506
736
    ret = set_cloexec_flag(pipefd[1]);
507
737
    if(ret < 0){
508
738
      perror("set_cloexec_flag");
509
739
      exitstatus = EXIT_FAILURE;
510
 
      goto end;
 
740
      goto fallback;
511
741
    }
512
742
    /* Block SIGCHLD until process is safely in process list */
513
743
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
514
744
    if(ret < 0){
515
745
      perror("sigprocmask");
516
746
      exitstatus = EXIT_FAILURE;
517
 
      goto end;
 
747
      goto fallback;
518
748
    }
519
749
    // Starting a new process to be watched
520
750
    pid_t pid = fork();
 
751
    if(pid == -1){
 
752
      perror("fork");
 
753
      exitstatus = EXIT_FAILURE;
 
754
      goto fallback;
 
755
    }
521
756
    if(pid == 0){
522
757
      /* this is the child process */
523
758
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
542
777
           above and must now close it manually here. */
543
778
        closedir(dir);
544
779
      }
545
 
      if(execv(filename, p->argv) < 0){
546
 
        perror("execv");
547
 
        _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
        }
548
790
      }
549
791
      /* no return */
550
792
    }
559
801
        perror("sigprocmask");
560
802
      }
561
803
      exitstatus = EXIT_FAILURE;
562
 
      goto end;
 
804
      goto fallback;
563
805
    }
564
806
    
565
807
    *new_process = (struct process){ .pid = pid,
573
815
    if(ret < 0){
574
816
      perror("sigprocmask");
575
817
      exitstatus = EXIT_FAILURE;
576
 
      goto end;
 
818
      goto fallback;
577
819
    }
578
820
    
579
821
    FD_SET(new_process->fd, &rfds_all);
583
825
    }
584
826
    
585
827
  }
586
 
  
587
 
  /* Free the plugin list */
588
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
589
 
    next = plugin_list->next;
590
 
    free(plugin_list->argv);
591
 
    free(plugin_list);
592
 
  }
 
828
 
 
829
  free_plugin_list(plugin_list);
 
830
  plugin_list = NULL;
593
831
  
594
832
  closedir(dir);
595
833
  dir = NULL;
605
843
    if (select_ret == -1){
606
844
      perror("select");
607
845
      exitstatus = EXIT_FAILURE;
608
 
      goto end;
 
846
      goto fallback;
609
847
    }
610
848
    /* OK, now either a process completed, or something can be read
611
849
       from one of them */
633
871
          /* Remove the plugin */
634
872
          FD_CLR(proc->fd, &rfds_all);
635
873
          /* Block signal while modifying process_list */
636
 
          ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
 
874
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
637
875
          if(ret < 0){
638
876
            perror("sigprocmask");
639
877
            exitstatus = EXIT_FAILURE;
640
 
            goto end;
 
878
            goto fallback;
641
879
          }
642
880
          /* Delete this process entry from the list */
643
881
          if(process_list == proc){
667
905
        }
668
906
        /* This process exited nicely, so print its buffer */
669
907
 
670
 
        bool bret = print_out_password(proc->buffer, proc->buffer_length);
 
908
        bool bret = print_out_password(proc->buffer,
 
909
                                       proc->buffer_length);
671
910
        if(not bret){
672
911
          perror("print_out_password");
673
912
          exitstatus = EXIT_FAILURE;
674
913
        }
675
 
        goto end;
 
914
        goto fallback;
676
915
      }
677
916
      /* This process has not completed.  Does it have any output? */
678
917
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
686
925
        if (proc->buffer == NULL){
687
926
          perror("malloc");
688
927
          exitstatus = EXIT_FAILURE;
689
 
          goto end;
 
928
          goto fallback;
690
929
        }
691
930
        proc->buffer_size += BUFFER_SIZE;
692
931
      }
705
944
      }
706
945
    }
707
946
  }
 
947
 
 
948
 
 
949
 fallback:
708
950
  
709
 
  if(process_list == NULL){
 
951
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
 
952
    /* Fallback if all plugins failed, none are found or an error
 
953
       occured */
710
954
    bool bret;
711
955
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
712
956
    char *passwordbuffer = getpass("Password: ");
714
958
    if(not bret){
715
959
      perror("print_out_password");
716
960
      exitstatus = EXIT_FAILURE;
717
 
      goto end;
718
 
    }
719
 
    bret = print_out_password("\n", 1);
720
 
    if(not bret){
721
 
      perror("print_out_password");
722
 
      exitstatus = EXIT_FAILURE;
723
961
    }
724
962
  }
725
 
 
726
 
 end:
727
963
  
728
964
  /* Restore old signal handler */
729
 
  sigaction(SIGCHLD, &old_sigchld_action, NULL);
730
 
  
731
 
  free(plus_argv);
732
 
  
733
 
  /* Free the plugin list */
734
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
735
 
    next = plugin_list->next;
736
 
    free(plugin_list->argv);
737
 
    free(plugin_list);
738
 
  }
 
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);
739
978
  
740
979
  if(dir != NULL){
741
980
    closedir(dir);
761
1000
  if(errno != ECHILD){
762
1001
    perror("wait");
763
1002
  }
 
1003
 
 
1004
  free(plugindir);
 
1005
  free(argfile);
764
1006
  
765
1007
  return exitstatus;
766
1008
}