/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-16 03:29:08 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080816032908-ihw7c05r2mnyk389
Add feature to specify custom environment variables for plugins.

* plugin-runner.c (plugin): New members "environ" and "envc" to
                            contain possible custom environment.
  (getplugin): Return NULL on failure instead of doing exit(); all
               callers changed.
  (add_to_char_array): New helper function for "add_argument" and
                       "add_environment".
  (addargument): Renamed to "add_argument".  Return bool.  Call
                 "add_to_char_array" to actually do things.
  (add_environment): New; analogous to "add_argument".
  (addcustomargument): Renamed to "add_to_argv" to avoid confusion
                       with "add_argument".
  (main): New options "--global-envs" and "--envs-for" to specify
          custom environment for plugins.  Print environment for
          plugins in debug mode.  Use asprintf instead of strcpy and
          strcat.  Use execve() for plugins with custom environments.
          Free environment for plugin when freeing plugin list.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 * Contact the authors at <mandos@fukt.bsnet.se>.
22
22
 */
23
23
 
24
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY() */
25
 
 
26
 
#include <stdio.h>              /* popen(), fileno(), fprintf(),
27
 
                                   stderr, STDOUT_FILENO */
 
24
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
 
25
                                   asprintf() */
 
26
#include <stddef.h>             /* size_t, NULL */
 
27
#include <stdlib.h>             /* malloc(), exit(), EXIT_FAILURE,
 
28
                                   EXIT_SUCCESS, realloc() */
 
29
#include <stdbool.h>            /* bool, true, false */
 
30
#include <stdio.h>              /* perror, popen(), fileno(),
 
31
                                   fprintf(), stderr, STDOUT_FILENO */
 
32
#include <sys/types.h>          /* DIR, opendir(), stat(), struct
 
33
                                   stat, waitpid(), WIFEXITED(),
 
34
                                   WEXITSTATUS(), wait(), pid_t,
 
35
                                   uid_t, gid_t, getuid(), getgid(),
 
36
                                   dirfd() */
 
37
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
 
38
                                   FD_SET(), FD_ISSET(), FD_CLR */
 
39
#include <sys/wait.h>           /* wait(), waitpid(), WIFEXITED(),
 
40
                                   WEXITSTATUS() */
 
41
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
28
42
#include <iso646.h>             /* and, or, not */
29
 
#include <sys/types.h>          /* DIR, opendir(), stat(),
30
 
                                   struct stat, waitpid(),
31
 
                                   WIFEXITED(), WEXITSTATUS(),
32
 
                                   wait() */
33
 
#include <sys/wait.h>           /* wait() */
34
43
#include <dirent.h>             /* DIR, struct dirent, opendir(),
35
 
                                   readdir(), closedir() */
36
 
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
 
44
                                   readdir(), closedir(), dirfd() */
37
45
#include <unistd.h>             /* struct stat, stat(), S_ISREG(),
38
 
                                   fcntl() */
39
 
#include <fcntl.h>              /* fcntl() */
40
 
#include <stddef.h>             /* NULL */
41
 
#include <stdlib.h>             /* EXIT_FAILURE */
42
 
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
43
 
                                   FD_SET(), FD_ISSET() */
44
 
#include <string.h>             /* strlen(), strcpy(), strcat() */
45
 
#include <stdbool.h>            /* true */
46
 
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
47
 
                                   WEXITSTATUS() */
 
46
                                   fcntl(), setuid(), setgid(),
 
47
                                   F_GETFD, F_SETFD, FD_CLOEXEC,
 
48
                                   access(), pipe(), fork(), close()
 
49
                                   dup2, STDOUT_FILENO, _exit(),
 
50
                                   execv(), write(), read(),
 
51
                                   close() */
 
52
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
 
53
                                   FD_CLOEXEC */
 
54
#include <string.h>             /* strsep, strlen(), asprintf() */
48
55
#include <errno.h>              /* errno */
49
 
#include <argp.h>               /* struct argp_option,
50
 
                                   struct argp_state, struct argp,
51
 
                                   argp_parse() */
 
56
#include <argp.h>               /* struct argp_option, struct
 
57
                                   argp_state, struct argp,
 
58
                                   argp_parse(), ARGP_ERR_UNKNOWN,
 
59
                                   ARGP_KEY_END, ARGP_KEY_ARG, error_t */
 
60
#include <signal.h>             /* struct sigaction, sigemptyset(),
 
