/mandos/trunk

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

« back to all changes in this revision

Viewing changes to plugin-runner.c

  • Committer: Teddy Hogeborn
  • Date: 2015-03-10 18:03:38 UTC
  • 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
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY() */
25
 
 
 
25
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
 
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>             /* strtok, strlen(), strcpy(),
55
 
                                   strcat() */
 
53
                                   FD_CLOEXEC, openat(), scandirat(),
 
54
                                   pipe2() */
 
55
#include <string.h>             /* strsep, strlen(), strsignal(),
 
56
                                   strcmp(), strncmp() */
56
57
#include <errno.h>              /* errno */
57
58
#include <argp.h>               /* struct argp_option, struct
58
59
                                   argp_state, struct argp,
59
60
                                   argp_parse(), ARGP_ERR_UNKNOWN,
60
 
                                   ARGP_KEY_END, ARGP_KEY_ARG, error_t */
 
61
                                   ARGP_KEY_END, ARGP_KEY_ARG,
 
62
                                   error_t */
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
 
const char *argp_program_version = "mandos-client 1.0";
70
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
71
 
 
72
 
struct process;
73
 
 
74
 
typedef struct process{
 
78
#define PDIR "/lib/mandos/plugins.d"
 
79
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
 
80
 
 
81
const char *argp_program_version = "plugin-runner " VERSION;
 
82
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
83
 
 
84
typedef struct plugin{
 
85
  char *name;                   /* can be NULL or any plugin name */
 
86
  char **argv;
 
87
  int argc;
 
88
  char **environ;
 
89
  int envc;
 
90
  bool disabled;
 
91
  
 
92
  /* Variables used for running processes*/
75
93
  pid_t pid;
76
94
  int fd;
77
95
  char *buffer;
78
96
  size_t buffer_size;
79
97
  size_t buffer_length;
80
98
  bool eof;
81
 
  bool completed;
 
99
  volatile sig_atomic_t completed;
82
100
  int status;
83
 
  struct process *next;
84
 
} process;
85
 
 
86
 
typedef struct plugin{
87
 
  char *name;                   /* can be NULL or any plugin name */
88
 
  char **argv;
89
 
  int argc;
90
 
  bool disabled;
91
101
  struct plugin *next;
92
102
} plugin;
93
103
 
94
 
static plugin *getplugin(char *name, plugin **plugin_list){
95
 
  for (plugin *p = *plugin_list; p != NULL; p = p->next){
96
 
    if ((p->name == name)
97
 
        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))){
98
114
      return p;
99
115
    }
100
116
  }
101
117
  /* Create a new plugin */
102
 
  plugin *new_plugin = malloc(sizeof(plugin));
103
 
  if (new_plugin == NULL){
104
 
    perror("malloc");
105
 
    exit(EXIT_FAILURE);
106
 
  }
107
 
  new_plugin->name = name;
108
 
  new_plugin->argv = malloc(sizeof(char *) * 2);
109
 
  if (new_plugin->argv == NULL){
110
 
    perror("malloc");
111
 
    exit(EXIT_FAILURE);
112
 
  }
113
 
  new_plugin->argv[0] = name;
 
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){
 
123
    return NULL;
 
124
  }
 
125
  char *copy_name = NULL;
 
126
  if(name != NULL){
 
127
    do {
 
128
      copy_name = strdup(name);
 
129
    } while(copy_name == NULL and errno == EINTR);
 
130
    if(copy_name == NULL){
 
131
      int e = errno;
 
132
      free(new_plugin);
 
133
      errno = e;
 
134
      return NULL;
 
135
    }
 
136
  }
 
137
  
 
138
  *new_plugin = (plugin){ .name = copy_name,
 
139
                          .argc = 1,
 
140
                          .disabled = false,
 
141
                          .next = plugin_list };
 
142
  
 
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;
 
148
    free(copy_name);
 
149
    free(new_plugin);
 
150
    errno = e;
 
151
    return NULL;
 
152
  }
 
153
  new_plugin->argv[0] = copy_name;
114
154
  new_plugin->argv[1] = NULL;
115
 
  new_plugin->argc = 1;
116
 
  new_plugin->disabled = false;
117
 
  new_plugin->next = *plugin_list;
 
155
  
 
156
  do {
 
157
    new_plugin->environ = malloc(sizeof(char *));
 
158
  } while(new_plugin->environ == NULL and errno == EINTR);
 
159
  if(new_plugin->environ == NULL){
 
160
    int e = errno;
 
161
    free(copy_name);
 
162
    free(new_plugin->argv);
 
163
    free(new_plugin);
 
164
    errno = e;
 
165
    return NULL;
 
166
  }
 
167
  new_plugin->environ[0] = NULL;
 
168
  
118
169
  /* Append the new plugin to the list */
119
 
  *plugin_list = new_plugin;
 
170
  plugin_list = new_plugin;
120
171
  return new_plugin;
121
172
}
122
173
 
123
 
static void addargument(plugin *p, char *arg){
124
 
  p->argv[p->argc] = arg;
125
 
  p->argv = realloc(p->argv, sizeof(char *) * (size_t)(p->argc + 2));
126
 
  if (p->argv == NULL){
127
 
    perror("malloc");
128
 
    exit(EXIT_FAILURE);
129
 
  }
130
 
  p->argc++;
131
 
  p->argv[p->argc] = NULL;
132
 
}
133
 
 
 
174
/* Helper function for add_argument and add_environment */
 
