/mandos/release

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

« back to all changes in this revision

Viewing changes to 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
23
22
 */
24
23
 
25
24
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
26
 
                                   asprintf(), O_CLOEXEC */
 
25
                                   asprintf() */
27
26
#include <stddef.h>             /* size_t, NULL */
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 */
33
 
#include <sys/types.h>          /* DIR, fdopendir(), stat(), struct
 
30
#include <stdio.h>              /* perror, popen(), fileno(),
 
31
                                   fprintf(), stderr, STDOUT_FILENO */
 
32
#include <sys/types.h>          /* DIR, opendir(), stat(), struct
34
33
                                   stat, waitpid(), WIFEXITED(),
35
34
                                   WEXITSTATUS(), wait(), pid_t,
36
35
                                   uid_t, gid_t, getuid(), getgid(),
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
 
#include <dirent.h>             /* DIR, struct dirent, fdopendir(),
 
43
#include <dirent.h>             /* DIR, struct dirent, opendir(),
46
44
                                   readdir(), closedir(), dirfd() */
47
45
#include <unistd.h>             /* struct stat, stat(), S_ISREG(),
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
 
  /* Open plugin directory with close_on_exec flag */
 
566
 
 
567
  if (plugindir == NULL){
 
568
    dir = opendir(PDIR);
 
569
  } else {
 
570
    dir = opendir(plugindir);
 
571
  }
 
572
  
 
573
  if(dir == NULL){
 
574
    perror("Could not open plugin dir");
 
575
    exitstatus = EXIT_FAILURE;
 
576
    goto fallback;
 
577
  }
 
578
  
 
579
  /* Set the FD_CLOEXEC flag on the directory, if possible */
702
580
  {
703
 
    int dir_fd = -1;
704
 
    if(plugindir == NULL){
705
 
      dir_fd = open(PDIR, O_RDONLY |
706
 
#ifdef O_CLOEXEC
707
 
                    O_CLOEXEC
708
 
#else  /* not O_CLOEXEC */
709
 
                    0
710
 
#endif  /* not O_CLOEXEC */
711
 
                    );
712
 
    } else {
713
 
      dir_fd = open(plugindir, O_RDONLY |
714
 
#ifdef O_CLOEXEC
715
 
                    O_CLOEXEC
716
 
#else  /* not O_CLOEXEC */
717
 
                    0
718
 
#endif  /* not O_CLOEXEC */
719
 
                    );
720
 
    }
721
 
    if(dir_fd == -1){
722
 
      perror("Could not open plugin dir");
723
 
      exitstatus = EXIT_FAILURE;
724
 
      goto fallback;
725
 
    }
726
 
    
727
 
#ifndef O_CLOEXEC
728
 
  /* Set the FD_CLOEXEC flag on the directory */
729
 
    ret = set_cloexec_flag(dir_fd);
730
 
    if(ret < 0){
731
 
      perror("set_cloexec_flag");
732
 
      TEMP_FAILURE_RETRY(close(dir_fd));
733
 
      exitstatus = EXIT_FAILURE;
734
 
      goto fallback;
735
 
    }
736
 
#endif  /* O_CLOEXEC */
737
 
    
738
 
    dir = fdopendir(dir_fd);
739
 
    if(dir == NULL){
740
 
      perror("Could not open plugin dir");
741
 
      TEMP_FAILURE_RETRY(close(dir_fd));
742
 
      exitstatus = EXIT_FAILURE;
743
 
      goto fallback;
 
581
    int dir_fd = dirfd(dir);
 
582
    if(dir_fd >= 0){
 
583
      ret = set_cloexec_flag(dir_fd);
 
584
      if(ret < 0){
 
585
        perror("set_cloexec_flag");
 
586
        exitstatus = EXIT_FAILURE;
 
587
        goto fallback;
 
588
      }
744
589
    }
745
590
  }
746
591
  
747
592
  FD_ZERO(&rfds_all);
748
593
  
749
 
  /* Read and execute any executable in the plugin directory*/
750
594
  while(true){
751
 
    do {
752
 
      dirst = readdir(dir);
753
 
    } while(dirst == NULL and errno == EINTR);
 
595
    dirst = readdir(dir);
754
596
    
755
 
    /* All directory entries have been processed */
 
597
    // All directory entries have been processed
756
598
    if(dirst == NULL){
757
 
      if(errno == EBADF){
 
599
      if (errno == EBADF){
758
600
        perror("readdir");
759
601
        exitstatus = EXIT_FAILURE;
760
602
        goto fallback;
764
606
    
765
607
    d_name_len = strlen(dirst->d_name);
766
608
    
767
 
    /* Ignore dotfiles, backup files and other junk */
 
609
    // Ignore dotfiles, backup files and other junk
768
610
    {
769
611
      bool bad_name = false;
770
612
      
772
614
      
773
615
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
774
616
                                           ".dpkg-old",
775
 
                                           ".dpkg-bak",
776
617
                                           ".dpkg-divert", NULL };
777
618
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
778
619
        size_t pre_len = strlen(*pre);
786
627
          break;
787
628
        }
788
629
      }
 
630
      
789
631
      if(bad_name){
790
632
        continue;
791
633
      }
 
634
      
792
635
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
793
636
        size_t suf_len = strlen(*suf);
794
637
        if((d_name_len >= suf_len)
807
650
        continue;
808
651
      }
809
652
    }
810
 
    
 
653
 
811
654
    char *filename;
812
 
    if(plugindir == NULL){
813
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s",
814
 
                                             dirst->d_name));
815
 
    } else {
816
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s",
817
 
                                             plugindir,
818
 
                                             dirst->d_name));
