/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: Björn Påhlsson
  • Date: 2008-07-20 02:52:20 UTC
  • Revision ID: belorn@braxen-20080720025220-r5u0388uy9iu23h6
Added following support:
Pluginbased client handler
rewritten Mandos client
       Avahi instead of udp server discovery
       openpgp encrypted key support
Passprompt stand alone application for direct console input
Added logging for Mandos server

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  -*- coding: utf-8; mode: c; mode: orgtbl -*- */
2
 
/*
3
 
 * Mandos plugin runner - Run Mandos plugins
4
 
 *
5
 
 * Copyright © 2008,2009 Teddy Hogeborn
6
 
 * Copyright © 2008,2009 Björn Påhlsson
7
 
 * 
8
 
 * This program is free software: you can redistribute it and/or
9
 
 * modify it under the terms of the GNU General Public License as
10
 
 * published by the Free Software Foundation, either version 3 of the
11
 
 * License, or (at your option) any later version.
12
 
 * 
13
 
 * This program is distributed in the hope that it will be useful, but
14
 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 
 * General Public License for more details.
17
 
 * 
18
 
 * You should have received a copy of the GNU General Public License
19
 
 * along with this program.  If not, see
20
 
 * <http://www.gnu.org/licenses/>.
21
 
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
23
 
 */
24
 
 
25
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
26
 
                                   asprintf() */
27
 
#include <stddef.h>             /* size_t, NULL */
28
 
#include <stdlib.h>             /* malloc(), exit(), EXIT_FAILURE,
29
 
                                   EXIT_SUCCESS, realloc() */
30
 
#include <stdbool.h>            /* bool, true, false */
31
 
#include <stdio.h>              /* perror, fileno(), fprintf(),
32
 
                                   stderr, STDOUT_FILENO */
33
 
#include <sys/types.h>          /* DIR, opendir(), stat(), struct
34
 
                                   stat, waitpid(), WIFEXITED(),
35
 
                                   WEXITSTATUS(), wait(), pid_t,
36
 
                                   uid_t, gid_t, getuid(), getgid(),
37
 
                                   dirfd() */
38
 
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
39
 
                                   FD_SET(), FD_ISSET(), FD_CLR */
40
 
#include <sys/wait.h>           /* wait(), waitpid(), WIFEXITED(),
41
 
                                   WEXITSTATUS(), WTERMSIG(),
42
 
                                   WCOREDUMP() */
43
 
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
44
 
#include <iso646.h>             /* and, or, not */
45
 
#include <dirent.h>             /* DIR, struct dirent, opendir(),
46
 
                                   readdir(), closedir(), dirfd() */
47
 
#include <unistd.h>             /* struct stat, stat(), S_ISREG(),
48
 
                                   fcntl(), setuid(), setgid(),
49
 
                                   F_GETFD, F_SETFD, FD_CLOEXEC,
50
 
                                   access(), pipe(), fork(), close()
51
 
                                   dup2(), STDOUT_FILENO, _exit(),
52
 
                                   execv(), write(), read(),
53
 
                                   close() */
54
 
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
55
 
                                   FD_CLOEXEC */
56
 
#include <string.h>             /* strsep, strlen(), asprintf(),
57
 
                                   strsignal() */
58
 
#include <errno.h>              /* errno */
59
 
#include <argp.h>               /* struct argp_option, struct
60
 
                                   argp_state, struct argp,
61
 
                                   argp_parse(), ARGP_ERR_UNKNOWN,
62
 
                                   ARGP_KEY_END, ARGP_KEY_ARG,
63
 
                                   error_t */
64
 
#include <signal.h>             /* struct sigaction, sigemptyset(),
65
 
                                   sigaddset(), sigaction(),
66
 
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
67
 
                                   SIG_UNBLOCK, kill(), sig_atomic_t
68
 
                                */
69
 
#include <errno.h>              /* errno, EBADF */
70
 
#include <inttypes.h>           /* intmax_t, PRIdMAX, strtoimax() */
71
 
 
72
 
#define BUFFER_SIZE 256
73
 
 
74
 
#define PDIR "/lib/mandos/plugins.d"
75
 
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
76
 
 
77
 
const char *argp_program_version = "plugin-runner " VERSION;
78
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
79
 
 
80
 
typedef struct plugin{
81
 
  char *name;                   /* can be NULL or any plugin name */
82
 
  char **argv;
83
 
  int argc;
84
 
  char **environ;
85
 
  int envc;
86
 
  bool disabled;
87
 
  
88
 
  /* Variables used for running processes*/
 
1
#include <stdio.h>      /* popen, fileno */
 
2
#include <iso646.h>     /* and, or, not */
 
3
#include <sys/types.h>  /* DIR, opendir, stat, struct stat, waitpid,
 
4
                           WIFEXITED, WEXITSTATUS, wait */
 
5
#include <sys/wait.h>   /* wait */
 
6
#include <dirent.h>     /* DIR, opendir */
 
7
#include <sys/stat.h>   /* stat, struct stat */
 
8
#include <unistd.h>     /* stat, struct stat, chdir */
 
9
#include <stdlib.h>     /* EXIT_FAILURE */
 
10
#include <sys/select.h> /* fd_set, select, FD_ZERO, FD_SET, FD_ISSET */
 
11
#include <string.h>     /* strlen, strcpy, strcat */
 
12
#include <stdbool.h>    /* true */
 
13
#include <sys/wait.h>   /* waitpid, WIFEXITED, WEXITSTATUS */
 
14
#include <errno.h>      /* errno */
 
15
 
 
16
struct process;
 
17
 
 
18
typedef struct process{
89
19
  pid_t pid;
90
20
  int fd;
91
21
  char *buffer;
92
 
  size_t buffer_size;
93
 
  size_t buffer_length;
94
 
  bool eof;
95
 
  volatile sig_atomic_t completed;
96
 
  int status;
97
 
  struct plugin *next;
98
 
} plugin;
99
 
 
100
 
static plugin *plugin_list = NULL;
101
 
 
102
 
/* Gets an existing plugin based on name,
103
 
   or if none is found, creates a new one */
104
 
static plugin *getplugin(char *name){
105
 
  /* Check for exiting plugin with that name */
106
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
107
 
    if((p->name == name)
108
 
       or (p->name and name and (strcmp(p->name, name) == 0))){
109
 
      return p;
110
 
    }
111
 
  }
112
 
  /* Create a new plugin */
113
 
  plugin *new_plugin = malloc(sizeof(plugin));
114
 
  if(new_plugin == NULL){
115
 
    return NULL;
116
 
  }
117
 
  char *copy_name = NULL;
118
 
  if(name != NULL){
119
 
    copy_name = strdup(name);
120
 
    if(copy_name == NULL){
121
 
      free(new_plugin);
122
 
      return NULL;
123
 
    }
124
 
  }
125
 
  
126
 
  *new_plugin = (plugin){ .name = copy_name,
127
 
                          .argc = 1,
128
 
                          .disabled = false,
129
 
                          .next = plugin_list };
130
 
  
131
 
  new_plugin->argv = malloc(sizeof(char *) * 2);
132
 
  if(new_plugin->argv == NULL){
133
 
    free(copy_name);
134
 
    free(new_plugin);
135
 
    return NULL;
136
 
  }
137
 
  new_plugin->argv[0] = copy_name;
138
 
  new_plugin->argv[1] = NULL;
139
 
  
140
 
  new_plugin->environ = malloc(sizeof(char *));
141
 
  if(new_plugin->environ == NULL){
142
 
    free(copy_name);
143
 
    free(new_plugin->argv);
144
 
    free(new_plugin);
145
 
    return NULL;
146
 
  }
147
 
  new_plugin->environ[0] = NULL;
148
 
  
149
 
  /* Append the new plugin to the list */
150
 
  plugin_list = new_plugin;
151
 
  return new_plugin;
152
 
}
153
 
 
154
 
