/mandos/trunk

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

« back to all changes in this revision

Viewing changes to plugin-runner.c

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

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

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

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  -*- coding: utf-8; mode: c; mode: orgtbl -*- */
 
1
/*  -*- coding: utf-8 -*- */
2
2
/*
3
3
 * Mandos plugin runner - Run Mandos plugins
4
4
 *
5
 
 * Copyright © 2008-2014 Teddy Hogeborn
6
 
 * Copyright © 2008-2014 Björn Påhlsson
 
5
 * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
7
6
 * 
8
7
 * This program is free software: you can redistribute it and/or
9
8
 * modify it under the terms of the GNU General Public License as
19
18
 * along with this program.  If not, see
20
19
 * <http://www.gnu.org/licenses/>.
21
20
 * 
22
 
 * Contact the authors at <mandos@recompile.se>.
 
21
 * Contact the authors at <mandos@fukt.bsnet.se>.
23
22
 */
24
23
 
25
24
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
26
 
                                   O_CLOEXEC, pipe2() */
 
25
                                   asprintf() */
27
26
#include <stddef.h>             /* size_t, NULL */
28
 
#include <stdlib.h>             /* malloc(), exit(), EXIT_SUCCESS,
29
 
                                   realloc() */
 
27
#include <stdlib.h>             /* malloc(), exit(), EXIT_FAILURE,
 
28
                                   EXIT_SUCCESS, realloc() */
30
29
#include <stdbool.h>            /* bool, true, false */
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() */
 
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() */
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(), WTERMSIG(),
41
 
                                   WCOREDUMP() */
42
 
#include <sys/stat.h>           /* struct stat, fstat(), S_ISREG() */
 
40
                                   WEXITSTATUS() */
 
41
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
43
42
#include <iso646.h>             /* and, or, not */
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
 
                                */
 
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() */
52
52
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
53
 
                                   FD_CLOEXEC, openat(), scandirat(),
54
 
                                   pipe2() */
55
 
#include <string.h>             /* strsep, strlen(), strsignal(),
56
 
                                   strcmp(), strncmp() */
 
53
                                   FD_CLOEXEC */
 
54
#include <string.h>             /* strsep, strlen(), asprintf() */
57
55
#include <errno.h>              /* errno */
58
56
#include <argp.h>               /* struct argp_option, struct
59
57
                                   argp_state, struct argp,
63
61
#include <signal.h>             /* struct sigaction, sigemptyset(),
64
62
                                   sigaddset(), sigaction(),
65
63
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
66
 
                                   SIG_UNBLOCK, kill(), sig_atomic_t
67
 
                                */
 
64
                                   SIG_UNBLOCK, kill() */
68
65
#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() */
75
66
 
76
67
#define BUFFER_SIZE 256
77
68
 
78
69
#define PDIR "/lib/mandos/plugins.d"
79
70
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
80
71
 
81
 
const char *argp_program_version = "plugin-runner " VERSION;
82
 
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
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;
83
88
 
84
89
typedef struct plugin{
85
90
  char *name;                   /* can be NULL or any plugin name */
88
93
  char **environ;
89
94
  int envc;
90
95
  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;
101
96
  struct plugin *next;
102
97
} plugin;
103
98
 
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))){
 
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))){
114
103
      return p;
115
104
    }
116
105
  }
