/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 at recompile
  • Date: 2020-12-03 20:30:45 UTC
  • mto: This revision was merged to the branch mainline in revision 404.
  • Revision ID: teddy@recompile.se-20201203203045-iqd6nq9y5nwalh1x
Minor fix of a test function

In dracut-module/password-agent, the test function
test_send_password_to_socket_EMSGSIZE() (which tests that the
send_password_to_socket() task function aborts properly when getting
EMSGSIZE when writing to the password socket), part of the test code
is supposed to find a message size which definitely does trigger
EMSGSIZE when send()ing to a socket.  Without a "break" in the proper
place, however, the size given is always exactly 1024 bytes too large.

This is very probably not a problem, since a too large message will
still be too large if it is increased by 1024 bytes, and send(2) in
practice checks the size before reading the buffer.  The biggest issue
would be if some version of send(2) would try to look at the last 1024
bytes of the message buffer before checking the message size; this
would then lead to a buffer over-read when running this test function.
(But even then there would be no security implications since the tests
are not run in the normal operation of the program.)

* dracut-module/password-agent.c
  (test_send_password_to_socket_EMSGSIZE): Break out early when ssret
  < 0 and errno == EMSGSIZE; don't allow loop to increase message_size
  again.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
7
 
 * 
8
 
 * This program is free software: you can redistribute it and/or
9
 
 * modify it under the terms of the GNU General Public License as
10
 
 * published by the Free Software Foundation, either version 3 of the
11
 
 * License, or (at your option) any later version.
12
 
 * 
13
 
 * This program is distributed in the hope that it will be useful, but
 
5
 * Copyright © 2008-2020 Teddy Hogeborn
 
6
 * Copyright © 2008-2020 Björn Påhlsson
 
7
 * 
 
8
 * This file is part of Mandos.
 
9
 * 
 
10
 * Mandos is free software: you can redistribute it and/or modify it
 
11
 * under the terms of the GNU General Public License as published by
 
12
 * the Free Software Foundation, either version 3 of the License, or
 
13
 * (at your option) any later version.
 
14
 * 
 
15
 * Mandos is distributed in the hope that it will be useful, but
14
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
18
 * General Public License for more details.
17
19
 * 
18
20
 * You should have received a copy of the GNU General Public License
19
 
 * along with this program.  If not, see
20
 
 * <http://www.gnu.org/licenses/>.
 
21
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
21
22
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
23
 * Contact the authors at <mandos@recompile.se>.
23
24
 */
24
25
 
25
26
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
26
 
                                   asprintf(), O_CLOEXEC */
 
27
                                   O_CLOEXEC, pipe2() */
27
28
#include <stddef.h>             /* size_t, NULL */
28
 
#include <stdlib.h>             /* malloc(), exit(), EXIT_FAILURE,
29
 
                                   EXIT_SUCCESS, realloc() */
 
29
#include <stdlib.h>             /* malloc(), reallocarray(), realloc(),
 
30
                                   EXIT_SUCCESS, exit() */
30
31
#include <stdbool.h>            /* bool, true, false */
31
 
#include <stdio.h>              /* perror, fileno(), fprintf(),
32
 
                                   stderr, STDOUT_FILENO */
33
 
#include <sys/types.h>          /* DIR, fdopendir(), stat(), struct
34
 
                                   stat, waitpid(), WIFEXITED(),
35
 
                                   WEXITSTATUS(), wait(), pid_t,
36
 
                                   uid_t, gid_t, getuid(), getgid(),
37
 
                                   dirfd() */
 
32
#include <stdio.h>              /* fileno(), fprintf(),
 
33
                                   stderr, STDOUT_FILENO, fclose() */
 
34
#include <sys/types.h>          /* fstat(), struct stat, waitpid(),
 
35
                                   WIFEXITED(), WEXITSTATUS(), wait(),
 
36
                                   pid_t, uid_t, gid_t, getuid(),
 
37
                                   getgid() */
38
38
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
39
39
                                   FD_SET(), FD_ISSET(), FD_CLR */
40
40
#include <sys/wait.h>           /* wait(), waitpid(), WIFEXITED(),
41
 
                                   WEXITSTATUS(), WTERMSIG(),
42
 
                                   WCOREDUMP() */
43
 
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
 
41
                                   WEXITSTATUS(), WTERMSIG() */
 
42
#include <sys/stat.h>           /* struct stat, fstat(), S_ISREG() */
44
43
#include <iso646.h>             /* and, or, not */
45
 
#include <dirent.h>             /* DIR, struct dirent, fdopendir(),
46
 
                                   readdir(), closedir(), dirfd() */
47
 
#include <unistd.h>             /* struct stat, stat(), S_ISREG(),
48
 
                                   fcntl(), setuid(), setgid(),
49
 
                                   F_GETFD, F_SETFD, FD_CLOEXEC,
50
 
                                   access(), pipe(), fork(), close()
51
 
                                   dup2(), STDOUT_FILENO, _exit(),
52
 
                                   execv(), write(), read(),
53
 
                                   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
                                */
54
52
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
55
 
                                   FD_CLOEXEC */
56
 
#include <string.h>             /* strsep, strlen(), asprintf(),
57
 
                                   strsignal() */
 
53
                                   FD_CLOEXEC, openat(), scandirat(),
 
54
                                   pipe2() */
 
55
#include <string.h>             /* strsep, strlen(), strsignal(),
 
56
                                   strcmp(), strncmp() */
58
57
#include <errno.h>              /* errno */
59
58
#include <argp.h>               /* struct argp_option, struct
60
59
                                   argp_state, struct argp,
68
67
                                */
69
68
#include <errno.h>              /* errno, EBADF */
70
69
#include <inttypes.h>           /* intmax_t, PRIdMAX, strtoimax() */
71
 
#include <sysexits.h>           /* EX_OSERR, EX_USAGE */
 
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() */
72
75
 
73
76
#define BUFFER_SIZE 256
74
77
 
75
78
#define PDIR "/lib/mandos/plugins.d"
 