819
 
    }
 
655
    ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
820
656
    if(ret < 0){
821
657
      perror("asprintf");
822
658
      continue;
823
659
    }
824
660
    
825
 
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
826
 
    if(ret == -1){
 
661
    ret = stat(filename, &st);
 
662
    if (ret == -1){
827
663
      perror("stat");
828
664
      free(filename);
829
665
      continue;
830
666
    }
831
667
    
832
 
    /* Ignore non-executable files */
833
 
    if(not S_ISREG(st.st_mode)
834
 
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
 
668
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
835
669
      if(debug){
836
670
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
837
671
                " with bad type or mode\n", filename);
839
673
      free(filename);
840
674
      continue;
841
675
    }
842
 
    
843
 
    plugin *p = getplugin(dirst->d_name);
 
676
    plugin *p = getplugin(dirst->d_name, &plugin_list);
844
677
    if(p == NULL){
845
678
      perror("getplugin");
846
679
      free(filename);
856
689
    }
857
690
    {
858
691
      /* Add global arguments to argument list for this plugin */
859
 
      plugin *g = getplugin(NULL);
 
692
      plugin *g = getplugin(NULL, &plugin_list);
860
693
      if(g != NULL){
861
694
        for(char **a = g->argv + 1; *a != NULL; a++){
862
695
          if(not add_argument(p, *a)){
865
698
        }
866
699
        /* Add global environment variables */
867
700
        for(char **e = g->environ; *e != NULL; e++){
868
 
          if(not add_environment(p, *e, false)){
 
701
          if(not add_environment(p, *e)){
869
702
            perror("add_environment");
870
703
          }
871
704
        }
876
709
       process, too. */
877
710
    if(p->environ[0] != NULL){
878
711
      for(char **e = environ; *e != NULL; e++){
879
 
        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)){
880
718
          perror("add_environment");
881
719
        }
882
720
      }
883
721
    }
884
722
    
885
723
    int pipefd[2];
886
 
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
887
 
    if(ret == -1){
 
724
    ret = pipe(pipefd);
 
725
    if (ret == -1){
888
726
      perror("pipe");
889
727
      exitstatus = EXIT_FAILURE;
890
728
      goto fallback;
891
729
    }
892
 
    /* Ask OS to automatic close the pipe on exec */
893
730
    ret = set_cloexec_flag(pipefd[0]);
894
731
    if(ret < 0){
895
732
      perror("set_cloexec_flag");
903
740
      goto fallback;
904
741
    }
905
742
    /* Block SIGCHLD until process is safely in process list */
906
 
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
907
 
                                              &sigchld_action.sa_mask,
908
 
                                              NULL));
 
743
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
909
744
    if(ret < 0){
910
745
      perror("sigprocmask");
911
746
      exitstatus = EXIT_FAILURE;
912
747
      goto fallback;
913
748
    }
914
 
    /* Starting a new process to be watched */
915
 
    pid_t pid;
916
 
    do {
917
 
      pid = fork();
918
 
    } while(pid == -1 and errno == EINTR);
 
749
    // Starting a new process to be watched
 
750
    pid_t pid = fork();
919
751
    if(pid == -1){
920
752
      perror("fork");
921
753
      exitstatus = EXIT_FAILURE;
928
760
        perror("sigaction");
929
761
        _exit(EXIT_FAILURE);
930
762
      }
931
 
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
763
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
932
764
      if(ret < 0){
933
765
        perror("sigprocmask");
934
766
        _exit(EXIT_FAILURE);
935
767
      }
936
 
      
 
768
 
937
769
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
938
770
      if(ret == -1){
939
771
        perror("dup2");
958
790
      }
959
791
      /* no return */
960
792
    }
961
 
    /* Parent process */
962
 
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
963
 
                                             pipe */
 
793
    /* parent process */
964
794
    free(filename);
965
 
    plugin *new_plugin = getplugin(dirst->d_name);
966
 
    if(new_plugin == NULL){
967
 
      perror("getplugin");
968
 
      ret = (int)(TEMP_FAILURE_RETRY
969
 
                  (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
970
 
                               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);
971
800
      if(ret < 0){
972
 
        perror("sigprocmask");
 
801
        perror("sigprocmask");
973
802
      }
974
803
      exitstatus = EXIT_FAILURE;
975
804
      goto fallback;
976
805
    }
977
806
    
978
 
    new_plugin->pid = pid;
979
 
    new_plugin->fd = pipefd[0];
980
 
    
 
807
    *new_process = (struct process){ .pid = pid,
 
808
                                     .fd = pipefd[0],
 
809
                                     .next = process_list };
 
810
    // List handling
 
811
    process_list = new_process;
981
812
    /* Unblock SIGCHLD so signal handler can be run if this process
982
813
       has already completed */
983
 
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
984
 
                                              &sigchld_action.sa_mask,
985
 
                                              NULL));
 
