/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: 2015-03-10 18:03:38 UTC
  • mto: (237.7.304 trunk)
  • mto: This revision was merged to the branch mainline in revision 325.
  • Revision ID: teddy@recompile.se-20150310180338-pcxw6r2qmw9k6br9
Add ":!RSA" to GnuTLS priority string, to disallow non-DHE kx.

If Mandos was somehow made to use a non-ephemeral Diffie-Hellman key
exchange algorithm in the TLS handshake, any saved network traffic
could then be decrypted later if the Mandos client key was obtained.
By default, Mandos uses ephemeral DH key exchanges which does not have
this problem, but a non-ephemeral key exchange algorithm was still
enabled by default.  The simplest solution is to simply turn that off,
which ensures that Mandos will always use ephemeral DH key exchanges.

There is a "PFS" priority string specifier, but we can't use it because:

1. Security-wise, it is a mix between "NORMAL" and "SECURE128" - it
   enables a lot more algorithms than "SECURE256".

2. It is only available since GnuTLS 3.2.4.

Thanks to Andreas Fischer <af@bantuX.org> for reporting this issue.

Show diffs side-by-side

added added

removed removed

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