61
                                   sigaddset(), sigaction(),
 
62
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
 
63
                                   SIG_UNBLOCK, kill() */
 
64
#include <errno.h>              /* errno, EBADF */
52
65
 
53
66
#define BUFFER_SIZE 256
 
67
#define ARGFILE "/conf/conf.d/mandos/plugin-runner.conf"
 
68
 
 
69
const char *argp_program_version = "plugin-runner 1.0";
 
70
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
54
71
 
55
72
struct process;
56
73
 
70
87
  char *name;                   /* can be NULL or any plugin name */
71
88
  char **argv;
72
89
  int argc;
 
90
  char **environ;
 
91
  int envc;
73
92
  bool disabled;
74
93
  struct plugin *next;
75
94
} plugin;
84
103
  /* Create a new plugin */
85
104
  plugin *new_plugin = malloc(sizeof(plugin));
86
105
  if (new_plugin == NULL){
87
 
    perror("malloc");
88
 
    exit(EXIT_FAILURE);
 
106
    return NULL;
89
107
  }
90
 
  new_plugin->name = name;
 
108
  *new_plugin = (plugin) { .name = name,
 
109
                           .argc = 1,
 
110
                           .envc = 0,
 
111
                           .disabled = false,
 
112
                           .next = *plugin_list };
 
113
  
91
114
  new_plugin->argv = malloc(sizeof(char *) * 2);
92
115
  if (new_plugin->argv == NULL){
93
 
    perror("malloc");
94
 
    exit(EXIT_FAILURE);
 
116
    free(new_plugin);
 
117
    return NULL;
95
118
  }
96
119
  new_plugin->argv[0] = name;
97
120
  new_plugin->argv[1] = NULL;
98
 
  new_plugin->argc = 1;
99
 
  new_plugin->disabled = false;
100
 
  new_plugin->next = *plugin_list;
 
121
 
 
122
  new_plugin->environ = malloc(sizeof(char *));
 
123
  if(new_plugin->environ == NULL){
 
124
    free(new_plugin->argv);
 
125
    free(new_plugin);
 
126
    return NULL;
 
127
  }
 
128
  new_plugin->environ[0] = NULL;
101
129
  /* Append the new plugin to the list */
102
130
  *plugin_list = new_plugin;
103
131
  return new_plugin;
104
132
}
105
133
 
106
 
static void addargument(plugin *p, char *arg){
107
 
  p->argv[p->argc] = arg;
108
 
  p->argv = realloc(p->argv, sizeof(char *) * (size_t)(p->argc + 2));
109
 
  if (p->argv == NULL){
110
 
    perror("malloc");
111
 
    exit(EXIT_FAILURE);
112
 
  }
113
 
  p->argc++;
114
 
  p->argv[p->argc] = NULL;
115
 
}
 
134
/* Helper function for add_argument and add_environment */
 
135
static bool add_to_char_array(const char *new, char ***array,
 
136
                              int *len){
 
137
  /* Resize the pointed-to array to hold one more pointer */
 
138
  *array = realloc(*array, sizeof(char *)
 
139
                   * (size_t) ((*len) + 2));
 
140
  /* Malloc check */
 
141
  if(*array == NULL){
 
142
    return false;
 
143
  }
 
144
  /* Make a copy of the new string */
 
145
  char *copy = strdup(new);
 
146
  if(copy == NULL){
 
147
    return false;
 
148
  }
 
149
  /* Insert the copy */
 
150
  (*array)[*len] = copy;
 
151
  (*len)++;
 
152
  /* Add a new terminating NULL pointer to the last element */
 
153
  (*array)[*len] = NULL;
 
154
  return true;
 
155
}
 
156
 
 
157
/* Add to a plugin's argument vector */
 
158
static bool add_argument(plugin *p, const char *arg){
 
159
  if(p == NULL){
 
160
    return false;
 
161
  }
 
162
  return add_to_char_array(arg, &(p->argv), &(p->argc));
 
163
}
 
164
 
 
165
/* Add to a plugin's environment */
 
166
static bool add_environment(plugin *p, const char *def){
 
167
  if(p == NULL){
 
168
    return false;
 
169
  }
 
170
  return add_to_char_array(def, &(p->environ), &(p->envc));
 
171
}
 
172
 
116
173
 