814
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
986
815
    if(ret < 0){
987
816
      perror("sigprocmask");
988
817
      exitstatus = EXIT_FAILURE;
989
818
      goto fallback;
990
819
    }
991
820
    
992
 
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
993
 
                                          -Wconversion */
 
821
    FD_SET(new_process->fd, &rfds_all);
994
822
    
995
 
    if(maxfd < new_plugin->fd){
996
 
      maxfd = new_plugin->fd;
 
823
    if (maxfd < new_process->fd){
 
824
      maxfd = new_process->fd;
997
825
    }
 
826
    
998
827
  }
 
828
 
 
829
  free_plugin_list(plugin_list);
 
830
  plugin_list = NULL;
999
831
  
1000
 
  TEMP_FAILURE_RETRY(closedir(dir));
 
832
  closedir(dir);
1001
833
  dir = NULL;
1002
 
  free_plugin(getplugin(NULL));
1003
 
  
1004
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1005
 
    if(p->pid != 0){
1006
 
      break;
1007
 
    }
1008
 
    if(p->next == NULL){
1009
 
      fprintf(stderr, "No plugin processes started. Incorrect plugin"
1010
 
              " directory?\n");
1011
 
      free_plugin_list();
1012
 
    }
 
834
    
 
835
  if (process_list == NULL){
 
836
    fprintf(stderr, "No plugin processes started. Incorrect plugin"
 
837
            " directory?\n");
 
838
    process_list = NULL;
1013
839
  }
