/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to plugbasedclient.c

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