/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 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
 
static void free_plugin_list(plugin *plugin_list){
251
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
252
 
    next = plugin_list->next;
253
 
    for(char **arg = plugin_list->argv; *arg != NULL; arg++){
254
 
      free(*arg);
255
 
    }
256
 
    free(plugin_list->argv);
257
 
    for(char **env = plugin_list->environ; *env != NULL; env++){
258
 
      free(*env);
259
 
    }
260
 
    free(plugin_list->environ);
261
 
    free(plugin_list);
262
 
  }
 
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;
263
153
}
264
154
 
265
155
int main(int argc, char *argv[]){
266
 
  const char *plugindir = "/lib/mandos/plugins.d";
267
 
  const char *argfile = ARGFILE;
268
 
  FILE *conffp;
 
156
  const char *plugindir = "/conf/conf.d/mandos/plugins.d";
269
157
  size_t d_name_len;
270
158
  DIR *dir = NULL;
271
159
  struct dirent *dirst;
272
160
  struct stat st;
273
161
  fd_set rfds_all;
274
162
  int ret, maxfd = 0;
275
 
  uid_t uid = 65534;
276
 
  gid_t gid = 65534;
277
163
  bool debug = false;
278
164
  int exitstatus = EXIT_SUCCESS;
279
 
  struct sigaction old_sigchld_action;
280
 
  struct sigaction sigchld_action = { .sa_handler = handle_sigchld,
281
 
                                      .sa_flags = SA_NOCLDSTOP };
282
 
  char **custom_argv = NULL;
283
 
  int custom_argc = 0;
284
165
  
285
166
  /* Establish a signal handler */
 
167
  struct sigaction old_sigchld_action,
 
168
    sigchld_action = { .sa_handler = handle_sigchld,
 
169
                       .sa_flags = SA_NOCLDSTOP };
286
170
  sigemptyset(&sigchld_action.sa_mask);
287
171
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
288
 
  if(ret == -1){
 
172
  if(ret < 0){
289
173
    perror("sigaddset");
290
 
    exitstatus = EXIT_FAILURE;
291
 
    goto fallback;
 
174
    exit(EXIT_FAILURE);
292
175
  }
293
176
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
294
 
  if(ret == -1){
 
177
  if(ret < 0){
295
178
    perror("sigaction");
296
 
    exitstatus = EXIT_FAILURE;
297
 
    goto fallback;
 
179
    exit(EXIT_FAILURE);
298
180
  }
299
181
  
300
182
  /* The options we understand. */
302
184
    { .name = "global-options", .key = 'g',
303
185
      .arg = "OPTION[,OPTION[,...]]",
304
186
      .doc = "Options passed to all plugins" },
305
 
    { .name = "global-envs", .key = 'e',
306
 
      .arg = "VAR=value",
307
 
      .doc = "Environment variable passed to all plugins" },
308
187
    { .name = "options-for", .key = 'o',
309
188
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
310
189
      .doc = "Options passed only to specified plugin" },
311
 
    { .name = "envs-for", .key = 'f',
312
 
      .arg = "PLUGIN:ENV=value",
313
 
      .doc = "Environment variable passed to specified plugin" },
314
190
    { .name = "disable", .key = 'd',
315
191
      .arg = "PLUGIN",
316
192
      .doc = "Disable a specific plugin", .group = 1 },
317
193
    { .name = "plugin-dir", .key = 128,
318
194
      .arg = "DIRECTORY",
319
195
      .doc = "Specify a different plugin directory", .group = 2 },
320
 
    { .name = "userid", .key = 129,
321
 
      .arg = "ID", .flags = 0,
322
 
      .doc = "User ID the plugins will run as", .group = 2 },
323
 
    { .name = "groupid", .key = 130,
324
 
      .arg = "ID", .flags = 0,
325
 
      .doc = "Group ID the plugins will run as", .group = 2 },
326
 
    { .name = "debug", .key = 131,
 
196
    { .name = "debug", .key = 129,
327
197
      .doc = "Debug mode", .group = 3 },
328
198
    { .name = NULL }
329
199
  };
335
205
    switch (key) {
336
206
    case 'g':
337
207
      if (arg != NULL){
338
 
        char *p;
339
 
        while((p = strsep(&arg, ",")) != NULL){
340
 
          if(p[0] == '\0'){
341
 
            continue;
342
 
          }
343
 
          if(not add_argument(getplugin(NULL, plugins), p)){
344
 
            perror("add_argument");
345
 
            return ARGP_ERR_UNKNOWN;
346
 
          }
347
 
        }
348
 
      }
349
 
      break;
350
 
    case 'e':
351
 
      if(arg == NULL){
352
 
        break;
353
 
      }
354
 
      {
355
 
        char *envdef = strdup(arg);
356
 
        if(envdef == NULL){
357
 
          break;
358
 
        }
359
 
        if(not add_environment(getplugin(NULL, plugins), envdef)){
360
 
          perror("add_environment");
361
 
        }
 
208
        char *p = strtok(arg, ",");
 
209
        do{
 
210
          addargument(getplugin(NULL, plugins), p);
 
211
          p = strtok(NULL, ",");
 
212
        } while (p);
362
213
      }
363
214
      break;
364
215
    case 'o':
365
216
      if (arg != NULL){
366
 
        char *p_name = strsep(&arg, ":");
367
 
        if(p_name[0] == '\0'){
368
 
          break;
369
 
        }
370
 
        char *opt = strsep(&arg, ":");
371
 
        if(opt[0] == '\0'){
372
 
          break;
373
 
        }
374
 
        if(opt != NULL){
375
 
          char *p;
376
 
          while((p = strsep(&opt, ",")) != NULL){
377
 
            if(p[0] == '\0'){
378
 
              continue;
379
 
            }
380
 
            if(not add_argument(getplugin(p_name, plugins), p)){
381
 
              perror("add_argument");
382
 
              return ARGP_ERR_UNKNOWN;
383
 
            }
384
 
          }
385
 
        }
386
 
      }
387
 
      break;
388
 
    case 'f':
389
 
      if(arg == NULL){
390
 
        break;
391
 
      }
392
 
      {
393
 
        char *envdef = strchr(arg, ':');
394
 
        if(envdef == NULL){
395
 
          break;
396
 
        }
397
 
        char *p_name = strndup(arg, (size_t) (envdef-arg));
398
 
        if(p_name == NULL){
399
 
          break;
400
 
        }
401
 
        envdef++;
402
 
        if(not add_environment(getplugin(p_name, plugins), envdef)){
403
 
          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);
404
225
        }
405
226
      }
406
227
      break;
407
228
    case 'd':
408
229
      if (arg != NULL){
409
 
        plugin *p = getplugin(arg, plugins);
410
 
        if(p == NULL){
411
 
          return ARGP_ERR_UNKNOWN;
412
 
        }
413
 
        p->disabled = true;
 
230
        getplugin(arg, plugins)->disabled = true;
414
231
      }
415
232
      break;
416
233
    case 128:
417
234
      plugindir = arg;
418
235
      break;
419
236
    case 129:
420
 
      uid = (uid_t)strtol(arg, NULL, 10);
421
 
      break;
422
 
    case 130:
423
 
      gid = (gid_t)strtol(arg, NULL, 10);
424
 
      break;
425
 
    case 131:
426
237
      debug = true;
427
238
      break;
428
239
    case ARGP_KEY_ARG:
429
 
      fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
 
240
      argp_usage (state);
430
241
      break;
431
242
    case ARGP_KEY_END:
432
243
      break;
439
250
  plugin *plugin_list = NULL;
440
251
  
441
252
  struct argp argp = { .options = options, .parser = parse_opt,
442
 
                       .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
 
253
                       .args_doc = "",
443
254
                       .doc = "Mandos plugin runner -- Run plugins" };
444
255
  
445
 
  ret = argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
446
 
  if (ret == ARGP_ERR_UNKNOWN){
447
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
448
 
    exitstatus = EXIT_FAILURE;
449
 
    goto fallback;
450
 
  }
451
 
 
452
 
  conffp = fopen(argfile, "r");
453
 
  if(conffp != NULL){
454
 
    char *org_line = NULL;
455
 
    char *p, *arg, *new_arg, *line;
456
 
    size_t size = 0;
457
 
    ssize_t sret;
458
 
    const char whitespace_delims[] = " \r\t\f\v\n";
459
 
    const char comment_delim[] = "#";
460
 
 
461
 
    custom_argc = 1;
462
 
    custom_argv = malloc(sizeof(char*) * 2);
463
 
    if(custom_argv == NULL){
464
 
      perror("malloc");
465
 
      exitstatus = EXIT_FAILURE;
466
 
      goto fallback;
467
 
    }
468
 
    custom_argv[0] = argv[0];
469
 
    custom_argv[1] = NULL;
470
 
    
471
 
    while(true){
472
 
      sret = getline(&org_line, &size, conffp);
473
 
      if(sret == -1){
474
 
        break;
475
 
      }
476
 
 
477
 
      line = org_line;
478
 
      arg = strsep(&line, comment_delim);
479
 
      while((p = strsep(&arg, whitespace_delims)) != NULL){
480
 
        if(p[0] == '\0'){
481
 
          continue;
482
 
        }
483
 
        new_arg = strdup(p);
484
 
 
485
 
        custom_argc += 1;
486
 
        custom_argv = realloc(custom_argv, sizeof(char *)
487
 
                              * ((unsigned int) custom_argc + 1));
488
 
        if(custom_argv == NULL){
489
 
          perror("realloc");
490
 
          exitstatus = EXIT_FAILURE;
491
 
          free(org_line);
492
 
          goto fallback;
493
 
        }
494
 
        custom_argv[custom_argc-1] = new_arg;
495
 
        custom_argv[custom_argc] = NULL;        
496
 
      }
497
 
    }
498
 
    free(org_line);
499
 
  } else{
500
 
    /* Check for harmful errors and go to fallback. Other errors might
501
 
       not affect opening plugins */
502
 
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
503
 
      perror("fopen");
504
 
      exitstatus = EXIT_FAILURE;
505
 
      goto fallback;
506
 
    }
507
 
  }
508
 
 
509
 
  if(custom_argv != NULL){
510
 
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, &plugin_list);
511
 
    if (ret == ARGP_ERR_UNKNOWN){
512
 
      fprintf(stderr, "Unknown error while parsing arguments\n");
513
 
      exitstatus = EXIT_FAILURE;
514
 
      goto fallback;
515
 
    }
516
 
  }
 
256
  argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
517
257
  
518
258
  if(debug){
519
259
    for(plugin *p = plugin_list; p != NULL; p=p->next){
522
262
      for(char **a = p->argv; *a != NULL; a++){
523
263
        fprintf(stderr, "\tArg: %s\n", *a);
524
264
      }
525
 
      fprintf(stderr, "...and %u environment variables\n", p->envc);
526
 
      for(char **a = p->environ; *a != NULL; a++){
527
 
        fprintf(stderr, "\t%s\n", *a);
528
 
      }
529
265
    }
530
266
  }
531
267
  
532
 
  ret = setuid(uid);
533
 
  if (ret == -1){
534
 
    perror("setuid");
535
 
  }
536
 
  
537
 
  setgid(gid);
538
 
  if (ret == -1){
539
 
    perror("setgid");
540
 
  }
541
 
  
542
268
  dir = opendir(plugindir);
543
269
  if(dir == NULL){
544
270
    perror("Could not open plugin dir");
545
271
    exitstatus = EXIT_FAILURE;
546
 
    goto fallback;
 
272
    goto end;
547
273
  }
548
274
  
549
275
  /* Set the FD_CLOEXEC flag on the directory, if possible */
554
280
      if(ret < 0){
555
281
        perror("set_cloexec_flag");
556
282
        exitstatus = EXIT_FAILURE;
557
 
        goto fallback;
 
283
        goto end;
558
284
      }
559
285
    }