117
106
  /* Create a new plugin */
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){
 
107
  plugin *new_plugin = malloc(sizeof(plugin));
 
108
  if (new_plugin == NULL){
123
109
    return NULL;
124
110
  }
125
111
  char *copy_name = NULL;
126
112
  if(name != NULL){
127
 
    do {
128
 
      copy_name = strdup(name);
129
 
    } while(copy_name == NULL and errno == EINTR);
 
113
    copy_name = strdup(name);
130
114
    if(copy_name == NULL){
131
 
      int e = errno;
132
 
      free(new_plugin);
133
 
      errno = e;
134
115
      return NULL;
135
116
    }
136
117
  }
137
118
  
138
 
  *new_plugin = (plugin){ .name = copy_name,
139
 
                          .argc = 1,
140
 
                          .disabled = false,
141
 
                          .next = plugin_list };
 
119
  *new_plugin = (plugin) { .name = copy_name,
 
120
                           .argc = 1,
 
121
                           .envc = 0,
 
122
                           .disabled = false,
 
123
                           .next = *plugin_list };
142
124
  
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;
 
125
  new_plugin->argv = malloc(sizeof(char *) * 2);
 
126
  if (new_plugin->argv == NULL){
148
127
    free(copy_name);
149
128
    free(new_plugin);
150
 
    errno = e;
151
129
    return NULL;
152
130
  }
153
131
  new_plugin->argv[0] = copy_name;
154
132
  new_plugin->argv[1] = NULL;
155
 
  
156
 
  do {
157
 
    new_plugin->environ = malloc(sizeof(char *));
158
 
  } while(new_plugin->environ == NULL and errno == EINTR);
 
133
 
 
134
  new_plugin->environ = malloc(sizeof(char *));
159
135
  if(new_plugin->environ == NULL){
160
 
    int e = errno;
161
136
    free(copy_name);
162
137
    free(new_plugin->argv);
163
138
    free(new_plugin);
164
 
    errno = e;
165
139
    return NULL;
166
140
  }
167
141
  new_plugin->environ[0] = NULL;
168
 
  
169
142
  /* Append the new plugin to the list */
170
 
  plugin_list = new_plugin;
 
143
  *plugin_list = new_plugin;
171
144
  return new_plugin;
172
145
}
173
146
 
174
147
/* Helper function for add_argument and add_environment */
175
 
__attribute__((nonnull, warn_unused_result))
176
148
static bool add_to_char_array(const char *new, char ***array,
177
149
                              int *len){
178
150
  /* 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);
 
151
  *array = realloc(*array, sizeof(char *)
 
152
                   * (size_t) ((*len) + 2));
184
153
  /* Malloc check */
185
 
  if(new_array == NULL){
 
154
  if(*array == NULL){
186
155
    return false;
187
156
  }
188
 
  *array = new_array;
189
157
  /* Make a copy of the new string */
190
 
  char *copy;
191
 
  do {
192
 
    copy = strdup(new);
193
 
  } while(copy == NULL and errno == EINTR);
 
158
  char *copy = strdup(new);
194
159
  if(copy == NULL){
195
160
    return false;
196
161
  }
203
168
}
204
169
 
205
170
/* Add to a plugin's argument vector */
206
 
__attribute__((nonnull(2), warn_unused_result))
207
171
static bool add_argument(plugin *p, const char *arg){
208
172
  if(p == NULL){
209
173
    return false;
212
176
}
213
177
 
214
178
/* 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){
 
179
static bool add_environment(plugin *p, const char *def){
217
180
  if(p == NULL){
218
181
    return false;
219
182
  }
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
183
  return add_to_char_array(def, &(p->environ), &(p->envc));
241
184
}
242
185
 
243
 
#ifndef O_CLOEXEC
 
186
 
244
187
/*
245
188
 * Based on the example in the GNU LibC manual chapter 13.13 "File
246
189
 * Descriptor Flags".
247
 
 | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
 
190
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
248
191
 */
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));
 
192
static int set_cloexec_flag(int fd)
 
193
{
 
194
  int ret = fcntl(fd, F_GETFD, 0);
252
195
  /* If reading the flags failed, return error indication now. */
253
196
  if(ret < 0){
254
197
    return ret;
255
198
  }
256
199
  /* Store modified flag word in the descriptor. */
257
 
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
258
 
                                       ret | FD_CLOEXEC));
 
200
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
259
201
}
260
 
#endif  /* not O_CLOEXEC */
261
202
 
 
203
process *process_list = NULL;
262
204
 
263
205
/* Mark processes as completed when they exit, and save their exit
264
206
   status. */
265
 