79
#define PHDIR "/lib/mandos/plugin-helpers"
76
80
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
77
81
 
78
82
const char *argp_program_version = "plugin-runner " VERSION;
79
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
83
const char *argp_program_bug_address = "<mandos@recompile.se>";
80
84
 
81
85
typedef struct plugin{
82
86
  char *name;                   /* can be NULL or any plugin name */
102
106
 
103
107
/* Gets an existing plugin based on name,
104
108
   or if none is found, creates a new one */
 
109
__attribute__((warn_unused_result))
105
110
static plugin *getplugin(char *name){
106
111
  /* Check for existing plugin with that name */
107
112
  for(plugin *p = plugin_list; p != NULL; p = p->next){
168
173
}
169
174
 
170
175
/* Helper function for add_argument and add_environment */
 
176
__attribute__((nonnull, warn_unused_result))
171
177
static bool add_to_char_array(const char *new, char ***array,
172
178
                              int *len){
173
179
  /* Resize the pointed-to array to hold one more pointer */
 
180
  char **new_array = NULL;
174
181
  do {
175
 
    *array = realloc(*array, sizeof(char *)
176
 
                     * (size_t) ((*len) + 2));
177
 
  } while(*array == NULL and errno == EINTR);
 
182
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26)
 
183
    new_array = reallocarray(*array, (size_t)((*len) + 2),
 
184
                             sizeof(char *));
 
185
#else
 
186
    if(((size_t)((*len) + 2)) > (SIZE_MAX / sizeof(char *))){
 
187
      /* overflow */
 
188
      new_array = NULL;
 
189
      errno = ENOMEM;
 
190
    } else {
 
191
      new_array = realloc(*array, (size_t)((*len) + 2)
 
192
                          * sizeof(char *));
 
193
    }
 
194
#endif
 
195
  } while(new_array == NULL and errno == EINTR);
178
196
  /* Malloc check */
179
 
  if(*array == NULL){
 
197
  if(new_array == NULL){
180
198
    return false;
181
199
  }
 
200
  *array = new_array;
182
201
  /* Make a copy of the new string */
183
202
  char *copy;
184
203
  do {
196
215
}
197
216
 
198
217
/* Add to a plugin's argument vector */
 
218
__attribute__((nonnull(2), warn_unused_result))
199
219
static bool add_argument(plugin *p, const char *arg){
200
220
  if(p == NULL){
201
221
    return false;
204
224
}
205
225
 
206
226
/* Add to a plugin's environment */
 
227
__attribute__((nonnull(2), warn_unused_result))
207
228
static bool add_environment(plugin *p, const char *def, bool replace){
208
229
  if(p == NULL){
209
230
    return false;
211
232
  /* namelen = length of name of environment variable */
212
233
  size_t namelen = (size_t)(strchrnul(def, '=') - def);
213
234
  /* Search for this environment variable */
214
 
  for(char **e = p->environ; *e != NULL; e++){
215
 
    if(strncmp(*e, def, namelen + 1) == 0){
 
235
  for(char **envdef = p->environ; *envdef != NULL; envdef++){
 
236
    if(strncmp(*envdef, def, namelen + 1) == 0){
216
237
      /* It already exists */
217
238
      if(replace){
218
 
        char *new;
 
239
        char *new_envdef;
219
240
        do {
220
 
          new = realloc(*e, strlen(def) + 1);
221
 
        } while(new == NULL and errno == EINTR);
222
 
        if(new == NULL){
 
241
          new_envdef = realloc(*envdef, strlen(def) + 1);
 
242
        } while(new_envdef == NULL and errno == EINTR);
 
243
        if(new_envdef == NULL){
223
244
          return false;
224
245
        }
225
 
        *e = new;
226
 
        strcpy(*e, def);
 
246
        *envdef = new_envdef;
 
247
        strcpy(*envdef, def);
227
248
      }
228
249
      return true;
229
250
    }
231
252
  return add_to_char_array(def, &(p->environ), &(p->envc));
232
253
}
233
254
 
 
255
#ifndef O_CLOEXEC
234
256
/*
235
257
 * Based on the example in the GNU LibC manual chapter 13.13 "File
236
258
 * Descriptor Flags".
237
259
 | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
238
260
 */
 
261
__attribute__((warn_unused_result))
239
262
static int set_cloexec_flag(int fd){
240
263
  int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
241
264
  /* If reading the flags failed, return error indication now. */
246
269
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
247
270
                                       ret | FD_CLOEXEC));
248
271
}
 
272
#endif  /* not O_CLOEXEC */
249
273
 
250
274
 
251
275
/* Mark processes as completed when they exit, and save their exit
265
289
        /* No child processes */
266
290
        break;
267
291
      }
268
 
      perror("waitpid");
 
292
      error(0, errno, "waitpid");
269
293
    }
270
294
    
271
295
    /* A child exited, find it in process_list */
283
307
}
284
308
 
285
309
/* Prints out a password to stdout */
 
310
__attribute__((nonnull, warn_unused_result))
286
311
static bool print_out_password(const char *buffer, size_t length){
287
312
  ssize_t ret;
288
313
  for(size_t written = 0; written < length; written += (size_t)ret){
296
321
}
297
322
 
298
323
/* Removes and free a plugin from the plugin list */
 
324
__attribute__((nonnull))
299
325
static void free_plugin(plugin *plugin_node){
300
326
  
301
 
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
 
327
  for(char **arg = (plugin_node->argv)+1; *arg != NULL; arg++){
302
328
    free(*arg);
303
329
  }
 
330
  free(plugin_node->name);
304
331
  free(plugin_node->argv);
305
332
  for(char **env = plugin_node->environ; *env != NULL; env++){
306
333
    free(*env);
333
360
 
334
361
int main(int argc, char *argv[]){
335
362
  char *plugindir = NULL;
 
363
  char *pluginhelperdir = NULL;
336
364
  char *argfile = NULL;
337
365
  FILE *conffp;
338
 
  size_t d_name_len;
339
 
  DIR *dir = NULL;
340
 
  struct dirent *dirst;
 
366
  struct dirent **direntries = NULL;
341
367
  struct stat st;
342
368
  fd_set rfds_all;
343
369
  int ret, maxfd = 0;
351
377
                                      .sa_flags = SA_NOCLDSTOP };
352
378
  char **custom_argv = NULL;
353
379
  int custom_argc = 0;
 
380
  int dir_fd = -1;
354
381
  
355
382
  /* Establish a signal handler */
356
383
  sigemptyset(&sigchld_action.sa_mask);
357
384
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
358
385
  if(ret == -1){
359
 
    perror("sigaddset");
360
 
    exitstatus = EXIT_FAILURE;
 
386
    error(0, errno, "sigaddset");
 
387
    exitstatus = EX_OSERR;
361
388
    goto fallback;
362
389
  }
363
390
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
364
391
  if(ret == -1){
365
 
    perror("sigaction");
366
 
    exitstatus = EXIT_FAILURE;
 
392
    error(0, errno, "sigaction");
 
393
    exitstatus = EX_OSERR;
367
394
    goto fallback;
368
395
  }
369
396
  
401
428
      .doc = "Group ID the plugins will run as", .group = 3 },
402
429
    { .name = "debug", .key = 132,
403
430
      .doc = "Debug mode", .group = 4 },
 
431
    { .name = "plugin-helper-dir", .key = 133,
 
432
      .arg = "DIRECTORY",
 
433
      .doc = "Specify a different plugin helper directory",
 
434
      .group = 2 },
404
435
    /*
405
436
     * These reproduce what we would get without ARGP_NO_HELP
406
437
     */
413
444
    { .name = NULL }
414
445
  };
415
446
  
 
447
  __attribute__((nonnull(3)))
416
448
  error_t parse_opt(int key, char *arg, struct argp_state *state){
417
449
    errno = 0;
418
450
    switch(key){
419
451
      char *tmp;
420
 
      intmax_t tmpmax;
 
452
      intmax_t tmp_id;
421
453
    case 'g':                   /* --global-options */
422
454
      {
423
455
        char *plugin_option;
426
458
            break;
427
459
          }
428
460
        }
 
461
        errno = 0;
429
462
      }
430
463
      break;
431
464
    case 'G':                   /* --global-env */
432
 
      add_environment(getplugin(NULL), arg, true);
 
465
      if(add_environment(getplugin(NULL), arg, true)){
 
466
        errno = 0;
 
467
      }
433
468
      break;
434
469
    case 'o':                   /* --options-for */
435
470
      {
452
487
            break;
453
488
          }
454
489
        }
 
490
        errno = 0;
455
491
      }
456
492
      break;
457
493
    case 'E':                   /* --env-for */
469
505
          errno = EINVAL;
470
506
          break;
471
507
        }
