/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 plugbasedclient.c

  • Committer: Teddy Hogeborn
  • Date: 2008-08-02 10:48:24 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080802104824-fx0miwp9o4g9r31e
* plugbasedclient.c (struct process): New fields "eof", "completed",
                                      and "status".
  (handle_sigchld): New function.
  (main): Initialize "dir" to NULL to only closedir() it if necessary.
          Move "process_list" to be a global variable to be accessible
          by "handle_sigchld".  Make "handle_sigchld" handle SIGCHLD.
          Remove redundant check for NULL "dir".  Free "filename" when
          no longer used.  Block SIGCHLD around fork()/exec().
          Restore normal signals in child.  Only loop while running
          processes exist.  Print process buffer when the process is
          done and it has emitted EOF, not when it only emits EOF.
          Remove processes from list which exit non-cleanly.  In
          cleaning up, closedir() if necessary.  Bug fix: set next
          pointer correctly when freeing process list.

* plugins.d/passprompt.c (main): Do not ignore SIGQUIT.

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(), 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() */
 
24
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY() */
 
25
 
 
26
#include <stdio.h>              /* popen(), fileno(), fprintf(),
 
27
                                   stderr, STDOUT_FILENO */
42
28
#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() */
43
34
#include <dirent.h>             /* DIR, struct dirent, opendir(),
44
 
                                   readdir(), closedir(), dirfd() */
 
35
                                   readdir(), closedir() */
 
36
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
45
37
#include <unistd.h>             /* struct stat, stat(), S_ISREG(),
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() */
 
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() */
55
48
#include <errno.h>              /* errno */
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,
60
 
                                   error_t */
61
 
#include <signal.h>             /* struct sigaction, sigemptyset(),
62
 
                                   sigaddset(), sigaction(),
63
 
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
64
 
                                   SIG_UNBLOCK, kill() */
65
 
#include <errno.h>              /* errno, EBADF */
66
 
 
67
 
#define BUFFER_SIZE 256
68
 
#define ARGFILE "/conf/conf.d/mandos/plugin-runner.conf"
69
 
 
70
 
const char *argp_program_version = "plugin-runner 1.0";
71
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
49
#include <argp.h>               /* struct argp_option,
 
50
                                   struct argp_state, struct argp,
 
51
                                   argp_parse() */
72
52
 
73
53
struct process;
74
54
 
79
59
  size_t buffer_size;
80
60
  size_t buffer_length;
81
61
  bool eof;
82
 
  volatile bool completed;
83
 
  volatile int status;
 
62
  bool completed;
 
63
  int status;
84
64
  struct process *next;
85
65
} process;
86
66
 
88
68
  char *name;                   /* can be NULL or any plugin name */
89
69
  char **argv;
90
70
  int argc;
91
 
  char **environ;
92
 
  int envc;
93
71
  bool disabled;
94
72
  struct plugin *next;
95
73
} plugin;
96
74
 
97
 