175
__attribute__((nonnull, warn_unused_result))
 
176
static bool add_to_char_array(const char *new, char ***array,
 
177
                              int *len){
 
178
  /* Resize the pointed-to array to hold one more pointer */
 
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);
 
184
  /* Malloc check */
 
185
  if(new_array == NULL){
 
186
    return false;
 
187
  }
 
188
  *array = new_array;
 
189
  /* Make a copy of the new string */
 
190
  char *copy;
 
191
  do {
 
192
    copy = strdup(new);
 
193
  } while(copy == NULL and errno == EINTR);
 
194
  if(copy == NULL){
 
195
    return false;
 
196
  }
 
197
  /* Insert the copy */
 
198
  (*array)[*len] = copy;
 
199
  (*len)++;
 
200
  /* Add a new terminating NULL pointer to the last element */
 
201
  (*array)[*len] = NULL;
 
202
  return true;
 
203
}
 
204
 
 
205
/* Add to a plugin's argument vector */
 
206
__attribute__((nonnull(2), warn_unused_result))
 
207
static bool add_argument(plugin *p, const char *arg){
 
208
  if(p == NULL){
 
209
    return false;
 
210
  }
 
211
  return add_to_char_array(arg, &(p->argv), &(p->argc));
 
212
}
 
213
 
 
214
/* Add to a plugin's environment */
 
215
__attribute__((nonnull(2), warn_unused_result))
 
216
static bool add_environment(plugin *p, const char *def, bool replace){
 
217
  if(p == NULL){
 
218
    return false;
 
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
  }
 
240
  return add_to_char_array(def, &(p->environ), &(p->envc));
 
241
}
 
242
 
 
243
#ifndef O_CLOEXEC
134
244
/*
135
245
 * Based on the example in the GNU LibC manual chapter 13.13 "File
136
246
 * Descriptor Flags".
137
 
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
 
247
 | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
138
248
 */
139
 
static int set_cloexec_flag(int fd)
140
 
{
141
 
  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));
142
252
  /* If reading the flags failed, return error indication now. */
143
253
  if(ret < 0){
144
254
    return ret;
145
255
  }
146
256
  /* Store modified flag word in the descriptor. */
147
 
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
 
257
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
 
258
                                       ret | FD_CLOEXEC));
148
259
}
149
 
 
150
 
process *process_list = NULL;
151
 
 
152
 
/* Mark a process as completed when it exits, and save its exit
 
260
#endif  /* not O_CLOEXEC */
 
261
 
 
262
 
 
263
/* Mark processes as completed when they exit, and save their exit
153
264
   status. */
154
 
void handle_sigchld(__attribute__((unused)) int sig){
155
 
  process *proc = process_list;
156
 
  int status;
157
 
  pid_t pid = wait(&status);
158
 
  if(pid == -1){
159
 
    perror("wait");
160
 
    return;
161
 
  }
162
 
  while(proc != NULL and proc->pid != pid){
163
 
    proc = proc->next;
164
 
  }
165
 
  if(proc == NULL){
166
 
    /* Process not found in process list */
167
 
    return;
168
 
  }
169
 
  proc->status = status;
170
 
  proc->completed = true;
 
265
static void handle_sigchld(__attribute__((unused)) int sig){
 
266
  int old_errno = errno;
 
267
  while(true){
 
268
    plugin *proc = plugin_list;
 
269
    int status;
 
270
    pid_t pid = waitpid(-1, &status, WNOHANG);
 
271
    if(pid == 0){
 
272
      /* Only still running child processes */
 
273
      break;
 
274
    }
 
275
    if(pid == -1){
 
276
      if(errno == ECHILD){
 
277
        /* No child processes */
 
278
        break;
 
279
      }
 
280
      error(0, errno, "waitpid");
 
281
    }
 
282
    
 
283
    /* A child exited, find it in process_list */
 
284
    while(proc != NULL and proc->pid != pid){
 
285
      proc = proc->next;
 
286
    }
 
287
    if(proc == NULL){
 
288
      /* Process not found in process list */
 
289
      continue;
 
290
    }
 
291
    proc->status = status;
 
292
    proc->completed = 1;
 
293
  }
 
294
  errno = old_errno;
171
295
}
172
296
 
173
 
bool print_out_password(const char *buffer, size_t length){
174
 
  size_t ret;
175
 
  for(size_t written = 0; written < length; written += ret){
 
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){
 
300
  ssize_t ret;
 
301
  for(size_t written = 0; written < length; written += (size_t)ret){
176
302
    ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
177
303
                                   length - written));
178
304
    if(ret < 0){
182
308
  return true;
183
309
}
184
310
 
 
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);
 
345
  }
 
346
}
 
347
 
