/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-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
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 */
 
30
#include <stdio.h>              /* perror, popen(), fileno(),
 
31
                                   fprintf(), stderr, STDOUT_FILENO */
33
32
#include <sys/types.h>          /* DIR, opendir(), stat(), struct
34
33
                                   stat, waitpid(), WIFEXITED(),
35
34
                                   WEXITSTATUS(), wait(), pid_t,
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
43
#include <dirent.h>             /* DIR, struct dirent, opendir(),
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");
699
 
  }
700
 
  
701
 
  if(plugindir == NULL){
702
 
    dir = opendir(PDIR);
703
 
  } else {
704
 
    dir = opendir(plugindir);
705
 
  }
706
 
  
 
508
  
 
509
  dir = opendir(plugindir);
707
510
  if(dir == NULL){
708
511
    perror("Could not open plugin dir");
709
512
    exitstatus = EXIT_FAILURE;
710
 
    goto fallback;
 
513
    goto end;
711
514
  }
712
515
  
713
516
  /* Set the FD_CLOEXEC flag on the directory, if possible */
718
521
      if(ret < 0){
719
522
        perror("set_cloexec_flag");
720
523
        exitstatus = EXIT_FAILURE;
721
 
        goto fallback;
 
524
        goto end;
722
525
      }
723
526
    }
724
527
  }
725
528
  
726
529
  FD_ZERO(&rfds_all);
727
530
  
728
 
  /* Read and execute any executable in the plugin directory*/
729
531
  while(true){
730
 
    do {
731
 
      dirst = readdir(dir);
732
 
    } while(dirst == NULL and errno == EINTR);
 
532
    dirst = readdir(dir);
733
533
    
734
 
    /* All directory entries have been processed */
 
534
    // All directory entries have been processed
735
535
    if(dirst == NULL){
736
 
      if(errno == EBADF){
 
536
      if (errno == EBADF){
737
537
        perror("readdir");
738
538
        exitstatus = EXIT_FAILURE;
739
 
        goto fallback;
 
539
        goto end;
740
540
      }
741
541
      break;
742
542
    }
743
543
    
744
544
    d_name_len = strlen(dirst->d_name);
745
545
    
746
 
    /* Ignore dotfiles, backup files and other junk */
 
546
    // Ignore dotfiles, backup files and other junk
747
547
    {
748
548
      bool bad_name = false;
749
549
      
751
551
      
752
552
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
753
553
                                           ".dpkg-old",
754
 
                                           ".dpkg-bak",
755
554
                                           ".dpkg-divert", NULL };
756
555
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
757
556
        size_t pre_len = strlen(*pre);
765
564
          break;
766
565
        }
767
566
      }
 
567
      
768
568
      if(bad_name){
769
569
        continue;
770
570
      }
 
571
      
771
572
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
772
573
        size_t suf_len = strlen(*suf);
773
574
        if((d_name_len >= suf_len)
786
587
        continue;
787
588
      }
788
589
    }
789
 
    
 
590
 
790
591
    char *filename;
791
 
    if(plugindir == NULL){
792
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s",
793
 
                                             dirst->d_name));
794
 
    } else {
795
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s",
796
 
                                             plugindir,
797
 
                                             dirst->d_name));
798
 
    }
 
592
    ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
799
593
    if(ret < 0){
800
594
      perror("asprintf");
801
595
      continue;
802
596
    }
803
597
    
