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