/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-07-20 03:03:33 UTC
  • Revision ID: teddy@recompile.se-20150720030333-203m2aeblypcsfte
Bug fix for GnuTLS 3: be compatible with old 2048-bit DSA keys.

The mandos-keygen program in Mandos version 1.6.0 and older generated
2048-bit DSA keys, and when GnuTLS uses these it has trouble
connecting using the Mandos default priority string.  This was
previously fixed in Mandos 1.6.2, but the bug reappeared when using
GnuTLS 3, so the default priority string has to change again; this
time also the Mandos client has to change its default, so now the
server and the client should use the same default priority string:

SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256

* mandos (main/server_defaults): Changed default priority string.
* mandos-options.xml (/section/para[id="priority_compat"]): Removed.
  (/section/para[id="priority"]): Changed default priority string.
* mandos.conf ([DEFAULT]/priority): - '' -
* mandos.conf.xml (OPTIONS/priority): Refer to the id "priority"
                                      instead of "priority_compat".
* mandos.xml (OPTIONS/--priority): - '' -
* plugins.d/mandos-client.c (main): Changed default priority string.

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-2012 Teddy Hogeborn
6
 
 * Copyright © 2008-2012 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
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
 
78
78
#define PDIR "/lib/mandos/plugins.d"
 
79
#define PHDIR "/lib/mandos/plugin-helpers"
79
80
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
80
81
 
81
82
const char *argp_program_version = "plugin-runner " VERSION;
105
106
 
106
107
/* Gets an existing plugin based on name,
107
108
   or if none is found, creates a new one */
 