/* Helper function for add_argument and add_environment */
155
 
static bool add_to_char_array(const char *new, char ***array,
156
 
                              int *len){
157
 
  /* Resize the pointed-to array to hold one more pointer */
158
 
  *array = realloc(*array, sizeof(char *)
159
 
                   * (size_t) ((*len) + 2));
160
 
  /* Malloc check */
161
 
  if(*array == NULL){
162
 
    return false;
163
 
  }
164
 
  /* Make a copy of the new string */
165
 
  char *copy = strdup(new);
166
 
  if(copy == NULL){
167
 
    return false;
168
 
  }
169
 
  /* Insert the copy */
170
 
  (*array)[*len] = copy;
171
 
  (*len)++;
172
 
  /* Add a new terminating NULL pointer to the last element */
173
 
  (*array)[*len] = NULL;
174
 
  return true;
175
 
}
176
 
 
177
 
/* Add to a plugin's argument vector */
178
 
static bool add_argument(plugin *p, const char *arg){
179
 
  if(p == NULL){
180
 
    return false;
181
 
  }
182
 
  return add_to_char_array(arg, &(p->argv), &(p->argc));
183
 
}
184
 
 
185
 
/* Add to a plugin's environment */
186
 
static bool add_environment(plugin *p, const char *def, bool replace){
187
 
  if(p == NULL){
188
 
    return false;
189
 
  }
190
 
  /* namelen = length of name of environment variable */
191
 
  size_t namelen = (size_t)(strchrnul(def, '=') - def);
192
 
  /* Search for this environment variable */
193
 
  for(char **e = p->environ; *e != NULL; e++){
194
 
    if(strncmp(*e, def, namelen + 1) == 0){
195
 
      /* It already exists */
196
 
      if(replace){
197
 
        char *new = realloc(*e, strlen(def) + 1);
198
 
        if(new == NULL){
199
 
          return false;
200
 
        }
201
 
        *e = new;
202
 
        strcpy(*e, def);
203
 
      }
204
 
      return true;
205
 
    }
206
 
  }
207
 
  return add_to_char_array(def, &(p->environ), &(p->envc));
208
 
}
209
 
 
210
 
/*
211
 
 * Based on the example in the GNU LibC manual chapter 13.13 "File
212
 
 * Descriptor Flags".
213
 
 | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
214
 
 */
215
 
static int set_cloexec_flag(int fd){
216
 
  int ret = fcntl(fd, F_GETFD, 0);
217
 
  /* If reading the flags failed, return error indication now. */
218
 
  if(ret < 0){
219
 
    return ret;
220
 
  }
221
 
  /* Store modified flag word in the descriptor. */
222
 
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
223
 
}
224
 
 
225
 
 
226
 
/* Mark processes as completed when they exit, and save their exit
227
 
   status. */
228
 
static void handle_sigchld(__attribute__((unused)) int sig){
229
 
  int old_errno = errno;
230
 
  while(true){
231
 
    plugin *proc = plugin_list;
232
 
    int status;
233
 
    pid_t pid = waitpid(-1, &status, WNOHANG);
234
 
    if(pid == 0){
235
 
      /* Only still running child processes */
236
 
      break;
237
 
    }
238
 
    if(pid == -1){
239
 
      if(errno == ECHILD){
240
 
        /* No child processes */
241
 
        break;
242
 
      }
243
 
      perror("waitpid");
244
 
    }
245
 
    
246
 
    /* A child exited, find it in process_list */
247
 
    while(proc != NULL and proc->pid != pid){
248
 
      proc = proc->next;
249
 
    }
250
 
    if(proc == NULL){
251
 
      /* Process not found in process list */
252
 
      continue;
253
 
    }
254
 
    proc->status = status;
255
 
    proc->completed = 1;
256
 
  }
257
 
  errno = old_errno;
258
 
}
259
 
 
260
 
/* Prints out a password to stdout */
261
 
static bool print_out_password(const char *buffer, size_t length){
262
 
  ssize_t ret;
263
 
  for(size_t written = 0; written < length; written += (size_t)ret){
264
 
    ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
265
 
                                   length - written));
266
 
    if(ret < 0){
267
 
      return false;
268
 
    }
269
 
  }
270
 
  return true;
271
 
}
272
 
 
273
 
/* Removes and free a plugin from the plugin list */
274
 
static void free_plugin(plugin *plugin_node){
275
 
  
276
 
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
277
 
    free(*arg);
278
 
  }
279
 
  free(plugin_node->argv);