static void handle_sigchld(__attribute__((unused)) int sig){
266
 
  int old_errno = errno;
 
207
void handle_sigchld(__attribute__((unused)) int sig){
267
208
  while(true){
268
 
    plugin *proc = plugin_list;
 
209
    process *proc = process_list;
269
210
    int status;
270
211
    pid_t pid = waitpid(-1, &status, WNOHANG);
271
212
    if(pid == 0){
273
214
      break;
274
215
    }
275
216
    if(pid == -1){
276
 
      if(errno == ECHILD){
277
 
        /* No child processes */
278
 
        break;
 
217
      if (errno != ECHILD){
 
218
        perror("waitpid");
279
219
      }
280
 
      error(0, errno, "waitpid");
 
220
      /* No child processes */
 
221
      break;
281
222
    }
282
 
    
 
223
 
283
224
    /* A child exited, find it in process_list */
284
225
    while(proc != NULL and proc->pid != pid){
285
226
      proc = proc->next;
289
230
      continue;
290
231
    }
291
232
    proc->status = status;
292
 
    proc->completed = 1;
 
233
    proc->completed = true;
293
234
  }
294
 
  errno = old_errno;
295
235
}
296
236
 
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){
 
237
bool print_out_password(const char *buffer, size_t length){
300
238
  ssize_t ret;
 
239
  if(length>0 and buffer[length-1] == '\n'){
 
240
    length--;
 
241
  }
301
242
  for(size_t written = 0; written < length; written += (size_t)ret){
302
243
    ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
303
244
                                   length - written));
308
249
  return true;
309
250
}
310
251
 
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);
 
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);
345
264
  }
346
265
}
347
266
 
349
268
  char *plugindir = NULL;
350
269
  char *argfile = NULL;
351
270
  FILE *conffp;
352
 
  struct dirent **direntries = NULL;
 
271
  size_t d_name_len;
 
272
  DIR *dir = NULL;
 
273
  struct dirent *dirst;
353
274
  struct stat st;
354
275
  fd_set rfds_all;
355
276
  int ret, maxfd = 0;
356
 
  ssize_t sret;
357
277
  uid_t uid = 65534;
358
278
  gid_t gid = 65534;
359
279
  bool debug = false;
363
283
                                      .sa_flags = SA_NOCLDSTOP };
364
284
  char **custom_argv = NULL;
365
285
  int custom_argc = 0;
366
 
  int dir_fd = -1;
367
286
  
368
287
  /* Establish a signal handler */
369
288
  sigemptyset(&sigchld_action.sa_mask);
370
289
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
371
290
  if(ret == -1){
372
 
    error(0, errno, "sigaddset");
373
 
    exitstatus = EX_OSERR;
 
291
    perror("sigaddset");
 
292
    exitstatus = EXIT_FAILURE;
374
293
    goto fallback;
375
294
  }
376
295
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
377
296
  if(ret == -1){
378
 
    error(0, errno, "sigaction");
379
 
    exitstatus = EX_OSERR;
 
297
    perror("sigaction");
 
298
    exitstatus = EXIT_FAILURE;
380
299
    goto fallback;
381
300
  }
382
301
  
385
304
    { .name = "global-options", .key = 'g',
386
305
      .arg = "OPTION[,OPTION[,...]]",
387
306
      .doc = "Options passed to all plugins" },
388
 
    { .name = "global-env", .key = 'G',
 
307
    { .name = "global-envs", .key = 'e',
389
308
      .arg = "VAR=value",
390
309
      .doc = "Environment variable passed to all plugins" },
391
310
    { .name = "options-for", .key = 'o',
392
311
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
393
312
      .doc = "Options passed only to specified plugin" },
394
 
    { .name = "env-for", .key = 'E',
 
313
    { .name = "envs-for", .key = 'f',
395
314
      .arg = "PLUGIN:ENV=value",
396
315
      .doc = "Environment variable passed to specified plugin" },
397
316
    { .name = "disable", .key = 'd',
398
317
      .arg = "PLUGIN",
399
318
      .doc = "Disable a specific plugin", .group = 1 },
400
 
    { .name = "enable", .key = 'e',
401
 
      .arg = "PLUGIN",
402
 
      .doc = "Enable a specific plugin", .group = 1 },
403
319
    { .name = "plugin-dir", .key = 128,
404
320
      .arg = "DIRECTORY",
405
321
      .doc = "Specify a different plugin directory", .group = 2 },
414
330
      .doc = "Group ID the plugins will run as", .group = 3 },
415
331
    { .name = "debug", .key = 132,
416
332
      .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 },
426
333
    { .name = NULL }
427
334
  };