1014
 
  
1015
 
  /* Main loop while running plugins exist */
1016
 
  while(plugin_list){
 
840
  while(process_list){
1017
841
    fd_set rfds = rfds_all;
1018
842
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
1019
 
    if(select_ret == -1 and errno != EINTR){
 
843
    if (select_ret == -1){
1020
844
      perror("select");
1021
845
      exitstatus = EXIT_FAILURE;
1022
846
      goto fallback;
1023
847
    }
1024
848
    /* OK, now either a process completed, or something can be read
1025
849
       from one of them */
1026
 
    for(plugin *proc = plugin_list; proc != NULL;){
 
850
    for(process *proc = process_list; proc ; proc = proc->next){
1027
851
      /* Is this process completely done? */
1028
 
      if(proc->completed and proc->eof){
 
852
      if(proc->eof and proc->completed){
1029
853
        /* Only accept the plugin output if it exited cleanly */
1030
854
        if(not WIFEXITED(proc->status)
1031
855
           or WEXITSTATUS(proc->status) != 0){
1032
856
          /* Bad exit by plugin */
1033
 
          
1034
857
          if(debug){
1035
858
            if(WIFEXITED(proc->status)){
1036
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
1037
 
                      " status %d\n", proc->name,
1038
 
                      (intmax_t) (proc->pid),
 
859
              fprintf(stderr, "Plugin %u exited with status %d\n",
 
860
                      (unsigned int) (proc->pid),
1039
861
                      WEXITSTATUS(proc->status));
1040
 
            } else if(WIFSIGNALED(proc->status)){
1041
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] killed by"
1042
 
                      " signal %d: %s\n", proc->name,
1043
 
                      (intmax_t) (proc->pid),
1044
 
                      WTERMSIG(proc->status),
1045
 
                      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));
1046
866
            } else if(WCOREDUMP(proc->status)){
1047
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
1048
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
 
867
              fprintf(stderr, "Plugin %d dumped core\n",
 
868
                      (unsigned int) (proc->pid));
1049
869
            }
1050
870
          }
1051
 
          
1052
871
          /* Remove the plugin */
1053
 
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1054
 
                                          -Wconversion */
1055
 
          
 
872
          FD_CLR(proc->fd, &rfds_all);
1056
873
          /* Block signal while modifying process_list */
1057
 
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1058
 
                                        (SIG_BLOCK,
1059
 
                                         &sigchld_action.sa_mask,
1060
 
                                         NULL));
 
874
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
1061
875
          if(ret < 0){
1062
876
            perror("sigprocmask");
1063
877
            exitstatus = EXIT_FAILURE;
1064
878
            goto fallback;
1065
879
          }
1066
 
          
1067
 
          plugin *next_plugin = proc->next;
1068
 
          free_plugin(proc);
1069
 
          proc = next_plugin;
1070
 
          
 
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
          }
1071
893
          /* We are done modifying process list, so unblock signal */
1072
 
          ret = (int)(TEMP_FAILURE_RETRY
1073
 
                      (sigprocmask(SIG_UNBLOCK,
1074
 
                                   &sigchld_action.sa_mask, NULL)));
 
894
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
 
895
                             NULL);
1075
896
          if(ret < 0){
1076
897
            perror("sigprocmask");
1077
 
            exitstatus = EXIT_FAILURE;
1078
 
            goto fallback;
1079
 
          }
1080
 
          
1081
 
          if(plugin_list == NULL){
1082
 
            break;
1083
 
          }
1084
 
          
1085
 
          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;
1086
905
        }
1087
 
        
1088
906
        /* This process exited nicely, so print its buffer */
1089
 
        
 
907
 
1090
908
        bool bret = print_out_password(proc->buffer,
1091
909
                                       proc->buffer_length);
1092
910
        if(not bret){
1095
913
        }
1096
914
        goto fallback;
1097
915
      }
1098
 
      
1099
916
      /* This process has not completed.  Does it have any output? */
1100
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1101
 
                                                         warning from
1102
 
                                                         -Wconversion */
 