560
286
  }
566
292
    
567
293
    // All directory entries have been processed
568
294
    if(dirst == NULL){
569
 
      if (errno == EBADF){
570
 
        perror("readdir");
571
 
        exitstatus = EXIT_FAILURE;
572
 
        goto fallback;
573
 
      }
574
295
      break;
575
296
    }
576
297
    
620
341
        continue;
621
342
      }
622
343
    }
623
 
 
624
 
    char *filename;
625
 
    ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
626
 
    if(ret < 0){
627
 
      perror("asprintf");
628
 
      continue;
629
 
    }
630
 
    
631
 
    ret = stat(filename, &st);
632
 
    if (ret == -1){
633
 
      perror("stat");
634
 
      free(filename);
635
 
      continue;
636
 
    }
 
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);
637
356
    
638
357
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
639
358
      if(debug){
643
362
      free(filename);
644
363
      continue;
645
364
    }
646
 
    plugin *p = getplugin(dirst->d_name, &plugin_list);
647
 
    if(p == NULL){
648
 
      perror("getplugin");
649
 
      free(filename);
650
 
      continue;
651
 
    }
652
 
    if(p->disabled){
 
365
    if(getplugin(dirst->d_name, &plugin_list)->disabled){
653
366
      if(debug){
654
367
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
655
368
                dirst->d_name);
657
370
      free(filename);
658
371
      continue;
659
372
    }
 
373
    plugin *p = getplugin(dirst->d_name, &plugin_list);
660
374
    {
661
375
      /* Add global arguments to argument list for this plugin */
662
376
      plugin *g = getplugin(NULL, &plugin_list);
663
 
      if(g != NULL){
664
 
        for(char **a = g->argv + 1; *a != NULL; a++){
665
 
          if(not add_argument(p, *a)){
666
 
            perror("add_argument");
667
 
          }
668
 
        }
669
 
        /* Add global environment variables */
670
 
        for(char **e = g->environ; *e != NULL; e++){
671
 
          if(not add_environment(p, *e)){
672
 
            perror("add_environment");
673
 
          }
674
 
        }
675
 
      }
676
 
    }
677
 
    /* If this plugin has any environment variables, we will call
678
 
       using execve and need to duplicate the environment from this
679
 
       process, too. */
680
 
    if(p->environ[0] != NULL){
681
 
      for(char **e = environ; *e != NULL; e++){
682
 
        char *copy = strdup(*e);
683
 
        if(copy == NULL){
684
 
          perror("strdup");
685
 
          continue;
686
 
        }
687
 
        if(not add_environment(p, copy)){
688
 
          perror("add_environment");
689
 
        }
690
 
      }
691
 
    }
692
 
    
693
 
    int pipefd[2];
 
377
      for(char **a = g->argv + 1; *a != NULL; a++){
 
378
        addargument(p, *a);
 
379
      }
 
380
    }
 