472
 
        add_environment(getplugin(arg), envdef, true);
 
508
        if(add_environment(getplugin(arg), envdef, true)){
 
509
          errno = 0;
 
510
        }
473
511
      }
474
512
      break;
475
513
    case 'd':                   /* --disable */
477
515
        plugin *p = getplugin(arg);
478
516
        if(p != NULL){
479
517
          p->disabled = true;
 
518
          errno = 0;
480
519
        }
481
520
      }
482
521
      break;
485
524
        plugin *p = getplugin(arg);
486
525
        if(p != NULL){
487
526
          p->disabled = false;
 
527
          errno = 0;
488
528
        }
489
529
      }
490
530
      break;
491
531
    case 128:                   /* --plugin-dir */
492
532
      free(plugindir);
493
533
      plugindir = strdup(arg);
 
534
      if(plugindir != NULL){
 
535
        errno = 0;
 
536
      }
494
537
      break;
495
538
    case 129:                   /* --config-file */
496
539
      /* This is already done by parse_opt_config_file() */
497
540
      break;
498
541
    case 130:                   /* --userid */
499
 
      tmpmax = strtoimax(arg, &tmp, 10);
 
542
      tmp_id = strtoimax(arg, &tmp, 10);
500
543
      if(errno != 0 or tmp == arg or *tmp != '\0'
501
 
         or tmpmax != (uid_t)tmpmax){
 
544
         or tmp_id != (uid_t)tmp_id){
502
545
        argp_error(state, "Bad user ID number: \"%s\", using %"
503
546
                   PRIdMAX, arg, (intmax_t)uid);
504
547
        break;
505
548
      }
506
 
      uid = (uid_t)tmpmax;
 
549
      uid = (uid_t)tmp_id;
 
550
      errno = 0;
507
551
      break;
508
552
    case 131:                   /* --groupid */
509
 
      tmpmax = strtoimax(arg, &tmp, 10);
 
553
      tmp_id = strtoimax(arg, &tmp, 10);
510
554
      if(errno != 0 or tmp == arg or *tmp != '\0'
511
 
         or tmpmax != (gid_t)tmpmax){
 
555
         or tmp_id != (gid_t)tmp_id){
512
556
        argp_error(state, "Bad group ID number: \"%s\", using %"
513
557
                   PRIdMAX, arg, (intmax_t)gid);
514
558
        break;
515
559
      }
516
 
      gid = (gid_t)tmpmax;
 
560
      gid = (gid_t)tmp_id;
 
561
      errno = 0;
517
562
      break;
518
563
    case 132:                   /* --debug */
519
564
      debug = true;
520
565
      break;
 
566
    case 133:                   /* --plugin-helper-dir */
 
567
      free(pluginhelperdir);
 
568
      pluginhelperdir = strdup(arg);
 
569
      if(pluginhelperdir != NULL){
 
570
        errno = 0;
 
571
      }
 
572
      break;
521
573
      /*
522
574
       * These reproduce what we would get without ARGP_NO_HELP
523
575
       */
524
576
    case '?':                   /* --help */
525
577
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
526
578
      argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
 
579
      __builtin_unreachable();
527
580
    case -3:                    /* --usage */