static plugin *getplugin(char *name, plugin **plugin_list){
 
75
plugin *getplugin(char *name, plugin **plugin_list){
98
76
  for (plugin *p = *plugin_list; p != NULL; p = p->next){
99
77
    if ((p->name == name)
100
78
        or (p->name and name and (strcmp(p->name, name) == 0))){
104
82
  /* Create a new plugin */
105
83
  plugin *new_plugin = malloc(sizeof(plugin));
106
84
  if (new_plugin == NULL){
107
 
    return NULL;
108
 
  }
109
 
  char *copy_name = NULL;
110
 
  if(name != NULL){
111
 
    copy_name = strdup(name);
112
 
    if(copy_name == NULL){
113
 
      return NULL;
114
 
    }
115
 
  }
116
 
  
117
 
  *new_plugin = (plugin) { .name = copy_name,
118
 
                           .argc = 1,
119
 
                           .envc = 0,
120
 
                           .disabled = false,
121
 
                           .next = *plugin_list };
122
 
  
 
85
    perror("malloc");
 
86
    exit(EXIT_FAILURE);
 
87
  }
 
88
  new_plugin->name = name;
123
89
  new_plugin->argv = malloc(sizeof(char *) * 2);
124
90
  if (new_plugin->argv == NULL){
125
 
    free(copy_name);
126
 
    free(new_plugin);
127
 
    return NULL;
 
91
    perror("malloc");
 
92
    exit(EXIT_FAILURE);
128
93
  }
129
 
  new_plugin->argv[0] = copy_name;
 
94
  new_plugin->argv[0] = name;
130
95
  new_plugin->argv[1] = NULL;
131
 
 
132
 
  new_plugin->environ = malloc(sizeof(char *));
133
 
  if(new_plugin->environ == NULL){
134
 
    free(copy_name);
135
 
    free(new_plugin->argv);
136
 
    free(new_plugin);
137
 
    return NULL;
138
 
  }
139
 
  new_plugin->environ[0] = NULL;
 
96
  new_plugin->argc = 1;
 
97
  new_plugin->disabled = false;
 
98
  new_plugin->next = *plugin_list;
140
99
  /* Append the new plugin to the list */
141
100
  *plugin_list = new_plugin;
142
101
  return new_plugin;
143
102
}
144
103
 
145
 
/* Helper function for add_argument and add_environment */
146
 
static bool add_to_char_array(const char *new, char ***array,
147
 
                              int *len){
148
 
  /* Resize the pointed-to array to hold one more pointer */
149
 
  *array = realloc(*array, sizeof(char *)
150
 
                   * (size_t) ((*len) + 2));
151
 
  /* Malloc check */
152
 
  if(*array == NULL){
153
 
    return false;
154
 
  }
155
 
  /* Make a copy of the new string */
156
 
  char *copy = strdup(new);
157
 
  if(copy == NULL){
158
 
    return false;
159
 
  }
160
 
  /* Insert the copy */
161
 
  (*array)[*len] = copy;
162
 
  (*len)++;
163
 
  /* Add a new terminating NULL pointer to the last element */
164
 
  (*array)[*len] = NULL;
165
 
  return true;
166
 
}
167
 
 
168
 
/* Add to a plugin's argument vector */
169
 
static bool add_argument(plugin *p, const char *arg){
170
 
  if(p == NULL){
171
 
    return false;
172
 
  }
173
 
  return add_to_char_array(arg, &(p->argv), &(p->argc));
174
 
}
175
 
 
176
 
/* Add to a plugin's environment */
177
 
static bool add_environment(plugin *p, const char *def){
178
 
  if(p == NULL){
179
 
    return false;
180
 
  }
181
 
  return add_to_char_array(def, &(p->environ), &(p->envc));
182
 
}
183
 
 
 
104
void addargument(plugin *p, char *arg){
 
105
  p->argv[p->argc] = arg;
 
106
  p->argv = realloc(p->argv, sizeof(char *) * (size_t)(p->argc + 2));
 
107
  if (p->argv == NULL){
 
108
    perror("malloc");
 
109
    exit(EXIT_FAILURE);
 
110
  }
 
111
  p->argc++;
 
112
  p->argv[p->argc] = NULL;
 
113
}
184
114
 
185
115
/*
186
116
 * Based on the example in the GNU LibC manual chapter 13.13 "File
187
117
 * Descriptor Flags".
188
118
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
189
119
 */
190
 
static int set_cloexec_flag(int fd)
 
120
int set_cloexec_flag(int fd)
191
121
{
192
122
  int ret = fcntl(fd, F_GETFD, 0);
193
123
  /* If reading the flags failed, return error indication now. */
198
128
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
199
129
}
200
130
 
 
131
#define BUFFER_SIZE 256
 
132
 
 
133
const char *argp_program_version = "plugbasedclient 0.9";
 
134
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
135
 
201
136
process *process_list = NULL;
202
137
 
203
 
/* Mark processes as completed when they exit, and save their exit
 
138
/* Mark a process as completed when it exits, and save its exit
204
139
   status. */
205
140
void handle_sigchld(__attribute__((unused)) int sig){
206
 
  while(true){
207
 
    process *proc = process_list;
208
 
    int status;
209
 
    pid_t pid = waitpid(-1, &status, WNOHANG);
210
 
    if(pid == 0){
211
 
      /* Only still running child processes */
212
 
      break;
213
 
    }
214
 
    if(pid == -1){
215
 
      if (errno != ECHILD){
216
 
        perror("waitpid");
217
 
      }
218
 
      /* No child processes */
219
 
      break;
220
 
    }
221
 
 
222
 
    /* A child exited, find it in process_list */
223
 
    while(proc != NULL and proc->pid != pid){
224
 
      proc = proc->next;
225
 
    }
226
 
    if(proc == NULL){
227
 
      /* Process not found in process list */
228
 
      continue;
229
 
    }
230
 
    proc->status = status;
231
 
    proc->completed = true;
232
 
  }
233
 
}
234
 
 
235
 
bool print_out_password(const char *buffer, size_t length){
236
 
  ssize_t ret;
237
 
  if(length>0 and buffer[length-1] == '\n'){
238
 
    length--;
239
 
  }
240
 
  for(size_t written = 0; written < length; written += (size_t)ret){
241
 
    ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
242
 
                                   length - written));
243
 
    if(ret < 0){
244
 
      return false;
245
 
    }
246
 
  }
247
 
  return true;
248
 
}
249
 
 
250
 