280
 
  for(char **env = plugin_node->environ; *env != NULL; env++){
281
 
    free(*env);
282
 
  }
283
 
  free(plugin_node->environ);
284
 
  free(plugin_node->buffer);
285
 
  
286
 
  /* Removes the plugin from the singly-linked list */
287
 
  if(plugin_node == plugin_list){
288
 
    /* First one - simple */
289
 
    plugin_list = plugin_list->next;
290
 
  } else {
291
 
    /* Second one or later */
292
 
    for(plugin *p = plugin_list; p != NULL; p = p->next){
293
 
      if(p->next == plugin_node){
294
 
        p->next = plugin_node->next;
295
 
        break;
296
 
      }
297
 
    }
298
 
  }
299
 
  
300
 
  free(plugin_node);
301
 
}
302
 
 
303
 
static void free_plugin_list(void){
304
 
  while(plugin_list != NULL){
305
 
    free_plugin(plugin_list);
306
 
  }
307
 
}
 
22
  int buffer_size;
 
23
  int buffer_length;
 
24
  struct process *next;
 
25
} process;
 
26
 
 
27
#define BUFFER_SIZE 256
308
28
 
309
29
int main(int argc, char *argv[]){
310
 
  char *plugindir = NULL;
311
 
  char *argfile = NULL;
312
 
  FILE *conffp;
313
 
  size_t d_name_len;
314
 
  DIR *dir = NULL;
 
30
  char plugindir[] = "plugins.d";
 
31
  size_t d_name_len, plugindir_len = sizeof(plugindir)-1;
 
32
  DIR *dir;
315
33
  struct dirent *dirst;
316
34
  struct stat st;
317
 
  fd_set rfds_all;
 
35
  fd_set rfds_orig;
318
36
  int ret, maxfd = 0;
319
 
  ssize_t sret;
320
 
  uid_t uid = 65534;
321
 
  gid_t gid = 65534;
322
 
  bool debug = false;
323
 
  int exitstatus = EXIT_SUCCESS;
324
 
  struct sigaction old_sigchld_action;
325
 
  struct sigaction sigchld_action = { .sa_handler = handle_sigchld,
326
 
                                      .sa_flags = SA_NOCLDSTOP };
327
 
  char **custom_argv = NULL;
328
 
  int custom_argc = 0;
329
 
  
330
 
  /* Establish a signal handler */
331
 
  sigemptyset(&sigchld_action.sa_mask);
332
 
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
333
 
  if(ret == -1){
334
 
    perror("sigaddset");
335
 
    exitstatus = EXIT_FAILURE;
336
 
    goto fallback;
337
 
  }
338
 
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
339
 
  if(ret == -1){
340
 
    perror("sigaction");
341
 
    exitstatus = EXIT_FAILURE;
342
 
    goto fallback;
343
 
  }
344
 
  
345
 
  /* The options we understand. */
346
 
  struct argp_option options[] = {
347
 
    { .name = "global-options", .key = 'g',
348
 
      .arg = "OPTION[,OPTION[,...]]",
349
 
      .doc = "Options passed to all plugins" },
350
 
    { .name = "global-env", .key = 'G',
351
 
      .arg = "VAR=value",
352
 
      .doc = "Environment variable passed to all plugins" },
353
 
    { .name = "options-for", .key = 'o',
354
 
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
355
 
      .doc = "Options passed only to specified plugin" },
356
 
    { .name = "env-for", .key = 'E',
357
 
      .arg = "PLUGIN:ENV=value",
358
 
      .doc = "Environment variable passed to specified plugin" },
359
 
    { .name = "disable", .key = 'd',
360
 
      .arg = "PLUGIN",
361
 
      .doc = "Disable a specific plugin", .group = 1 },
362
 
    { .name = "enable", .key = 'e',
363
 
      .arg = "PLUGIN",
364
 
      .doc = "Enable a specific plugin", .group = 1 },
365
 
    { .name = "plugin-dir", .key = 128,
366
 
      .arg = "DIRECTORY",
367
 
      .doc = "Specify a different plugin directory", .group = 2 },
368
 
    { .name = "config-file", .key = 129,
369
 
      .arg = "FILE",
370
 
      .doc = "Specify a different configuration file", .group = 2 },
371
 
    { .name = "userid", .key = 130,
372
 
      .arg = "ID", .flags = 0,
373
 
      .doc = "User ID the plugins will run as", .group = 3 },
374
 
    { .name = "groupid", .key = 131,
375
 
      .arg = "ID", .flags = 0,
376
 
      .doc = "Group ID the plugins will run as", .group = 3 },
377
 
    { .name = "debug", .key = 132,
378
 
      .doc = "Debug mode", .group = 4 },
379
 
    { .name = NULL }
380
 
  };
381
 
  
382
 
  error_t parse_opt(int key, char *arg, __attribute__((unused))
383
 
                    struct argp_state *state){
384
 
    switch(key){
385
 
      char *tmp;
386
 
      intmax_t tmpmax;
387
 
    case 'g':                   /* --global-options */
388
 
      if(arg != NULL){
389
 
        char *plugin_option;
390
 
        while((plugin_option = strsep(&arg, ",")) != NULL){
391
 
          if(plugin_option[0] == '\0'){
392
 
            continue;
393
 
          }
394
 
          if(not add_argument(getplugin(NULL), plugin_option)){
395
 
            perror("add_argument");
396
 
            return ARGP_ERR_UNKNOWN;
397
 
          }
398
 
        }
399
 
      }
400
 
      break;
401
 
    case 'G':                   /* --global-env */
402
 
      if(arg == NULL){
403
 
        break;
404
 
      }
405
 
      if(not add_environment(getplugin(NULL), arg, true)){
406
 
        perror("add_environment");
407
 
      }
408
 
      break;
409
 
    case 'o':                   /* --options-for */
410
 
      if(arg != NULL){
411
 
        char *plugin_name = strsep(&arg, ":");
412
 
        if(plugin_name[0] == '\0'){
413
 
          break;
414
 
        }
415
 
        char *plugin_option;
416
 
        while((plugin_option = strsep(&arg, ",")) != NULL){
417
 
          if(not add_argument(getplugin(plugin_name), plugin_option)){
418
 
            perror("add_argument");
419
 
            return ARGP_ERR_UNKNOWN;
420
 
          }
421
 
        }
422
 
      }
423
 
      break;
424
 
    case 'E':                   /* --env-for */
425
 
      if(arg == NULL){
426
 
        break;
427
 
      }
428
 
      {
429
 
        char *envdef = strchr(arg, ':');
430
 
        if(envdef == NULL){
431
 
          break;
432
 
        }
433
 
        *envdef = '\0';
434
 
        if(not add_environment(getplugin(arg), envdef+1, true)){
435
 
          perror("add_environment");
436
 
        }
437
 
      }
438
 
      break;
439
 
    case 'd':                   /* --disable */
440
 
      if(arg != NULL){
441
 
        plugin *p = getplugin(arg);
442
 
        if(p == NULL){
443
 
          return ARGP_ERR_UNKNOWN;
444
 
        }
445
 
        p->disabled = true;
446
 
      }
447
 
      break;
448
 
    case 'e':                   /* --enable */
449
 
      if(arg != NULL){
450
 
        plugin *p = getplugin(arg);
451
 
        if(p == NULL){
452
 
          return ARGP_ERR_UNKNOWN;
453
 
        }
454
 
        p->disabled = false;
455
 
      }
456
 
      break;
457
 
    case 128:                   /* --plugin-dir */
458
 
      free(plugindir);
459
 
      plugindir = strdup(arg);
460
 
      if(plugindir == NULL){
461
 
        perror("strdup");
462
 
      }
463
 
      break;
464
 
    case 129:                   /* --config-file */
465
 
      /* This is already done by parse_opt_config_file() */
466
 
      break;
467
 
    case 130:                   /* --userid */
468
 
      errno = 0;
469
 
      tmpmax = strtoimax(arg, &tmp, 10);
470
 
      if(errno != 0 or tmp == arg or *tmp != '\0'
471
 
         or tmpmax != (uid_t)tmpmax){
472
 
        fprintf(stderr, "Bad user ID number: \"%s\", using %"
473
 
                PRIdMAX "\n", arg, (intmax_t)uid);
474
 
      } else {
475
 
        uid = (uid_t)tmpmax;
476
 
      }
477
 
      break;
478
 
    case 131:                   /* --groupid */
479
 
      errno = 0;
480
 
      tmpmax = strtoimax(arg, &tmp, 10);
481
 
      if(errno != 0 or tmp == arg or *tmp != '\0'
482
 
         or tmpmax != (gid_t)tmpmax){
483
 
        fprintf(stderr, "Bad group ID number: \"%s\", using %"
484
 
                PRIdMAX "\n", arg, (intmax_t)gid);
485
 
      } else {
486
 
        gid = (gid_t)tmpmax;
487
 
      }
488
 
      break;
489
 
    case 132:                   /* --debug */
490
 
      debug = true;
491
 
      break;
492
 
/*
493
 
 * When adding more options before this line, remember to also add a
494
 
 * "case" to the "parse_opt_config_file" function below.
495
 
 */
496
 
    case ARGP_KEY_ARG:
497
 
      /* Cryptsetup always passes an argument, which is an empty
498
 
         string if "none" was specified in /etc/crypttab.  So if
499
 
         argument was empty, we ignore it silently. */
500
 
      if(arg[0] != '\0'){
501
 
        fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
502
 
      }
503
 
      break;
504
 
    case ARGP_KEY_END:
505
 
      break;
506
 
    default:
507
 
      return ARGP_ERR_UNKNOWN;
508
 
    }
509
 
    return 0;
510
 
  }
511
 
  
512
 
  /* This option parser is the same as parse_opt() above, except it
513
 
     ignores everything but the --config-file option. */
514
 
  error_t parse_opt_config_file(int key, char *arg,
515
 
                                __attribute__((unused))
516
 
                                struct argp_state *state){
517
 
    switch(key){
518
 
    case 'g':                   /* --global-options */
519
 
    case 'G':                   /* --global-env */
520
 
    case 'o':                   /* --options-for */
521
 
    case 'E':                   /* --env-for */
522
 
    case 'd':                   /* --disable */
523
 
    case 'e':                   /* --enable */
524
 
    case 128:                   /* --plugin-dir */
525
 
      break;
526
 
    case 129:                   /* --config-file */
527
 
      free(argfile);
528
 
      argfile = strdup(arg);
529
 
      if(argfile == NULL){
530
 
        perror("strdup");
531
 
      }
532
 
      break;
533
 
    case 130:                   /* --userid */
534
 
    case 131:                   /* --groupid */
535
 
    case 132:                   /* --debug */
536
 
    case ARGP_KEY_ARG:
537
 
    case ARGP_KEY_END:
538
 
      break;
539
 
    default:
540
 
      return ARGP_ERR_UNKNOWN;
541
 
    }
542
 
    return 0;
543
 
  }
544
 
  
545
 
  struct argp argp = { .options = options,
546
 
                       .parser = parse_opt_config_file,
547
 
                       .args_doc = "",
548
 
                       .doc = "Mandos plugin runner -- Run plugins" };
549
 
  
550
 
  /* Parse using parse_opt_config_file() in order to get the custom
551
 
     config file location, if any. */
552
 
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
553
 
  if(ret == ARGP_ERR_UNKNOWN){
554
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
555
 
    exitstatus = EXIT_FAILURE;
556
 
    goto fallback;
557
 
  }
558
 
  
559
 
  /* Reset to the normal argument parser */
560
 
  argp.parser = parse_opt;
561
 
  
562
 
  /* Open the configfile if available */
563
 
  if(argfile == NULL){
564
 
    conffp = fopen(AFILE, "r");
565
 
  } else {
566
 
    conffp = fopen(argfile, "r");
567
 
  }
568
 
  if(conffp != NULL){
569
 
    char *org_line = NULL;
570
 
    char *p, *arg, *new_arg, *line;
571
 
    size_t size = 0;
572
 
    const char whitespace_delims[] = " \r\t\f\v\n";
573
 
    const char comment_delim[] = "#";
574
 
    
575
 
    custom_argc = 1;
576
 
    custom_argv = malloc(sizeof(char*) * 2);
577
 
    if(custom_argv == NULL){
578
 
      perror("malloc");
579
 
      exitstatus = EXIT_FAILURE;
580
 
      goto fallback;
581
 
    }
582
 
    custom_argv[0] = argv[0];
583
 
    custom_argv[1] = NULL;
584
 
    
585
 
    /* for each line in the config file, strip whitespace and ignore
586
 
       commented text */
587
 
    while(true){
588
 
      sret = getline(&org_line, &size, conffp);
589
 
      if(sret == -1){
590
 
        break;
591
 
      }
592
 
      
593
 
      line = org_line;
594
 
      arg = strsep(&line, comment_delim);
595
 
      while((p = strsep(&arg, whitespace_delims)) != NULL){
596
 
        if(p[0] == '\0'){
597
 
          continue;
598
 
        }
599
 
        new_arg = strdup(p);
600
 
        if(new_arg == NULL){
601
 
          perror("strdup");
602
 
          exitstatus = EXIT_FAILURE;
603
 
          free(org_line);
604
 
          goto fallback;
605
 
        }
606
 
        
607
 
        custom_argc += 1;
608
 
        custom_argv = realloc(custom_argv, sizeof(char *)
609
 
                              * ((unsigned int) custom_argc + 1));
610
 
        if(custom_argv == NULL){
611
 
          perror("realloc");
612
 
          exitstatus = EXIT_FAILURE;
613
 
          free(org_line);
614
 
          goto fallback;
615
 
        }
616
 
        custom_argv[custom_argc-1] = new_arg;
617
 
        custom_argv[custom_argc] = NULL;
618
 
      }
619
 
    }
620
 
    do{
621
 
      ret = fclose(conffp);
622
 
    }while(ret == EOF and errno == EINTR);
623
 
    if(ret == EOF){
624
 
      perror("fclose");
625
 
      exitstatus = EXIT_FAILURE;
626
 
      goto fallback;
627
 
    }
628
 
    free(org_line);
629
 
  } else {
630
 
    /* Check for harmful errors and go to fallback. Other errors might
631
 
       not affect opening plugins */
632
 
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
633
 
      perror("fopen");
634
 
      exitstatus = EXIT_FAILURE;
635
 
      goto fallback;
636
 
    }
637
 
  }