528
581
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
529
582
      argp_state_help(state, state->out_stream,
530
583
                      ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
 
584
      __builtin_unreachable();
531
585
    case 'V':                   /* --version */
532
586
      fprintf(state->out_stream, "%s\n", argp_program_version);
533
587
      exit(EXIT_SUCCESS);
543
597
      if(arg[0] == '\0'){
544
598
        break;
545
599
      }
 
600
#if __GNUC__ >= 7
 
601
      __attribute__((fallthrough));
 
602
#else
 
603
          /* FALLTHROUGH */
 
604
#endif
546
605
    default:
547
606
      return ARGP_ERR_UNKNOWN;
548
607
    }
567
626
    case 129:                   /* --config-file */
568
627
      free(argfile);
569
628
      argfile = strdup(arg);
 
629
      if(argfile != NULL){
 
630
        errno = 0;
 
631
      }
570
632
      break;
571
633
    case 130:                   /* --userid */
572
634
    case 131:                   /* --groupid */
573
635
    case 132:                   /* --debug */
 
636
    case 133:                   /* --plugin-helper-dir */
574
637
    case '?':                   /* --help */
575
638
    case -3:                    /* --usage */
576
639
    case 'V':                   /* --version */
598
661
  case ENOMEM:
599
662
  default:
600
663
    errno = ret;
601
 
    perror("argp_parse");
 
664
    error(0, errno, "argp_parse");
602
665
    exitstatus = EX_OSERR;
603
666
    goto fallback;
604
667
  case EINVAL:
625
688
    custom_argc = 1;
626
689
    custom_argv = malloc(sizeof(char*) * 2);
627
690
    if(custom_argv == NULL){
628
 
      perror("malloc");
629
 
      exitstatus = EXIT_FAILURE;
 
691
      error(0, errno, "malloc");
 
692
      exitstatus = EX_OSERR;
630
693
      goto fallback;
631
694
    }
632
695
    custom_argv[0] = argv[0];
648
711
        }
649
712
        new_arg = strdup(p);
650
713
        if(new_arg == NULL){
651
 
          perror("strdup");
652
 
          exitstatus = EXIT_FAILURE;
 
714
          error(0, errno, "strdup");
 
715
          exitstatus = EX_OSERR;
653
716
          free(org_line);
654
717
          goto fallback;
655
718
        }
656
719
        
657
720
        custom_argc += 1;
658
 
        custom_argv = realloc(custom_argv, sizeof(char *)
659
 
                              * ((unsigned int) custom_argc + 1));
660
 
        if(custom_argv == NULL){
661
 
          perror("realloc");
662
 
          exitstatus = EXIT_FAILURE;
663
 
          free(org_line);
664
 
          goto fallback;
 
721
        {
 
722
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26)
 
723
          char **new_argv = reallocarray(custom_argv, (size_t)custom_argc + 1,
 
724
                                         sizeof(char *));
 
725
#else
 
726
          char **new_argv = NULL;
 
727
          if(((size_t)custom_argc + 1) > (SIZE_MAX / sizeof(char *))){
 
728
            /* overflow */
 
729
            errno = ENOMEM;
 
730
          } else {
 
731
            new_argv = realloc(custom_argv, ((size_t)custom_argc + 1)
 
732
                               * sizeof(char *));
 
733
          }
 
734
#endif
 
735
          if(new_argv == NULL){
 
736
            error(0, errno, "reallocarray");
 
737
            exitstatus = EX_OSERR;
 
738
            free(new_arg);
 
739
            free(org_line);
 
740
            goto fallback;
 
741
          } else {
 
742
            custom_argv = new_argv;
 
743
          }
665
744
        }
666
745
        custom_argv[custom_argc-1] = new_arg;
667
746
        custom_argv[custom_argc] = NULL;
671
750
      ret = fclose(conffp);
672
751
    } while(ret == EOF and errno == EINTR);
673
752
    if(ret == EOF){
674
 
      perror("fclose");
675
 
      exitstatus = EXIT_FAILURE;
 
753
      error(0, errno, "fclose");
 
754
      exitstatus = EX_IOERR;
676
755
      goto fallback;
677
756
    }
678
757
    free(org_line);
680
759
    /* Check for harmful errors and go to fallback. Other errors might
681
760
       not affect opening plugins */
682
761
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
683
 
      perror("fopen");
684
 
      exitstatus = EXIT_FAILURE;
 
762
      error(0, errno, "fopen");
 
763
      exitstatus = EX_OSERR;
685
764
      goto fallback;
686
765
    }
687
766
  }
697
776
    case ENOMEM:
698
777
    default:
699
778
      errno = ret;
700
 
      perror("argp_parse");
 
779
      error(0, errno, "argp_parse");
701
780
      exitstatus = EX_OSERR;
702
781
      goto fallback;
703
782
    case EINVAL:
717
796
  case ENOMEM:
718
797
  default:
719
798
    errno = ret;
720
 
    perror("argp_parse");
 
799
    error(0, errno, "argp_parse");
721
800
    exitstatus = EX_OSERR;
722
801
    goto fallback;
723
802
  case EINVAL:
725
804
    goto fallback;
726
805
  }
727
806
  
 
807
  {
 
808
    char *pluginhelperenv;
 
809
    bool bret = true;
 
810
    ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s",
 
811
                   pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
812
    if(ret != -1){
 
813
      bret = add_environment(getplugin(NULL), pluginhelperenv, true);
 
814
    }
 
815
    if(ret == -1 or not bret){
 
816
      error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR"
 
817
            " environment variable to \"%s\" for all plugins\n",
 
818
            pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
819
    }
 
820
    if(ret != -1){
 
821
      free(pluginhelperenv);
 
822
    }
 
823
  }
 
824
  