381
    int pipefd[2]; 
694
382
    ret = pipe(pipefd);
695
383
    if (ret == -1){
696
384
      perror("pipe");
697
385
      exitstatus = EXIT_FAILURE;
698
 
      goto fallback;
 
386
      goto end;
699
387
    }
700
388
    ret = set_cloexec_flag(pipefd[0]);
701
389
    if(ret < 0){
702
390
      perror("set_cloexec_flag");
703
391
      exitstatus = EXIT_FAILURE;
704
 
      goto fallback;
 
392
      goto end;
705
393
    }
706
394
    ret = set_cloexec_flag(pipefd[1]);
707
395
    if(ret < 0){
708
396
      perror("set_cloexec_flag");
709
397
      exitstatus = EXIT_FAILURE;
710
 
      goto fallback;
 
398
      goto end;
711
399
    }
712
400
    /* Block SIGCHLD until process is safely in process list */
713
401
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
714
402
    if(ret < 0){
715
403
      perror("sigprocmask");
716
404
      exitstatus = EXIT_FAILURE;
717
 
      goto fallback;
 
405
      goto end;
718
406
    }
719
407
    // Starting a new process to be watched
720
408
    pid_t pid = fork();
721
 
    if(pid == -1){
722
 
      perror("fork");
723
 
      exitstatus = EXIT_FAILURE;
724
 
      goto fallback;
725
 
    }