185
348
int main(int argc, char *argv[]){
186
 
  const char *plugindir = "/conf/conf.d/mandos/plugins.d";
187
 
  size_t d_name_len;
188
 
  DIR *dir = NULL;
189
 
  struct dirent *dirst;
 
349
  char *plugindir = NULL;
 
350
  char *argfile = NULL;
 
351
  FILE *conffp;
 
352
  struct dirent **direntries = NULL;
190
353
  struct stat st;
191
354
  fd_set rfds_all;
192
355
  int ret, maxfd = 0;
 
356
  ssize_t sret;
193
357
  uid_t uid = 65534;
194
358
  gid_t gid = 65534;
195
359
  bool debug = false;
197
361
  struct sigaction old_sigchld_action;
198
362
  struct sigaction sigchld_action = { .sa_handler = handle_sigchld,
199
363
                                      .sa_flags = SA_NOCLDSTOP };
200
 
  char *plus_options = NULL;
201
 
  char **plus_argv = NULL;
 
364
  char **custom_argv = NULL;
 
365
  int custom_argc = 0;
 
366
  int dir_fd = -1;
202
367
  
203
368
  /* Establish a signal handler */
204
369
  sigemptyset(&sigchld_action.sa_mask);
205
370
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
206
 
  if(ret < 0){
207
 
    perror("sigaddset");
208
 
    exit(EXIT_FAILURE);
 
371
  if(ret == -1){
 
372
    error(0, errno, "sigaddset");
 
373
    exitstatus = EX_OSERR;
 
374
    goto fallback;
209
375
  }
210
376
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
211
 
  if(ret < 0){
212
 
    perror("sigaction");
213
 
    exit(EXIT_FAILURE);
 
377
  if(ret == -1){
 
378
    error(0, errno, "sigaction");
 
379
    exitstatus = EX_OSERR;
 
380
    goto fallback;
214
381
  }
215
382
  
216
383
  /* The options we understand. */
218
385
    { .name = "global-options", .key = 'g',
219
386
      .arg = "OPTION[,OPTION[,...]]",
220
387
      .doc = "Options passed to all plugins" },
 
388
    { .name = "global-env", .key = 'G',
 
389
      .arg = "VAR=value",
 
390
      .doc = "Environment variable passed to all plugins" },
221
391
    { .name = "options-for", .key = 'o',
222
392
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
223
393
      .doc = "Options passed only to specified plugin" },
 
394
    { .name = "env-for", .key = 'E',
 
395
      .arg = "PLUGIN:ENV=value",
 
396
      .doc = "Environment variable passed to specified plugin" },
224
397
    { .name = "disable", .key = 'd',
225
398
      .arg = "PLUGIN",
226
399
      .doc = "Disable a specific plugin", .group = 1 },
 
400
    { .name = "enable", .key = 'e',
 
401
      .arg = "PLUGIN",
 
402
      .doc = "Enable a specific plugin", .group = 1 },
227
403
    { .name = "plugin-dir", .key = 128,
228
404
      .arg = "DIRECTORY",
229
405
      .doc = "Specify a different plugin directory", .group = 2 },
230
 
    { .name = "userid", .key = 129,
231
 
      .arg = "ID", .flags = 0,
232
 
      .doc = "User ID the plugins will run as", .group = 2 },
233
 
    { .name = "groupid", .key = 130,
234
 
      .arg = "ID", .flags = 0,
235
 
      .doc = "Group ID the plugins will run as", .group = 2 },
236
 
    { .name = "debug", .key = 131,
237
 
      .doc = "Debug mode", .group = 3 },
 
406
    { .name = "config-file", .key = 129,
 
407
      .arg = "FILE",
 
408
      .doc = "Specify a different configuration file", .group = 2 },
 
409
    { .name = "userid", .key = 130,
 
410
      .arg = "ID", .flags = 0,
 
411
      .doc = "User ID the plugins will run as", .group = 3 },
 
412
    { .name = "groupid", .key = 131,
 
413
      .arg = "ID", .flags = 0,
 
414
      .doc = "Group ID the plugins will run as", .group = 3 },
 
415
    { .name = "debug", .key = 132,
 
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 },
238
426
    { .name = NULL }
239
427
  };
240
428
  
241
 
  error_t parse_opt (int key, char *arg, struct argp_state *state) {
242
 
    /* Get the INPUT argument from `argp_parse', which we know is a
243
 
       pointer to our plugin list pointer. */
244
 
    plugin **plugins = state->input;
245
 
    switch (key) {
246
 
    case 'g':
247
 
      if (arg != NULL){
248
 
        char *p = strtok(arg, ",");
249
 
        do{
250
 
          addargument(getplugin(NULL, plugins), p);
251
 
          p = strtok(NULL, ",");
252
 
        } while (p != NULL);
253
 
      }
254
 
      break;
255
 
    case 'o':
256
 
      if (arg != NULL){
257
 
        char *name = strtok(arg, ":");
258
 
        char *p = strtok(NULL, ":");
259
 
        if(p != NULL){
260
 
          p = strtok(p, ",");
261
 
          do{
262
 
            addargument(getplugin(name, plugins), p);
263
 
            p = strtok(NULL, ",");
264
 
          } while (p != NULL);
265
 
        }
266
 
      }
267
 
      break;
268
 
    case 'd':
269
 
      if (arg != NULL){
270
 
        getplugin(arg, plugins)->disabled = true;
271
 
      }
272
 
      break;
273
 
    case 128:
274
 
      plugindir = arg;
275
 
      break;
276
 
    case 129:
277
 
      uid = (uid_t)strtol(arg, NULL, 10);
278
 
      break;
279
 
    case 130:
280
 
      gid = (gid_t)strtol(arg, NULL, 10);
281
 
      break;
282
 
    case 131:
 
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 */
 
476
      {
 
477
        char *envdef = strchr(arg, ':');
 
478
        if(envdef == NULL){
 
479
          argp_error(state, "No colon in \"%s\"", arg);
 
480
          errno = EINVAL;
 
481
          break;
 
482
        }
 
483
        *envdef = '\0';
 
484
        envdef++;
 
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);
 
515
      plugindir = strdup(arg);
 
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 */
283
546
      debug = true;
284
547
      break;
285
 
    case ARGP_KEY_ARG:
286
 
      if(plus_options != NULL or arg == NULL or arg[0] != '+'){
287
 
        argp_usage (state);
288
 
      }
289
 
      plus_options = arg;
290
 
      break;
291
 
    case ARGP_KEY_END:
292
 
      break;
293
 
    default:
294
 
      return ARGP_ERR_UNKNOWN;
295
 
    }
296
 
    return 0;
297
 
  }
298
 
  
299
 
  plugin *plugin_list = NULL;
300
 
  
301
 
  struct argp argp = { .options = options, .parser = parse_opt,
302
 
                       .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
 
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);
 
596
      argfile = strdup(arg);
 
597
      if(argfile != NULL){
 
598
        errno = 0;
 
599
      }
 
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 */
 
607
    case ARGP_KEY_ARG:
 
608
      break;
 
609
    default:
 
610
      return ARGP_ERR_UNKNOWN;
 
611
    }
 
612
    return errno;
 
613
  }
 