117
174
/*
118
175
 * Based on the example in the GNU LibC manual chapter 13.13 "File
130
187
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
131
188
}
132
189
 
133
 
const char *argp_program_version = "plugbasedclient 0.9";
134
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
135
 
 
136
190
process *process_list = NULL;
137
191
 
138
192
/* Mark a process as completed when it exits, and save its exit
141
195
  process *proc = process_list;
142
196
  int status;
143
197
  pid_t pid = wait(&status);
 
198
  if(pid == -1){
 
199
    perror("wait");
 
200
    return;
 
201
  }
144
202
  while(proc != NULL and proc->pid != pid){
145
203
    proc = proc->next;
146
204
  }
152
210
  proc->completed = true;
153
211
}
154
212
 
 
213
bool print_out_password(const char *buffer, size_t length){
 
214
  ssize_t ret;
 
215
  if(length>0 and buffer[length-1] == '\n'){
 
216
    length--;
 
217
  }
 
218
  for(size_t written = 0; written < length; written += (size_t)ret){
 
219
    ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
 
220
                                   length - written));
 
221
    if(ret < 0){
 
222
      return false;
 
223
    }
 
224
  }
 
225
  return true;
 
226
}
 
227
 
 
228
char **add_to_argv(char **argv, int *argc, char *arg){
 
229
  if (argv == NULL){
 
230
    *argc = 1;
 
231
    argv = malloc(sizeof(char*) * 2);
 
232
    if(argv == NULL){
 
233
      return NULL;
 
234
    }
 
235
    argv[0] = NULL;     /* Will be set to argv[0] in main before parsing */
 
236
    argv[1] = NULL;
 
237
  }
 
238
  *argc += 1;
 
239
  argv = realloc(argv, sizeof(char *)
 
240
                  * ((unsigned int) *argc + 1));
 
241
  if(argv == NULL){
 
242
    return NULL;
 
243
  }
 
244
  argv[*argc-1] = arg;
 
245
  argv[*argc] = NULL;   
 
246
  return argv;
 
247
}
 
248
 
