/mandos/trunk

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

« back to all changes in this revision

Viewing changes to plugin-runner.c

  • Committer: Teddy Hogeborn
  • Date: 2015-03-10 18:03:38 UTC
  • Revision ID: teddy@recompile.se-20150310180338-pcxw6r2qmw9k6br9
Add ":!RSA" to GnuTLS priority string, to disallow non-DHE kx.

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

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

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

2. It is only available since GnuTLS 3.2.4.

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

Show diffs side-by-side

added added

removed removed

Lines of Context:
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-2014 Teddy Hogeborn
 
6
 * Copyright © 2008-2014 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(), O_CLOEXEC */
 
26
                                   O_CLOEXEC, pipe2() */
27
27
#include <stddef.h>             /* size_t, NULL */
28
28
#include <stdlib.h>             /* malloc(), exit(), EXIT_SUCCESS,
29
29
                                   realloc() */
30
30
#include <stdbool.h>            /* bool, true, false */
31
31
#include <stdio.h>              /* 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
                                   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
40
                                   WEXITSTATUS(), WTERMSIG(),
42
41
                                   WCOREDUMP() */
43
 
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
 
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(), strcmp(), strncmp() */
 
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,
72
71
                                   EX_CONFIG, EX_UNAVAILABLE, EX_OK */
73
72
#include <errno.h>              /* errno */
74
73
#include <error.h>              /* error() */
 
74
#include <fnmatch.h>            /* fnmatch() */
75
75
 
76
76
#define BUFFER_SIZE 256
77
77
 
79
79
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
80
80
 
81
81
const char *argp_program_version = "plugin-runner " VERSION;
82
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
82
const char *argp_program_bug_address = "<mandos@recompile.se>";
83
83
 