614
  
 
615
  struct argp argp = { .options = options,
 
616
                       .parser = parse_opt_config_file,
 
617
                       .args_doc = "",
303
618
                       .doc = "Mandos plugin runner -- Run plugins" };
304
619
  
305
 
  ret = argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
306
 
  if (ret == ARGP_ERR_UNKNOWN){
307
 
    fprintf(stderr, "Unkown error while parsing arguments\n");
308
 
    exitstatus = EXIT_FAILURE;
309
 
    goto end;
310
 
  }
311
 
  
312
 
  if(plus_options){
313
 
    /* This is a mangled argument in the form of
314
 
     "+--option+--other-option=parameter+--yet-another-option", etc */
315
 
    /* Make new argc and argv vars, and call argp_parse() again. */
316
 
    plus_options++;             /* skip the first '+' character */
317
 
    const char delims[] = "+";
318
 
    char *arg;
319
 
    int new_argc = 1;
320
 
    plus_argv = malloc(sizeof(char*) * 2);
321
 
    if(plus_argv == NULL){
322
 
      perror("malloc");
323
 
      exitstatus = EXIT_FAILURE;
324
 
      goto end;
325
 
    }
326
 
    plus_argv[0] = argv[0];
327
 
    plus_argv[1] = NULL;
328
 
    arg = strtok(plus_options, delims); /* Get first argument */
329
 
    while(arg != NULL){
330
 
      new_argc++;
331
 
      plus_argv = realloc(plus_argv, sizeof(char *)
332
 
                         * ((unsigned int) new_argc + 1));
333
 
      if(plus_argv == NULL){
334
 
        perror("realloc");
335
 
        exitstatus = EXIT_FAILURE;
336
 
        goto end;
337
 
      }
338
 
      plus_argv[new_argc-1] = arg;
339
 
      plus_argv[new_argc] = NULL;
340
 
      arg = strtok(NULL, delims); /* Get next argument */
341
 
    }
342
 
    ret = argp_parse (&argp, new_argc, plus_argv, 0, 0, &plugin_list);
343
 
    if (ret == ARGP_ERR_UNKNOWN){
344
 
      fprintf(stderr, "Unkown error while parsing arguments\n");
345
 
      exitstatus = EXIT_FAILURE;
346
 
      goto end;
347
 
    }
 
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;
 
636
    goto fallback;
 
637
  }
 
638
  
 
639
  /* Reset to the normal argument parser */
 
640
  argp.parser = parse_opt;
 
641
  
 
642
  /* Open the configfile if available */
 
643
  if(argfile == NULL){
 
644
    conffp = fopen(AFILE, "r");
 
645
  } else {
 
646
    conffp = fopen(argfile, "r");
 
647
  }
 
648
  if(conffp != NULL){
 
649
    char *org_line = NULL;
 
650
    char *p, *arg, *new_arg, *line;
 
651
    size_t size = 0;
 
652
    const char whitespace_delims[] = " \r\t\f\v\n";
 
653
    const char comment_delim[] = "#";
 
654
    
 
655
    custom_argc = 1;
 
656
    custom_argv = malloc(sizeof(char*) * 2);
 
657
    if(custom_argv == NULL){
 
658
      error(0, errno, "malloc");
 
659
      exitstatus = EX_OSERR;
 
660
      goto fallback;
 
661
    }
 
662
    custom_argv[0] = argv[0];
 
663
    custom_argv[1] = NULL;
 
664
    
 
665
    /* for each line in the config file, strip whitespace and ignore
 
666
       commented text */
 
667
    while(true){
 
668
      sret = getline(&org_line, &size, conffp);
 
669
      if(sret == -1){
 
670
        break;
 
671
      }
 
672
      
 
673
      line = org_line;
 
674
      arg = strsep(&line, comment_delim);
 
675
      while((p = strsep(&arg, whitespace_delims)) != NULL){
 
676
        if(p[0] == '\0'){
 
677
          continue;
 
678
        }
 
679
        new_arg = strdup(p);
 
680
        if(new_arg == NULL){
 
681
          error(0, errno, "strdup");
 
682
          exitstatus = EX_OSERR;
 
683
          free(org_line);
 
684
          goto fallback;
 
685
        }
 
686
        
 
687
        custom_argc += 1;
 
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
          }
 
701
        }
 
702
        custom_argv[custom_argc-1] = new_arg;
 
703
        custom_argv[custom_argc] = NULL;
 
704
      }
 
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
    }
 