428
335
  
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 */
 
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
      }
476
397
      {
477
398
        char *envdef = strchr(arg, ':');
478
399
        if(envdef == NULL){
479
 
          argp_error(state, "No colon in \"%s\"", arg);
480
 
          errno = EINVAL;
481
 
          break;
482
 
        }
483
 
        *envdef = '\0';
 
400
          break;
 
401
        }
 
402
        char *p_name = strndup(arg, (size_t) (envdef-arg));
 
403
        if(p_name == NULL){
 
404
          break;
 
405
        }
484
406
        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);
 
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:
515
422
      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 */
 
423
      if(plugindir == NULL){
 
424
        perror("strdup");
 
425
      }      
 
426
      break;
 
427
    case 129:
 
428
      argfile = strdup(arg);
 
429
      if(argfile == NULL){
 
430
        perror("strdup");
 
431
      }
 
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:
546
440
      debug = true;
547
441
      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);
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 = "",
 
442
    case ARGP_KEY_ARG:
 
443
      fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
 
444
      break;
 
445
    case ARGP_KEY_END:
 
446
      break;
 
447
    default:
 
448
      return ARGP_ERR_UNKNOWN;
 
449
    }
 
450
    return 0;
 
451
  }
 
452
  
 
453
  plugin *plugin_list = NULL;
 
454
  
 
455
  struct argp argp = { .options = options, .parser = parse_opt,
 
456
                       .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
618
457
                       .doc = "Mandos plugin runner -- Run plugins" };
619
458
  
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;
 
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;
636
463
    goto fallback;
637
464
  }
638
 
  
639
 
  /* Reset to the normal argument parser */
640
 
  argp.parser = parse_opt;
641
 
  
642
 
  /* Open the configfile if available */
643
 
  if(argfile == NULL){
 
465
 
 
466
  if (argfile == NULL){
644
467
    conffp = fopen(AFILE, "r");
645
468
  } else {
646
469
    conffp = fopen(argfile, "r");
647
470
  }
 
471
  
648
472
  if(conffp != NULL){
649
473
    char *org_line = NULL;
650
474
    char *p, *arg, *new_arg, *line;
651
475
    size_t size = 0;
 
476
    ssize_t sret;
652
477
    const char whitespace_delims[] = " \r\t\f\v\n";
653
478
    const char comment_delim[] = "#";
654
 
    
 
479
 
655
480
    custom_argc = 1;
656
481
    custom_argv = malloc(sizeof(char*) * 2);
657
482
    if(custom_argv == NULL){
658
 
      error(0, errno, "malloc");
659
 
      exitstatus = EX_OSERR;
 
483
      perror("malloc");
 
484
      exitstatus = EXIT_FAILURE;
660
485
      goto fallback;
661
486
    }
662
487
    custom_argv[0] = argv[0];
663
488
    custom_argv[1] = NULL;
664
489
    
665
 
    /* for each line in the config file, strip whitespace and ignore
666
 
       commented text */
667
490
    while(true){
668
491
      sret = getline(&org_line, &size, conffp);
669
492
      if(sret == -1){
670
493
        break;
671
494
      }
672
 
      
 
495
 
673
496
      line = org_line;
674
497
      arg = strsep(&line, comment_delim);
675
498
      while((p = strsep(&arg, whitespace_delims)) != NULL){
678
501
        }
679
502
        new_arg = strdup(p);
680
503
        if(new_arg == NULL){
681
 
          error(0, errno, "strdup");
682
 
          exitstatus = EX_OSERR;
 
504
          perror("strdup");
 
505
          exitstatus = EXIT_FAILURE;
683
506
          free(org_line);
684
507
          goto fallback;
685
508
        }
686
509
        
687
510
        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
 
          }
 
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;
701
518
        }
702
519
        custom_argv[custom_argc-1] = new_arg;
703
 
        custom_argv[custom_argc] = NULL;
 
520
        custom_argv[custom_argc] = NULL;        
704
521
      }