char **add_to_argv(char **argv, int *argc, char *arg){
251
 
  if (argv == NULL){
252
 
    *argc = 1;
253
 
    argv = malloc(sizeof(char*) * 2);
254
 
    if(argv == NULL){
255
 
      return NULL;
256
 
    }
257
 
    argv[0] = NULL;     /* Will be set to argv[0] in main before
258
 
                           parsing */
259
 
    argv[1] = NULL;
260
 
  }
261
 
  *argc += 1;
262
 
  argv = realloc(argv, sizeof(char *)
263
 
                  * ((unsigned int) *argc + 1));
264
 
  if(argv == NULL){
265
 
    return NULL;
266
 
  }
267
 
  argv[*argc-1] = arg;
268
 
  argv[*argc] = NULL;
269
 
  return argv;
270
 
}
271
 
 
272
 
static void free_plugin_list(plugin *plugin_list){
273
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
274
 
    next = plugin_list->next;
275
 
    for(char **arg = plugin_list->argv; *arg != NULL; arg++){
276
 
      free(*arg);
277
 
    }
278
 
    free(plugin_list->argv);
279
 
    for(char **env = plugin_list->environ; *env != NULL; env++){
280
 
      free(*env);
281
 
    }
282
 
    free(plugin_list->environ);
283
 
    free(plugin_list);
284
 
  }
 
141
  process *proc = process_list;
 
142
  int status;
 
143
  pid_t pid = wait(&status);
 
144
  while(proc != NULL and proc->pid != pid){
 
145
    proc = proc->next;    
 
146
  }
 
147
  if(proc == NULL){
 
148
    /* Process not found in process list */
 
149
    return;
 
150
  }
 
151
  proc->status = status;
 
152
  proc->completed = true;
285
153
}
286
154
 