728
825
  if(debug){
729
 
    for(plugin *p = plugin_list; p != NULL; p=p->next){
 
826
    for(plugin *p = plugin_list; p != NULL; p = p->next){
730
827
      fprintf(stderr, "Plugin: %s has %d arguments\n",
731
828
              p->name ? p->name : "Global", p->argc - 1);
732
829
      for(char **a = p->argv; *a != NULL; a++){
739
836
    }
740
837
  }
741
838
  
742
 
  /* Strip permissions down to nobody */
743
 
  setgid(gid);
 
839
  if(getuid() == 0){
 
840
    /* Work around Debian bug #633582:
 
841
       <https://bugs.debian.org/633582> */
 
842
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
 
843
    if(plugindir_fd == -1){
 
844
      if(errno != ENOENT){
 
845
        error(0, errno, "open(\"" PDIR "\")");
 
846
      }
 
847
    } else {
 
848
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
 
849
      if(ret == -1){
 
850
        error(0, errno, "fstat");
 
851
      } else {
 
852
        if(S_ISDIR(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
 
853
          ret = fchown(plugindir_fd, uid, gid);
 
854
          if(ret == -1){
 
855
            error(0, errno, "fchown");
 
856
          }
 
857
        }
 
858
      }
 
859
      close(plugindir_fd);
 
860
    }
 
861
  }
 
862
  
 
863
  /* Lower permissions */
 
864
  ret = setgid(gid);
744
865
  if(ret == -1){
745
 
    perror("setgid");
 
866
    error(0, errno, "setgid");
746
867
  }
747
868
  ret = setuid(uid);
748
869
  if(ret == -1){
749
 
    perror("setuid");
 
870
    error(0, errno, "setuid");
750
871
  }
751
872
  
752
873
  /* Open plugin directory with close_on_exec flag */
753
874
  {
754
 
    int dir_fd = -1;
755
 
    if(plugindir == NULL){
756
 
      dir_fd = open(PDIR, O_RDONLY |
757
 
#ifdef O_CLOEXEC
758
 
                    O_CLOEXEC
759
 
#else  /* not O_CLOEXEC */
760
 
                    0
761
 
#endif  /* not O_CLOEXEC */
762
 
                    );
763
 
    } else {
764
 
      dir_fd = open(plugindir, O_RDONLY |
765
 
#ifdef O_CLOEXEC
766
 
                    O_CLOEXEC
767
 
#else  /* not O_CLOEXEC */
768
 
                    0
769
 
#endif  /* not O_CLOEXEC */
770
 
                    );
771
 
    }
 
875
    dir_fd = open(plugindir != NULL ? plugindir : PDIR, O_RDONLY |
 
876
#ifdef O_CLOEXEC
 
877
                  O_CLOEXEC
 
878
#else  /* not O_CLOEXEC */
 
879
                  0
 
880
#endif  /* not O_CLOEXEC */
 
881
                  );
772
882
    if(dir_fd == -1){
773
 
      perror("Could not open plugin dir");
774
 
      exitstatus = EXIT_FAILURE;
 
883
      error(0, errno, "Could not open plugin dir");
 
884
      exitstatus = EX_UNAVAILABLE;
775
885
      goto fallback;
776
886
    }
777
887
    
779
889
  /* Set the FD_CLOEXEC flag on the directory */
780
890
    ret = set_cloexec_flag(dir_fd);
781
891
    if(ret < 0){
782
 
      perror("set_cloexec_flag");
783
 
      TEMP_FAILURE_RETRY(close(dir_fd));
784
 
      exitstatus = EXIT_FAILURE;
 
892
      error(0, errno, "set_cloexec_flag");
 
893
      exitstatus = EX_OSERR;
785
894
      goto fallback;
786
895
    }
787
896
#endif  /* O_CLOEXEC */
788
 
    
789
 
    dir = fdopendir(dir_fd);
790
 
    if(dir == NULL){
791
 
      perror("Could not open plugin dir");
792
 
      TEMP_FAILURE_RETRY(close(dir_fd));
793
 
      exitstatus = EXIT_FAILURE;
794
 
      goto fallback;
 
897
  }
 
898
  
 
899
  int good_name(const struct dirent * const dirent){
 
900
    const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new",
 
901
                                      "*.dpkg-old", "*.dpkg-bak",
 
902
                                      "*.dpkg-divert", NULL };
 
903
#ifdef __GNUC__
 
904
#pragma GCC diagnostic push
 
905
#pragma GCC diagnostic ignored "-Wcast-qual"
 
906
#endif
 
907
    for(const char **pat = (const char **)patterns;
 
908
        *pat != NULL; pat++){
 
909
#ifdef __GNUC__
 
910
#pragma GCC diagnostic pop
 
911
#endif
 
912
      if(fnmatch(*pat, dirent->d_name, FNM_FILE_NAME | FNM_PERIOD)
 
913
         != FNM_NOMATCH){
 
914
        if(debug){
 
915
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
916
                    " matching pattern %s\n", dirent->d_name, *pat);
 
917
        }
 
918
        return 0;
 
919
      }
795
920
    }
 
921
    return 1;
 
922
  }
 
923
  
 
924
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
 
925
                             alphasort);
 
926
  if(numplugins == -1){
 
927
    error(0, errno, "Could not scan plugin dir");
 
928
    direntries = NULL;
 
929
    exitstatus = EX_OSERR;
 
930
    goto fallback;
796
931
  }
797
932
  
798
933
  FD_ZERO(&rfds_all);
799
934
  
800
935
  /* Read and execute any executable in the plugin directory*/
801
 
  while(true){
802
 
    do {
803
 
      dirst = readdir(dir);
804
 
    } while(dirst == NULL and errno == EINTR);
805
 
    
806
 
    /* All directory entries have been processed */
807
 
    if(dirst == NULL){
808
 
      if(errno == EBADF){
809
 
        perror("readdir");
810
 
        exitstatus = EXIT_FAILURE;
811
 
        goto fallback;
812
 
      }
813
 
      break;
814
 
    }
815
 
    
816
 
    d_name_len = strlen(dirst->d_name);
817
 
    
818
 
    /* Ignore dotfiles, backup files and other junk */
819
 
    {
820
 
      bool bad_name = false;
821
 
      
822
 
      const char const *bad_prefixes[] = { ".", "#", NULL };
823
 
      
824
 
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
825
 
                                           ".dpkg-old",
826
 
                                           ".dpkg-bak",
827
 
                                           ".dpkg-divert", NULL };
828
 
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
829
 
        size_t pre_len = strlen(*pre);
830
 
        if((d_name_len >= pre_len)
831
 
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
832
 
          if(debug){
833
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
834
 
                    " with bad prefix %s\n", dirst->d_name, *pre);
835
 
          }
836
 
          bad_name = true;
837
 
          break;
838
 
        }
839
 
      }