705
522
    }
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
523
    free(org_line);
715
 
  } else {
 
524
  } else{
716
525
    /* Check for harmful errors and go to fallback. Other errors might
717
526
       not affect opening plugins */
718
 
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
719
 
      error(0, errno, "fopen");
720
 
      exitstatus = EX_OSERR;
 
527
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
 
528
      perror("fopen");
 
529
      exitstatus = EXIT_FAILURE;
721
530
      goto fallback;
722
531
    }
723
532
  }
724
 
  /* If there were any arguments from the configuration file, pass
725
 
     them to parser as command line arguments */
 
533
 
726
534
  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;
 
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;
741
539
      goto fallback;
742
540
    }
743
541
  }
744
542
  
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
 
  
764
543
  if(debug){
765
544
    for(plugin *p = plugin_list; p != NULL; p=p->next){
766
545
      fprintf(stderr, "Plugin: %s has %d arguments\n",
768
547
      for(char **a = p->argv; *a != NULL; a++){
769
548
        fprintf(stderr, "\tArg: %s\n", *a);
770
549
      }
771
 
      fprintf(stderr, "...and %d environment variables\n", p->envc);
 
550
      fprintf(stderr, "...and %u environment variables\n", p->envc);
772
551
      for(char **a = p->environ; *a != NULL; a++){
773
552
        fprintf(stderr, "\t%s\n", *a);
774
553
      }
775
554
    }
776
555
  }
777
556
  
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
 
  }
807
557
  ret = setuid(uid);
808
 
  if(ret == -1){
809
 
    error(0, errno, "setuid");
810
 
  }
811
 
  
812
 
  /* Open plugin directory with close_on_exec flag */
 
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 */
813
580
  {
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;
 
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;
858
588
      }
859
589
    }
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;
880
590
  }
881
591
  
882
592
  FD_ZERO(&rfds_all);
883
593
  
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)){
 
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)){
905
669
      if(debug){
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);
 
670
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
671
                " with bad type or mode\n", filename);
910
672
      }
911
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
912
 
      free(direntries[i]);
 
673
      free(filename);
913
674
      continue;
914
675
    }
915
 
    
916
 
    plugin *p = getplugin(direntries[i]->d_name);
 
676
    plugin *p = getplugin(dirst->d_name, &plugin_list);
917
677
    if(p == NULL){
918
 
      error(0, errno, "getplugin");
919
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
920
 
      free(direntries[i]);
 
678
      perror("getplugin");
 
679
      free(filename);
921
680
      continue;
922
681
    }
923
682
    if(p->disabled){
924
683
      if(debug){
925
684
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
926
 
                direntries[i]->d_name);
 
685
                dirst->d_name);
927
686
      }
928
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
929
 
      free(direntries[i]);
 
687
      free(filename);
930
688
      continue;
931
689
    }