714
    free(org_line);
 
715
  } else {
 
716
    /* Check for harmful errors and go to fallback. Other errors might
 
717
       not affect opening plugins */
 
718
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
 
719
      error(0, errno, "fopen");
 
720
      exitstatus = EX_OSERR;
 
721
      goto fallback;
 
722
    }
 
723
  }
 
724
  /* If there were any arguments from the configuration file, pass
 
725
     them to parser as command line arguments */
 
726
  if(custom_argv != NULL){
 
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;
 
741
      goto fallback;
 
742
    }
 
743
  }
 
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;
348
762
  }
349
763
  
350
764
  if(debug){
354
768
      for(char **a = p->argv; *a != NULL; a++){
355
769
        fprintf(stderr, "\tArg: %s\n", *a);
356
770
      }
357
 
    }
358
 
  }
359
 
  
 
771
      fprintf(stderr, "...and %d environment variables\n", p->envc);
 
772
      for(char **a = p->environ; *a != NULL; a++){
 
773
        fprintf(stderr, "\t%s\n", *a);
 
774
      }
 
775
    }
 
776
  }
 
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
  }
360
807
  ret = setuid(uid);
361
 
  if (ret == -1){
362
 
    perror("setuid");
363
 
  }
364
 
  
365
 
  setgid(gid);
366
 
  if (ret == -1){
367
 
    perror("setgid");
368
 
  }
369
 
  
370
 
  dir = opendir(plugindir);
371
 
  if(dir == NULL){
372
 
    perror("Could not open plugin dir");
373
 
    exitstatus = EXIT_FAILURE;
374
 
    goto end;
375
 
  }
376
 
  
377
 
  /* 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 */
378
813
  {
379
 
    int dir_fd = dirfd(dir);
380
 
    if(dir_fd >= 0){
381
 
      ret = set_cloexec_flag(dir_fd);
382
 
      if(ret < 0){
383
 
        perror("set_cloexec_flag");
384
 
        exitstatus = EXIT_FAILURE;
385
 
        goto end;
 
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;
386
858
      }
387
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;
388
880
  }
389
881
  
390
882
  FD_ZERO(&rfds_all);
391
883
  
392
 
  while(true){
393
 
    dirst = readdir(dir);
394
 
    
395
 
    // All directory entries have been processed
396
 
    if(dirst == NULL){
397
 
      if (errno == EBADF){
398
 
        perror("readdir");
399
 
        exitstatus = EXIT_FAILURE;
400
 
        goto end;
401
 
      }
402
 
      break;
403
 
    }
404
 
    
405
 
    d_name_len = strlen(dirst->d_name);
406
 
    
407
 
    // Ignore dotfiles, backup files and other junk
408
 
    {
409
 
      bool bad_name = false;
410
 
      
411
 
      const char const *bad_prefixes[] = { ".", "#", NULL };
412
 
      
413
 
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
414
 
                                           ".dpkg-old",
415
 
                                           ".dpkg-divert", NULL };
416
 
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
417
 
        size_t pre_len = strlen(*pre);
418
 
        if((d_name_len >= pre_len)
419
 
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
420
 
          if(debug){
421
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
422
 
                    " with bad prefix %s\n", dirst->d_name, *pre);
423
 
          }
424
 
          bad_name = true;
425
 
          break;
426
 
        }
427
 
      }
428
 
      
429
 
      if(bad_name){
430
 
        continue;
431
 
      }
432
 
      
433
 
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
434
 
        size_t suf_len = strlen(*suf);
435
 
        if((d_name_len >= suf_len)
436
 
           and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
437
 
                == 0)){
438
 
          if(debug){
439
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
440
 
                    " with bad suffix %s\n", dirst->d_name, *suf);
441
 
          }
442
 
          bad_name = true;
443
 
          break;
444
 
        }
445
 
      }
446
 
      
447
 
      if(bad_name){
448
 
        continue;
449
 
      }
450
 
    }
451
 
    
452
 
    char *filename = malloc(d_name_len + strlen(plugindir) + 2);
453
 
    if (filename == NULL){
454
 
      perror("malloc");
455
 
      exitstatus = EXIT_FAILURE;
456
 
      goto end;
457
 
    }
458
 
    strcpy(filename, plugindir); /* Spurious warning */
459
 
    strcat(filename, "/");      /* Spurious warning */
460
 
    strcat(filename, dirst->d_name); /* Spurious warning */
461
 
    
462
 
    ret = stat(filename, &st);
463
 
    if (ret == -1){
464
 
      perror("stat");
465
 
      exitstatus = EXIT_FAILURE;
466
 
      goto end;
467
 
    }
468
 
    
469
 
    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)){
470
905
      if(debug){
471
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
472
 
                " 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);
473
910
      }
474
 
      free(filename);
475
 
      continue;
476
 
    }
477
 
    if(getplugin(dirst->d_name, &plugin_list)->disabled){
 
911
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
912
      free(direntries[i]);
 
913
      continue;
 
914
    }
 
915
    
 
916
    plugin *p = getplugin(direntries[i]->d_name);
 
917
    if(p == NULL){
 
918
      error(0, errno, "getplugin");
 
919
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
920
      free(direntries[i]);
 
921
      continue;
 
922
    }
 
923
    if(p->disabled){
478
924
      if(debug){
479
925
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
480
 
                dirst->d_name);
 
926
                direntries[i]->d_name);
