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