840
 
      if(bad_name){
841
 
        continue;
842
 
      }
843
 
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
844
 
        size_t suf_len = strlen(*suf);
845
 
        if((d_name_len >= suf_len)
846
 
           and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
847
 
                == 0)){
848
 
          if(debug){
849
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
850
 
                    " with bad suffix %s\n", dirst->d_name, *suf);
851
 
          }
852
 
          bad_name = true;
853
 
          break;
854
 
        }
855
 
      }
856
 
      
857
 
      if(bad_name){
858
 
        continue;
859
 
      }
860
 
    }
861
 
    
862
 
    char *filename;
863
 
    if(plugindir == NULL){
864
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s",
865
 
                                             dirst->d_name));
866
 
    } else {
867
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s",
868
 
                                             plugindir,
869
 
                                             dirst->d_name));
870
 
    }
871
 
    if(ret < 0){
872
 
      perror("asprintf");
 
936
  for(int i = 0; i < numplugins; i++){
 
937
    
 
938
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
 
939
    if(plugin_fd == -1){
 
940
      error(0, errno, "Could not open plugin");
 
941
      free(direntries[i]);
873
942
      continue;
874
943
    }
875
 
    
876
 
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
 
944
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
877
945
    if(ret == -1){
878
 
      perror("stat");
879
 
      free(filename);
 
946
      error(0, errno, "stat");
 
947
      close(plugin_fd);
 
948
      free(direntries[i]);
880
949
      continue;
881
950
    }
882
951
    
883
952
    /* Ignore non-executable files */
884
953
    if(not S_ISREG(st.st_mode)
885
 
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
 
954
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
 
955
                                        X_OK, 0)) != 0)){
886
956
      if(debug){
887
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
888
 
                " with bad type or mode\n", filename);
 
957
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
 
958
                " with bad type or mode\n",
 
959
                plugindir != NULL ? plugindir : PDIR,
 
960
                direntries[i]->d_name);
889
961
      }
890
 
      free(filename);
 
962
      close(plugin_fd);
 
963
      free(direntries[i]);
891
964
      continue;
892
965
    }
893
966
    
894
 
    plugin *p = getplugin(dirst->d_name);
 
967
    plugin *p = getplugin(direntries[i]->d_name);
895
968
    if(p == NULL){
896
 
      perror("getplugin");
897
 
      free(filename);
 
969
      error(0, errno, "getplugin");
 
970
      close(plugin_fd);
 
971
      free(direntries[i]);
898
972
      continue;
899
973
    }
900
974
    if(p->disabled){
901
975
      if(debug){
902
976
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
903
 
                dirst->d_name);
 
977
                direntries[i]->d_name);
904
978
      }
905
 
      free(filename);
 
979
      close(plugin_fd);
 
980
      free(direntries[i]);
906
981
      continue;
907
982
    }
908
983
    {
911
986
      if(g != NULL){
912
987
        for(char **a = g->argv + 1; *a != NULL; a++){
913
988
          if(not add_argument(p, *a)){
914
 
            perror("add_argument");
 
989
            error(0, errno, "add_argument");
915
990
          }
916
991
        }
917
992
        /* Add global environment variables */
918
993
        for(char **e = g->environ; *e != NULL; e++){
919
994
          if(not add_environment(p, *e, false)){
920
 
            perror("add_environment");
 
995
            error(0, errno, "add_environment");
921
996
          }
922
997
        }
923
998
      }
924
999
    }
925
 
    /* If this plugin has any environment variables, we will call
926
 
       using execve and need to duplicate the environment from this
927
 
       process, too. */
 
1000
    /* If this plugin has any environment variables, we need to
 
1001
       duplicate the environment from this process, too. */
928
1002
    if(p->environ[0] != NULL){
929
1003
      for(char **e = environ; *e != NULL; e++){
930
1004
        if(not add_environment(p, *e, false)){
931
 
          perror("add_environment");
 
1005
          error(0, errno, "add_environment");
932
1006
        }
933
1007
      }
934
1008
    }
935
1009
    
936
1010
    int pipefd[2];
 
1011
#ifndef O_CLOEXEC
937
1012
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
 
1013
#else  /* O_CLOEXEC */
 
1014
    ret = (int)TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC));
 
1015
#endif  /* O_CLOEXEC */
938
1016
    if(ret == -1){
939
 
      perror("pipe");
940
 
      exitstatus = EXIT_FAILURE;
941
 
      goto fallback;
942
 
    }
 
1017
      error(0, errno, "pipe");
 
1018
      exitstatus = EX_OSERR;
 
1019
      free(direntries[i]);
 
1020
      goto fallback;
 
1021
    }
 
1022
    if(pipefd[0] >= FD_SETSIZE){
 
1023
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
 
1024
              FD_SETSIZE);
 
1025
      close(pipefd[0]);
 
1026
      close(pipefd[1]);
 
1027
      exitstatus = EX_OSERR;
 
1028
      free(direntries[i]);
 
1029
      goto fallback;
 
1030
    }
 
1031
#ifndef O_CLOEXEC
943
1032
    /* Ask OS to automatic close the pipe on exec */
944
1033
    ret = set_cloexec_flag(pipefd[0]);
945
1034
    if(ret < 0){
946
 
      perror("set_cloexec_flag");
947
 
      exitstatus = EXIT_FAILURE;
 
1035
      error(0, errno, "set_cloexec_flag");
 
1036
      close(pipefd[0]);
 
1037
      close(pipefd[1]);
 
1038
      exitstatus = EX_OSERR;
 
1039
      free(direntries[i]);
948
1040
      goto fallback;
949
1041
    }
950
1042
    ret = set_cloexec_flag(pipefd[1]);
