/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: 2015-03-10 18:03:38 UTC
  • mto: (237.7.304 trunk)
  • mto: This revision was merged to the branch mainline in revision 325.
  • Revision ID: teddy@recompile.se-20150310180338-pcxw6r2qmw9k6br9
Add ":!RSA" to GnuTLS priority string, to disallow non-DHE kx.

If Mandos was somehow made to use a non-ephemeral Diffie-Hellman key
exchange algorithm in the TLS handshake, any saved network traffic
could then be decrypted later if the Mandos client key was obtained.
By default, Mandos uses ephemeral DH key exchanges which does not have
this problem, but a non-ephemeral key exchange algorithm was still
enabled by default.  The simplest solution is to simply turn that off,
which ensures that Mandos will always use ephemeral DH key exchanges.

There is a "PFS" priority string specifier, but we can't use it because:

1. Security-wise, it is a mix between "NORMAL" and "SECURE128" - it
   enables a lot more algorithms than "SECURE256".

2. It is only available since GnuTLS 3.2.4.

Thanks to Andreas Fischer <af@bantuX.org> for reporting this issue.

Show diffs side-by-side

added added

removed removed

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