804
 
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
805
 
    if(ret == -1){
 
598
    ret = stat(filename, &st);
 
599
    if (ret == -1){
806
600
      perror("stat");
807
601
      free(filename);
808
602
      continue;
809
603
    }
810
604
    
811
 
    /* Ignore non-executable files */
812
 
    if(not S_ISREG(st.st_mode)
813
 
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
 
605
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
814
606
      if(debug){
815
607
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
816
608
                " with bad type or mode\n", filename);
818
610
      free(filename);
819
611
      continue;
820
612
    }
821
 
    
822
 
    plugin *p = getplugin(dirst->d_name);
 
613
    plugin *p = getplugin(dirst->d_name, &plugin_list);
823
614
    if(p == NULL){
824
615
      perror("getplugin");
825
616
      free(filename);
835
626
    }
836
627
    {
837
628
      /* Add global arguments to argument list for this plugin */
838
 
      plugin *g = getplugin(NULL);
 
629
      plugin *g = getplugin(NULL, &plugin_list);
839
630
      if(g != NULL){
840
631
        for(char **a = g->argv + 1; *a != NULL; a++){
841
632
          if(not add_argument(p, *a)){
844
635
        }
845
636
        /* Add global environment variables */
846
637
        for(char **e = g->environ; *e != NULL; e++){
847
 
          if(not add_environment(p, *e, false)){
 
638
          if(not add_environment(p, *e)){
848
639
            perror("add_environment");
849
640
          }
850
641
        }
855
646
       process, too. */
856
647
    if(p->environ[0] != NULL){
857
648
      for(char **e = environ; *e != NULL; e++){
858
 
        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)){
859
655
          perror("add_environment");
860
656
        }
861
657
      }
862
658
    }
863
659
    
864
 
    int pipefd[2];
865
 
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
866
 
    if(ret == -1){
 
660
    int pipefd[2]; 
 
661
    ret = pipe(pipefd);
 
662
    if (ret == -1){
867
663
      perror("pipe");
868
664
      exitstatus = EXIT_FAILURE;
869
 
      goto fallback;
 
665
      goto end;
870
666
    }
871
 
    /* Ask OS to automatic close the pipe on exec */
872
667
    ret = set_cloexec_flag(pipefd[0]);
873
668
    if(ret < 0){
874
669
      perror("set_cloexec_flag");
875
670
      exitstatus = EXIT_FAILURE;
876
 
      goto fallback;
 
671
      goto end;
877
672
    }
878
673
    ret = set_cloexec_flag(pipefd[1]);
879
674
    if(ret < 0){
880
675
      perror("set_cloexec_flag");
881
676
      exitstatus = EXIT_FAILURE;
882
 
      goto fallback;
 
677
      goto end;
883
678
    }
884
679
    /* Block SIGCHLD until process is safely in process list */
885
 
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
886
 
                                              &sigchld_action.sa_mask,
887
 
                                              NULL));
 
680
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
888
681
    if(ret < 0){
889
682
      perror("sigprocmask");
890
683
      exitstatus = EXIT_FAILURE;
891
 
      goto fallback;
 
684
      goto end;
892
685
    }
893
 
    /* Starting a new process to be watched */
894
 
    pid_t pid;
895
 
    do {
896
 
      pid = fork();
897
 
    } while(pid == -1 and errno == EINTR);
 
686
    // Starting a new process to be watched
 
687
    pid_t pid = fork();
898
688
    if(pid == -1){
899
689
      perror("fork");
900
690
      exitstatus = EXIT_FAILURE;
901
 
      goto fallback;
 
691
      goto end;
902
692
    }
903
693
    if(pid == 0){
904
694
      /* this is the child process */
907
697
        perror("sigaction");
908
698
        _exit(EXIT_FAILURE);
909
699
      }
910
 
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
700
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
911
701
      if(ret < 0){
912
702
        perror("sigprocmask");
913
703
        _exit(EXIT_FAILURE);
914
704
      }
915
 
      
 
705
 
916
706
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
917
707
      if(ret == -1){
918
708
        perror("dup2");
937
727
      }
938
728
      /* no return */
939
729
    }
940
 
    /* Parent process */
941
 
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
942
 
                                             pipe */
 
730
    /* parent process */
943
731
    free(filename);
944
 
    plugin *new_plugin = getplugin(dirst->d_name);
945
 
    if(new_plugin == NULL){
946
 
      perror("getplugin");
947
 
      ret = (int)(TEMP_FAILURE_RETRY
948
 
                  (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
949
 
                               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);
950
737
      if(ret < 0){
951
 
        perror("sigprocmask");
 
738
        perror("sigprocmask");
952
739
      }
953
740
      exitstatus = EXIT_FAILURE;
954
 
      goto fallback;
 
741
      goto end;
955
742
    }
956
743
    
957
 
    new_plugin->pid = pid;
958
 
    new_plugin->fd = pipefd[0];
959
 
    
 
744
    *new_process = (struct process){ .pid = pid,
 
745
                                     .fd = pipefd[0],
 
746
                                     .next = process_list };
 
747
    // List handling
 
748
    process_list = new_process;
960
749
    /* Unblock SIGCHLD so signal handler can be run if this process
961
750
       has already completed */
962
 
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
963
 
                                              &sigchld_action.sa_mask,
964
 
                                              NULL));
 
751
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
965
752
    if(ret < 0){
966
753
      perror("sigprocmask");
967
754
      exitstatus = EXIT_FAILURE;
968
 
      goto fallback;
969
 
    }
970
 
    
971
 
    FD_SET(new_plugin->fd, &rfds_all);
972
 
    
973
 
    if(maxfd < new_plugin->fd){
974
 
      maxfd = new_plugin->fd;
975
 
    }
