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