84
84
typedef struct plugin{
85
85
  char *name;                   /* can be NULL or any plugin name */
105
105
 
106
106
/* Gets an existing plugin based on name,
107
107
   or if none is found, creates a new one */
 
108
__attribute__((warn_unused_result))
108
109
static plugin *getplugin(char *name){
109
110
  /* Check for existing plugin with that name */
110
111
  for(plugin *p = plugin_list; p != NULL; p = p->next){
171
172
}
172
173
 
173
174
/* Helper function for add_argument and add_environment */
 
175
__attribute__((nonnull, warn_unused_result))
174
176
static bool add_to_char_array(const char *new, char ***array,
175
177
                              int *len){
176
178
  /* Resize the pointed-to array to hold one more pointer */
 
179
  char **new_array = NULL;
177
180
  do {
178
 
    *array = realloc(*array, sizeof(char *)
179
 
                     * (size_t) ((*len) + 2));
180
 
  } while(*array == NULL and errno == EINTR);
 
181
    new_array = realloc(*array, sizeof(char *)
 
182
                        * (size_t) ((*len) + 2));
 
183
  } while(new_array == NULL and errno == EINTR);
181
184
  /* Malloc check */
182
 
  if(*array == NULL){
 
185
  if(new_array == NULL){
183
186
    return false;
184
187
  }
 
188
  *array = new_array;
185
189
  /* Make a copy of the new string */
186
190
  char *copy;
187
191
  do {
199
203
}
200
204
 
201
205
/* Add to a plugin's argument vector */
 
206
__attribute__((nonnull(2), warn_unused_result))
202
207
static bool add_argument(plugin *p, const char *arg){
203
208
  if(p == NULL){
204
209
    return false;
207
212
}
208
213
 
209
214
/* Add to a plugin's environment */
 
215
__attribute__((nonnull(2), warn_unused_result))
210
216
static bool add_environment(plugin *p, const char *def, bool replace){
211
217
  if(p == NULL){
212
218
    return false;
214
220
  /* namelen = length of name of environment variable */
215
221
  size_t namelen = (size_t)(strchrnul(def, '=') - def);
216
222
  /* Search for this environment variable */
217
 
  for(char **e = p->environ; *e != NULL; e++){
218
 
    if(strncmp(*e, def, namelen + 1) == 0){
 
223
  for(char **envdef = p->environ; *envdef != NULL; envdef++){
 
224
    if(strncmp(*envdef, def, namelen + 1) == 0){
219
225
      /* It already exists */
220
226
      if(replace){
221
 
        char *new;
 
227
        char *new_envdef;
222
228
        do {
223
 
          new = realloc(*e, strlen(def) + 1);
224
 
        } while(new == NULL and errno == EINTR);
225
 
        if(new == NULL){
 
229
          new_envdef = realloc(*envdef, strlen(def) + 1);
 
230
        } while(new_envdef == NULL and errno == EINTR);
 
231
        if(new_envdef == NULL){
226
232
          return false;
227
233
        }
228
 
        *e = new;
229
 
        strcpy(*e, def);
 
234
        *envdef = new_envdef;
 
235
        strcpy(*envdef, def);
230
236
      }
231
237
      return true;
232
238
    }
234
240
  return add_to_char_array(def, &(p->environ), &(p->envc));
235
241
}
236
242
 
 
243
#ifndef O_CLOEXEC
237
244
/*
238
245
 * Based on the example in the GNU LibC manual chapter 13.13 "File
239
246
 * Descriptor Flags".
240
247
 | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
241
248
 */
 
249
__attribute__((warn_unused_result))
242
250
static int set_cloexec_flag(int fd){
243
251
  int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
244
252
  /* If reading the flags failed, return error indication now. */
249
257
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
250
258
                                       ret | FD_CLOEXEC));
251
259
}
 
260
#endif  /* not O_CLOEXEC */
252
261
 
253
262
 
254
263
/* Mark processes as completed when they exit, and save their exit
286
295
}
287
296
 
288
297
/* Prints out a password to stdout */
 
298
__attribute__((nonnull, warn_unused_result))
289
299
static bool print_out_password(const char *buffer, size_t length){
290
300
  ssize_t ret;
291
301
  for(size_t written = 0; written < length; written += (size_t)ret){
299
309
}
300
310
 
301
311
/* Removes and free a plugin from the plugin list */
 
312
__attribute__((nonnull))
302
313
static void free_plugin(plugin *plugin_node){
303
314
  
304
315
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
338
349
  char *plugindir = NULL;
339
350
  char *argfile = NULL;
340
351
  FILE *conffp;
341
 
  size_t d_name_len;
342
 
  DIR *dir = NULL;
343
 
  struct dirent *dirst;
 
352
  struct dirent **direntries = NULL;
344
353
  struct stat st;
345
354
  fd_set rfds_all;
346
355
  int ret, maxfd = 0;
354
363
                                      .sa_flags = SA_NOCLDSTOP };
355
364
  char **custom_argv = NULL;
356
365
  int custom_argc = 0;
 
366
  int dir_fd = -1;
357
367
  
358
368
  /* Establish a signal handler */
359
369
  sigemptyset(&sigchld_action.sa_mask);
416
426
    { .name = NULL }
417
427
  };
418
428
  
 
429
  __attribute__((nonnull(3)))
419
430
  error_t parse_opt(int key, char *arg, struct argp_state *state){
420
431
    errno = 0;
421
432
    switch(key){
422
433
      char *tmp;
423
 
      intmax_t tmpmax;
 
434
      intmax_t tmp_id;
424
435
    case 'g':                   /* --global-options */
425
436
      {
426
437
        char *plugin_option;
429
440
            break;
430
441
          }
431
442
        }
 
443
        errno = 0;
432
444
      }
433
445
      break;
434
446
    case 'G':                   /* --global-env */
435
 
      add_environment(getplugin(NULL), arg, true);
 
447
      if(add_environment(getplugin(NULL), arg, true)){
 
448
        errno = 0;
 
449
      }
436
450
      break;
437
451
    case 'o':                   /* --options-for */
438
452
      {
455
469
            break;
456
470
          }
457
471
        }
 
472
        errno = 0;
458
473
      }
459
474
      break;
460
475
    case 'E':                   /* --env-for */
472
487
          errno = EINVAL;
473
488
          break;
474
489
        }
475
 
        add_environment(getplugin(arg), envdef, true);
 
490
        if(add_environment(getplugin(arg), envdef, true)){
 
491
          errno = 0;
 
492
        }
476
493
      }
477
494
      break;
478
495
    case 'd':                   /* --disable */
480
497
        plugin *p = getplugin(arg);
481
498
        if(p != NULL){
482
499
          p->disabled = true;
 
500
          errno = 0;
483
501
        }
484
502
      }
485
503
      break;
488
506
        plugin *p = getplugin(arg);
489
507
        if(p != NULL){
490
508
          p->disabled = false;
 
509
          errno = 0;
491
510
        }
492
511
      }