287
155
int main(int argc, char *argv[]){
288
 
  const char *plugindir = "/lib/mandos/plugins.d";
289
 
  const char *argfile = ARGFILE;
290
 
  FILE *conffp;
 
156
  const char *plugindir = "/conf/conf.d/mandos/plugins.d";
291
157
  size_t d_name_len;
292
158
  DIR *dir = NULL;
293
159
  struct dirent *dirst;
294
160
  struct stat st;
295
161
  fd_set rfds_all;
296
162
  int ret, maxfd = 0;
297
 
  uid_t uid = 65534;
298
 
  gid_t gid = 65534;
299
163
  bool debug = false;
300
164
  int exitstatus = EXIT_SUCCESS;
301
 
  struct sigaction old_sigchld_action;
302
 
  struct sigaction sigchld_action = { .sa_handler = handle_sigchld,
303
 
                                      .sa_flags = SA_NOCLDSTOP };
304
 
  char **custom_argv = NULL;
305
 
  int custom_argc = 0;
306
165
  
307
166
  /* Establish a signal handler */
 
167
  struct sigaction old_sigchld_action,
 
168
    sigchld_action = { .sa_handler = handle_sigchld,
 
169
                       .sa_flags = SA_NOCLDSTOP };
308
170
  sigemptyset(&sigchld_action.sa_mask);
309
171
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
310
 
  if(ret == -1){
 
172
  if(ret < 0){
311
173
    perror("sigaddset");
312
 
    exitstatus = EXIT_FAILURE;
313
 
    goto fallback;
 
174
    exit(EXIT_FAILURE);
314
175
  }
315
176
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
316
 
  if(ret == -1){
 
177
  if(ret < 0){
317
178
    perror("sigaction");
318
 
    exitstatus = EXIT_FAILURE;
319
 
    goto fallback;
 
179
    exit(EXIT_FAILURE);
320
180
  }
321
181
  
322
182
  /* The options we understand. */
324
184
    { .name = "global-options", .key = 'g',
325
185
      .arg = "OPTION[,OPTION[,...]]",
326
186
      .doc = "Options passed to all plugins" },
327
 
    { .name = "global-envs", .key = 'e',
328
 
      .arg = "VAR=value",
329
 
      .doc = "Environment variable passed to all plugins" },
330
187
    { .name = "options-for", .key = 'o',
331
188
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
332
189
      .doc = "Options passed only to specified plugin" },
333
 
    { .name = "envs-for", .key = 'f',
334
 
      .arg = "PLUGIN:ENV=value",
335
 
      .doc = "Environment variable passed to specified plugin" },
336
190
    { .name = "disable", .key = 'd',
337
191
      .arg = "PLUGIN",
338
192
      .doc = "Disable a specific plugin", .group = 1 },
339
193
    { .name = "plugin-dir", .key = 128,
340
194
      .arg = "DIRECTORY",
341
195
      .doc = "Specify a different plugin directory", .group = 2 },
342
 
    { .name = "userid", .key = 129,
343
 
      .arg = "ID", .flags = 0,
344
 
      .doc = "User ID the plugins will run as", .group = 2 },
345
 
    { .name = "groupid", .key = 130,
346
 
      .arg = "ID", .flags = 0,
347
 
      .doc = "Group ID the plugins will run as", .group = 2 },
348
 
    { .name = "debug", .key = 131,
 
196
    { .name = "debug", .key = 129,
349
197
      .doc = "Debug mode", .group = 3 },
350
198
    { .name = NULL }
351
199
  };
357
205
    switch (key) {
358
206
    case 'g':
359
207
      if (arg != NULL){
360
 
        char *p;
361
 
        while((p = strsep(&arg, ",")) != NULL){
362
 
          if(p[0] == '\0'){
363
 
            continue;
364
 
          }
365
 
          if(not add_argument(getplugin(NULL, plugins), p)){
366
 
            perror("add_argument");
367
 
            return ARGP_ERR_UNKNOWN;
368
 
          }
369
 
        }
370
 
      }
371
 
      break;
372
 
    case 'e':
373
 
      if(arg == NULL){
374
 
        break;
375
 
      }
376
 
      {
377
 
        char *envdef = strdup(arg);
378
 
        if(envdef == NULL){
379
 
          break;
380
 
        }
381
 
        if(not add_environment(getplugin(NULL, plugins), envdef)){
382
 
          perror("add_environment");
383
 
        }
 
208
        char *p = strtok(arg, ",");
 
209
        do{
 
210
          addargument(getplugin(NULL, plugins), p);
 
211
          p = strtok(NULL, ",");
 
212
        } while (p);
384
213
      }
385
214
      break;
386
215
    case 'o':
387
216
      if (arg != NULL){
388
 
        char *p_name = strsep(&arg, ":");
389
 
        if(p_name[0] == '\0'){
390
 
          break;
391
 
        }
392
 
        char *opt = strsep(&arg, ":");
393
 
        if(opt[0] == '\0'){
394
 
          break;
395
 
        }
396
 
        if(opt != NULL){
397
 
          char *p;
398
 
          while((p = strsep(&opt, ",")) != NULL){
399
 
            if(p[0] == '\0'){
400
 
              continue;
401
 
            }
402
 
            if(not add_argument(getplugin(p_name, plugins), p)){
403
 
              perror("add_argument");
404
 
              return ARGP_ERR_UNKNOWN;
405
 
            }
406
 
          }
407
 
        }
408
 
      }
409
 
      break;
410
 
    case 'f':
411
 
      if(arg == NULL){
412
 
        break;
413
 
      }
414
 
      {
415
 
        char *envdef = strchr(arg, ':');
416
 
        if(envdef == NULL){
417
 
          break;
418
 
        }
419
 
        char *p_name = strndup(arg, (size_t) (envdef-arg));
420
 
        if(p_name == NULL){
421
 
          break;
422
 
        }
423
 
        envdef++;
424
 
        if(not add_environment(getplugin(p_name, plugins), envdef)){
425
 
          perror("add_environment");
 
217
        char *name = strtok(arg, ":");
 
218
        char *p = strtok(NULL, ":");
 
219
        if(p){
 
220
          p = strtok(p, ",");
 
221
          do{
 
222
            addargument(getplugin(name, plugins), p);
 
223
            p = strtok(NULL, ",");
 
224
          } while (p);
426
225
        }
427
226
      }
428
227
      break;
429
228
    case 'd':
430
229
      if (arg != NULL){
431
 
        plugin *p = getplugin(arg, plugins);
432
 
        if(p == NULL){
433
 
          return ARGP_ERR_UNKNOWN;
434
 
        }
435
 
        p->disabled = true;
 
230
        getplugin(arg, plugins)->disabled = true;
436
231
      }
437
232
      break;
438
233
    case 128:
439
234
      plugindir = arg;
440
235
      break;
441
236
    case 129:
442
 
      uid = (uid_t)strtol(arg, NULL, 10);
443
 
      break;
444
 
    case 130:
445
 
      gid = (gid_t)strtol(arg, NULL, 10);
446
 
      break;
447
 
    case 131:
448
237
      debug = true;
449
238
      break;
450
239
    case ARGP_KEY_ARG:
451
 
      fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
 
240
      argp_usage (state);
452
241
      break;
453
242
    case ARGP_KEY_END:
454
243
      break;
461
250
  plugin *plugin_list = NULL;
462
251
  
463
252
  struct argp argp = { .options = options, .parser = parse_opt,
464
 
                       .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
 
253
                       .args_doc = "",
465
254
                       .doc = "Mandos plugin runner -- Run plugins" };
466
255
  
467
 
  ret = argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
468
 
  if (ret == ARGP_ERR_UNKNOWN){
469
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
470
 
    exitstatus = EXIT_FAILURE;
471
 
    goto fallback;
472
 
  }
473
 
 
474
 
  conffp = fopen(argfile, "r");
475
 
  if(conffp != NULL){
476
 
    char *org_line = NULL;
477
 
    char *p, *arg, *new_arg, *line;
478
 
    size_t size = 0;
479
 
    ssize_t sret;
480
 
    const char whitespace_delims[] = " \r\t\f\v\n";
481
 
    const char comment_delim[] = "#";
482
 
 
483
 
    while(true){
484
 
      sret = getline(&org_line, &size, conffp);
485
 
      if(sret == -1){
486
 
        break;
487
 
      }
488
 
 
489
 
      line = org_line;
490
 
      arg = strsep(&line, comment_delim);
491
 
      while((p = strsep(&arg, whitespace_delims)) != NULL){
492
 
        if(p[0] == '\0'){
493
 
          continue;
494
 
        }
495
 
        new_arg = strdup(p);
496
 
        custom_argv = add_to_argv(custom_argv, &custom_argc, new_arg);
497
 
        if (custom_argv == NULL){
498
 
          perror("add_to_argv");
499
 
          exitstatus = EXIT_FAILURE;
500
 
          goto fallback;
501
 
        }
502
 
      }
503
 
    }
504
 
    free(org_line);
505
 
  } else{
506
 
    /* Check for harmful errors and go to fallback. Other errors might
507
 
       not affect opening plugins */
508
 
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
509
 
      perror("fopen");
510
 
      exitstatus = EXIT_FAILURE;
511
 
      goto fallback;
512
 
    }
513
 
  }
514
 
 
515
 
  if(custom_argv != NULL){
516
 
    custom_argv[0] = argv[0];
517
 
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0,
518
 
                      &plugin_list);
519
 
    if (ret == ARGP_ERR_UNKNOWN){
520
 
      fprintf(stderr, "Unknown error while parsing arguments\n");
521
 
      exitstatus = EXIT_FAILURE;
522
 
      goto fallback;
523
 
    }
524
 
  }
 
256
  argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
525
257
  
526
258
  if(debug){
527
259
    for(plugin *p = plugin_list; p != NULL; p=p->next){
530
262
      for(char **a = p->argv; *a != NULL; a++){
531
263
        fprintf(stderr, "\tArg: %s\n", *a);
532
264
      }
533
 
      fprintf(stderr, "...and %u environment variables\n", p->envc);
534
 
      for(char **a = p->environ; *a != NULL; a++){
535
 
        fprintf(stderr, "\t%s\n", *a);
536
 
      }
537
265
    }
538
266
  }
539
267
  
540
 
  ret = setuid(uid);
541
 
  if (ret == -1){
542
 
    perror("setuid");
543
 
  }
544
 
  
545
 
  setgid(gid);
546
 
  if (ret == -1){
547
 
    perror("setgid");
548
 
  }
549
 
  
550
268
  dir = opendir(plugindir);
551
269
  if(dir == NULL){
552
270
    perror("Could not open plugin dir");
553
271
    exitstatus = EXIT_FAILURE;
554
 
    goto fallback;
 
272
    goto end;
555
273
  }
556
274
  
557
275
  /* Set the FD_CLOEXEC flag on the directory, if possible */
562
280
      if(ret < 0){
563
281
        perror("set_cloexec_flag");
564
282
        exitstatus = EXIT_FAILURE;
565
 
        goto fallback;
 
283
        goto end;
566
284
      }
567
285
    }
568
286
  }
574
292
    
575
293
    // All directory entries have been processed
576
294
    if(dirst == NULL){
577
 
      if (errno == EBADF){
578
 
        perror("readdir");
579
 
        exitstatus = EXIT_FAILURE;
580
 
        goto fallback;
581
 
      }
582
295
      break;
583
296
    }
584
297
    
628
341
        continue;
629
342
      }