932
690
    {
933
691
      /* Add global arguments to argument list for this plugin */
934
 
      plugin *g = getplugin(NULL);
 
692
      plugin *g = getplugin(NULL, &plugin_list);
935
693
      if(g != NULL){
936
694
        for(char **a = g->argv + 1; *a != NULL; a++){
937
695
          if(not add_argument(p, *a)){
938
 
            error(0, errno, "add_argument");
 
696
            perror("add_argument");
939
697
          }
940
698
        }
941
699
        /* Add global environment variables */
942
700
        for(char **e = g->environ; *e != NULL; e++){
943
 
          if(not add_environment(p, *e, false)){
944
 
            error(0, errno, "add_environment");
 
701
          if(not add_environment(p, *e)){
 
702
            perror("add_environment");
945
703
          }
946
704
        }
947
705
      }
948
706
    }
949
 
    /* If this plugin has any environment variables, we need to
950
 
       duplicate the environment from this process, too. */
 
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. */
951
710
    if(p->environ[0] != NULL){
952
711
      for(char **e = environ; *e != NULL; e++){
953
 
        if(not add_environment(p, *e, false)){
954
 
          error(0, errno, "add_environment");
 
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");
955
719
        }
956
720
      }
957
721
    }
958
722
    
959
723
    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 */
 
724
    ret = pipe(pipefd);
 
725
    if (ret == -1){
 
726
      perror("pipe");
 
727
      exitstatus = EXIT_FAILURE;
 
728
      goto fallback;
 
729
    }
982
730
    ret = set_cloexec_flag(pipefd[0]);
983
731
    if(ret < 0){
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]);
 
732
      perror("set_cloexec_flag");
 
733
      exitstatus = EXIT_FAILURE;
989
734
      goto fallback;
990
735
    }
991
736
    ret = set_cloexec_flag(pipefd[1]);
992
737
    if(ret < 0){
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]);
 
738
      perror("set_cloexec_flag");
 
739
      exitstatus = EXIT_FAILURE;
998
740
      goto fallback;
999
741
    }
1000
 
#endif  /* not O_CLOEXEC */
1001
742
    /* Block SIGCHLD until process is safely in process list */
1002
 
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
1003
 
                                              &sigchld_action.sa_mask,
1004
 
                                              NULL));
 
743
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
1005
744
    if(ret < 0){
1006
 
      error(0, errno, "sigprocmask");
1007
 
      exitstatus = EX_OSERR;
1008
 
      free(direntries[i]);
 
745
      perror("sigprocmask");
 
746
      exitstatus = EXIT_FAILURE;
1009
747
      goto fallback;
1010
748
    }
1011
 
    /* Starting a new process to be watched */
1012
 
    pid_t pid;
1013
 
    do {
1014
 
      pid = fork();
1015
 
    } while(pid == -1 and errno == EINTR);
 
749
    // Starting a new process to be watched
 
750
    pid_t pid = fork();
1016
751
    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]);
 
752
      perror("fork");
 
753
      exitstatus = EXIT_FAILURE;
1024
754
      goto fallback;
1025
755
    }
1026
756
    if(pid == 0){
1027
757
      /* this is the child process */
1028
758
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1029
759
      if(ret < 0){
1030
 
        error(0, errno, "sigaction");
1031
 
        _exit(EX_OSERR);
 
760
        perror("sigaction");
 
761
        _exit(EXIT_FAILURE);
1032
762
      }
1033
 
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
763
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
1034
764
      if(ret < 0){
1035
 
        error(0, errno, "sigprocmask");
1036
 
        _exit(EX_OSERR);
 
765
        perror("sigprocmask");
 
766
        _exit(EXIT_FAILURE);
1037
767
      }
1038
 
      
 
768
 
1039
769
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
1040
770
      if(ret == -1){
1041
 
        error(0, errno, "dup2");
1042
 
        _exit(EX_OSERR);
 
771
        perror("dup2");
 
772
        _exit(EXIT_FAILURE);
1043
773
      }
1044
774
      
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);
 
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
        }
1051
790
      }
1052
791
      /* no return */
1053
792
    }
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)));
 
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);
1064
800
      if(ret < 0){
1065
 
        error(0, errno, "sigprocmask");
 
801
        perror("sigprocmask");
1066
802
      }
1067
 
      exitstatus = EX_OSERR;
1068
 
      free(direntries[i]);
 
803
      exitstatus = EXIT_FAILURE;
1069
804
      goto fallback;
1070
805
    }
1071
 
    free(direntries[i]);
1072
 
    
1073
 
    new_plugin->pid = pid;
1074
 
    new_plugin->fd = pipefd[0];
1075
 
    
 
806
    
 
807
    *new_process = (struct process){ .pid = pid,
 
808
                                     .fd = pipefd[0],
 
809
                                     .next = process_list };
 