638
 
  /* If there was any arguments from configuration file,
639
 
     pass them to parser as command arguments */
640
 
  if(custom_argv != NULL){
641
 
    ret = argp_parse(&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
642
 
                     0, NULL);
643
 
    if(ret == ARGP_ERR_UNKNOWN){
644
 
      fprintf(stderr, "Unknown error while parsing arguments\n");
645
 
      exitstatus = EXIT_FAILURE;
646
 
      goto fallback;
647
 
    }
648
 
  }
649
 
  
650
 
  /* Parse actual command line arguments, to let them override the
651
 
     config file */
652
 
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
653
 
  if(ret == ARGP_ERR_UNKNOWN){
654
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
655
 
    exitstatus = EXIT_FAILURE;
656
 
    goto fallback;
657
 
  }
658
 
  
659
 
  if(debug){
660
 
    for(plugin *p = plugin_list; p != NULL; p=p->next){
661
 
      fprintf(stderr, "Plugin: %s has %d arguments\n",
662
 
              p->name ? p->name : "Global", p->argc - 1);
663
 
      for(char **a = p->argv; *a != NULL; a++){
664
 
        fprintf(stderr, "\tArg: %s\n", *a);
665
 
      }
666
 
      fprintf(stderr, "...and %d environment variables\n", p->envc);
667
 
      for(char **a = p->environ; *a != NULL; a++){
668
 
        fprintf(stderr, "\t%s\n", *a);
669
 
      }
670
 
    }
671
 
  }