481
927
      }
482
 
      free(filename);
 
928
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
929
      free(direntries[i]);
483
930
      continue;
484
931
    }
485
 
    plugin *p = getplugin(dirst->d_name, &plugin_list);
486
932
    {
487
933
      /* Add global arguments to argument list for this plugin */
488
 
      plugin *g = getplugin(NULL, &plugin_list);
489
 
      for(char **a = g->argv + 1; *a != NULL; a++){
490
 
        addargument(p, *a);
491
 
      }
492
 
    }
493
 
    int pipefd[2]; 
494
 
    ret = pipe(pipefd);
495
 
    if (ret == -1){
496
 
      perror("pipe");
497
 
      exitstatus = EXIT_FAILURE;
498
 
      goto end;
499
 
    }
 
934
      plugin *g = getplugin(NULL);
 
935
      if(g != NULL){
 
936
        for(char **a = g->argv + 1; *a != NULL; a++){
 
937
          if(not add_argument(p, *a)){
 
938
            error(0, errno, "add_argument");
 
939
          }
 
940
        }
 
941
        /* Add global environment variables */
 
942
        for(char **e = g->environ; *e != NULL; e++){
 
943
          if(not add_environment(p, *e, false)){
 
944
            error(0, errno, "add_environment");
 
945
          }
 
946
        }
 
947
      }
 
948
    }
 
949
    /* If this plugin has any environment variables, we need to
 
950
       duplicate the environment from this process, too. */
 
951
    if(p->environ[0] != NULL){
 
952
      for(char **e = environ; *e != NULL; e++){
 
953
        if(not add_environment(p, *e, false)){
 
954
          error(0, errno, "add_environment");
 
955
        }
 
956
      }
 
957
    }
 
958
    
 
959
    int pipefd[2];
 
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 */
500
982
    ret = set_cloexec_flag(pipefd[0]);
501
983
    if(ret < 0){
502
 
      perror("set_cloexec_flag");
503
 
      exitstatus = EXIT_FAILURE;
504
 
      goto end;
 
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]);
 
989
      goto fallback;
505
990
    }
506
991
    ret = set_cloexec_flag(pipefd[1]);
507
992
    if(ret < 0){
508
 
      perror("set_cloexec_flag");
509
 
      exitstatus = EXIT_FAILURE;
510
 
      goto end;
 
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]);
 
998
      goto fallback;
511
999
    }
 
1000
#endif  /* not O_CLOEXEC */
512
1001
    /* Block SIGCHLD until process is safely in process list */
513
 
    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));
514
1005
    if(ret < 0){
515
 
      perror("sigprocmask");
516
 
      exitstatus = EXIT_FAILURE;
517
 
      goto end;
518
 
    }
519
 
    // Starting a new process to be watched
520
 
    pid_t pid = fork();
 
1006
      error(0, errno, "sigprocmask");
 
1007
      exitstatus = EX_OSERR;
 
1008
      free(direntries[i]);
 
1009
      goto fallback;
 
1010
    }
 
1011
    /* Starting a new process to be watched */
 
1012
    pid_t pid;
 
1013
    do {
 
1014
      pid = fork();
 
1015
    } while(pid == -1 and errno == EINTR);
 
1016
    if(pid == -1){
 
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]);
 
1024
      goto fallback;
 
1025
    }
521
1026
    if(pid == 0){
522
1027
      /* this is the child process */
523
1028
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
524
1029
      if(ret < 0){
525
 
        perror("sigaction");
526
 
        _exit(EXIT_FAILURE);
 
1030
        error(0, errno, "sigaction");
 
1031
        _exit(EX_OSERR);
527
1032
      }
528
 
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
1033
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
529
1034
      if(ret < 0){
530
 
        perror("sigprocmask");
531
 
        _exit(EXIT_FAILURE);
 
1035
        error(0, errno, "sigprocmask");
 
1036
        _exit(EX_OSERR);
532
1037
      }
533
 
 
 
1038
      
534
1039
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
535
1040
      if(ret == -1){
536
 
        perror("dup2");
537
 
        _exit(EXIT_FAILURE);
 
1041
        error(0, errno, "dup2");
 
1042
        _exit(EX_OSERR);
538
1043
      }
539
1044
      
540
 
      if(dirfd(dir) < 0){
541
 
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
542
 
           above and must now close it manually here. */
543
 
        closedir(dir);
544
 
      }
545
 
      if(execv(filename, p->argv) < 0){
546
 
        perror("execv");
547
 
        _exit(EXIT_FAILURE);
 
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);
548
1051
      }
549
1052
      /* no return */
550
1053
    }
551
 
    /* parent process */
552
 
    free(filename);
553
 
    close(pipefd[1]);           /* close unused write end of pipe */
554
 
    process *new_process = malloc(sizeof(process));
555
 
    if (new_process == NULL){
556
 
      perror("malloc");
557
 
      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)));
558
1064
      if(ret < 0){
559
 
        perror("sigprocmask");
 
1065
        error(0, errno, "sigprocmask");
560
1066
      }
561
 
      exitstatus = EXIT_FAILURE;
562
 
      goto end;
 
1067
      exitstatus = EX_OSERR;
 
1068
      free(direntries[i]);
 
1069
      goto fallback;
563
1070
    }
564
 
    
565
 
    *new_process = (struct process){ .pid = pid,
566
 
                                     .fd = pipefd[0],
567
 
                                     .next = process_list };