726
409
    if(pid == 0){
727
410
      /* this is the child process */
728
411
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
735
418
        perror("sigprocmask");
736
419
        _exit(EXIT_FAILURE);
737
420
      }
738
 
 
739
 
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
740
 
      if(ret == -1){
741
 
        perror("dup2");
742
 
        _exit(EXIT_FAILURE);
743
 
      }
 
421
      dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
744
422
      
745
423
      if(dirfd(dir) < 0){
746
424
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
747
425
           above and must now close it manually here. */
748
426
        closedir(dir);
749
427
      }
750
 
      if(p->environ[0] == NULL){
751
 
        if(execv(filename, p->argv) < 0){
752
 
          perror("execv");
753
 
          _exit(EXIT_FAILURE);
754
 
        }
755
 
      } else {
756
 
        if(execve(filename, p->argv, p->environ) < 0){
757
 
          perror("execve");
758
 
          _exit(EXIT_FAILURE);
759
 
        }
 
428
      if(execv(filename, p->argv) < 0){
 
429
        perror("execv");
 
430
        _exit(EXIT_FAILURE);
760
431
      }
761
432
      /* no return */
762
433
    }
771
442
        perror("sigprocmask");
772
443
      }
773
444
      exitstatus = EXIT_FAILURE;
774
 
      goto fallback;
 