951
1043
    if(ret < 0){
952
 
      perror("set_cloexec_flag");
953
 
      exitstatus = EXIT_FAILURE;
 
1044
      error(0, errno, "set_cloexec_flag");
 
1045
      close(pipefd[0]);
 
1046
      close(pipefd[1]);
 
1047
      exitstatus = EX_OSERR;
 
1048
      free(direntries[i]);
954
1049
      goto fallback;
955
1050
    }
 
1051
#endif  /* not O_CLOEXEC */
956
1052
    /* Block SIGCHLD until process is safely in process list */
957
1053
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
958
1054
                                              &sigchld_action.sa_mask,
959
1055
                                              NULL));
960
1056
    if(ret < 0){
961
 
      perror("sigprocmask");
962
 
      exitstatus = EXIT_FAILURE;
 
1057
      error(0, errno, "sigprocmask");
 
1058
      exitstatus = EX_OSERR;
 
1059
      free(direntries[i]);
963
1060
      goto fallback;
964
1061
    }
965
1062
    /* Starting a new process to be watched */
968
1065
      pid = fork();
969
1066
    } while(pid == -1 and errno == EINTR);
970
1067
    if(pid == -1){
971
 
      perror("fork");
972
 
      exitstatus = EXIT_FAILURE;
 
1068
      error(0, errno, "fork");
 
1069
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
 
1070
                                     &sigchld_action.sa_mask, NULL));
 
1071
      close(pipefd[0]);
 
1072
      close(pipefd[1]);
 
1073
      exitstatus = EX_OSERR;
 
1074
      free(direntries[i]);
973
1075
      goto fallback;
974
1076
    }
975
1077
    if(pid == 0){
976
1078
      /* this is the child process */
977
1079
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
978
1080
      if(ret < 0){
979
 
        perror("sigaction");
980
 
        _exit(EXIT_FAILURE);
 
1081
        error(0, errno, "sigaction");
 
1082
        _exit(EX_OSERR);
981
1083
      }
982
1084
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
983
1085
      if(ret < 0){
984
 
        perror("sigprocmask");
985
 
        _exit(EXIT_FAILURE);
 
1086
        error(0, errno, "sigprocmask");
 
1087
        _exit(EX_OSERR);
986
1088
      }
987
1089
      
988
1090
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
989
1091
      if(ret == -1){
990
 
        perror("dup2");
991
 
        _exit(EXIT_FAILURE);
 
1092
        error(0, errno, "dup2");
 
1093
        _exit(EX_OSERR);
992
1094
      }
993
1095
      
994
 
      if(dirfd(dir) < 0){
995
 
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
996
 
           above and must now close it manually here. */
997
 
        closedir(dir);
998
 
      }
999
 
      if(p->environ[0] == NULL){
1000
 
        if(execv(filename, p->argv) < 0){
1001
 
          perror("execv");
1002
 
          _exit(EXIT_FAILURE);
1003
 
        }
1004
 
      } else {
1005
 
        if(execve(filename, p->argv, p->environ) < 0){
1006
 
          perror("execve");
1007
 
          _exit(EXIT_FAILURE);
1008
 
        }
 
1096
      if(fexecve(plugin_fd, p->argv,
 
1097
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
 
1098
        error(0, errno, "fexecve for %s/%s",
 
1099
              plugindir != NULL ? plugindir : PDIR,
 
1100
              direntries[i]->d_name);
 
1101
        _exit(EX_OSERR);
1009
1102
      }
1010
1103
      /* no return */
1011
1104
    }
1012
1105
    /* Parent process */
1013
 
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1014
 
                                             pipe */
1015
 
    free(filename);
1016
 
    plugin *new_plugin = getplugin(dirst->d_name);
 
1106
    close(pipefd[1]);           /* Close unused write end of pipe */
 
1107
    close(plugin_fd);
 
1108
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1017
1109
    if(new_plugin == NULL){
1018
 
      perror("getplugin");
 
1110
      error(0, errno, "getplugin");
1019
1111
      ret = (int)(TEMP_FAILURE_RETRY
1020
1112
                  (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
1021
1113
                               NULL)));
1022
1114
      if(ret < 0){
1023
 
        perror("sigprocmask");
 
1115
        error(0, errno, "sigprocmask");
1024
1116
      }
1025
 
      exitstatus = EXIT_FAILURE;
 
1117
      exitstatus = EX_OSERR;
 
1118
      free(direntries[i]);
1026
1119
      goto fallback;
1027
1120
    }
 
1121
    free(direntries[i]);
1028
1122
    
1029
1123
    new_plugin->pid = pid;
1030
1124
    new_plugin->fd = pipefd[0];
1031
 
    
 
1125
 
 
1126
    if(debug){
 
1127
      fprintf(stderr, "Plugin %s started (PID %" PRIdMAX ")\n",
 
1128
              new_plugin->name, (intmax_t) (new_plugin->pid));
 
1129
    }
 
1130
 
1032
1131
    /* Unblock SIGCHLD so signal handler can be run if this process
1033
1132
       has already completed */
1034
1133
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1035
1134
                                              &sigchld_action.sa_mask,
1036
1135
                                              NULL));
1037
1136
    if(ret < 0){
1038
 
      perror("sigprocmask");
1039
 
      exitstatus = EXIT_FAILURE;
 
1137
      error(0, errno, "sigprocmask");
 
1138
      exitstatus = EX_OSERR;
1040
1139
      goto fallback;
1041
1140
    }
1042
1141
    
1043
 
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
1044
 
                                          -Wconversion */
 
1142
    FD_SET(new_plugin->fd, &rfds_all);
1045
1143
    
1046
1144
    if(maxfd < new_plugin->fd){
1047
1145
      maxfd = new_plugin->fd;
1048
1146
    }
1049
1147
  }
1050
1148
  
1051
 
  TEMP_FAILURE_RETRY(closedir(dir));
1052
 
  dir = NULL;
 
1149
  free(direntries);
 
1150
  direntries = NULL;
 
1151
  close(dir_fd);
 