976
 
  }
977
 
  
978
 
  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);
979
779
  dir = NULL;
980
 
  free_plugin(getplugin(NULL));
981
 
  
982
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
983
 
    if(p->pid != 0){
984
 
      break;
985
 
    }
986
 
    if(p->next == NULL){
987
 
      fprintf(stderr, "No plugin processes started. Incorrect plugin"
988
 
              " directory?\n");
989
 
      free_plugin_list();
990
 
    }
 
780
    
 
781
  if (process_list == NULL){
 
782
    fprintf(stderr, "No plugin processes started. Incorrect plugin"
 
783
            " directory?\n");
 
784
    process_list = NULL;
991
785
  }
992
 
  
993
 
  /* Main loop while running plugins exist */
994
 
  while(plugin_list){
 
786
  while(process_list){
995
787
    fd_set rfds = rfds_all;
996
788
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
997
 
    if(select_ret == -1 and errno != EINTR){
 
789
    if (select_ret == -1){
998
790
      perror("select");
999
791
      exitstatus = EXIT_FAILURE;
1000
 
      goto fallback;
 
792
      goto end;
1001
793
    }
1002
794
    /* OK, now either a process completed, or something can be read
1003
795
       from one of them */
1004
 
    for(plugin *proc = plugin_list; proc != NULL;){
 
796
    for(process *proc = process_list; proc ; proc = proc->next){
1005
797
      /* Is this process completely done? */
1006
 
      if(proc->completed and proc->eof){
 
798
      if(proc->eof and proc->completed){
1007
799
        /* Only accept the plugin output if it exited cleanly */
1008
800
        if(not WIFEXITED(proc->status)
1009
801
           or WEXITSTATUS(proc->status) != 0){
1010
802
          /* Bad exit by plugin */
1011
 
          
1012
803
          if(debug){
1013
804
            if(WIFEXITED(proc->status)){
1014
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
1015
 
                      " status %d\n", proc->name,
1016
 
                      (intmax_t) (proc->pid),
 
805
              fprintf(stderr, "Plugin %u exited with status %d\n",
 
806
                      (unsigned int) (proc->pid),
1017
807
                      WEXITSTATUS(proc->status));
1018
 
            } else if(WIFSIGNALED(proc->status)){
1019
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] killed by"
1020
 
                      " signal %d: %s\n", proc->name,
1021
 
                      (intmax_t) (proc->pid),
1022
 
                      WTERMSIG(proc->status),
1023
 
                      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));
1024
812
            } else if(WCOREDUMP(proc->status)){
1025
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
1026
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
 
813
              fprintf(stderr, "Plugin %d dumped core\n",
 
814
                      (unsigned int) (proc->pid));
1027
815
            }
1028
816
          }
1029
 
          
1030
817
          /* Remove the plugin */
1031
818
          FD_CLR(proc->fd, &rfds_all);
1032
 
          
1033
819
          /* Block signal while modifying process_list */
1034
 
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1035
 
                                        (SIG_BLOCK,
1036
 
                                         &sigchld_action.sa_mask,
1037
 
                                         NULL));
 
820
          ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
1038
821
          if(ret < 0){
1039
822
            perror("sigprocmask");
1040
823
            exitstatus = EXIT_FAILURE;
1041
 
            goto fallback;
1042
 
          }
1043
 
          
1044
 
          plugin *next_plugin = proc->next;
1045
 
          free_plugin(proc);
1046
 
          proc = next_plugin;
1047
 
          
 
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
          }
1048
839
          /* We are done modifying process list, so unblock signal */
1049
 
          ret = (int)(TEMP_FAILURE_RETRY
1050
 
                      (sigprocmask(SIG_UNBLOCK,
1051
 
                                   &sigchld_action.sa_mask, NULL)));
 
840
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
 
841
                             NULL);
1052
842
          if(ret < 0){
1053
843
            perror("sigprocmask");
1054
 
            exitstatus = EXIT_FAILURE;
1055
 
            goto fallback;
1056
 
          }
1057
 
          
1058
 
          if(plugin_list == NULL){
1059
 
            break;
1060
 
          }
1061
 
          
1062
 
          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;
1063
851
        }
1064
 
        
1065
852
        /* This process exited nicely, so print its buffer */
1066
 
        
1067
 
        bool bret = print_out_password(proc->buffer,
1068
 
                                       proc->buffer_length);
 
853
 
 
854
        bool bret = print_out_password(proc->buffer, proc->buffer_length);
1069
855
        if(not bret){
1070
856
          perror("print_out_password");
1071
857
          exitstatus = EXIT_FAILURE;
1072
858
        }
1073
 
        goto fallback;
 
859
        goto end;
1074
860
      }