155
249
int main(int argc, char *argv[]){
156
 
  const char *plugindir = "/conf/conf.d/mandos/plugins.d";
 
250
  const char *plugindir = "/lib/mandos/plugins.d";
 
251
  const char *argfile = ARGFILE;
 
252
  FILE *conffp;
157
253
  size_t d_name_len;
158
254
  DIR *dir = NULL;
159
255
  struct dirent *dirst;
167
263
  struct sigaction old_sigchld_action;
168
264
  struct sigaction sigchld_action = { .sa_handler = handle_sigchld,
169
265
                                      .sa_flags = SA_NOCLDSTOP };
170
 
  char *plus_options = NULL;
171
 
  char **plus_argv = NULL;
 
266
  char **custom_argv = NULL;
 
267
  int custom_argc = 0;
172
268
  
173
269
  /* Establish a signal handler */
174
270
  sigemptyset(&sigchld_action.sa_mask);
188
284
    { .name = "global-options", .key = 'g',
189
285
      .arg = "OPTION[,OPTION[,...]]",
190
286
      .doc = "Options passed to all plugins" },
 
287
    { .name = "global-envs", .key = 'e',
 
288
      .arg = "VAR=value",
 
289
      .doc = "Environment variable passed to all plugins" },
191
290
    { .name = "options-for", .key = 'o',
192
291
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
193
292
      .doc = "Options passed only to specified plugin" },
 
293
    { .name = "envs-for", .key = 'f',
 
294
      .arg = "PLUGIN:ENV=value",
 
295
      .doc = "Environment variable passed to specified plugin" },
194
296
    { .name = "disable", .key = 'd',
195
297
      .arg = "PLUGIN",
196
298
      .doc = "Disable a specific plugin", .group = 1 },
215
317
    switch (key) {
216
318
    case 'g':
217
319
      if (arg != NULL){
218
 
        char *p = strtok(arg, ",");
219
 
        do{
220
 
          addargument(getplugin(NULL, plugins), p);
221
 
          p = strtok(NULL, ",");
222
 
        } while (p != NULL);
 
320
        char *p;
 
321
        while((p = strsep(&arg, ",")) != NULL){
 
322
          if(p[0] == '\0'){
 
323
            continue;
 
324
          }
 
325
          if(not add_argument(getplugin(NULL, plugins), p)){
 
326
            perror("add_argument");
 
327
            return ARGP_ERR_UNKNOWN;
 
328
          }
 
329
        }
 
330
      }
 
331
      break;
 
332
    case 'e':
 
333
      if(arg == NULL){
 
334
        break;
 
335
      }
 
336
      {
 
337
        char *envdef = strdup(arg);
 
338
        if(envdef == NULL){
 
339
          break;
 
340
        }
 
341
        if(not add_environment(getplugin(NULL, plugins), envdef)){
 
342
          perror("add_environment");
 
343
        }
223
344
      }
224
345
      break;
225
346
    case 'o':
226
347
      if (arg != NULL){
227
 
        char *name = strtok(arg, ":");
228
 
        char *p = strtok(NULL, ":");
229
 
        if(p != NULL){
230
 
          p = strtok(p, ",");
231
 
          do{
232
 
            addargument(getplugin(name, plugins), p);
233
 
            p = strtok(NULL, ",");
234
 
          } while (p != NULL);
 
348
        char *p_name = strsep(&arg, ":");
 
349
        if(p_name[0] == '\0'){
 
350
          break;
 
351
        }
 
352
        char *opt = strsep(&arg, ":");
 
353
        if(opt[0] == '\0'){
 
354
          break;
 
355
        }
 
356
        if(opt != NULL){
 
357
          char *p;
 
358
          while((p = strsep(&opt, ",")) != NULL){
 
359
            if(p[0] == '\0'){
 
360
              continue;
 
361
            }
 
362
            if(not add_argument(getplugin(p_name, plugins), p)){
 
363
              perror("add_argument");
 
364
              return ARGP_ERR_UNKNOWN;
 
365
            }
 
366
          }
 
367
        }
 
368
      }
 
369
      break;
 
370
    case 'f':
 
371
      if(arg == NULL){
 
372
        break;
 
373
      }
 
374
      {
 
375
        char *envdef = strchr(arg, ':');
 
376
        if(envdef == NULL){
 
377
          break;
 
378
        }
 
379
        char *p_name = strndup(arg, (size_t) (envdef-arg));
 
380
        if(p_name == NULL){
 
381
          break;
 
382
        }
 
383
        envdef++;
 
384
        if(not add_environment(getplugin(p_name, plugins), envdef)){
 
385
          perror("add_environment");
235
386
        }
236
387
      }
237
388
      break;
238
389
    case 'd':
239
390
      if (arg != NULL){
240
 
        getplugin(arg, plugins)->disabled = true;
 
391
        plugin *p = getplugin(arg, plugins);
 
392
        if(p == NULL){
 
393
          return ARGP_ERR_UNKNOWN;
 
394
        }
 
395
        p->disabled = true;
241
396
      }
242
397
      break;
243
398
    case 128:
253
408
      debug = true;
254
409
      break;
255
410
    case ARGP_KEY_ARG:
256
 
      if(plus_options != NULL or arg == NULL or arg[0] != '+'){
257
 
        argp_usage (state);
258
 
      }
259
 
      plus_options = arg;
 
411
      fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
260
412
      break;
261
413
    case ARGP_KEY_END:
262
414
      break;
272
424
                       .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
273
425
                       .doc = "Mandos plugin runner -- Run plugins" };
274
426
  
275
 
  argp_parse (&argp, argc, argv, 0, 0, &plugin_list);  
276
 
  
277
 
  if(plus_options){
278
 
    /* This is a mangled argument in the form of
279
 
     "+--option+--other-option=parameter+--yet-another-option", etc */
280
 
    /* Make new argc and argv vars, and call argp_parse() again. */
281
 
    plus_options++;             /* skip the first '+' character */
282
 
    const char delims[] = "+";
283
 
    char *arg;
284
 
    int new_argc = 1;
285
 
    plus_argv = malloc(sizeof(char*) * 2);
286
 
    if(plus_argv == NULL){
287
 
      perror("malloc");
288
 
      exitstatus = EXIT_FAILURE;
289
 
      goto end;
290
 
    }
291
 
    plus_argv[0] = argv[0];
292
 
    plus_argv[1] = NULL;
293
 
    arg = strtok(plus_options, delims); /* Get first argument */
294
 
    while(arg != NULL){
295
 
      new_argc++;
296
 
      plus_argv = realloc(plus_argv, sizeof(char *)
297
 
                         * ((unsigned int) new_argc + 1));
298
 
      if(plus_argv == NULL){
299
 
        perror("malloc");
300
 
        exitstatus = EXIT_FAILURE;
301
 
        goto end;
302
 
      }
303
 
      plus_argv[new_argc-1] = arg;
304
 
      plus_argv[new_argc] = NULL;
305
 
      arg = strtok(NULL, delims); /* Get next argument */
306
 
    }
307
 
    argp_parse (&argp, new_argc, plus_argv, 0, 0, &plugin_list);  
 
427
  ret = argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
 
428
  if (ret == ARGP_ERR_UNKNOWN){
 
429
    fprintf(stderr, "Unknown error while parsing arguments\n");
 
430
    exitstatus = EXIT_FAILURE;
 
431
    goto end;
 
432
  }
 
433
 
 
434
  conffp = fopen(argfile, "r");
 
435
  if(conffp != NULL){
 
436
    char *org_line = NULL;
 
437
    size_t size = 0;
 
438
    ssize_t sret;
 
439
    char *p, *arg, *new_arg, *line;
 
440
    const char whitespace_delims[] = " \r\t\f\v\n";
 
441
    const char comment_delim[] = "#";
 
442
 
 
443
    while(true){
 
444
      sret = getline(&org_line, &size, conffp);
 
445
      if(sret == -1){
 
446
        break;
 
447
      }
 
448
 
 
449
      line = org_line;
 
450
      arg = strsep(&line, comment_delim);
 
451
      while((p = strsep(&arg, whitespace_delims)) != NULL){
 
452
        if(p[0] == '\0'){
 
453
          continue;
 
454
        }
 
455
        new_arg = strdup(p);
 
456
        custom_argv = add_to_argv(custom_argv, &custom_argc, new_arg);
 
457
        if (custom_argv == NULL){
 
458
          perror("add_to_argv");
 
459
          exitstatus = EXIT_FAILURE;
 
460
          goto end;
 
461
        }
 
462
      }
 
463
    }
 
464
    free(org_line);
 
465
  } else{
 
466
    /* Check for harmful errors and go to fallback. Other errors might
 
467
       not affect opening plugins */
 
468
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
 
469
      perror("fopen");
 
470
      exitstatus = EXIT_FAILURE;
 
471
      goto end;
 
472
    }
 
473
  }
 
474
 
 
475
  if(custom_argv != NULL){
 
476
    custom_argv[0] = argv[0];
 
477
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, &plugin_list);
 
478
    if (ret == ARGP_ERR_UNKNOWN){
 
479
      fprintf(stderr, "Unknown error while parsing arguments\n");
 
480
      exitstatus = EXIT_FAILURE;
 
481
      goto end;
 
482
    }
308
483
  }
309
484
  
310
485
  if(debug){
314
489
      for(char **a = p->argv; *a != NULL; a++){
315
490
        fprintf(stderr, "\tArg: %s\n", *a);
316
491
      }
 
492
      fprintf(stderr, "...and %u environment variables\n", p->envc);
 
493
      for(char **a = p->environ; *a != NULL; a++){
 
494
        fprintf(stderr, "\t%s\n", *a);
 
495
      }
317
496
    }
318
497
  }
