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