630
343
    }
631
 
 
632
 
    char *filename;
633
 
    ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
634
 
    if(ret < 0){
635
 
      perror("asprintf");
636
 
      continue;
637
 
    }
638
 
    
639
 
    ret = stat(filename, &st);
640
 
    if (ret == -1){
641
 
      perror("stat");
642
 
      free(filename);
643
 
      continue;
644
 
    }
 
344
    
 
345
    char *filename = malloc(d_name_len + strlen(plugindir) + 2);
 
346
    if (filename == NULL){
 
347
      perror("malloc");
 
348
      exitstatus = EXIT_FAILURE;
 
349
      goto end;
 
350
    }
 
351
    strcpy(filename, plugindir); /* Spurious warning */
 
352
    strcat(filename, "/");      /* Spurious warning */
 
353
    strcat(filename, dirst->d_name); /* Spurious warning */
 
354
    
 
355
    stat(filename, &st);
645
356
    
646
357
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
647
358
      if(debug){
651
362
      free(filename);
652
363
      continue;
653
364
    }
654
 
    plugin *p = getplugin(dirst->d_name, &plugin_list);
655
 
    if(p == NULL){
656
 
      perror("getplugin");
657
 
      free(filename);
658
 
      continue;
659
 
    }
660
 
    if(p->disabled){
 
365
    if(getplugin(dirst->d_name, &plugin_list)->disabled){
661
366
      if(debug){
662
367
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
663
368
                dirst->d_name);
665
370
      free(filename);
666
371
      continue;
667
372
    }
 
373
    plugin *p = getplugin(dirst->d_name, &plugin_list);
668
374
    {
669
375
      /* Add global arguments to argument list for this plugin */
670
376
      plugin *g = getplugin(NULL, &plugin_list);
671
 
      if(g != NULL){
672
 
        for(char **a = g->argv + 1; *a != NULL; a++){
673
 
          if(not add_argument(p, *a)){
674
 
            perror("add_argument");
675
 
          }
676
 
        }
677
 
        /* Add global environment variables */
678
 
        for(char **e = g->environ; *e != NULL; e++){
679
 
          if(not add_environment(p, *e)){
680
 
            perror("add_environment");
681
 
          }
682
 
        }
683
 
      }
684
 
    }
685
 
    /* If this plugin has any environment variables, we will call
686
 
       using execve and need to duplicate the environment from this
687
 
       process, too. */
688
 
    if(p->environ[0] != NULL){
689
 
      for(char **e = environ; *e != NULL; e++){
690
 
        char *copy = strdup(*e);
691
 
        if(copy == NULL){
692
 
          perror("strdup");
693
 
          continue;
694
 
        }
695
 
        if(not add_environment(p, copy)){
696
 
          perror("add_environment");
697
 
        }
698
 
      }
699
 
    }
700
 
    
701
 
    int pipefd[2];
 
377
      for(char **a = g->argv + 1; *a != NULL; a++){
 
378
        addargument(p, *a);
 
379
      }
 
380
    }
 