109
__attribute__((warn_unused_result))
108
110
static plugin *getplugin(char *name){
109
111
  /* Check for existing plugin with that name */
110
112
  for(plugin *p = plugin_list; p != NULL; p = p->next){
171
173
}
172
174
 
173
175
/* Helper function for add_argument and add_environment */
174
 
__attribute__((nonnull))
 
176
__attribute__((nonnull, warn_unused_result))
175
177
static bool add_to_char_array(const char *new, char ***array,
176
178
                              int *len){
177
179
  /* Resize the pointed-to array to hold one more pointer */
 
180
  char **new_array = NULL;
178
181
  do {
179
 
    *array = realloc(*array, sizeof(char *)
180
 
                     * (size_t) ((*len) + 2));
181
 
  } while(*array == NULL and errno == EINTR);
 
182
    new_array = realloc(*array, sizeof(char *)
 
183
                        * (size_t) ((*len) + 2));
 
184
  } while(new_array == NULL and errno == EINTR);
182
185
  /* Malloc check */
183
 
  if(*array == NULL){
 
186
  if(new_array == NULL){
184
187
    return false;
185
188
  }
 
189
  *array = new_array;
186
190
  /* Make a copy of the new string */
187
191
  char *copy;
188
192
  do {
200
204
}
201
205
 
202
206
/* Add to a plugin's argument vector */
203
 
__attribute__((nonnull(2)))
 
207
__attribute__((nonnull(2), warn_unused_result))
204
208
static bool add_argument(plugin *p, const char *arg){
205
209
  if(p == NULL){
206
210
    return false;
209
213
}
210
214
 
211
215
/* Add to a plugin's environment */
212
 
__attribute__((nonnull(2)))
 
216
__attribute__((nonnull(2), warn_unused_result))
213
217
static bool add_environment(plugin *p, const char *def, bool replace){
214
218
  if(p == NULL){
215
219
    return false;
217
221
  /* namelen = length of name of environment variable */
218
222
  size_t namelen = (size_t)(strchrnul(def, '=') - def);
219
223
  /* Search for this environment variable */
220
 
  for(char **e = p->environ; *e != NULL; e++){
221
 
    if(strncmp(*e, def, namelen + 1) == 0){
 
224
  for(char **envdef = p->environ; *envdef != NULL; envdef++){
 
225
    if(strncmp(*envdef, def, namelen + 1) == 0){
222
226
      /* It already exists */
223
227
      if(replace){
224
 
        char *new;
 
228
        char *new_envdef;
225
229
        do {
226
 
          new = realloc(*e, strlen(def) + 1);
227
 
        } while(new == NULL and errno == EINTR);
228
 
        if(new == NULL){
 
230
          new_envdef = realloc(*envdef, strlen(def) + 1);
 
231
        } while(new_envdef == NULL and errno == EINTR);
 
232
        if(new_envdef == NULL){
229
233
          return false;
230
234
        }
231
 
        *e = new;
232
 
        strcpy(*e, def);
 
235
        *envdef = new_envdef;
 
236
        strcpy(*envdef, def);
233
237
      }
234
238
      return true;
235
239
    }
237
241
  return add_to_char_array(def, &(p->environ), &(p->envc));
238
242
}
239
243
 
 
244
#ifndef O_CLOEXEC
240
245
/*
241
246
 * Based on the example in the GNU LibC manual chapter 13.13 "File
242
247
 * Descriptor Flags".
243
248
 | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
244
249
 */
 
250
__attribute__((warn_unused_result))
245
251
static int set_cloexec_flag(int fd){
246
252
  int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
247
253
  /* If reading the flags failed, return error indication now. */
252
258
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
253
259
                                       ret | FD_CLOEXEC));
254
260
}
 
261
#endif  /* not O_CLOEXEC */
255
262
 
256
263
 
257
264
/* Mark processes as completed when they exit, and save their exit
289
296
}
290
297
 
291
298
/* Prints out a password to stdout */
292
 
__attribute__((nonnull))
 
299
__attribute__((nonnull, warn_unused_result))
293
300
static bool print_out_password(const char *buffer, size_t length){
294
301
  ssize_t ret;
295
302
  for(size_t written = 0; written < length; written += (size_t)ret){
341
348
 
342
349
int main(int argc, char *argv[]){
343
350
  char *plugindir = NULL;
 
351
  char *pluginhelperdir = NULL;
344
352
  char *argfile = NULL;
345
353
  FILE *conffp;
346
 
  size_t d_name_len;
347
 
  DIR *dir = NULL;
348
 
  struct dirent *dirst;
 
354
  struct dirent **direntries = NULL;
349
355
  struct stat st;
350
356
  fd_set rfds_all;
351
357
  int ret, maxfd = 0;
359
365
                                      .sa_flags = SA_NOCLDSTOP };
360
366
  char **custom_argv = NULL;
361
367
  int custom_argc = 0;
 
368
  int dir_fd = -1;
362
369
  
363
370
  /* Establish a signal handler */
364
371
  sigemptyset(&sigchld_action.sa_mask);
409
416
      .doc = "Group ID the plugins will run as", .group = 3 },
410
417
    { .name = "debug", .key = 132,
411
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 },
412
423
    /*
413
424
     * These reproduce what we would get without ARGP_NO_HELP
414
425
     */
435
446
            break;
436
447
          }
437
448
        }
 
449
        errno = 0;
438
450
      }
439
451
      break;
440
452
    case 'G':                   /* --global-env */
441
 
      add_environment(getplugin(NULL), arg, true);
 
453
      if(add_environment(getplugin(NULL), arg, true)){
 
454
        errno = 0;
 
455
      }
442
456
      break;
443
457
    case 'o':                   /* --options-for */
444
458
      {
461
475
            break;
462
476
          }
463
477
        }
 
478
        errno = 0;
464
479
      }
465
480
      break;
466
481
    case 'E':                   /* --env-for */
478
493
          errno = EINVAL;
479
494
          break;
480
495
        }
481
 
        add_environment(getplugin(arg), envdef, true);
 
496
        if(add_environment(getplugin(arg), envdef, true)){
 
497
          errno = 0;
 
498
        }
482
499
      }
483
500
      break;
484
501
    case 'd':                   /* --disable */
486
503
        plugin *p = getplugin(arg);
487
504
        if(p != NULL){
488
505
          p->disabled = true;
 
506
          errno = 0;
489
507
        }
490
508
      }
491
509
      break;
494
512
        plugin *p = getplugin(arg);
495
513
        if(p != NULL){
496
514
          p->disabled = false;
 
515
          errno = 0;
497
516
        }
498
517
      }
499
518
      break;
500
519
    case 128:                   /* --plugin-dir */
501
520
      free(plugindir);
502
521
      plugindir = strdup(arg);
 
522
      if(plugindir != NULL){
 
523
        errno = 0;
 
524
      }
503
525
      break;
504
526
    case 129:                   /* --config-file */
505
527
      /* This is already done by parse_opt_config_file() */
513
535
        break;
514
536
      }
515
537
      uid = (uid_t)tmp_id;
 
538
      errno = 0;
516
539
      break;
517
540
    case 131:                   /* --groupid */