493
512
      break;
494
513
    case 128:                   /* --plugin-dir */
495
514
      free(plugindir);
496
515
      plugindir = strdup(arg);
 
516
      if(plugindir != NULL){
 
517
        errno = 0;
 
518
      }
497
519
      break;
498
520
    case 129:                   /* --config-file */
499
521
      /* This is already done by parse_opt_config_file() */
500
522
      break;
501
523
    case 130:                   /* --userid */
502
 
      tmpmax = strtoimax(arg, &tmp, 10);
 
524
      tmp_id = strtoimax(arg, &tmp, 10);
503
525
      if(errno != 0 or tmp == arg or *tmp != '\0'
504
 
         or tmpmax != (uid_t)tmpmax){
 
526
         or tmp_id != (uid_t)tmp_id){
505
527
        argp_error(state, "Bad user ID number: \"%s\", using %"
506
528
                   PRIdMAX, arg, (intmax_t)uid);
507
529
        break;
508
530
      }
509
 
      uid = (uid_t)tmpmax;
 
531
      uid = (uid_t)tmp_id;
 
532
      errno = 0;
510
533
      break;
511
534
    case 131:                   /* --groupid */
512
 
      tmpmax = strtoimax(arg, &tmp, 10);
 
535
      tmp_id = strtoimax(arg, &tmp, 10);
513
536
      if(errno != 0 or tmp == arg or *tmp != '\0'
514
 
         or tmpmax != (gid_t)tmpmax){
 
537
         or tmp_id != (gid_t)tmp_id){
515
538
        argp_error(state, "Bad group ID number: \"%s\", using %"
516
539
                   PRIdMAX, arg, (intmax_t)gid);
517
540
        break;
518
541
      }
519
 
      gid = (gid_t)tmpmax;
 
542
      gid = (gid_t)tmp_id;
 
543
      errno = 0;
520
544
      break;
521
545
    case 132:                   /* --debug */
522
546
      debug = true;
570
594
    case 129:                   /* --config-file */
571
595
      free(argfile);
572
596
      argfile = strdup(arg);
 
597
      if(argfile != NULL){
 
598
        errno = 0;
 
599
      }
573
600
      break;
574
601
    case 130:                   /* --userid */
575
602
    case 131:                   /* --groupid */
658
685
        }
659
686
        
660
687
        custom_argc += 1;
661
 
        custom_argv = realloc(custom_argv, sizeof(char *)
662
 
                              * ((unsigned int) custom_argc + 1));
663
 
        if(custom_argv == NULL){
664
 
          error(0, errno, "realloc");
665
 
          exitstatus = EX_OSERR;
666
 
          free(org_line);
667
 
          goto fallback;
 
688
        {
 
689
          char **new_argv = realloc(custom_argv, sizeof(char *)
 
690
                                    * ((unsigned int)
 
691
                                       custom_argc + 1));
 
692
          if(new_argv == NULL){
 
693
            error(0, errno, "realloc");
 
694
            exitstatus = EX_OSERR;
 
695
            free(new_arg);
 
696
            free(org_line);
 
697
            goto fallback;
 
698
          } else {
 
699
            custom_argv = new_argv;
 
700
          }
668
701
        }
669
702
        custom_argv[custom_argc-1] = new_arg;
670
703
        custom_argv[custom_argc] = NULL;
742
775
    }