1075
 
      
1076
861
      /* This process has not completed.  Does it have any output? */
1077
862
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1078
863
        /* This process had nothing to say at this time */
1079
 
        proc = proc->next;
1080
864
        continue;
1081
865
      }
1082
866
      /* Before reading, make the process' data buffer large enough */
1083
867
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1084
868
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1085
869
                               + (size_t) BUFFER_SIZE);
1086
 
        if(proc->buffer == NULL){
 
870
        if (proc->buffer == NULL){
1087
871
          perror("malloc");
1088
872
          exitstatus = EXIT_FAILURE;
1089
 
          goto fallback;
 
873
          goto end;
1090
874
        }
1091
875
        proc->buffer_size += BUFFER_SIZE;
1092
876
      }
1093
877
      /* Read from the process */
1094
 
      sret = TEMP_FAILURE_RETRY(read(proc->fd,
1095
 
                                     proc->buffer
1096
 
                                     + proc->buffer_length,
1097
 
                                     BUFFER_SIZE));
1098
 
      if(sret < 0){
 
878
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
 
879
                 BUFFER_SIZE);
 
880
      if(ret < 0){
1099
881
        /* Read error from this process; ignore the error */
1100
 
        proc = proc->next;
1101
882
        continue;
1102
883
      }
1103
 
      if(sret == 0){
 
884
      if(ret == 0){
1104
885
        /* got EOF */
1105
886
        proc->eof = true;
1106
887
      } else {
1107
 
        proc->buffer_length += (size_t) sret;
 
888
        proc->buffer_length += (size_t) ret;
1108
889
      }
1109
890
    }
1110
891
  }
1111
 
  
1112
 
  
1113
 
 fallback:
1114
 
  
1115
 
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
1116
 
    /* Fallback if all plugins failed, none are found or an error
1117
 
       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 */
1118
898
    bool bret;
1119
899
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
1120
900
    char *passwordbuffer = getpass("Password: ");
1121
 
    size_t len = strlen(passwordbuffer);
1122
 
    /* Strip trailing newline */
1123
 
    if(len > 0 and passwordbuffer[len-1] == '\n'){
1124
 
      passwordbuffer[len-1] = '\0'; /* not strictly necessary */
1125
 
      len--;
1126
 
    }
1127
 
    bret = print_out_password(passwordbuffer, len);
 
901
    bret = print_out_password(passwordbuffer, strlen(passwordbuffer));
1128
902
    if(not bret){
1129
903
      perror("print_out_password");
1130
904
      exitstatus = EXIT_FAILURE;
 
905
      goto end;
1131
906
    }
1132
907
  }
1133
908
  
1134
909
  /* Restore old signal handler */
1135
 
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1136
 
  if(ret == -1){
1137
 
    perror("sigaction");
1138
 
    exitstatus = EXIT_FAILURE;
1139
 
  }
1140
 
  
1141
 
  if(custom_argv != NULL){
1142
 
    for(char **arg = custom_argv+1; *arg != NULL; arg++){
1143
 
      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
      }
1144
922
    }
1145
 
    free(custom_argv);
 
923
    free(plugin_list->environ);
 
924
    free(plugin_list);
1146
925
  }
1147
926
  
1148
927
  if(dir != NULL){
1149
928
    closedir(dir);
1150
929
  }
1151
930
  
1152
 
  /* Kill the processes */
1153
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1154
 
    if(p->pid != 0){
1155
 
      close(p->fd);
1156
 
      ret = kill(p->pid, SIGTERM);
1157
 
      if(ret == -1 and errno != ESRCH){
1158
 
        /* Set-uid proccesses might not get closed */
1159
 
        perror("kill");
1160
 
      }
 
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");
1161
939
    }
 
940
    free(process_list->buffer);
 
941
    free(process_list);
1162
942
  }
1163
943
  
1164
944
  /* Wait for any remaining child processes to terminate */
1165
 
  do {
 
945
  do{
1166
946
    ret = wait(NULL);
1167
947
  } while(ret >= 0);
1168
948
  if(errno != ECHILD){
1169
949
    perror("wait");
1170
950
  }
1171
951
  
1172
 
  free_plugin_list();
1173
 
  
1174
 
  free(plugindir);
1175
 
  free(argfile);
1176
 
  
1177
952
  return exitstatus;
1178
953
}