672
 
  
673
 
  /* Strip permissions down to nobody */
674
 
  setgid(gid);
675
 
  if(ret == -1){
676
 
    perror("setgid");
677
 
  }
678
 
  ret = setuid(uid);
679
 
  if(ret == -1){
680
 
    perror("setuid");
681
 
  }
682
 
  
683
 
  if(plugindir == NULL){
684
 
    dir = opendir(PDIR);
685
 
  } else {
686
 
    dir = opendir(plugindir);
687
 
  }
688
 
  
 
37
  process *process_list = NULL;
 
38
  
 
39
  dir = opendir(plugindir);
 
40
 
689
41
  if(dir == NULL){
690
 
    perror("Could not open plugin dir");
691
 
    exitstatus = EXIT_FAILURE;
692
 
    goto fallback;
693
 
  }
694
 
  
695
 
  /* Set the FD_CLOEXEC flag on the directory, if possible */
696
 
  {
697
 
    int dir_fd = dirfd(dir);
698
 
    if(dir_fd >= 0){
699
 
      ret = set_cloexec_flag(dir_fd);
700
 
      if(ret < 0){
701
 
        perror("set_cloexec_flag");
702
 
        exitstatus = EXIT_FAILURE;
703
 
        goto fallback;
704
 
      }
705
 
    }
706
 
  }
707
 
  
708
 
  FD_ZERO(&rfds_all);
709
 
  
710
 
  /* Read and execute any executable in the plugin directory*/
 
42
    fprintf(stderr, "Can not open directory\n");
 
43
    return EXIT_FAILURE;
 
44
  }
 
45
  
 
46
  FD_ZERO(&rfds_orig);
 
47
  
711
48
  while(true){
712
49
    dirst = readdir(dir);
713
50
    
714
 
    /* All directory entries have been processed */
 
51
    // All directory entries have been processed
715
52
    if(dirst == NULL){
716
 
      if(errno == EBADF){
717
 
        perror("readdir");
718
 
        exitstatus = EXIT_FAILURE;
719
 
        goto fallback;
720
 
      }
721
53
      break;
722
54
    }
723
55
    
724
56
    d_name_len = strlen(dirst->d_name);
725
57
    
726
 
    /* Ignore dotfiles, backup files and other junk */
727
 
    {
728
 
      bool bad_name = false;
729
 
      
730
 
      const char const *bad_prefixes[] = { ".", "#", NULL };
731
 
      
732
 
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
733
 
                                           ".dpkg-old",
734
 
                                           ".dpkg-bak",
735
 
                                           ".dpkg-divert", NULL };
736
 
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
737
 
        size_t pre_len = strlen(*pre);
738
 
        if((d_name_len >= pre_len)
739
 
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
740
 
          if(debug){
741
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
742
 
                    " with bad prefix %s\n", dirst->d_name, *pre);
743
 
          }
744
 
          bad_name = true;
745
 
          break;
746
 
        }