743
776
  }
744
777
  
745
 
  /* Strip permissions down to nobody */
746
 
  setgid(gid);
 
778
  if(getuid() == 0){
 
779
    /* Work around Debian bug #633582:
 
780
       <http://bugs.debian.org/633582> */
 
781
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
 
782
    if(plugindir_fd == -1){
 
783
      if(errno != ENOENT){
 
784
        error(0, errno, "open(\"" PDIR "\")");
 
785
      }
 
786
    } else {
 
787
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
 
788
      if(ret == -1){
 
789
        error(0, errno, "fstat");
 
790
      } else {
 
791
        if(S_ISDIR(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
 
792
          ret = fchown(plugindir_fd, uid, gid);
 
793
          if(ret == -1){
 
794
            error(0, errno, "fchown");
 
795
          }
 
796
        }
 
797
      }
 
798
      TEMP_FAILURE_RETRY(close(plugindir_fd));
 
799
    }
 
800
  }
 
801
  
 
802
  /* Lower permissions */
 
803
  ret = setgid(gid);
747
804
  if(ret == -1){
748
805
    error(0, errno, "setgid");
749
806
  }
754
811
  
755
812
  /* Open plugin directory with close_on_exec flag */
756
813
  {
757
 
    int dir_fd = -1;
758
 
    if(plugindir == NULL){
759
 
      dir_fd = open(PDIR, O_RDONLY |
760
 
#ifdef O_CLOEXEC
761
 
                    O_CLOEXEC
762
 
#else  /* not O_CLOEXEC */
763
 
                    0
764
 
#endif  /* not O_CLOEXEC */
765
 
                    );
766
 
    } else {
767
 
      dir_fd = open(plugindir, O_RDONLY |
768
 
#ifdef O_CLOEXEC
769
 
                    O_CLOEXEC
770
 
#else  /* not O_CLOEXEC */
771
 
                    0
772
 
#endif  /* not O_CLOEXEC */
773
 
                    );
774
 
    }
 
814
    dir_fd = open(plugindir != NULL ? plugindir : PDIR, O_RDONLY |
 
815
#ifdef O_CLOEXEC
 
816
                  O_CLOEXEC
 
817
#else  /* not O_CLOEXEC */
 
818
                  0
 
819
#endif  /* not O_CLOEXEC */
 
820
                  );
775
821
    if(dir_fd == -1){
776
822
      error(0, errno, "Could not open plugin dir");
777
823
      exitstatus = EX_UNAVAILABLE;
783
829
    ret = set_cloexec_flag(dir_fd);
784
830
    if(ret < 0){
785
831
      error(0, errno, "set_cloexec_flag");
786
 
      TEMP_FAILURE_RETRY(close(dir_fd));
787
832
      exitstatus = EX_OSERR;
788
833
      goto fallback;
789
834
    }
790
835
#endif  /* O_CLOEXEC */
791
 
    
792
 
    dir = fdopendir(dir_fd);
793
 
    if(dir == NULL){
794
 
      error(0, errno, "Could not open plugin dir");
795
 
      TEMP_FAILURE_RETRY(close(dir_fd));
796
 
      exitstatus = EX_OSERR;
797
 
      goto fallback;
 
836
  }
 
837
  
 
838
  int good_name(const struct dirent * const dirent){
 
839
    const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new",
 
840
                                      "*.dpkg-old", "*.dpkg-bak",
 
841
                                      "*.dpkg-divert", NULL };
 
842
#ifdef __GNUC__
 
843
#pragma GCC diagnostic push
 
844
#pragma GCC diagnostic ignored "-Wcast-qual"
 
845
#endif
 
846
    for(const char **pat = (const char **)patterns;
 
847
        *pat != NULL; pat++){
 
848
#ifdef __GNUC__
 
849
#pragma GCC diagnostic pop
 
850
#endif
 
851
      if(fnmatch(*pat, dirent->d_name, FNM_FILE_NAME | FNM_PERIOD)
 
852
         != FNM_NOMATCH){
 
853
        if(debug){
 
854
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
855
                    " matching pattern %s\n", dirent->d_name, *pat);
 
856
        }
 
857
        return 0;
 
858
      }
798
859
    }
 
860
    return 1;
 
861
  }
 
862
  
 
863
#ifdef __GLIBC__
 
864
#if __GLIBC_PREREQ(2, 15)
 
865
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
 
866
                             alphasort);
 