381
    int pipefd[2]; 
702
382
    ret = pipe(pipefd);
703
383
    if (ret == -1){
704
384
      perror("pipe");
705
385
      exitstatus = EXIT_FAILURE;
706
 
      goto fallback;
 
386
      goto end;
707
387
    }
708
388
    ret = set_cloexec_flag(pipefd[0]);
709
389
    if(ret < 0){
710
390
      perror("set_cloexec_flag");
711
391
      exitstatus = EXIT_FAILURE;
712
 
      goto fallback;
 
392
      goto end;
713
393
    }
714
394
    ret = set_cloexec_flag(pipefd[1]);
715
395
    if(ret < 0){
716
396
      perror("set_cloexec_flag");
717
397
      exitstatus = EXIT_FAILURE;
718
 
      goto fallback;
 
398
      goto end;
719
399
    }
720
400
    /* Block SIGCHLD until process is safely in process list */
721
401
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
722
402
    if(ret < 0){
723
403
      perror("sigprocmask");
724
404
      exitstatus = EXIT_FAILURE;
725
 
      goto fallback;
 
405
      goto end;
726
406
    }
727
407
    // Starting a new process to be watched
728
408
    pid_t pid = fork();
729
 
    if(pid == -1){
730
 
      perror("fork");
731
 
      exitstatus = EXIT_FAILURE;
732
 
      goto fallback;
733
 
    }
