/mandos/release

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

« back to all changes in this revision

Viewing changes to plugin-runner.c

  • Committer: Teddy Hogeborn
  • Date: 2008-08-16 03:29:08 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080816032908-ihw7c05r2mnyk389
Add feature to specify custom environment variables for plugins.

* plugin-runner.c (plugin): New members "environ" and "envc" to
                            contain possible custom environment.
  (getplugin): Return NULL on failure instead of doing exit(); all
               callers changed.
  (add_to_char_array): New helper function for "add_argument" and
                       "add_environment".
  (addargument): Renamed to "add_argument".  Return bool.  Call
                 "add_to_char_array" to actually do things.
  (add_environment): New; analogous to "add_argument".
  (addcustomargument): Renamed to "add_to_argv" to avoid confusion
                       with "add_argument".
  (main): New options "--global-envs" and "--envs-for" to specify
          custom environment for plugins.  Print environment for
          plugins in debug mode.  Use asprintf instead of strcpy and
          strcat.  Use execve() for plugins with custom environments.
          Free environment for plugin when freeing plugin list.

Show diffs side-by-side

added added

removed removed

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