518
541
      tmp_id = strtoimax(arg, &tmp, 10);
523
546
        break;
524
547
      }
525
548
      gid = (gid_t)tmp_id;
 
549
      errno = 0;
526
550
      break;
527
551
    case 132:                   /* --debug */
528
552
      debug = true;
529
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;
530
561
      /*
531
562
       * These reproduce what we would get without ARGP_NO_HELP
532
563
       */
576
607
    case 129:                   /* --config-file */
577
608
      free(argfile);
578
609
      argfile = strdup(arg);
 
610
      if(argfile != NULL){
 
611
        errno = 0;
 
612
      }
579
613
      break;
580
614
    case 130:                   /* --userid */
581
615
    case 131:                   /* --groupid */
582
616
    case 132:                   /* --debug */
 
617
    case 133:                   /* --plugin-helper-dir */
583
618
    case '?':                   /* --help */
584
619
    case -3:                    /* --usage */
585
620
    case 'V':                   /* --version */
664
699
        }
665
700
        
666
701
        custom_argc += 1;
667
 
        custom_argv = realloc(custom_argv, sizeof(char *)
668
 
                              * ((unsigned int) custom_argc + 1));
669
 
        if(custom_argv == NULL){
670
 
          error(0, errno, "realloc");
671
 
          exitstatus = EX_OSERR;
672
 
          free(org_line);
673
 
          goto fallback;
 
702
        {
 
703
          char **new_argv = realloc(custom_argv, sizeof(char *)
 
704
                                    * ((unsigned int)
 
705
                                       custom_argc + 1));
 
706
          if(new_argv == NULL){
 
707
            error(0, errno, "realloc");
 
708
            exitstatus = EX_OSERR;
 
709
            free(new_arg);
 
710
            free(org_line);
 
711
            goto fallback;
 
712
          } else {
 
713
            custom_argv = new_argv;
 
714
          }
674
715
        }
675
716
        custom_argv[custom_argc-1] = new_arg;
676
717
        custom_argv[custom_argc] = NULL;
734
775
    goto fallback;
735
776
  }
736
777
  
 
778
  {
 
779
    char *pluginhelperenv;
 
780
    bool bret = true;
 
781
    ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s",
 
782
                   pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
783
    if(ret != -1){
 
784
      bret = add_environment(getplugin(NULL), pluginhelperenv, true);
 
785
    }
 
786
    if(ret == -1 or not bret){
 
787
      error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR"
 
788
            " environment variable to \"%s\" for all plugins\n",
 
789
            pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
790
    }
 
791
    if(ret != -1){
 
792
      free(pluginhelperenv);
 
793
    }
 
794
  }
 
795
  
737
796
  if(debug){
738
797
    for(plugin *p = plugin_list; p != NULL; p=p->next){
739
798
      fprintf(stderr, "Plugin: %s has %d arguments\n",
753
812
       <http://bugs.debian.org/633582> */
754
813
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
755
814
    if(plugindir_fd == -1){
756
 
      error(0, errno, "open");
 
815
      if(errno != ENOENT){
 
816
        error(0, errno, "open(\"" PDIR "\")");
 
817
      }
757
818
    } else {
758
819
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
759
820
      if(ret == -1){
766
827
          }
767
828
        }
768
829
      }
769
 
      TEMP_FAILURE_RETRY(close(plugindir_fd));
 
830
      close(plugindir_fd);
770
831
    }
771
832
  }
772
833
  
773
834
  /* Lower permissions */
774
 
  setgid(gid);
 
835
  ret = setgid(gid);
775
836
  if(ret == -1){
776
837
    error(0, errno, "setgid");
777
838
  }
782
843
  
783
844
  /* Open plugin directory with close_on_exec flag */