445
      goto end;
775
446
    }
776
447
    
777
448
    *new_process = (struct process){ .pid = pid,
785
456
    if(ret < 0){
786
457
      perror("sigprocmask");
787
458
      exitstatus = EXIT_FAILURE;
788
 
      goto fallback;
 
459
      goto end;
789
460
    }
790
461
    
791
462
    FD_SET(new_process->fd, &rfds_all);
796
467
    
797
468
  }
798
469
  
799
 
  free_plugin_list(plugin_list);
800
 
  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
  }
801
476
  
802
477
  closedir(dir);
803
478
  dir = NULL;
804
 
    
 
479
  
805
480
  if (process_list == NULL){
806
 
    fprintf(stderr, "No plugin processes started. Incorrect plugin"
807
 
            " directory?\n");
808
 
    process_list = NULL;
 
481
    fprintf(stderr, "No plugin processes started, exiting\n");
 
482
    exitstatus = EXIT_FAILURE;
 
483
    goto end;
809
484
  }
810
485
  while(process_list){
811
486
    fd_set rfds = rfds_all;
813
488
    if (select_ret == -1){
814
489
      perror("select");
815
490
      exitstatus = EXIT_FAILURE;
816
 
      goto fallback;
 
491
      goto end;
817
492
    }
818
493
    /* OK, now either a process completed, or something can be read
819
494
       from one of them */
826
501
          /* Bad exit by plugin */
827
502
          if(debug){
828
503
            if(WIFEXITED(proc->status)){
829
 
              fprintf(stderr, "Plugin %u exited with status %d\n",
830
 
                      (unsigned int) (proc->pid),
831
 
                      WEXITSTATUS(proc->status));
 
504
              fprintf(stderr, "Plugin %d exited with status %d\n",
 
505
                      proc->pid, WEXITSTATUS(proc->status));
832
506
            } else if(WIFSIGNALED(proc->status)) {
833
 
              fprintf(stderr, "Plugin %u killed by signal %d\n",
834
 
                      (unsigned int) (proc->pid),
835
 
                      WTERMSIG(proc->status));
 
507
              fprintf(stderr, "Plugin %d killed by signal %d\n",
 
508
                      proc->pid, WTERMSIG(proc->status));
836
509
            } else if(WCOREDUMP(proc->status)){
837
 
              fprintf(stderr, "Plugin %d dumped core\n",
838
 
                      (unsigned int) (proc->pid));
 
510
              fprintf(stderr, "Plugin %d dumped core\n", proc->pid);
839
511
            }
840
512
          }
841
513
          /* Remove the plugin */
842
514
          FD_CLR(proc->fd, &rfds_all);
843
515
          /* Block signal while modifying process_list */
844
 
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
 
516
          ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
845
517
          if(ret < 0){
846
518
            perror("sigprocmask");
847
519
            exitstatus = EXIT_FAILURE;
848
 
            goto fallback;
 
520
            goto end;
849
521
          }
850
522
          /* Delete this process entry from the list */
851
523
          if(process_list == proc){
874
546
          break;
875
547
        }
876
548
        /* This process exited nicely, so print its buffer */
877
 
 
878
 
        bool bret = print_out_password(proc->buffer,
879
 
                                       proc->buffer_length);
880
 
        if(not bret){
881
 
          perror("print_out_password");
882
 
          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
          }
883
560
        }