917
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1103
918
        /* This process had nothing to say at this time */
1104
 
        proc = proc->next;
1105
919
        continue;
1106
920
      }
1107
921
      /* Before reading, make the process' data buffer large enough */
1108
922
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1109
923
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1110
924
                               + (size_t) BUFFER_SIZE);
1111
 
        if(proc->buffer == NULL){
 
925
        if (proc->buffer == NULL){
1112
926
          perror("malloc");
1113
927
          exitstatus = EXIT_FAILURE;
1114
928
          goto fallback;
1116
930
        proc->buffer_size += BUFFER_SIZE;
1117
931
      }
1118
932
      /* Read from the process */
1119
 
      sret = TEMP_FAILURE_RETRY(read(proc->fd,
1120
 
                                     proc->buffer
1121
 
                                     + proc->buffer_length,
1122
 
                                     BUFFER_SIZE));
1123
 
      if(sret < 0){
 
933
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
 
934
                 BUFFER_SIZE);
 
935
      if(ret < 0){
1124
936
        /* Read error from this process; ignore the error */
1125
 
        proc = proc->next;
1126
937
        continue;
1127
938
      }
1128
 
      if(sret == 0){
 
939
      if(ret == 0){
1129
940
        /* got EOF */
1130
941
        proc->eof = true;
1131
942
      } else {
1132
 
        proc->buffer_length += (size_t) sret;
 
943
        proc->buffer_length += (size_t) ret;
1133
944
      }
1134
945
    }
1135
946
  }
1136
 
  
1137
 
  
 
947
 
 
948
 
1138
949
 fallback:
1139
950
  
1140
 
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
 
951
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
1141
952
    /* Fallback if all plugins failed, none are found or an error
1142
953
       occured */
1143
954
    bool bret;
1144
955
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
1145
956
    char *passwordbuffer = getpass("Password: ");
1146
 
    size_t len = strlen(passwordbuffer);
1147
 
    /* Strip trailing newline */
1148
 
    if(len > 0 and passwordbuffer[len-1] == '\n'){
1149
 
      passwordbuffer[len-1] = '\0'; /* not strictly necessary */
1150
 
      len--;
1151
 
    }
1152
 
    bret = print_out_password(passwordbuffer, len);
 
957
    bret = print_out_password(passwordbuffer, strlen(passwordbuffer));
1153
958
    if(not bret){
1154
959
      perror("print_out_password");
1155
960
      exitstatus = EXIT_FAILURE;
1162
967
    perror("sigaction");
1163
968
    exitstatus = EXIT_FAILURE;
1164
969
  }
1165
 
  
 
970
 
1166
971
  if(custom_argv != NULL){
1167
972
    for(char **arg = custom_argv+1; *arg != NULL; arg++){
1168
973
      free(*arg);
1169
974
    }
1170
975
    free(custom_argv);
1171
976
  }
 
977
  free_plugin_list(plugin_list);
1172
978
  
1173
979
  if(dir != NULL){
1174
980
    closedir(dir);
1175
981
  }
1176
982
  
1177
 
  /* Kill the processes */
1178
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1179
 
    if(p->pid != 0){
1180
 
      close(p->fd);
1181
 
      ret = kill(p->pid, SIGTERM);
1182
 
      if(ret == -1 and errno != ESRCH){
1183
 
        /* Set-uid proccesses might not get closed */
1184
 
        perror("kill");
1185
 
      }
 
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");
1186
991
    }
 
992
    free(process_list->buffer);
 
993
    free(process_list);
1187
994
  }
1188
995
  
1189
996
  /* Wait for any remaining child processes to terminate */
1190
 
  do {
 
997
  do{
1191
998
    ret = wait(NULL);
1192
999
  } while(ret >= 0);
1193
1000
  if(errno != ECHILD){
1194
1001
    perror("wait");
1195
1002
  }
1196
 
  
1197
 
  free_plugin_list();
1198
 
  
 
1003
 
1199
1004
  free(plugindir);
1200
1005
  free(argfile);
1201
1006