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