867
#else  /* not __GLIBC_PREREQ(2, 15) */
 
868
  int numplugins = scandir(plugindir != NULL ? plugindir : PDIR,
 
869
                           &direntries, good_name, alphasort);
 
870
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
871
#else   /* not __GLIBC__ */
 
872
  int numplugins = scandir(plugindir != NULL ? plugindir : PDIR,
 
873
                           &direntries, good_name, alphasort);
 
874
#endif  /* not __GLIBC__ */
 
875
  if(numplugins == -1){
 
876
    error(0, errno, "Could not scan plugin dir");
 
877
    direntries = NULL;
 
878
    exitstatus = EX_OSERR;
 
879
    goto fallback;
799
880
  }
800
881
  
801
882
  FD_ZERO(&rfds_all);
802
883
  
803
884
  /* Read and execute any executable in the plugin directory*/
804
 
  while(true){
805
 
    do {
806
 
      dirst = readdir(dir);
807
 
    } while(dirst == NULL and errno == EINTR);
808
 
    
809
 
    /* All directory entries have been processed */
810
 
    if(dirst == NULL){
811
 
      if(errno == EBADF){
812
 
        error(0, errno, "readdir");
813
 
        exitstatus = EX_IOERR;
814
 
        goto fallback;
815
 
      }
816
 
      break;
817
 
    }
818
 
    
819
 
    d_name_len = strlen(dirst->d_name);
820
 
    
821
 
    /* Ignore dotfiles, backup files and other junk */
822
 
    {
823
 
      bool bad_name = false;
824
 
      
825
 
      const char const *bad_prefixes[] = { ".", "#", NULL };
826
 
      
827
 
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
828
 
                                           ".dpkg-old",
829
 
                                           ".dpkg-bak",
830
 
                                           ".dpkg-divert", NULL };
831
 
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
832
 
        size_t pre_len = strlen(*pre);
833
 
        if((d_name_len >= pre_len)
834
 
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
835
 
          if(debug){
836
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
837
 
                    " with bad prefix %s\n", dirst->d_name, *pre);
838
 
          }
839
 
          bad_name = true;
840
 
          break;
841
 
        }
842
 
      }
843
 
      if(bad_name){
844
 
        continue;
845
 
      }
846
 
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
847
 
        size_t suf_len = strlen(*suf);
848
 
        if((d_name_len >= suf_len)
849
 
           and (strcmp((dirst->d_name) + d_name_len-suf_len, *suf)
850
 
                == 0)){
851
 
          if(debug){
852
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
853
 
                    " with bad suffix %s\n", dirst->d_name, *suf);
854
 
          }
855
 
          bad_name = true;
856
 
          break;
857
 
        }
858
 
      }
859
 
      
860
 
      if(bad_name){
861
 
        continue;
862
 
      }
863
 
    }
864
 
    
865
 
    char *filename;
866
 
    if(plugindir == NULL){
867
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s",
868
 
                                             dirst->d_name));
869
 
    } else {
870
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s",
871
 
                                             plugindir,
872
 
                                             dirst->d_name));
873
 
    }