884
 
        goto fallback;
 
561
        goto end;
885
562
      }
886
563
      /* This process has not completed.  Does it have any output? */
887
564
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
895
572
        if (proc->buffer == NULL){
896
573
          perror("malloc");
897
574
          exitstatus = EXIT_FAILURE;
898
 
          goto fallback;
 
575
          goto end;
899
576
        }
900
577
        proc->buffer_size += BUFFER_SIZE;
901
578
      }
914
591
      }
915
592
    }
916
593
  }
917
 
 
918
 
 
919
 
 fallback:
920
 
  
921
 
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
922
 
    /* Fallback if all plugins failed, none are found or an error
923
 
       occured */
924
 
    bool bret;
925
 
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
926
 
    char *passwordbuffer = getpass("Password: ");
927
 
    bret = print_out_password(passwordbuffer, strlen(passwordbuffer));
928
 
    if(not bret){
929
 
      perror("print_out_password");
930
 
      exitstatus = EXIT_FAILURE;
931
 
    }
 
594
  if(process_list == NULL){
 
595
    fprintf(stderr, "All plugin processes failed, exiting\n");
 
596
    exitstatus = EXIT_FAILURE;
932
597
  }
933
598
  
 
599
 end:
934
600
  /* Restore old signal handler */
935
 
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
936
 
  if(ret == -1){
937
 
    perror("sigaction");
938
 
    exitstatus = EXIT_FAILURE;
939
 
  }
940
 
 
941
 
  if(custom_argv != NULL){
942
 
    for(char **arg = custom_argv+1; *arg != NULL; arg++){
943
 
      free(*arg);
944
 
    }
945
 
    free(custom_argv);
946
 
  }
947
 
  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
  }
948
609
  
949
610
  if(dir != NULL){
950
611
    closedir(dir);
954
615
  for(process *next; process_list != NULL; process_list = next){
955
616
    next = process_list->next;
956
617
    close(process_list->fd);
957
 
    ret = kill(process_list->pid, SIGTERM);
958
 
    if(ret == -1 and errno != ESRCH){
959
 
      /* set-uid proccesses migth not get closed */
960
 
      perror("kill");
961
 
    }
 
618
    kill(process_list->pid, SIGTERM);
962
619
    free(process_list->buffer);
963
620
    free(process_list);
964
621
  }