1152
  dir_fd = -1;
1053
1153
  free_plugin(getplugin(NULL));
1054
1154
  
1055
1155
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1068
1168
    fd_set rfds = rfds_all;
1069
1169
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
1070
1170
    if(select_ret == -1 and errno != EINTR){
1071
 
      perror("select");
1072
 
      exitstatus = EXIT_FAILURE;
 
1171
      error(0, errno, "select");
 
1172
      exitstatus = EX_OSERR;
1073
1173
      goto fallback;
1074
1174
    }
1075
1175
    /* OK, now either a process completed, or something can be read
1094
1194
                      (intmax_t) (proc->pid),
1095
1195
                      WTERMSIG(proc->status),
1096
1196
                      strsignal(WTERMSIG(proc->status)));
1097
 
            } else if(WCOREDUMP(proc->status)){
1098
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
1099
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
1100
1197
            }
1101
1198
          }
1102
1199
          
1103
1200
          /* Remove the plugin */
1104
 
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1105
 
                                          -Wconversion */
 
1201
          FD_CLR(proc->fd, &rfds_all);
1106
1202
          
1107
1203
          /* Block signal while modifying process_list */
1108
1204
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1110
1206
                                         &sigchld_action.sa_mask,
1111
1207
                                         NULL));
1112
1208
          if(ret < 0){
1113
 
            perror("sigprocmask");
1114
 
            exitstatus = EXIT_FAILURE;
 
1209
            error(0, errno, "sigprocmask");
 
1210
            exitstatus = EX_OSERR;
1115
1211
            goto fallback;
1116
1212
          }
1117
1213
          
1124
1220
                      (sigprocmask(SIG_UNBLOCK,
1125
1221
                                   &sigchld_action.sa_mask, NULL)));
1126
1222
          if(ret < 0){
1127
 
            perror("sigprocmask");
1128
 
            exitstatus = EXIT_FAILURE;
 
1223
            error(0, errno, "sigprocmask");
 
1224
            exitstatus = EX_OSERR;
1129
1225
            goto fallback;
1130
1226
          }
1131
1227
          
1141
1237
        bool bret = print_out_password(proc->buffer,
1142
1238
                                       proc->buffer_length);
1143
1239
        if(not bret){
1144
 
          perror("print_out_password");
1145
 
          exitstatus = EXIT_FAILURE;
 
1240
          error(0, errno, "print_out_password");
 
1241
          exitstatus = EX_IOERR;
1146
1242
        }
1147
1243
        goto fallback;
1148
1244
      }
1149
1245
      
1150
1246
      /* This process has not completed.  Does it have any output? */
1151
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1152
 
                                                         warning from
1153
 
                                                         -Wconversion */
 
1247
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1154
1248
        /* This process had nothing to say at this time */
1155
1249
        proc = proc->next;
1156
1250
        continue;
1157
1251
      }
1158
1252
      /* Before reading, make the process' data buffer large enough */
1159
1253
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1160
 
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1161
 
                               + (size_t) BUFFER_SIZE);
1162
 
        if(proc->buffer == NULL){
1163
 
          perror("malloc");
1164
 
          exitstatus = EXIT_FAILURE;
 
1254
        char *new_buffer = realloc(proc->buffer, proc->buffer_size
 
1255
                                   + (size_t) BUFFER_SIZE);
 
1256
        if(new_buffer == NULL){
 
1257
          error(0, errno, "malloc");
 
1258
          exitstatus = EX_OSERR;
1165
1259
          goto fallback;
1166
1260
        }
 
1261
        proc->buffer = new_buffer;
1167
1262
        proc->buffer_size += BUFFER_SIZE;
1168
1263
      }
1169
1264
      /* Read from the process */
1188
1283
  
1189
1284
 fallback:
1190
1285
  
1191
 
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
 
1286
  if(plugin_list == NULL or (exitstatus != EXIT_SUCCESS
 
1287
                             and exitstatus != EX_OK)){
1192
1288
    /* Fallback if all plugins failed, none are found or an error
1193
1289
       occured */
1194
1290
    bool bret;
1202
1298
    }
1203
1299
    bret = print_out_password(passwordbuffer, len);
1204
1300
    if(not bret){
1205
 
      perror("print_out_password");
1206
 
      exitstatus = EXIT_FAILURE;
 
1301
      error(0, errno, "print_out_password");
 
1302
      exitstatus = EX_IOERR;
1207
1303
    }
1208
1304
  }
1209
1305
  
1210
1306
  /* Restore old signal handler */
1211
1307
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1212
1308
  if(ret == -1){
1213
 
    perror("sigaction");
1214
 
    exitstatus = EXIT_FAILURE;
 
1309
    error(0, errno, "sigaction");
 
1310
    exitstatus = EX_OSERR;
1215
1311
  }
1216
1312
  
1217
1313
  if(custom_argv != NULL){
1221
1317
    free(custom_argv);
1222
1318
  }
1223
1319
  
1224
 
  if(dir != NULL){
1225
 
    closedir(dir);
 
1320
  free(direntries);
 
1321
  
 
1322
  if(dir_fd != -1){
 
1323
    close(dir_fd);
1226
1324
  }
1227
1325
  
1228
1326
  /* Kill the processes */
1232
1330
      ret = kill(p->pid, SIGTERM);
1233
1331
      if(ret == -1 and errno != ESRCH){
1234
1332
        /* Set-uid proccesses might not get closed */
1235
 
        perror("kill");
 
1333
        error(0, errno, "kill");
1236
1334
      }
1237
1335
    }
1238
1336
  }
1242
1340
    ret = wait(NULL);
1243
1341
  } while(ret >= 0);
1244
1342
  if(errno != ECHILD){
1245
 
    perror("wait");
 
1343
    error(0, errno, "wait");
1246
1344
  }
1247
1345
  
1248
1346
  free_plugin_list();
1249
1347
  
1250
1348
  free(plugindir);
 
1349
  free(pluginhelperdir);
1251
1350
  free(argfile);
1252
1351
  
1253
1352
  return exitstatus;