319
498
  
324
503
  
325
504
  setgid(gid);
326
505
  if (ret == -1){
327
 
    perror("setuid");
 
506
    perror("setgid");
328
507
  }
329
508
  
330
509
  dir = opendir(plugindir);
354
533
    
355
534
    // All directory entries have been processed
356
535
    if(dirst == NULL){
 
536
      if (errno == EBADF){
 
537
        perror("readdir");
 
538
        exitstatus = EXIT_FAILURE;
 
539
        goto end;
 
540
      }
357
541
      break;
358
542
    }
359
543
    
403
587
        continue;
404
588
      }
405
589
    }
406
 
    
407
 
    char *filename = malloc(d_name_len + strlen(plugindir) + 2);
408
 
    if (filename == NULL){
409
 
      perror("malloc");
410
 
      exitstatus = EXIT_FAILURE;
411
 
      goto end;
412
 
    }
413
 
    strcpy(filename, plugindir); /* Spurious warning */
414
 
    strcat(filename, "/");      /* Spurious warning */
415
 
    strcat(filename, dirst->d_name); /* Spurious warning */
416
 
    
417
 
    stat(filename, &st);
 
590
 
 
591
    char *filename;
 
592
    ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
 
593
    if(ret < 0){
 
594
      perror("asprintf");
 
595
      continue;
 
596
    }
 
597
    
 
598
    ret = stat(filename, &st);
 