874
 
    if(ret < 0){
875
 
      error(0, errno, "asprintf");
 
885
  for(int i = 0; i < numplugins; i++){
 
886
    
 
887
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
 
888
    if(plugin_fd == -1){
 
889
      error(0, errno, "Could not open plugin");
 
890
      free(direntries[i]);
876
891
      continue;
877
892
    }
878
 
    
879
 
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
 
893
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
880
894
    if(ret == -1){
881
895
      error(0, errno, "stat");
882
 
      free(filename);
 
896
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
897
      free(direntries[i]);
883
898
      continue;
884
899
    }
885
900
    
886
901
    /* Ignore non-executable files */
887
902
    if(not S_ISREG(st.st_mode)
888
 
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
 
903
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
 
904
                                        X_OK, 0)) != 0)){
889
905
      if(debug){
890
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
891
 
                " with bad type or mode\n", filename);
 
906
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
 
907
                " with bad type or mode\n",
 
908
                plugindir != NULL ? plugindir : PDIR,
 
909
                direntries[i]->d_name);
892
910
      }
893
 
      free(filename);
 
911
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
912
      free(direntries[i]);
894
913
      continue;
895
914
    }
896
915
    
897
 
    plugin *p = getplugin(dirst->d_name);
 
916
    plugin *p = getplugin(direntries[i]->d_name);
898
917
    if(p == NULL){
899
918
      error(0, errno, "getplugin");
900
 
      free(filename);
 
919
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
920
      free(direntries[i]);
901
921
      continue;
902
922
    }
903
923
    if(p->disabled){
904
924
      if(debug){
905
925
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
906
 
                dirst->d_name);
 
926
                direntries[i]->d_name);
907
927
      }
908
 
      free(filename);
 
928
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
929
      free(direntries[i]);
909
930
      continue;
910
931
    }
911
932
    {
925
946
        }
926
947
      }
927
948
    }
928
 
    /* If this plugin has any environment variables, we will call
929
 
       using execve and need to duplicate the environment from this
930
 
       process, too. */
 
949
    /* If this plugin has any environment variables, we need to
 
950
       duplicate the environment from this process, too. */
931
951
    if(p->environ[0] != NULL){
932
952
      for(char **e = environ; *e != NULL; e++){
933
953
        if(not add_environment(p, *e, false)){
937
957
    }
938
958
    
939
959
    int pipefd[2];
 
960
#ifndef O_CLOEXEC
940
961
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
 
962
#else  /* O_CLOEXEC */
 
963
    ret = (int)TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC));
 
964
#endif  /* O_CLOEXEC */
941
965
    if(ret == -1){
942
966
      error(0, errno, "pipe");
943
967
      exitstatus = EX_OSERR;
944
 
      goto fallback;
945
 
    }
 
968
      free(direntries[i]);
 
969
      goto fallback;
 
970
    }
 
971
    if(pipefd[0] >= FD_SETSIZE){
 
972
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
 
973
              FD_SETSIZE);
 
974
      TEMP_FAILURE_RETRY(close(pipefd[0]));
 
975
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
976
      exitstatus = EX_OSERR;
 
977
      free(direntries[i]);
 
978
      goto fallback;
 
979
    }
 
980
#ifndef O_CLOEXEC
946
981
    /* Ask OS to automatic close the pipe on exec */
947
982
    ret = set_cloexec_flag(pipefd[0]);
948
983
    if(ret < 0){
949
984
      error(0, errno, "set_cloexec_flag");
 
985
      TEMP_FAILURE_RETRY(close(pipefd[0]));
 
986
      TEMP_FAILURE_RETRY(close(pipefd[1]));
950
987
      exitstatus = EX_OSERR;
 
988
      free(direntries[i]);
951
989
      goto fallback;
952
990
    }
953
991
    ret = set_cloexec_flag(pipefd[1]);