734
409
    if(pid == 0){
735
410
      /* this is the child process */
736
411
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
743
418
        perror("sigprocmask");
744
419
        _exit(EXIT_FAILURE);
745
420
      }
746
 
 
747
 
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
748
 
      if(ret == -1){
749
 
        perror("dup2");
750
 
        _exit(EXIT_FAILURE);
751
 
      }
 
421
      dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
752
422
      
753
423
      if(dirfd(dir) < 0){
754
424
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
755
425
           above and must now close it manually here. */
756
426
        closedir(dir);
757
427
      }
758
 
      if(p->environ[0] == NULL){
759
 
        if(execv(filename, p->argv) < 0){
760
 
          perror("execv");
761
 
          _exit(EXIT_FAILURE);
762
 
        }
763
 
      } else {
764
 
        if(execve(filename, p->argv, p->environ) < 0){
765
 
          perror("execve");
766
 
          _exit(EXIT_FAILURE);
767
 
        }
 
428
      if(execv(filename, p->argv) < 0){
 
429
        perror("execv");
 
430
        _exit(EXIT_FAILURE);
768
431
      }
769
432
      /* no return */
770
433
    }
779
442
        perror("sigprocmask");
780
443
      }
781
444
      exitstatus = EXIT_FAILURE;
782
 
      goto fallback;
 
445
      goto end;
783
446
    }
784
447
    
785
448
    *new_process = (struct process){ .pid = pid,
793
456
    if(ret < 0){
794
457
      perror("sigprocmask");
795
458
      exitstatus = EXIT_FAILURE;
796
 
      goto fallback;
 
459
      goto end;
797
460
    }
798
461
    
799
462
    FD_SET(new_process->fd, &rfds_all);
804
467
    
805
468
  }
806
469
  
807
 
  free_plugin_list(plugin_list);
808
 
  plugin_list = NULL;
 
470
  /* Free the plugin list */
 
471
  for(plugin *next; plugin_list != NULL; plugin_list = next){
 
472
    next = plugin_list->next;
 
473
    free(plugin_list->argv);
 
474
    free(plugin_list);
 
475
  }
809
476
  
810
477
  closedir(dir);
811
478
  dir = NULL;
812
 
    
 
479
  
813
480
  if (process_list == NULL){
814
 
    fprintf(stderr, "No plugin processes started. Incorrect plugin"
815
 
            " directory?\n");
816
 
    process_list = NULL;
 
481
    fprintf(stderr, "No plugin processes started, exiting\n");
 
482
    exitstatus = EXIT_FAILURE;
 
483
    goto end;
817
484
  }