568
 
    // List handling
569
 
    process_list = new_process;
 
1071
    free(direntries[i]);
 
1072
    
 
1073
    new_plugin->pid = pid;
 
1074
    new_plugin->fd = pipefd[0];
 
1075
    
570
1076
    /* Unblock SIGCHLD so signal handler can be run if this process
571
1077
       has already completed */
572
 
    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));
573
1081
    if(ret < 0){
574
 
      perror("sigprocmask");
575
 
      exitstatus = EXIT_FAILURE;
576
 
      goto end;
577
 
    }
578
 
    
579
 
    FD_SET(new_process->fd, &rfds_all);
580
 
    
581
 
    if (maxfd < new_process->fd){
582
 
      maxfd = new_process->fd;
583
 
    }
584
 
    
585
 
  }
586
 
  
587
 
  /* Free the plugin list */
588
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
589
 
    next = plugin_list->next;
590
 
    free(plugin_list->argv);
591
 
    free(plugin_list);
592
 
  }
593
 
  
594
 
  closedir(dir);
595
 
  dir = NULL;
596
 
    
597
 
  if (process_list == NULL){
598
 
    fprintf(stderr, "No plugin processes started. Incorrect plugin"
599
 
            " directory?\n");
600
 
    process_list = NULL;
601
 
  }
602
 
  while(process_list){
 
1082
      error(0, errno, "sigprocmask");
 
1083
      exitstatus = EX_OSERR;
 
1084
      goto fallback;
 
1085
    }
 
1086
    
 
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){
603
1126
    fd_set rfds = rfds_all;
604
1127
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
605
 
    if (select_ret == -1){
606
 
      perror("select");
607
 
      exitstatus = EXIT_FAILURE;
608
 
      goto end;
 
1128
    if(select_ret == -1 and errno != EINTR){
 
1129
      error(0, errno, "select");
 
1130
      exitstatus = EX_OSERR;
 
1131
      goto fallback;
609
1132
    }
610
1133
    /* OK, now either a process completed, or something can be read
611
1134
       from one of them */
612
 
    for(process *proc = process_list; proc ; proc = proc->next){
 
1135
    for(plugin *proc = plugin_list; proc != NULL;){
613
1136
      /* Is this process completely done? */
614
 
      if(proc->eof and proc->completed){
 
1137
      if(proc->completed and proc->eof){
615
1138
        /* Only accept the plugin output if it exited cleanly */
616
1139
        if(not WIFEXITED(proc->status)
617
1140
           or WEXITSTATUS(proc->status) != 0){
618
1141
          /* Bad exit by plugin */
 
1142
          
619
1143
          if(debug){
620
1144
            if(WIFEXITED(proc->status)){
621
 
              fprintf(stderr, "Plugin %u exited with status %d\n",
622
 
                      (unsigned int) (proc->pid),
 
1145
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
 
1146
                      " status %d\n", proc->name,
 
1147
                      (intmax_t) (proc->pid),
623
1148
                      WEXITSTATUS(proc->status));
624
 
            } else if(WIFSIGNALED(proc->status)) {
625
 
              fprintf(stderr, "Plugin %u killed by signal %d\n",
626
 
                      (unsigned int) (proc->pid),
627
 
                      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)));
628
1155
            } else if(WCOREDUMP(proc->status)){
629
 
              fprintf(stderr, "Plugin %d dumped core\n",
630
 
                      (unsigned int) (proc->pid));
 
1156
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
 
1157
                      " core\n", proc->name, (intmax_t) (proc->pid));
631
1158
            }
632
1159
          }
 
1160
          
633
1161
          /* Remove the plugin */
634
 
          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
          
635
1177
          /* Block signal while modifying process_list */
636
 
          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));
637
1182
          if(ret < 0){
638
 
            perror("sigprocmask");
639
 
            exitstatus = EXIT_FAILURE;
640
 
            goto end;
641
 
          }
642
 
          /* Delete this process entry from the list */
643
 
          if(process_list == proc){
644
 
            /* First one - simple */
645
 
            process_list = proc->next;
646
 
          } else {
647
 
            /* Second one or later */
648
 
            for(process *p = process_list; p != NULL; p = p->next){
649
 
              if(p->next == proc){
650
 
                p->next = proc->next;
651
 
                break;
652
 
              }
653
 
            }
654
 
          }
 
1183
            error(0, errno, "sigprocmask");
 
1184
            exitstatus = EX_OSERR;
 
1185
            goto fallback;
 
1186
          }
 
1187
          
 
1188
          plugin *next_plugin = proc->next;
 
1189
          free_plugin(proc);
 
1190
          proc = next_plugin;
 
1191
          
655
1192
          /* We are done modifying process list, so unblock signal */
656
 
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
657
 
                             NULL);
 
1193
          ret = (int)(TEMP_FAILURE_RETRY
 
1194
                      (sigprocmask(SIG_UNBLOCK,
 
1195
                                   &sigchld_action.sa_mask, NULL)));
658
1196
          if(ret < 0){
659
 
            perror("sigprocmask");
660
 
          }
661
 
          free(proc->buffer);
662
 
          free(proc);
663
 
          /* We deleted this process from the list, so we can't go
664
 
             proc->next.  Therefore, start over from the beginning of
665
 
             the process list */
666
 
          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;
667
1207
        }
 
1208
        
668
1209
        /* This process exited nicely, so print its buffer */