784
845
  {
785
 
    int dir_fd = -1;
786
 
    if(plugindir == NULL){
787
 
      dir_fd = open(PDIR, O_RDONLY |
788
 
#ifdef O_CLOEXEC
789
 
                    O_CLOEXEC
790
 
#else  /* not O_CLOEXEC */
791
 
                    0
792
 
#endif  /* not O_CLOEXEC */
793
 
                    );
794
 
    } else {
795
 
      dir_fd = open(plugindir, O_RDONLY |
796
 
#ifdef O_CLOEXEC
797
 
                    O_CLOEXEC
798
 
#else  /* not O_CLOEXEC */
799
 
                    0
800
 
#endif  /* not O_CLOEXEC */
801
 
                    );
802
 
    }
 
846
    dir_fd = open(plugindir != NULL ? plugindir : PDIR, O_RDONLY |
 
847
#ifdef O_CLOEXEC
 
848
                  O_CLOEXEC
 
849
#else  /* not O_CLOEXEC */
 
850
                  0
 
851
#endif  /* not O_CLOEXEC */
 
852
                  );
803
853
    if(dir_fd == -1){
804
854
      error(0, errno, "Could not open plugin dir");
805
855
      exitstatus = EX_UNAVAILABLE;
811
861
    ret = set_cloexec_flag(dir_fd);
812
862
    if(ret < 0){
813
863
      error(0, errno, "set_cloexec_flag");
814
 
      TEMP_FAILURE_RETRY(close(dir_fd));
815
864
      exitstatus = EX_OSERR;
816
865
      goto fallback;
817
866
    }
818
867
#endif  /* O_CLOEXEC */
819
 
    
820
 
    dir = fdopendir(dir_fd);
821
 
    if(dir == NULL){
822
 
      error(0, errno, "Could not open plugin dir");
823
 
      TEMP_FAILURE_RETRY(close(dir_fd));
824
 
      exitstatus = EX_OSERR;
825
 
      goto fallback;
 
868
  }
 
869
  
 
870
  int good_name(const struct dirent * const dirent){
 
871
    const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new",
 
872
                                      "*.dpkg-old", "*.dpkg-bak",
 
873
                                      "*.dpkg-divert", NULL };
 
874
#ifdef __GNUC__
 
875
#pragma GCC diagnostic push
 
876
#pragma GCC diagnostic ignored "-Wcast-qual"
 
877
#endif
 
878
    for(const char **pat = (const char **)patterns;
 
879
        *pat != NULL; pat++){
 
880
#ifdef __GNUC__
 
881
#pragma GCC diagnostic pop
 
882
#endif
 
883
      if(fnmatch(*pat, dirent->d_name, FNM_FILE_NAME | FNM_PERIOD)
 
884
         != FNM_NOMATCH){
 
885
        if(debug){
 
886
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
887
                    " matching pattern %s\n", dirent->d_name, *pat);
 
888
        }
 
889
        return 0;
 
890
      }
826
891
    }
 
892
    return 1;
 
893
  }
 
894
  
 
895
#ifdef __GLIBC__
 
896
#if __GLIBC_PREREQ(2, 15)
 
897
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
 
898
                             alphasort);
 
899
#else  /* not __GLIBC_PREREQ(2, 15) */
 
900
  int numplugins = scandir(plugindir != NULL ? plugindir : PDIR,
 
901
                           &direntries, good_name, alphasort);
 
902
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
903
#else   /* not __GLIBC__ */
 
904
  int numplugins = scandir(plugindir != NULL ? plugindir : PDIR,
 
905
                           &direntries, good_name, alphasort);
 
906
#endif  /* not __GLIBC__ */
 
907
  if(numplugins == -1){
 
908
    error(0, errno, "Could not scan plugin dir");
 
909
    direntries = NULL;
 
910
    exitstatus = EX_OSERR;
 
911
    goto fallback;
827
912
  }
828
913
  
829
914
  FD_ZERO(&rfds_all);
830
915
  
831
916
  /* Read and execute any executable in the plugin directory*/
832
 
  while(true){
833
 
    do {
834
 
      dirst = readdir(dir);
835
 
    } while(dirst == NULL and errno == EINTR);
836
 
    
837
 
    /* All directory entries have been processed */
838
 
    if(dirst == NULL){
839
 
      if(errno == EBADF){
840
 
        error(0, errno, "readdir");
841
 
        exitstatus = EX_IOERR;
842
 
        goto fallback;
843
 
      }
844
 
      break;
845
 
    }
846
 
    
847
 
    d_name_len = strlen(dirst->d_name);
848
 
    
849
 
    /* Ignore dotfiles, backup files and other junk */
850
 
    {
851
 
      bool bad_name = false;
852
 
      
853
 
      const char const *bad_prefixes[] = { ".", "#", NULL };
854
 
      
855
 
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
856
 
                                           ".dpkg-old",
857
 
                                           ".dpkg-bak",
858
 
                                           ".dpkg-divert", NULL };
859
 
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
860
 
        size_t pre_len = strlen(*pre);
861
 
        if((d_name_len >= pre_len)
862
 
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
863
 
          if(debug){
864
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
865
 
                    " with bad prefix %s\n", dirst->d_name, *pre);
866
 
          }
867
 
          bad_name = true;
868
 
          break;
869
 
        }