747
 
      }
748
 
      if(bad_name){
749
 
        continue;
750
 
      }
751
 
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
752
 
        size_t suf_len = strlen(*suf);
753
 
        if((d_name_len >= suf_len)
754
 
           and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
755
 
                == 0)){
756
 
          if(debug){
757
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
758
 
                    " with bad suffix %s\n", dirst->d_name, *suf);
759
 
          }
760
 
          bad_name = true;
761
 
          break;
762
 
        }
763
 
      }
764
 
      
765
 
      if(bad_name){
766
 
        continue;
767
 
      }
768
 
    }
769
 
    
770
 
    char *filename;
771
 
    if(plugindir == NULL){
772
 
      ret = asprintf(&filename, PDIR "/%s", dirst->d_name);
773
 
    } else {
774
 
      ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
775
 
    }
776
 
    if(ret < 0){
777
 
      perror("asprintf");
778
 
      continue;
779
 
    }
780
 
    
781
 
    ret = stat(filename, &st);
782
 
    if(ret == -1){
783
 
      perror("stat");
784
 
      free(filename);
785
 
      continue;
786
 
    }
787
 
    
788
 
    /* Ignore non-executable files */
789
 
    if(not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
790
 
      if(debug){
791
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
792
 
                " with bad type or mode\n", filename);
793
 
      }
794
 
      free(filename);
795
 
      continue;
796
 
    }
797
 
    
798
 
    plugin *p = getplugin(dirst->d_name);
799
 
    if(p == NULL){
800
 
      perror("getplugin");
801
 
      free(filename);
802
 
      continue;
803
 
    }
804
 
    if(p->disabled){
805
 
      if(debug){
806
 
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
807
 
                dirst->d_name);
808
 
      }
809
 
      free(filename);
810
 
      continue;
811
 
    }
812
 
    {
813
 
      /* Add global arguments to argument list for this plugin */
814
 
      plugin *g = getplugin(NULL);
815
 
      if(g != NULL){
816
 
        for(char **a = g->argv + 1; *a != NULL; a++){
817
 
          if(not add_argument(p, *a)){
818
 
            perror("add_argument");
819
 
          }
820
 
        }
821
 
        /* Add global environment variables */
822
 
        for(char **e = g->environ; *e != NULL; e++){
823
 
          if(not add_environment(p, *e, false)){
824
 
            perror("add_environment");
825
 
          }
826
 
        }
827
 
      }
828
 
    }
829
 
    /* If this plugin has any environment variables, we will call
830
 
       using execve and need to duplicate the environment from this
831
 
       process, too. */
832
 
    if(p->environ[0] != NULL){
833
 
      for(char **e = environ; *e != NULL; e++){
834
 
        if(not add_environment(p, *e, false)){
835
 
          perror("add_environment");
836
 
        }
837
 
      }
838
 
    }
839
 
    
840
 
    int pipefd[2];
841
 
    ret = pipe(pipefd);
842
 
    if(ret == -1){
843
 
      perror("pipe");
844
 
      exitstatus = EXIT_FAILURE;
845
 
      goto fallback;
846
 
    }
847
 
    /* Ask OS to automatic close the pipe on exec */
848
 
    ret = set_cloexec_flag(pipefd[0]);
849
 
    if(ret < 0){
850
 
      perror("set_cloexec_flag");
851
 
      exitstatus = EXIT_FAILURE;
852
 
      goto fallback;
853
 
    }
854
 
    ret = set_cloexec_flag(pipefd[1]);
855
 
    if(ret < 0){
856
 
      perror("set_cloexec_flag");
857
 
      exitstatus = EXIT_FAILURE;
858
 
      goto fallback;
859
 
    }
860
 
    /* Block SIGCHLD until process is safely in process list */
861
 
    ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
862
 
    if(ret < 0){
863
 
      perror("sigprocmask");
864
 
      exitstatus = EXIT_FAILURE;
865
 
      goto fallback;
866
 
    }
867
 
    /* Starting a new process to be watched */
868
 
    pid_t pid = fork();
869
 
    if(pid == -1){
870
 
      perror("fork");
871
 
      exitstatus = EXIT_FAILURE;
872
 
      goto fallback;
873
 
    }
