/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 plugin-runner.c

  • Committer: Teddy Hogeborn
  • Date: 2008-08-29 05:53:59 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080829055359-wkdasnyxtylmnxus
* mandos.xml (EXAMPLE): Replaced all occurences of command name with
                        "&COMMANDNAME;".

* plugins.d/password-prompt.c (main): Improved some documentation
                                      strings.  Do perror() of
                                      tcgetattr() fails.  Add debug
                                      output if interrupted by signal.
                                      Loop over write() instead of
                                      using fwrite() when outputting
                                      password.  Add debug output if
                                      getline() returns 0, unless it
                                      was caused by a signal.  Add
                                      exit status code to debug
                                      output.

* plugins.d/password-prompt.xml: Changed all single quotes to double
                                 quotes for consistency.  Removed
                                 <?xml-stylesheet>.
  (ENTITY TIMESTAMP): New.  Automatically updated by Emacs time-stamp
                      by using Emacs local variables.
  (/refentry/refentryinfo/title): Changed to "Mandos Manual".
  (/refentry/refentryinfo/productname): Changed to "Mandos".
  (/refentry/refentryinfo/date): New; set to "&TIMESTAMP;".
  (/refentry/refentryinfo/copyright): Split copyright holders.
  (/refentry/refnamediv/refpurpose): Improved wording.
  (SYNOPSIS): Fix to use correct markup.  Add short options.
  (DESCRIPTION, OPTIONS): Improved wording.
  (OPTIONS): Improved wording.  Use more correct markup.  Document
             short options.
  (EXIT STATUS): Add text.
  (ENVIRONMENT): Document use of "cryptsource" and "crypttarget".
  (FILES): REMOVED.
  (BUGS): Add text.
  (EXAMPLE): Added some examples.
  (SECURITY): Added text.
  (SEE ALSO): Remove reference to mandos(8).  Add reference to
              crypttab(5).

Show diffs side-by-side

added added

removed removed

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