870
 
      }
871
 
      if(bad_name){
872
 
        continue;
873
 
      }
874
 
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
875
 
        size_t suf_len = strlen(*suf);
876
 
        if((d_name_len >= suf_len)
877
 
           and (strcmp((dirst->d_name) + d_name_len-suf_len, *suf)
878
 
                == 0)){
879
 
          if(debug){
880
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
881
 
                    " with bad suffix %s\n", dirst->d_name, *suf);
882
 
          }
883
 
          bad_name = true;
884
 
          break;
885
 
        }
886
 
      }
887
 
      
888
 
      if(bad_name){
889
 
        continue;
890
 
      }
891
 
    }
892
 
    
893
 
    char *filename;
894
 
    if(plugindir == NULL){
895
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s",
896
 
                                             dirst->d_name));
897
 
    } else {
898
 
      ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s",
899
 
                                             plugindir,
900
 
                                             dirst->d_name));
901
 
    }
902
 
    if(ret < 0){
903
 
      error(0, errno, "asprintf");
 
917
  for(int i = 0; i < numplugins; i++){
 
918
    
 
919
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
 
920
    if(plugin_fd == -1){
 
921
      error(0, errno, "Could not open plugin");
 
922
      free(direntries[i]);
904
923
      continue;
905
924
    }
906
 
    
907
 
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
 
925
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
908
926
    if(ret == -1){
909
927
      error(0, errno, "stat");
910
 
      free(filename);
 
928
      close(plugin_fd);
 
929
      free(direntries[i]);
911
930
      continue;
912
931
    }
913
932
    
914
933
    /* Ignore non-executable files */
915
934
    if(not S_ISREG(st.st_mode)
916
 
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
 
935
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
 
936
                                        X_OK, 0)) != 0)){
917
937
      if(debug){
918
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
919
 
                " with bad type or mode\n", filename);
 
938
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
 
939
                " with bad type or mode\n",
 
940
                plugindir != NULL ? plugindir : PDIR,
 
941
                direntries[i]->d_name);
920
942
      }
921
 
      free(filename);
 
943
      close(plugin_fd);
 
944
      free(direntries[i]);
922
945
      continue;
923
946
    }
924
947
    
925
 
    plugin *p = getplugin(dirst->d_name);
 
948
    plugin *p = getplugin(direntries[i]->d_name);
926
949
    if(p == NULL){
927
950
      error(0, errno, "getplugin");
928
 
      free(filename);
 
951
      close(plugin_fd);
 
952
      free(direntries[i]);
929
953
      continue;
930
954
    }
931
955
    if(p->disabled){
932
956
      if(debug){
933
957
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
934
 
                dirst->d_name);
 
958
                direntries[i]->d_name);
935
959
      }
936
 
      free(filename);
 
960
      close(plugin_fd);
 
961
      free(direntries[i]);
937
962
      continue;
938
963
    }
939
964
    {
953
978
        }
954
979
      }
955
980
    }
956
 
    /* If this plugin has any environment variables, we will call
957
 
       using execve and need to duplicate the environment from this
958
 
       process, too. */
 
981
    /* If this plugin has any environment variables, we need to
 
982
       duplicate the environment from this process, too. */
959
983
    if(p->environ[0] != NULL){
960
984
      for(char **e = environ; *e != NULL; e++){
961
985
        if(not add_environment(p, *e, false)){
965
989
    }
966
990
    
967
991
    int pipefd[2];
 
992
#ifndef O_CLOEXEC
968
993
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
 
994
#else  /* O_CLOEXEC */
 
995
    ret = (int)TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC));
 
996
#endif  /* O_CLOEXEC */
969
997
    if(ret == -1){
970
998
      error(0, errno, "pipe");
971
999
      exitstatus = EX_OSERR;
972
 
      goto fallback;
973
 
    }
 
1000
      free(direntries[i]);
 
1001
      goto fallback;
 
1002
    }
 
1003
    if(pipefd[0] >= FD_SETSIZE){
 
1004
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
 
1005
              FD_SETSIZE);
 
1006
      close(pipefd[0]);
 
1007
      close(pipefd[1]);
 