810
    // List handling
 
811
    process_list = new_process;
1076
812
    /* Unblock SIGCHLD so signal handler can be run if this process
1077
813
       has already completed */
1078
 
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1079
 
                                              &sigchld_action.sa_mask,
1080
 
                                              NULL));
 
814
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
1081
815
    if(ret < 0){
1082
 
      error(0, errno, "sigprocmask");
1083
 
      exitstatus = EX_OSERR;
 
816
      perror("sigprocmask");
 
817
      exitstatus = EXIT_FAILURE;
1084
818
      goto fallback;
1085
819
    }
1086
820
    
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){
 
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){
1126
841
    fd_set rfds = rfds_all;
1127
842
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
1128
 
    if(select_ret == -1 and errno != EINTR){
1129
 
      error(0, errno, "select");
1130
 
      exitstatus = EX_OSERR;
 
843
    if (select_ret == -1){
 
844
      perror("select");
 
845
      exitstatus = EXIT_FAILURE;
1131
846
      goto fallback;
1132
847
    }
1133
848
    /* OK, now either a process completed, or something can be read
1134
849
       from one of them */
1135
 
    for(plugin *proc = plugin_list; proc != NULL;){
 
850
    for(process *proc = process_list; proc ; proc = proc->next){
1136
851
      /* Is this process completely done? */
1137
 
      if(proc->completed and proc->eof){
 
852
      if(proc->eof and proc->completed){
1138
853
        /* Only accept the plugin output if it exited cleanly */
1139
854
        if(not WIFEXITED(proc->status)
1140
855
           or WEXITSTATUS(proc->status) != 0){
1141
856
          /* Bad exit by plugin */
1142
 
          
1143
857
          if(debug){
1144
858
            if(WIFEXITED(proc->status)){
1145
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
1146
 
                      " status %d\n", proc->name,
1147
 
                      (intmax_t) (proc->pid),
 
859
              fprintf(stderr, "Plugin %u exited with status %d\n",
 
860
                      (unsigned int) (proc->pid),
1148
861
                      WEXITSTATUS(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)));
 
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));
1155
866
            } else if(WCOREDUMP(proc->status)){
1156
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
1157
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
 
867
              fprintf(stderr, "Plugin %d dumped core\n",
 
868
                      (unsigned int) (proc->pid));
1158
869
            }
1159
870
          }
1160
 
          
1161
871
          /* Remove the plugin */
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
 
          
 
872
          FD_CLR(proc->fd, &rfds_all);
1177
873
          /* Block signal while modifying process_list */
1178
 
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1179
 
                                        (SIG_BLOCK,
1180
 
                                         &sigchld_action.sa_mask,
1181
 
                                         NULL));
 
874
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
1182
875
          if(ret < 0){
1183
 
            error(0, errno, "sigprocmask");
1184
 
            exitstatus = EX_OSERR;
 
876
            perror("sigprocmask");
 
877
            exitstatus = EXIT_FAILURE;
1185
878
            goto fallback;
1186
879
          }
1187
 
          
1188
 
          plugin *next_plugin = proc->next;
1189
 
          free_plugin(proc);
1190
 
          proc = next_plugin;
1191
 
          
 
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
          }
1192
893
          /* We are done modifying process list, so unblock signal */
1193
 
          ret = (int)(TEMP_FAILURE_RETRY
1194
 
                      (sigprocmask(SIG_UNBLOCK,
1195
 
                                   &sigchld_action.sa_mask, NULL)));
 
894
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
 
895
                             NULL);
1196
896
          if(ret < 0){
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;
 
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;
1207
905
        }
1208
 
        
1209
906
        /* This process exited nicely, so print its buffer */
1210
 
        
 
907
 
1211
908
        bool bret = print_out_password(proc->buffer,
1212
909
                                       proc->buffer_length);
1213
910
        if(not bret){
1214
 
          error(0, errno, "print_out_password");
1215
 
          exitstatus = EX_IOERR;
 
911
          perror("print_out_password");
 
912
          exitstatus = EXIT_FAILURE;
1216
913
        }
1217
914
        goto fallback;
1218
915
      }