669
 
 
670
 
        bool bret = print_out_password(proc->buffer, proc->buffer_length);
 
1210
        
 
1211
        bool bret = print_out_password(proc->buffer,
 
1212
                                       proc->buffer_length);
671
1213
        if(not bret){
672
 
          perror("print_out_password");
673
 
          exitstatus = EXIT_FAILURE;
 
1214
          error(0, errno, "print_out_password");
 
1215
          exitstatus = EX_IOERR;
674
1216
        }
675
 
        goto end;
 
1217
        goto fallback;
676
1218
      }
 
1219
      
677
1220
      /* This process has not completed.  Does it have any output? */
678
 
      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
679
1238
        /* This process had nothing to say at this time */
 
1239
        proc = proc->next;
680
1240
        continue;
681
1241
      }
682
1242
      /* Before reading, make the process' data buffer large enough */
683
1243
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
684
 
        proc->buffer = realloc(proc->buffer, proc->buffer_size
685
 
                               + (size_t) BUFFER_SIZE);
686
 
        if (proc->buffer == NULL){
687
 
          perror("malloc");
688
 
          exitstatus = EXIT_FAILURE;
689
 
          goto end;
 
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;
 
1249
          goto fallback;
690
1250
        }
 
1251
        proc->buffer = new_buffer;
691
1252
        proc->buffer_size += BUFFER_SIZE;
692
1253
      }
693
1254
      /* Read from the process */
694
 
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
695
 
                 BUFFER_SIZE);
696
 
      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){
697
1260
        /* Read error from this process; ignore the error */
 
1261
        proc = proc->next;
698
1262
        continue;
699
1263
      }
700
 
      if(ret == 0){
 
1264
      if(sret == 0){
701
1265
        /* got EOF */
702
1266
        proc->eof = true;
703
1267
      } else {
704
 
        proc->buffer_length += (size_t) ret;
 
1268
        proc->buffer_length += (size_t) sret;
705
1269
      }
706
1270
    }
707
1271
  }
708
1272
  
709
 
  if(process_list == NULL){
 
1273
  
 
1274
 fallback:
 
1275
  
 
1276
  if(plugin_list == NULL or (exitstatus != EXIT_SUCCESS
 
1277
                             and exitstatus != EX_OK)){
 
1278
    /* Fallback if all plugins failed, none are found or an error
 
1279
       occured */
710
1280
    bool bret;
711
1281
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
712
1282
    char *passwordbuffer = getpass("Password: ");
713
 
    bret = print_out_password(passwordbuffer, strlen(passwordbuffer));
714
 
    if(not bret){
715
 
      perror("print_out_password");
716
 
      exitstatus = EXIT_FAILURE;
717
 
      goto end;
 
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--;
718
1288
    }
719
 
    bret = print_out_password("\n", 1);
 
1289
    bret = print_out_password(passwordbuffer, len);
720
1290
    if(not bret){
721
 
      perror("print_out_password");
722
 
      exitstatus = EXIT_FAILURE;
 
1291
      error(0, errno, "print_out_password");
 
1292
      exitstatus = EX_IOERR;
723
1293
    }
724
1294
  }
725
 
 
726
 
 end:
727
1295
  
728
1296
  /* Restore old signal handler */
729
 
  sigaction(SIGCHLD, &old_sigchld_action, NULL);
730
 
  
731
 
  free(plus_argv);
732
 
  
733
 
  /* Free the plugin list */
734
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
735
 
    next = plugin_list->next;
736
 
    free(plugin_list->argv);
737
 
    free(plugin_list);
738
 
  }
739
 
  
740
 
  if(dir != NULL){
741
 
    closedir(dir);
742
 
  }
743
 
  
744
 
  /* Free the process list and kill the processes */
745
 
  for(process *next; process_list != NULL; process_list = next){
746
 
    next = process_list->next;
747
 
    close(process_list->fd);
748
 
    ret = kill(process_list->pid, SIGTERM);
749
 
    if(ret == -1 and errno != ESRCH){
750
 
      /* set-uid proccesses migth not get closed */
751
 
      perror("kill");
752
 
    }
753
 
    free(process_list->buffer);
754
 
    free(process_list);
 
1297
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
 
1298
  if(ret == -1){
 
1299
    error(0, errno, "sigaction");
 
1300
    exitstatus = EX_OSERR;
 
1301
  }
 
1302
  
 
1303
  if(custom_argv != NULL){
 
1304
    for(char **arg = custom_argv+1; *arg != NULL; arg++){
 
1305
      free(*arg);
 
1306
    }
 
1307
    free(custom_argv);
 
1308
  }
 
1309
  
 
1310
  free(direntries);
 
1311
  
 
1312
  if(dir_fd != -1){
 
1313
    TEMP_FAILURE_RETRY(close(dir_fd));
 
1314
  }
 
1315
  
 
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
      }
 
1325
    }
755
1326
  }
756
1327
  
757
1328
  /* Wait for any remaining child processes to terminate */
758
 
  do{
 
1329
  do {
759
1330
    ret = wait(NULL);
760
1331
  } while(ret >= 0);
761
1332
  if(errno != ECHILD){
762
 
    perror("wait");
 
1333
    error(0, errno, "wait");
763
1334
  }
764
1335
  
 
1336
  free_plugin_list();
 
1337
  
 
1338
  free(plugindir);
 
1339
  free(argfile);
 
1340
  
765
1341
  return exitstatus;
766
1342
}