1008
      exitstatus = EX_OSERR;
 
1009
      free(direntries[i]);
 
1010
      goto fallback;
 
1011
    }
 
1012
#ifndef O_CLOEXEC
974
1013
    /* Ask OS to automatic close the pipe on exec */
975
1014
    ret = set_cloexec_flag(pipefd[0]);
976
1015
    if(ret < 0){
977
1016
      error(0, errno, "set_cloexec_flag");
 
1017
      close(pipefd[0]);
 
1018
      close(pipefd[1]);
978
1019
      exitstatus = EX_OSERR;
 
1020
      free(direntries[i]);
979
1021
      goto fallback;
980
1022
    }
981
1023
    ret = set_cloexec_flag(pipefd[1]);
982
1024
    if(ret < 0){
983
1025
      error(0, errno, "set_cloexec_flag");
 
1026
      close(pipefd[0]);
 
1027
      close(pipefd[1]);
984
1028
      exitstatus = EX_OSERR;
 
1029
      free(direntries[i]);
985
1030
      goto fallback;
986
1031
    }
 
1032
#endif  /* not O_CLOEXEC */
987
1033
    /* Block SIGCHLD until process is safely in process list */
988
1034
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
989
1035
                                              &sigchld_action.sa_mask,
991
1037
    if(ret < 0){
992
1038
      error(0, errno, "sigprocmask");
993
1039
      exitstatus = EX_OSERR;
 
1040
      free(direntries[i]);
994
1041
      goto fallback;
995
1042
    }
996
1043
    /* Starting a new process to be watched */
1000
1047
    } while(pid == -1 and errno == EINTR);
1001
1048
    if(pid == -1){
1002
1049
      error(0, errno, "fork");
 
1050
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
 
1051
                                     &sigchld_action.sa_mask, NULL));
 
1052
      close(pipefd[0]);
 
1053
      close(pipefd[1]);
1003
1054
      exitstatus = EX_OSERR;
 
1055
      free(direntries[i]);
1004
1056
      goto fallback;
1005
1057
    }
1006
1058
    if(pid == 0){
1022
1074
        _exit(EX_OSERR);
1023
1075
      }
1024
1076
      
1025
 
      if(dirfd(dir) < 0){
1026
 
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
1027
 
           above and must now close it manually here. */
1028
 
        closedir(dir);
1029
 
      }
1030
 
      if(p->environ[0] == NULL){
1031
 
        if(execv(filename, p->argv) < 0){
1032
 
          error(0, errno, "execv for %s", filename);
1033
 
          _exit(EX_OSERR);
1034
 
        }
1035
 
      } else {
1036
 
        if(execve(filename, p->argv, p->environ) < 0){
1037
 
          error(0, errno, "execve for %s", filename);
1038
 
          _exit(EX_OSERR);
1039
 
        }
 
1077
      if(fexecve(plugin_fd, p->argv,
 
1078
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
 
1079
        error(0, errno, "fexecve for %s/%s",
 
1080
              plugindir != NULL ? plugindir : PDIR,
 
1081
              direntries[i]->d_name);
 
1082
        _exit(EX_OSERR);
1040
1083
      }
1041
1084
      /* no return */
1042
1085
    }
1043
1086
    /* Parent process */
1044
 
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1045
 
                                             pipe */
1046
 
    free(filename);
1047
 
    plugin *new_plugin = getplugin(dirst->d_name);
 
1087
    close(pipefd[1]);           /* Close unused write end of pipe */
 
1088
    close(plugin_fd);
 
1089
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1048
1090
    if(new_plugin == NULL){
1049
1091
      error(0, errno, "getplugin");
1050
1092
      ret = (int)(TEMP_FAILURE_RETRY
1054
1096
        error(0, errno, "sigprocmask");
1055
1097
      }
1056
1098
      exitstatus = EX_OSERR;
 
1099
      free(direntries[i]);
1057
1100
      goto fallback;
1058
1101
    }
 
1102
    free(direntries[i]);
1059
1103
    
1060
1104
    new_plugin->pid = pid;
1061
1105
    new_plugin->fd = pipefd[0];
1071
1115
      goto fallback;
1072
1116
    }
1073
1117
    
 
1118
#if defined (__GNUC__) and defined (__GLIBC__)
 
1119
#if not __GLIBC_PREREQ(2, 16)
 
1120
#pragma GCC diagnostic push
 