954
992
    if(ret < 0){
955
993
      error(0, errno, "set_cloexec_flag");
 
994
      TEMP_FAILURE_RETRY(close(pipefd[0]));
 
995
      TEMP_FAILURE_RETRY(close(pipefd[1]));
956
996
      exitstatus = EX_OSERR;
 
997
      free(direntries[i]);
957
998
      goto fallback;
958
999
    }
 
1000
#endif  /* not O_CLOEXEC */
959
1001
    /* Block SIGCHLD until process is safely in process list */
960
1002
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
961
1003
                                              &sigchld_action.sa_mask,
963
1005
    if(ret < 0){
964
1006
      error(0, errno, "sigprocmask");
965
1007
      exitstatus = EX_OSERR;
 
1008
      free(direntries[i]);
966
1009
      goto fallback;
967
1010
    }
968
1011
    /* Starting a new process to be watched */
972
1015
    } while(pid == -1 and errno == EINTR);
973
1016
    if(pid == -1){
974
1017
      error(0, errno, "fork");
 
1018
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
 
1019
                                     &sigchld_action.sa_mask, NULL));
 
1020
      TEMP_FAILURE_RETRY(close(pipefd[0]));
 
1021
      TEMP_FAILURE_RETRY(close(pipefd[1]));
975
1022
      exitstatus = EX_OSERR;
 
1023
      free(direntries[i]);
976
1024
      goto fallback;
977
1025
    }
978
1026
    if(pid == 0){
994
1042
        _exit(EX_OSERR);
995
1043
      }
996
1044
      
997
 
      if(dirfd(dir) < 0){
998
 
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
999
 
           above and must now close it manually here. */
1000
 
        closedir(dir);
1001
 
      }
1002
 
      if(p->environ[0] == NULL){
1003
 
        if(execv(filename, p->argv) < 0){
1004
 
          error(0, errno, "execv for %s", filename);
1005
 
          _exit(EX_OSERR);
1006
 
        }
1007
 
      } else {
1008
 
        if(execve(filename, p->argv, p->environ) < 0){
1009
 
          error(0, errno, "execve for %s", filename);
1010
 
          _exit(EX_OSERR);
1011
 
        }
 
1045
      if(fexecve(plugin_fd, p->argv,
 
1046
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
 
1047
        error(0, errno, "fexecve for %s/%s",
 
1048
              plugindir != NULL ? plugindir : PDIR,
 
1049
              direntries[i]->d_name);
 
1050
        _exit(EX_OSERR);
1012
1051
      }
1013
1052
      /* no return */
1014
1053
    }
1015
1054
    /* Parent process */
1016
1055
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1017
1056
                                             pipe */
1018
 
    free(filename);
1019
 
    plugin *new_plugin = getplugin(dirst->d_name);
 
1057
    TEMP_FAILURE_RETRY(close(plugin_fd));
 
1058
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1020
1059
    if(new_plugin == NULL){
1021
1060
      error(0, errno, "getplugin");
1022
1061
      ret = (int)(TEMP_FAILURE_RETRY
1026
1065
        error(0, errno, "sigprocmask");
1027
1066
      }
1028
1067
      exitstatus = EX_OSERR;
 
1068
      free(direntries[i]);
1029
1069
      goto fallback;
1030
1070
    }
 
1071
    free(direntries[i]);
1031
1072
    
1032
1073
    new_plugin->pid = pid;
1033
1074
    new_plugin->fd = pipefd[0];
1043
1084
      goto fallback;
1044
1085
    }
1045
1086
    
 
1087
#if defined (__GNUC__) and defined (__GLIBC__)
 
1088
#if not __GLIBC_PREREQ(2, 16)
 
1089
#pragma GCC diagnostic push
 
1090
#pragma GCC diagnostic ignored "-Wsign-conversion"
 
1091
#endif
 
1092
#endif
1046
1093
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
1047
 
                                          -Wconversion */
 
1094
                                          -Wconversion in GNU libc
 
1095
                                          before 2.16 */
 