1219
 
      
1220
916
      /* This process has not completed.  Does it have any output? */
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
 
917
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1238
918
        /* This process had nothing to say at this time */
1239
 
        proc = proc->next;
1240
919
        continue;
1241
920
      }
1242
921
      /* Before reading, make the process' data buffer large enough */
1243
922
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
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;
 
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;
1249
928
          goto fallback;
1250
929
        }
1251
 
        proc->buffer = new_buffer;
1252
930
        proc->buffer_size += BUFFER_SIZE;
1253
931
      }
1254
932
      /* Read from the process */
1255
 
      sret = TEMP_FAILURE_RETRY(read(proc->fd,
1256
 
                                     proc->buffer
1257
 
                                     + proc->buffer_length,
1258
 
                                     BUFFER_SIZE));
1259
 
      if(sret < 0){
 
933
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
 
934
                 BUFFER_SIZE);
 
935
      if(ret < 0){
1260
936
        /* Read error from this process; ignore the error */
1261
 
        proc = proc->next;
1262
937
        continue;
1263
938
      }
1264
 
      if(sret == 0){
 
939
      if(ret == 0){
1265
940
        /* got EOF */
1266
941
        proc->eof = true;
1267
942
      } else {
1268
 
        proc->buffer_length += (size_t) sret;
 
943
        proc->buffer_length += (size_t) ret;
1269
944
      }
1270
945
    }
1271
946
  }
1272
 
  
1273
 
  
 
947
 
 
948
 
1274
949
 fallback:
1275
950
  
1276
 
  if(plugin_list == NULL or (exitstatus != EXIT_SUCCESS
1277
 
                             and exitstatus != EX_OK)){
 
951
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
1278
952
    /* Fallback if all plugins failed, none are found or an error
1279
953
       occured */
1280
954
    bool bret;
1281
955
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
1282
956
    char *passwordbuffer = getpass("Password: ");
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);
 
957
    bret = print_out_password(passwordbuffer, strlen(passwordbuffer));
1290
958
    if(not bret){
1291
 
      error(0, errno, "print_out_password");
1292
 
      exitstatus = EX_IOERR;
 
959
      perror("print_out_password");
 
960
      exitstatus = EXIT_FAILURE;
1293
961
    }
1294
962
  }
1295
963
  
1296
964
  /* Restore old signal handler */
1297
965
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1298
966
  if(ret == -1){
1299
 
    error(0, errno, "sigaction");
1300
 
    exitstatus = EX_OSERR;
 
967
    perror("sigaction");
 
968
    exitstatus = EXIT_FAILURE;
1301
969
  }
1302
 
  
 
970
 
1303
971
  if(custom_argv != NULL){
1304
972
    for(char **arg = custom_argv+1; *arg != NULL; arg++){
1305
973
      free(*arg);
1306
974
    }
1307
975
    free(custom_argv);
1308
976
  }
1309
 
  
1310
 
  free(direntries);
1311
 
  
1312
 
  if(dir_fd != -1){
1313
 
    TEMP_FAILURE_RETRY(close(dir_fd));
 
977
  free_plugin_list(plugin_list);
 
978
  
 
979
  if(dir != NULL){
 
980
    closedir(dir);
1314
981
  }
1315
982
  
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
 
      }
 
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");
1325
991
    }
 
992
    free(process_list->buffer);
 
993
    free(process_list);
1326
994
  }
1327
995
  
1328
996
  /* Wait for any remaining child processes to terminate */
1329
 
  do {
 
997
  do{
1330
998
    ret = wait(NULL);
1331
999
  } while(ret >= 0);
1332
1000
  if(errno != ECHILD){
1333
 
    error(0, errno, "wait");
 
1001
    perror("wait");
1334
1002
  }
1335
 
  
1336
 
  free_plugin_list();
1337
 
  
 
1003
 
1338
1004
  free(plugindir);
1339
1005
  free(argfile);
1340
1006