874
 
    if(pid == 0){
875
 
      /* this is the child process */
876
 
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
877
 
      if(ret < 0){
878
 
        perror("sigaction");
879
 
        _exit(EXIT_FAILURE);
880
 
      }
881
 
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
882
 
      if(ret < 0){
883
 
        perror("sigprocmask");
884
 
        _exit(EXIT_FAILURE);
885
 
      }
886
 
      
887
 
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
888
 
      if(ret == -1){
889
 
        perror("dup2");
890
 
        _exit(EXIT_FAILURE);
891
 
      }
892
 
      
893
 
      if(dirfd(dir) < 0){
894
 
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
895
 
           above and must now close it manually here. */
 
58
    // Ignore dotfiles and backup files
 
59
    if (dirst->d_name[0] == '.'
 
60
        or dirst->d_name[d_name_len - 1] == '~'){
 
61
      continue;
 
62
    }
 
63
    
 
64
    char *filename = malloc(d_name_len + plugindir_len + 1);
 
65
    strcpy(filename, plugindir);
 
66
    strcat(filename, "/");
 
67
    strcat(filename, dirst->d_name);    
 
68
    
 
69
    stat(filename, &st);
 
70
 
 
71
    if (S_ISREG(st.st_mode) and (access(filename, X_OK) == 0)){
 
72
      // Starting a new process to be watched
 
73
      process *new_process = malloc(sizeof(process));
 
74
      int pipefd[2];
 
75
      pipe(pipefd);
 
76
      new_process->pid = fork();
 
77
      if(new_process->pid == 0){
 
78
        /* this is the child process */
896
79
        closedir(dir);
897
 
      }
898
 
      if(p->environ[0] == NULL){
899
 
        if(execv(filename, p->argv) < 0){
900
 
          perror("execv");
901
 
          _exit(EXIT_FAILURE);
902
 
        }
903
 
      } else {
904
 
        if(execve(filename, p->argv, p->environ) < 0){
905
 
          perror("execve");
906
 
          _exit(EXIT_FAILURE);
907
 
        }
908
 
      }
909
 
      /* no return */
910
 
    }
911
 
    /* Parent process */
912
 
    close(pipefd[1]);           /* Close unused write end of pipe */
913
 
    free(filename);
914
 
    plugin *new_plugin = getplugin(dirst->d_name);
915
 
    if(new_plugin == NULL){
916
 
      perror("getplugin");
917
 
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
918
 
      if(ret < 0){
919
 
        perror("sigprocmask");
920
 
      }
921
 
      exitstatus = EXIT_FAILURE;
922
 
      goto fallback;
923
 
    }
924
 
    
925
 
    new_plugin->pid = pid;
926
 
    new_plugin->fd = pipefd[0];
927
 
    
928
 
    /* Unblock SIGCHLD so signal handler can be run if this process
929
 
       has already completed */
930
 
    ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
931
 
    if(ret < 0){
932
 
      perror("sigprocmask");
933
 
      exitstatus = EXIT_FAILURE;
934
 
      goto fallback;
935
 
    }
936
 
    
937
 
    FD_SET(new_plugin->fd, &rfds_all);
938
 
    
939
 
    if(maxfd < new_plugin->fd){
940
 
      maxfd = new_plugin->fd;
 
80
        close(pipefd[0]);       /* close unused read end of pipe */
 
81
        dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
 
82
        /* create a new modified argument list */
 
83
        char **new_argv = malloc(sizeof(char *) * argc + 1);
 
84
        new_argv[0] = filename;
 
85
        for(int i = 1; i < argc; i++){
 
86
          new_argv[i] = argv[i];
 
87
        }
 
88
        new_argv[argc] = NULL;
 
89
        if(execv(filename, new_argv) < 0){
 
90
          perror(argv[0]);
 
91
          close(pipefd[1]);
 
92
          exit(EXIT_FAILURE);
 
93
        }
 
94
        /* no return */
 
95
      }
 
96
      close(pipefd[1]);         /* close unused write end of pipe */
 
97
      new_process->fd = pipefd[0];
 
98
      new_process->buffer = malloc(BUFFER_SIZE);
 
99
      if (new_process->buffer == NULL){
 
100
        perror(argv[0]);
 
101
        goto end;
 
102
      }
 
103
      new_process->buffer_size = BUFFER_SIZE;
 
104
      new_process->buffer_length = 0;
 
105
      FD_SET(new_process->fd, &rfds_orig);
 
106
      
 
107
      if (maxfd < new_process->fd){
 
108
        maxfd = new_process->fd;
 
109
      }
 
110
      
 
111
      //List handling
 
112
      new_process->next = process_list;
 
113
      process_list = new_process;
941
114
    }
942
115
  }
943
116
  
944
117
  closedir(dir);
945
 
  dir = NULL;
946
 
  free_plugin(getplugin(NULL));
947
 
  
948
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
949
 
    if(p->pid != 0){
 
118
  
 
119
  if (process_list != NULL){
 
120
    while(true){
 
121
      fd_set rfds = rfds_orig;
 
122
      int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
 
123
      if (select_ret == -1){
 
124
        perror(argv[0]);
 
125
        goto end;
 
126
      }else{    
 
127
        for(process *process_itr = process_list; process_itr != NULL;
 
128
            process_itr = process_itr->next){
 
129
          if(FD_ISSET(process_itr->fd, &rfds)){
 
130
            if(process_itr->buffer_length + BUFFER_SIZE
 
131
               > process_itr->buffer_size){
 
132
                process_itr->buffer = realloc(process_itr->buffer,
 
133
                                              process_itr->buffer_size
 
134
                                              + BUFFER_SIZE);
 
135
                if (process_itr->buffer == NULL){
 
136
                  perror(argv[0]);
 
137
                  goto end;
 
138
                }
 
139
                process_itr->buffer_size += BUFFER_SIZE;
 
140
            }
 
141
            ret = read(process_itr->fd, process_itr->buffer
 
142
                       + process_itr->buffer_length, BUFFER_SIZE);
 
143
            process_itr->buffer_length+=ret;
 
144
            if(ret == 0){
 
145
              /* got EOF */
 
146
              /* wait for process exit */
 
147
              int status;
 
148
              waitpid(process_itr->pid, &status, 0);
 
149
              if(WIFEXITED(status) and WEXITSTATUS(status) == 0){
 
150
                write(STDOUT_FILENO, process_itr->buffer,
 
151
                      process_itr->buffer_length);
 
152
                goto end;
 
153
              } else {
 
154
                FD_CLR(process_itr->fd, &rfds_orig);
 
155
              }
 
156
            }
 
157
          }
 
158
        }
 
159
      }
 
160
    }
 
161
  }
 
162
  
 
163
 end:
 
164
  for(process *process_itr = process_list; process_itr != NULL;
 
165
      process_itr = process_itr->next){
 
166
    close(process_itr->fd);
 
167
    kill(process_itr->pid, SIGTERM);
 
168
    free(process_itr->buffer);
 
169
  }
 
170
  
 
171
  while(true){
 
172
    int status;
 
173
    ret = wait(&status);
 
174
    if (ret == -1){
 
175
      if(errno != ECHILD){
 
176
        perror("wait");
 
177
      }
950
178
      break;
951
179
    }
952
 
    if(p->next == NULL){
953
 
      fprintf(stderr, "No plugin processes started. Incorrect plugin"
954
 
              " directory?\n");
955
 
      free_plugin_list();
956
 
    }
957
 
  }
958
 
  
959
 
  /* Main loop while running plugins exist */
960
 
  while(plugin_list){
961
 
    fd_set rfds = rfds_all;
962
 
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
963
 
    if(select_ret == -1){
964
 
      perror("select");
965
 
      exitstatus = EXIT_FAILURE;
966
 
      goto fallback;
967
 
    }
968
 
    /* OK, now either a process completed, or something can be read
969
 
       from one of them */
970
 
    for(plugin *proc = plugin_list; proc != NULL;){
971
 
      /* Is this process completely done? */
972
 
      if(proc->completed and proc->eof){
973
 
        /* Only accept the plugin output if it exited cleanly */
974
 
        if(not WIFEXITED(proc->status)
975
 
           or WEXITSTATUS(proc->status) != 0){
976
 
          /* Bad exit by plugin */
977
 
          
978
 
          if(debug){
979
 
            if(WIFEXITED(proc->status)){
980
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
981
 
                      " status %d\n", proc->name,
982
 
                      (intmax_t) (proc->pid),
983
 
                      WEXITSTATUS(proc->status));
984
 
            } else if(WIFSIGNALED(proc->status)){
985
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] killed by"
986
 
                      " signal %d: %s\n", proc->name,
987
 
                      (intmax_t) (proc->pid),
988
 
                      WTERMSIG(proc->status),
989
 
                      strsignal(WTERMSIG(proc->status)));
990
 
            } else if(WCOREDUMP(proc->status)){
991
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
992
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
993
 
            }
994
 
          }
995
 
          
996
 
          /* Remove the plugin */
997
 
          FD_CLR(proc->fd, &rfds_all);
998
 
          
999
 
          /* Block signal while modifying process_list */
1000
 
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
1001
 
          if(ret < 0){
1002
 
            perror("sigprocmask");
1003
 
            exitstatus = EXIT_FAILURE;
1004
 
            goto fallback;
1005
 
          }
1006
 
          
1007
 
          plugin *next_plugin = proc->next;
1008
 
          free_plugin(proc);
1009
 
          proc = next_plugin;
1010
 
          
1011
 
          /* We are done modifying process list, so unblock signal */
1012
 
          ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
1013
 
                            NULL);
1014
 
          if(ret < 0){
1015
 
            perror("sigprocmask");
1016
 
            exitstatus = EXIT_FAILURE;
1017
 
            goto fallback;
1018
 
          }
1019
 
          
1020
 
          if(plugin_list == NULL){
1021
 
            break;
1022
 
          }
1023
 
          
1024
 
          continue;
1025
 
        }