599
    if (ret == -1){
 
600
      perror("stat");
 
601
      free(filename);
 
602
      continue;
 
603
    }
418
604
    
419
605
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
420
606
      if(debug){
424
610
      free(filename);
425
611
      continue;
426
612
    }
427
 
    if(getplugin(dirst->d_name, &plugin_list)->disabled){
 
613
    plugin *p = getplugin(dirst->d_name, &plugin_list);
 
614
    if(p == NULL){
 
615
      perror("getplugin");
 
616
      free(filename);
 
617
      continue;
 
618
    }
 
619
    if(p->disabled){
428
620
      if(debug){
429
621
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
430
622
                dirst->d_name);
432
624
      free(filename);
433
625
      continue;
434
626
    }
435
 
    plugin *p = getplugin(dirst->d_name, &plugin_list);
436
627
    {
437
628
      /* Add global arguments to argument list for this plugin */
438
629
      plugin *g = getplugin(NULL, &plugin_list);
439
 
      for(char **a = g->argv + 1; *a != NULL; a++){
440
 
        addargument(p, *a);
441
 
      }
442
 
    }
 
630
      if(g != NULL){
 
631
        for(char **a = g->argv + 1; *a != NULL; a++){
 
632
          if(not add_argument(p, *a)){
 
633
            perror("add_argument");
 
634
          }
 
635
        }
 
636
        /* Add global environment variables */
 
637
        for(char **e = g->environ; *e != NULL; e++){
 
638
          if(not add_environment(p, *e)){
 
639
            perror("add_environment");
 
640
          }
 
641
        }
 
642
      }
 
643
    }
 
644
    /* If this plugin has any environment variables, we will call
 
645
       using execve and need to duplicate the environment from this
 
646
       process, too. */
 
647
    if(p->environ[0] != NULL){
 
648
      for(char **e = environ; *e != NULL; e++){
 
649
        char *copy = strdup(*e);
 
650
        if(copy == NULL){
 
651
          perror("strdup");
 
652
          continue;
 
653
        }
 
654
        if(not add_environment(p, copy)){
 
655
          perror("add_environment");
 
656
        }
 
657
      }
 
658
    }
 
659
    
443
660
    int pipefd[2]; 
444
661
    ret = pipe(pipefd);
445
662
    if (ret == -1){
468
685
    }
469
686
    // Starting a new process to be watched
470
687
    pid_t pid = fork();
 
688
    if(pid == -1){
 
689
      perror("fork");
 
690
      exitstatus = EXIT_FAILURE;
 
691
      goto end;
 
692
    }
471
693
    if(pid == 0){
472
694
      /* this is the child process */
473
695
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
480
702
        perror("sigprocmask");
481
703
        _exit(EXIT_FAILURE);
482
704
      }
483
 
      dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
 
705
 
 
706
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
 
707
      if(ret == -1){
 
708
        perror("dup2");
 
709
        _exit(EXIT_FAILURE);
 
710
      }
484
711
      
485
712
      if(dirfd(dir) < 0){
486
713
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
487
714
           above and must now close it manually here. */
488
715
        closedir(dir);
489
716
      }
490
 
      if(execv(filename, p->argv) < 0){
491
 
        perror("execv");
492
 
        _exit(EXIT_FAILURE);
 
717
      if(p->environ[0] == NULL){
 
718
        if(execv(filename, p->argv) < 0){
 
719
          perror("execv");
 
720
          _exit(EXIT_FAILURE);
 
721
        }
 
722
      } else {
 
723
        if(execve(filename, p->argv, p->environ) < 0){
 
724
          perror("execve");
 
725
          _exit(EXIT_FAILURE);
 
726
        }
493
727
      }
494
728
      /* no return */
495
729
    }
533
767
  for(plugin *next; plugin_list != NULL; plugin_list = next){
534
768
    next = plugin_list->next;
535
769
    free(plugin_list->argv);
 
770
    if(plugin_list->environ[0] != NULL){
 
771
      for(char **e = plugin_list->environ; *e != NULL; e++){
 
772
        free(*e);
 
773
      }
 
774
    }
536
775
    free(plugin_list);
537
776
  }
538
777
  
539
778
  closedir(dir);
540
779
  dir = NULL;
541
 
  
 
780
    