818
485
  while(process_list){
819
486
    fd_set rfds = rfds_all;
821
488
    if (select_ret == -1){
822
489
      perror("select");
823
490
      exitstatus = EXIT_FAILURE;
824
 
      goto fallback;
 
491
      goto end;
825
492
    }
826
493
    /* OK, now either a process completed, or something can be read
827
494
       from one of them */
834
501
          /* Bad exit by plugin */
835
502
          if(debug){
836
503
            if(WIFEXITED(proc->status)){
837
 
              fprintf(stderr, "Plugin %u exited with status %d\n",
838
 
                      (unsigned int) (proc->pid),
839
 
                      WEXITSTATUS(proc->status));
 
504
              fprintf(stderr, "Plugin %d exited with status %d\n",
 
505
                      proc->pid, WEXITSTATUS(proc->status));
840
506
            } else if(WIFSIGNALED(proc->status)) {
841
 
              fprintf(stderr, "Plugin %u killed by signal %d\n",
842
 
                      (unsigned int) (proc->pid),
843
 
                      WTERMSIG(proc->status));
 
507
              fprintf(stderr, "Plugin %d killed by signal %d\n",
 
508
                      proc->pid, WTERMSIG(proc->status));
844
509
            } else if(WCOREDUMP(proc->status)){
845
 
              fprintf(stderr, "Plugin %d dumped core\n",
846
 
                      (unsigned int) (proc->pid));
 
510
              fprintf(stderr, "Plugin %d dumped core\n", proc->pid);
847
511
            }
848
512
          }
849
513
          /* Remove the plugin */
850
514
          FD_CLR(proc->fd, &rfds_all);
851
515
          /* Block signal while modifying process_list */
852
 
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
 
516
          ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
853
517
          if(ret < 0){
854
518
            perror("sigprocmask");
855
519
            exitstatus = EXIT_FAILURE;
856
 
            goto fallback;
 
520
            goto end;
857
521
          }
858
522
          /* Delete this process entry from the list */
859
523
          if(process_list == proc){
882
546
          break;
883
547
        }
884
548
        /* This process exited nicely, so print its buffer */
885
 
 
886
 
        bool bret = print_out_password(proc->buffer,
887
 
                                       proc->buffer_length);
888
 
        if(not bret){
889
 
          perror("print_out_password");
890
 
          exitstatus = EXIT_FAILURE;
 
549
        for(size_t written = 0; written < proc->buffer_length;
 
550
            written += (size_t)ret){
 
551
          ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO,
 
552
                                         proc->buffer + written,
 
553
                                         proc->buffer_length
 
554
                                         - written));
 
555
          if(ret < 0){
 
556
            perror("write");
 
557
            exitstatus = EXIT_FAILURE;
 
558
            goto end;
 
559
          }
891
560
        }
892
 
        goto fallback;
 
561
        goto end;
893
562
      }
894
563
      /* This process has not completed.  Does it have any output? */
895
564
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
903
572
        if (proc->buffer == NULL){
904
573
          perror("malloc");
905
574
          exitstatus = EXIT_FAILURE;
906
 
          goto fallback;
 
575
          goto end;
907
576
        }
908
577
        proc->buffer_size += BUFFER_SIZE;
909
578
      }
922
591
      }
923
592
    }
924
593
  }
925
 
 
926
 
 
927
 
 fallback:
928
 
  
929
 
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
930
 
    /* Fallback if all plugins failed, none are found or an error
931
 
       occured */
932
 
    bool bret;
933
 
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
934
 
    char *passwordbuffer = getpass("Password: ");
935
 
    bret = print_out_password(passwordbuffer, strlen(passwordbuffer));
936
 
    if(not bret){
937
 
      perror("print_out_password");
938
 
      exitstatus = EXIT_FAILURE;
939
 
    }
 
594
  if(process_list == NULL){
 
595
    fprintf(stderr, "All plugin processes failed, exiting\n");
 
596
    exitstatus = EXIT_FAILURE;
940
597
  }
941
598
  
 
599
 end:
942
600
  /* Restore old signal handler */
943
 
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
944
 
  if(ret == -1){
945
 
    perror("sigaction");
946
 
    exitstatus = EXIT_FAILURE;
947
 
  }
948
 
 
949
 
  if(custom_argv != NULL){
950
 
    for(char **arg = custom_argv; *arg != NULL; arg++){
951
 
      free(*arg);
952
 
    }
953
 
    free(custom_argv);
954
 
  }
955
 
  free_plugin_list(plugin_list);
 
601
  sigaction(SIGCHLD, &old_sigchld_action, NULL);
 
602
  
 
603
  /* Free the plugin list */
 
604
  for(plugin *next; plugin_list != NULL; plugin_list = next){
 
605
    next = plugin_list->next;
 
606
    free(plugin_list->argv);
 
607
    free(plugin_list);
 
608
  }
956
609
  
957
610
  if(dir != NULL){
958
611
    closedir(dir);
962
615
  for(process *next; process_list != NULL; process_list = next){
963
616
    next = process_list->next;
964
617
    close(process_list->fd);
965
 
    ret = kill(process_list->pid, SIGTERM);
966
 
    if(ret == -1 and errno != ESRCH){
967
 
      /* set-uid proccesses migth not get closed */
968
 
      perror("kill");
969
 
    }
 
618
    kill(process_list->pid, SIGTERM);
970
619
    free(process_list->buffer);
971
620
    free(process_list);
972
621
  }