/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: 2016-03-17 20:40:55 UTC
  • mto: (237.7.594 trunk)
  • mto: This revision was merged to the branch mainline in revision 341.
  • Revision ID: teddy@recompile.se-20160317204055-bhsh5xsidq7w5cxu
Client: Fix plymouth agent; broken since 1.7.2.

Fix an very old memory bug in the plymouth agent (which has been
present since its apperance in version 1.2), but which was only
recently detected at run time due to the new -fsanitize=address
compile- time flag, which has been used since version 1.7.2.  This
detection of a memory access violation causes the program to abort,
making the Plymouth graphical boot system unable to accept interactive
input of passwords when using the Mandos client.

* plugins.d/plymouth.c (exec_and_wait): Fix memory allocation bug when
  allocating new_argv.  Also tolerate a zero-length argv.

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