1026
 
        
1027
 
        /* This process exited nicely, so print its buffer */
1028
 
        
1029
 
        bool bret = print_out_password(proc->buffer,
1030
 
                                       proc->buffer_length);
1031
 
        if(not bret){
1032
 
          perror("print_out_password");
1033
 
          exitstatus = EXIT_FAILURE;
1034
 
        }
1035
 
        goto fallback;
1036
 
      }
1037
 
      
1038
 
      /* This process has not completed.  Does it have any output? */
1039
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1040
 
        /* This process had nothing to say at this time */
1041
 
        proc = proc->next;
1042
 
        continue;
1043
 
      }
1044
 
      /* Before reading, make the process' data buffer large enough */
1045
 
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1046
 
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1047
 
                               + (size_t) BUFFER_SIZE);
1048
 
        if(proc->buffer == NULL){
1049
 
          perror("malloc");
1050
 
          exitstatus = EXIT_FAILURE;
1051
 
          goto fallback;
1052
 
        }
1053
 
        proc->buffer_size += BUFFER_SIZE;
1054
 
      }
1055
 
      /* Read from the process */
1056
 
      sret = read(proc->fd, proc->buffer + proc->buffer_length,
1057
 
                  BUFFER_SIZE);
1058
 
      if(sret < 0){
1059
 
        /* Read error from this process; ignore the error */
1060
 
        proc = proc->next;
1061
 
        continue;
1062
 
      }
1063
 
      if(sret == 0){
1064
 
        /* got EOF */
1065
 
        proc->eof = true;
1066
 
      } else {
1067
 
        proc->buffer_length += (size_t) sret;
1068
 
      }
1069
 
    }
1070
 
  }
1071
 
  
1072
 
  
1073
 
 fallback:
1074
 
  
1075
 
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
1076
 
    /* Fallback if all plugins failed, none are found or an error
1077
 
       occured */
1078
 
    bool bret;
1079
 
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
1080
 
    char *passwordbuffer = getpass("Password: ");
1081
 
    size_t len = strlen(passwordbuffer);
1082
 
    /* Strip trailing newline */
1083
 
    if(len > 0 and passwordbuffer[len-1] == '\n'){
1084
 
      passwordbuffer[len-1] = '\0'; /* not strictly necessary */
1085
 
      len--;
1086
 
    }
1087
 
    bret = print_out_password(passwordbuffer, len);
1088
 
    if(not bret){
1089
 
      perror("print_out_password");
1090
 
      exitstatus = EXIT_FAILURE;
1091
 
    }
1092
 
  }
1093
 
  
1094
 
  /* Restore old signal handler */
1095
 
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1096
 
  if(ret == -1){
1097
 
    perror("sigaction");
1098
 
    exitstatus = EXIT_FAILURE;
1099
 
  }
1100
 
  
1101
 
  if(custom_argv != NULL){
1102
 
    for(char **arg = custom_argv+1; *arg != NULL; arg++){
1103
 
      free(*arg);
1104
 
    }
1105
 
    free(custom_argv);
1106
 
  }
1107
 
  
1108
 
  if(dir != NULL){
1109
 
    closedir(dir);
1110
 
  }
1111
 
  
1112
 
  /* Kill the processes */
1113
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1114
 
    if(p->pid != 0){
1115
 
      close(p->fd);
1116
 
      ret = kill(p->pid, SIGTERM);
1117
 
      if(ret == -1 and errno != ESRCH){
1118
 
        /* Set-uid proccesses might not get closed */
1119
 
        perror("kill");
1120
 
      }
1121
 
    }
1122
 
  }
1123
 
  
1124
 
  /* Wait for any remaining child processes to terminate */
1125
 
  do{
1126
 
    ret = wait(NULL);
1127
 
  } while(ret >= 0);
1128
 
  if(errno != ECHILD){
1129
 
    perror("wait");
1130
 
  }
1131
 
  
1132
 
  free_plugin_list();
1133
 
  
1134
 
  free(plugindir);
1135
 
  free(argfile);
1136
 
  
1137
 
  return exitstatus;
 
180
  }  
 
181
  return EXIT_SUCCESS;
1138
182
}