1121
#pragma GCC diagnostic ignored "-Wsign-conversion"
 
1122
#endif
 
1123
#endif
1074
1124
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
1075
 
                                          -Wconversion */
 
1125
                                          -Wconversion in GNU libc
 
1126
                                          before 2.16 */
 
1127
#if defined (__GNUC__) and defined (__GLIBC__)
 
1128
#if not __GLIBC_PREREQ(2, 16)
 
1129
#pragma GCC diagnostic pop
 
1130
#endif
 
1131
#endif
1076
1132
    
1077
1133
    if(maxfd < new_plugin->fd){
1078
1134
      maxfd = new_plugin->fd;
1079
1135
    }
1080
1136
  }
1081
1137
  
1082
 
  TEMP_FAILURE_RETRY(closedir(dir));
1083
 
  dir = NULL;
 
1138
  free(direntries);
 
1139
  direntries = NULL;
 
1140
  close(dir_fd);
 
1141
  dir_fd = -1;
1084
1142
  free_plugin(getplugin(NULL));
1085
1143
  
1086
1144
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1132
1190
          }
1133
1191
          
1134
1192
          /* Remove the plugin */
 
1193
#if defined (__GNUC__) and defined (__GLIBC__)
 
1194
#if not __GLIBC_PREREQ(2, 16)
 
1195
#pragma GCC diagnostic push
 
1196
#pragma GCC diagnostic ignored "-Wsign-conversion"
 
1197
#endif
 
1198
#endif
1135
1199
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1136
 
                                          -Wconversion */
 
1200
                                          -Wconversion in GNU libc
 
1201
                                          before 2.16 */
 
1202
#if defined (__GNUC__) and defined (__GLIBC__)
 
1203
#if not __GLIBC_PREREQ(2, 16)
 
1204
#pragma GCC diagnostic pop
 
1205
#endif
 
1206
#endif
1137
1207
          
1138
1208
          /* Block signal while modifying process_list */
1139
1209
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1179
1249
      }
1180
1250
      
1181
1251
      /* This process has not completed.  Does it have any output? */
 
1252
#if defined (__GNUC__) and defined (__GLIBC__)
 
1253
#if not __GLIBC_PREREQ(2, 16)
 
1254
#pragma GCC diagnostic push
 
1255
#pragma GCC diagnostic ignored "-Wsign-conversion"
 
1256
#endif
 
1257
#endif
1182
1258
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1183
1259
                                                         warning from
1184
 
                                                         -Wconversion */
 
1260
                                                         -Wconversion
 
1261
                                                         in GNU libc
 
1262
                                                         before
 
1263
                                                         2.16 */
 
1264
#if defined (__GNUC__) and defined (__GLIBC__)
 
1265
#if not __GLIBC_PREREQ(2, 16)
 
1266
#pragma GCC diagnostic pop
 
1267
#endif
 
1268
#endif
1185
1269
        /* This process had nothing to say at this time */
1186
1270
        proc = proc->next;
1187
1271
        continue;
1188
1272
      }
1189
1273
      /* Before reading, make the process' data buffer large enough */
1190
1274
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1191
 
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1192
 
                               + (size_t) BUFFER_SIZE);
1193
 
        if(proc->buffer == NULL){
 
1275
        char *new_buffer = realloc(proc->buffer, proc->buffer_size
 
1276
                                   + (size_t) BUFFER_SIZE);
 
1277
        if(new_buffer == NULL){
1194
1278
          error(0, errno, "malloc");
1195
1279
          exitstatus = EX_OSERR;
1196
1280
          goto fallback;
1197
1281
        }
 
1282
        proc->buffer = new_buffer;
1198
1283
        proc->buffer_size += BUFFER_SIZE;
1199
1284
      }
1200
1285
      /* Read from the process */
1253
1338
    free(custom_argv);
1254
1339
  }
1255
1340
  
1256
 
  if(dir != NULL){
1257
 
    closedir(dir);
 
1341
  free(direntries);
 
1342
  
 
1343
  if(dir_fd != -1){
 
1344
    close(dir_fd);
1258
1345
  }
1259
1346
  
1260
1347
  /* Kill the processes */
1280
1367
  free_plugin_list();
1281
1368
  
1282
1369
  free(plugindir);
 
1370
  free(pluginhelperdir);
1283
1371
  free(argfile);
1284
1372
  
1285
1373
  return exitstatus;