1096
#if defined (__GNUC__) and defined (__GLIBC__)
 
1097
#if not __GLIBC_PREREQ(2, 16)
 
1098
#pragma GCC diagnostic pop
 
1099
#endif
 
1100
#endif
1048
1101
    
1049
1102
    if(maxfd < new_plugin->fd){
1050
1103
      maxfd = new_plugin->fd;
1051
1104
    }
1052
1105
  }
1053
1106
  
1054
 
  TEMP_FAILURE_RETRY(closedir(dir));
1055
 
  dir = NULL;
 
1107
  free(direntries);
 
1108
  direntries = NULL;
 
1109
  TEMP_FAILURE_RETRY(close(dir_fd));
 
1110
  dir_fd = -1;
1056
1111
  free_plugin(getplugin(NULL));
1057
1112
  
1058
1113
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1104
1159
          }
1105
1160
          
1106
1161
          /* Remove the plugin */
 
1162
#if defined (__GNUC__) and defined (__GLIBC__)
 
1163
#if not __GLIBC_PREREQ(2, 16)
 
1164
#pragma GCC diagnostic push
 
1165
#pragma GCC diagnostic ignored "-Wsign-conversion"
 
1166
#endif
 
1167
#endif
1107
1168
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1108
 
                                          -Wconversion */
 
1169
                                          -Wconversion in GNU libc
 
1170
                                          before 2.16 */
 
1171
#if defined (__GNUC__) and defined (__GLIBC__)
 
1172
#if not __GLIBC_PREREQ(2, 16)
 
1173
#pragma GCC diagnostic pop
 
1174
#endif
 
1175
#endif
1109
1176
          
1110
1177
          /* Block signal while modifying process_list */
1111
1178
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1151
1218
      }
1152
1219
      
1153
1220
      /* This process has not completed.  Does it have any output? */
 
1221
#if defined (__GNUC__) and defined (__GLIBC__)
 
1222
#if not __GLIBC_PREREQ(2, 16)
 
1223
#pragma GCC diagnostic push
 
1224
#pragma GCC diagnostic ignored "-Wsign-conversion"
 
1225
#endif
 
1226
#endif
1154
1227
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1155
1228
                                                         warning from
1156
 
                                                         -Wconversion */
 
1229
                                                         -Wconversion
 
1230
                                                         in GNU libc
 
1231
                                                         before
 
1232
                                                         2.16 */
 
1233
#if defined (__GNUC__) and defined (__GLIBC__)
 
1234
#if not __GLIBC_PREREQ(2, 16)
 
1235
#pragma GCC diagnostic pop
 
1236
#endif
 
1237
#endif
1157
1238
        /* This process had nothing to say at this time */
1158
1239
        proc = proc->next;
1159
1240
        continue;
1160
1241
      }
1161
1242
      /* Before reading, make the process' data buffer large enough */
1162
1243
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1163
 
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1164
 
                               + (size_t) BUFFER_SIZE);
1165
 
        if(proc->buffer == NULL){
 
1244
        char *new_buffer = realloc(proc->buffer, proc->buffer_size
 
1245
                                   + (size_t) BUFFER_SIZE);
 
1246
        if(new_buffer == NULL){
1166
1247
          error(0, errno, "malloc");
1167
1248
          exitstatus = EX_OSERR;
1168
1249
          goto fallback;
1169
1250
        }
 
1251
        proc->buffer = new_buffer;
1170
1252
        proc->buffer_size += BUFFER_SIZE;
1171
1253
      }
1172
1254
      /* Read from the process */
1225
1307
    free(custom_argv);
1226
1308
  }
1227
1309
  
1228
 
  if(dir != NULL){
1229
 
    closedir(dir);
 
1310
  free(direntries);
 
1311
  
 
1312
  if(dir_fd != -1){
 
1313
    TEMP_FAILURE_RETRY(close(dir_fd));
1230
1314
  }
1231
1315
  
1232
1316
  /* Kill the processes */