542
781
  if (process_list == NULL){
543
 
    fprintf(stderr, "No plugin processes started, exiting\n");
544
 
    exitstatus = EXIT_FAILURE;
545
 
    goto end;
 
782
    fprintf(stderr, "No plugin processes started. Incorrect plugin"
 
783
            " directory?\n");
 
784
    process_list = NULL;
546
785
  }
547
786
  while(process_list){
548
787
    fd_set rfds = rfds_all;
563
802
          /* Bad exit by plugin */
564
803
          if(debug){
565
804
            if(WIFEXITED(proc->status)){
566
 
              fprintf(stderr, "Plugin %d exited with status %d\n",
567
 
                      proc->pid, WEXITSTATUS(proc->status));
 
805
              fprintf(stderr, "Plugin %u exited with status %d\n",
 
806
                      (unsigned int) (proc->pid),
 
807
                      WEXITSTATUS(proc->status));
568
808
            } else if(WIFSIGNALED(proc->status)) {
569
 
              fprintf(stderr, "Plugin %d killed by signal %d\n",
570
 
                      proc->pid, WTERMSIG(proc->status));
 
809
              fprintf(stderr, "Plugin %u killed by signal %d\n",
 
810
                      (unsigned int) (proc->pid),
 
811
                      WTERMSIG(proc->status));
571
812
            } else if(WCOREDUMP(proc->status)){
572
 
              fprintf(stderr, "Plugin %d dumped core\n", proc->pid);
 
813
              fprintf(stderr, "Plugin %d dumped core\n",
 
814
                      (unsigned int) (proc->pid));
573
815
            }
574
816
          }
575
817
          /* Remove the plugin */
608
850
          break;
609
851
        }
610
852
        /* This process exited nicely, so print its buffer */
611
 
        for(size_t written = 0; written < proc->buffer_length;
612
 
            written += (size_t)ret){
613
 
          ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO,
614
 
                                         proc->buffer + written,
615
 
                                         proc->buffer_length
616
 
                                         - written));
617
 
          if(ret < 0){
618
 
            perror("write");
619
 
            exitstatus = EXIT_FAILURE;
620
 
            goto end;
621
 
          }
 
853
 
 
854
        bool bret = print_out_password(proc->buffer, proc->buffer_length);
 
855
        if(not bret){
 
856
          perror("print_out_password");
 
857
          exitstatus = EXIT_FAILURE;
622
858
        }
623
859
        goto end;
624
860
      }
653
889
      }
654
890
    }
655
891
  }
656
 
  if(process_list == NULL){
657
 
    fprintf(stderr, "All plugin processes failed, exiting\n");
658
 
    exitstatus = EXIT_FAILURE;
659
 
  }
660
 
  
 
892
 
 
893
 
661
894
 end:
 
895
  
 
896
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
 
897
    /* Fallback if all plugins failed, none are found or an error occured */
 
898
    bool bret;
 
899
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
 
900
    char *passwordbuffer = getpass("Password: ");
 
901
    bret = print_out_password(passwordbuffer, strlen(passwordbuffer));
 
902
    if(not bret){
 
903
      perror("print_out_password");
 
904
      exitstatus = EXIT_FAILURE;
 
905
      goto end;
 
906
    }
 
907
  }
 
908
  
662
909
  /* Restore old signal handler */
663
910
  sigaction(SIGCHLD, &old_sigchld_action, NULL);
664
911
  
665
 
  free(plus_argv);
 
912
  free(custom_argv);
666
913
  
667
914
  /* Free the plugin list */
668
915
  for(plugin *next; plugin_list != NULL; plugin_list = next){
669
916
    next = plugin_list->next;
670
917
    free(plugin_list->argv);
 
918
    if(plugin_list->environ[0] != NULL){
 
919
      for(char **e = plugin_list->environ; *e != NULL; e++){
 
920
        free(*e);
 
921
      }
 
922
    }
 
923
    free(plugin_list->environ);
671
924
    free(plugin_list);
672
925
  }
673
926
  
679
932
  for(process *next; process_list != NULL; process_list = next){
680
933
    next = process_list->next;
681
934
    close(process_list->fd);
682
 
    kill(process_list->pid, SIGTERM);
 
935
    ret = kill(process_list->pid, SIGTERM);
 
936
    if(ret == -1 and errno != ESRCH){
 
937
      /* set-uid proccesses migth not get closed */
 
938
      perror("kill");
 
939
    }
683
940
    free(process_list->buffer);
684
941
    free(process_list);
685
942
  }