/mandos/release

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

« back to all changes in this revision

Viewing changes to plugin-runner.c

  • Committer: Teddy Hogeborn
  • Date: 2016-03-17 20:40:55 UTC
  • mto: (237.7.594 trunk)
  • mto: This revision was merged to the branch mainline in revision 341.
  • Revision ID: teddy@recompile.se-20160317204055-bhsh5xsidq7w5cxu
Client: Fix plymouth agent; broken since 1.7.2.

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

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

Show diffs side-by-side

added added

removed removed

Lines of Context:
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-2016 Teddy Hogeborn
 
6
 * Copyright © 2008-2016 Björn Påhlsson
7
7
 * 
8
8
 * This program is free software: you can redistribute it and/or
9
9
 * modify it under the terms of the GNU General Public License as
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
                                    * ((size_t)custom_argc + 1));
 
705
          if(new_argv == NULL){
 
706
            error(0, errno, "realloc");
 
707
            exitstatus = EX_OSERR;
 
708
            free(new_arg);
 
709
            free(org_line);
 
710
            goto fallback;
 
711
          } else {
 
712
            custom_argv = new_argv;
 
713
          }
674
714
        }
675
715
        custom_argv[custom_argc-1] = new_arg;
676
716
        custom_argv[custom_argc] = NULL;
734
774
    goto fallback;
735
775
  }
736
776
  
 
777
  {
 
778
    char *pluginhelperenv;
 
779
    bool bret = true;
 
780
    ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s",
 
781
                   pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
782
    if(ret != -1){
 
783
      bret = add_environment(getplugin(NULL), pluginhelperenv, true);
 
784
    }
 
785
    if(ret == -1 or not bret){
 
786
      error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR"
 
787
            " environment variable to \"%s\" for all plugins\n",
 
788
            pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
789
    }
 
790
    if(ret != -1){
 
791
      free(pluginhelperenv);
 
792
    }
 
793
  }
 
794
  
737
795
  if(debug){
738
796
    for(plugin *p = plugin_list; p != NULL; p=p->next){
739
797
      fprintf(stderr, "Plugin: %s has %d arguments\n",
753
811
       <http://bugs.debian.org/633582> */
754
812
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
755
813
    if(plugindir_fd == -1){
756
 
      error(0, errno, "open");
 
814
      if(errno != ENOENT){
 
815
        error(0, errno, "open(\"" PDIR "\")");
 
816
      }
757
817
    } else {
758
818
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
759
819
      if(ret == -1){
766
826
          }
767
827
        }
768
828
      }
769
 
      TEMP_FAILURE_RETRY(close(plugindir_fd));
 
829
      close(plugindir_fd);
770
830
    }
771
831
  }
772
832
  
773
833
  /* Lower permissions */
774
 
  setgid(gid);
 
834
  ret = setgid(gid);
775
835
  if(ret == -1){
776
836
    error(0, errno, "setgid");
777
837
  }
782
842
  
783
843
  /* Open plugin directory with close_on_exec flag */
784
844
  {
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
 
    }
 
845
    dir_fd = open(plugindir != NULL ? plugindir : PDIR, O_RDONLY |
 
846
#ifdef O_CLOEXEC
 
847
                  O_CLOEXEC
 
848
#else  /* not O_CLOEXEC */
 
849
                  0
 
850
#endif  /* not O_CLOEXEC */
 
851
                  );
803
852
    if(dir_fd == -1){
804
853
      error(0, errno, "Could not open plugin dir");
805
854
      exitstatus = EX_UNAVAILABLE;
811
860
    ret = set_cloexec_flag(dir_fd);
812
861
    if(ret < 0){
813
862
      error(0, errno, "set_cloexec_flag");
814
 
      TEMP_FAILURE_RETRY(close(dir_fd));
815
863
      exitstatus = EX_OSERR;
816
864
      goto fallback;
817
865
    }
818
866
#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;
 
867
  }
 
868
  
 
869
  int good_name(const struct dirent * const dirent){
 
870
    const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new",
 
871
                                      "*.dpkg-old", "*.dpkg-bak",
 
872
                                      "*.dpkg-divert", NULL };
 
873
#ifdef __GNUC__
 
874
#pragma GCC diagnostic push
 
875
#pragma GCC diagnostic ignored "-Wcast-qual"
 
876
#endif
 
877
    for(const char **pat = (const char **)patterns;
 
878
        *pat != NULL; pat++){
 
879
#ifdef __GNUC__
 
880
#pragma GCC diagnostic pop
 
881
#endif
 
882
      if(fnmatch(*pat, dirent->d_name, FNM_FILE_NAME | FNM_PERIOD)
 
883
         != FNM_NOMATCH){
 
884
        if(debug){
 
885
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
 
886
                    " matching pattern %s\n", dirent->d_name, *pat);
 
887
        }
 
888
        return 0;
 
889
      }
826
890
    }
 
891
    return 1;
 
892
  }
 
893
  
 
894
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
 
895
                             alphasort);
 
896
  if(numplugins == -1){
 
897
    error(0, errno, "Could not scan plugin dir");
 
898
    direntries = NULL;
 
899
    exitstatus = EX_OSERR;
 
900
    goto fallback;
827
901
  }
828
902
  
829
903
  FD_ZERO(&rfds_all);
830
904
  
831
905
  /* 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");
 
906
  for(int i = 0; i < numplugins; i++){
 
907
    
 
908
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
 
909
    if(plugin_fd == -1){
 
910
      error(0, errno, "Could not open plugin");
 
911
      free(direntries[i]);
904
912
      continue;
905
913
    }
906
 
    
907
 
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
 
914
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
908
915
    if(ret == -1){
909
916
      error(0, errno, "stat");
910
 
      free(filename);
 
917
      close(plugin_fd);
 
918
      free(direntries[i]);
911
919
      continue;
912
920
    }
913
921
    
914
922
    /* Ignore non-executable files */
915
923
    if(not S_ISREG(st.st_mode)
916
 
       or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
 
924
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
 
925
                                        X_OK, 0)) != 0)){
917
926
      if(debug){
918
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
919
 
                " with bad type or mode\n", filename);
 
927
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
 
928
                " with bad type or mode\n",
 
929
                plugindir != NULL ? plugindir : PDIR,
 
930
                direntries[i]->d_name);
920
931
      }
921
 
      free(filename);
 
932
      close(plugin_fd);
 
933
      free(direntries[i]);
922
934
      continue;
923
935
    }
924
936
    
925
 
    plugin *p = getplugin(dirst->d_name);
 
937
    plugin *p = getplugin(direntries[i]->d_name);
926
938
    if(p == NULL){
927
939
      error(0, errno, "getplugin");
928
 
      free(filename);
 
940
      close(plugin_fd);
 
941
      free(direntries[i]);
929
942
      continue;
930
943
    }
931
944
    if(p->disabled){
932
945
      if(debug){
933
946
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
934
 
                dirst->d_name);
 
947
                direntries[i]->d_name);
935
948
      }
936
 
      free(filename);
 
949
      close(plugin_fd);
 
950
      free(direntries[i]);
937
951
      continue;
938
952
    }
939
953
    {
953
967
        }
954
968
      }
955
969
    }
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. */
 
970
    /* If this plugin has any environment variables, we need to
 
971
       duplicate the environment from this process, too. */
959
972
    if(p->environ[0] != NULL){
960
973
      for(char **e = environ; *e != NULL; e++){
961
974
        if(not add_environment(p, *e, false)){
965
978
    }
966
979
    
967
980
    int pipefd[2];
 
981
#ifndef O_CLOEXEC
968
982
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
 
983
#else  /* O_CLOEXEC */
 
984
    ret = (int)TEMP_FAILURE_RETRY(pipe2(pipefd, O_CLOEXEC));
 
985
#endif  /* O_CLOEXEC */
969
986
    if(ret == -1){
970
987
      error(0, errno, "pipe");
971
988
      exitstatus = EX_OSERR;
972
 
      goto fallback;
973
 
    }
 
989
      free(direntries[i]);
 
990
      goto fallback;
 
991
    }
 
992
    if(pipefd[0] >= FD_SETSIZE){
 
993
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
 
994
              FD_SETSIZE);
 
995
      close(pipefd[0]);
 
996
      close(pipefd[1]);
 
997
      exitstatus = EX_OSERR;
 
998
      free(direntries[i]);
 
999
      goto fallback;
 
1000
    }
 
1001
#ifndef O_CLOEXEC
974
1002
    /* Ask OS to automatic close the pipe on exec */
975
1003
    ret = set_cloexec_flag(pipefd[0]);
976
1004
    if(ret < 0){
977
1005
      error(0, errno, "set_cloexec_flag");
 
1006
      close(pipefd[0]);
 
1007
      close(pipefd[1]);
978
1008
      exitstatus = EX_OSERR;
 
1009
      free(direntries[i]);
979
1010
      goto fallback;
980
1011
    }
981
1012
    ret = set_cloexec_flag(pipefd[1]);
982
1013
    if(ret < 0){
983
1014
      error(0, errno, "set_cloexec_flag");
 
1015
      close(pipefd[0]);
 
1016
      close(pipefd[1]);
984
1017
      exitstatus = EX_OSERR;
 
1018
      free(direntries[i]);
985
1019
      goto fallback;
986
1020
    }
 
1021
#endif  /* not O_CLOEXEC */
987
1022
    /* Block SIGCHLD until process is safely in process list */
988
1023
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
989
1024
                                              &sigchld_action.sa_mask,
991
1026
    if(ret < 0){
992
1027
      error(0, errno, "sigprocmask");
993
1028
      exitstatus = EX_OSERR;
 
1029
      free(direntries[i]);
994
1030
      goto fallback;
995
1031
    }
996
1032
    /* Starting a new process to be watched */
1000
1036
    } while(pid == -1 and errno == EINTR);
1001
1037
    if(pid == -1){
1002
1038
      error(0, errno, "fork");
 
1039
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
 
1040
                                     &sigchld_action.sa_mask, NULL));
 
1041
      close(pipefd[0]);
 
1042
      close(pipefd[1]);
1003
1043
      exitstatus = EX_OSERR;
 
1044
      free(direntries[i]);
1004
1045
      goto fallback;
1005
1046
    }
1006
1047
    if(pid == 0){
1022
1063
        _exit(EX_OSERR);
1023
1064
      }
1024
1065
      
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
 
        }
 
1066
      if(fexecve(plugin_fd, p->argv,
 
1067
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
 
1068
        error(0, errno, "fexecve for %s/%s",
 
1069
              plugindir != NULL ? plugindir : PDIR,
 
1070
              direntries[i]->d_name);
 
1071
        _exit(EX_OSERR);
1040
1072
      }
1041
1073
      /* no return */
1042
1074
    }
1043
1075
    /* 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);
 
1076
    close(pipefd[1]);           /* Close unused write end of pipe */
 
1077
    close(plugin_fd);
 
1078
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1048
1079
    if(new_plugin == NULL){
1049
1080
      error(0, errno, "getplugin");
1050
1081
      ret = (int)(TEMP_FAILURE_RETRY
1054
1085
        error(0, errno, "sigprocmask");
1055
1086
      }
1056
1087
      exitstatus = EX_OSERR;
 
1088
      free(direntries[i]);
1057
1089
      goto fallback;
1058
1090
    }
 
1091
    free(direntries[i]);
1059
1092
    
1060
1093
    new_plugin->pid = pid;
1061
1094
    new_plugin->fd = pipefd[0];
1071
1104
      goto fallback;
1072
1105
    }
1073
1106
    
1074
 
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
1075
 
                                          -Wconversion */
 
1107
    FD_SET(new_plugin->fd, &rfds_all);
1076
1108
    
1077
1109
    if(maxfd < new_plugin->fd){
1078
1110
      maxfd = new_plugin->fd;
1079
1111
    }
1080
1112
  }
1081
1113
  
1082
 
  TEMP_FAILURE_RETRY(closedir(dir));
1083
 
  dir = NULL;
 
1114
  free(direntries);
 
1115
  direntries = NULL;
 
1116
  close(dir_fd);
 
1117
  dir_fd = -1;
1084
1118
  free_plugin(getplugin(NULL));
1085
1119
  
1086
1120
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1132
1166
          }
1133
1167
          
1134
1168
          /* Remove the plugin */
1135
 
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1136
 
                                          -Wconversion */
 
1169
          FD_CLR(proc->fd, &rfds_all);
1137
1170
          
1138
1171
          /* Block signal while modifying process_list */
1139
1172
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1179
1212
      }
1180
1213
      
1181
1214
      /* This process has not completed.  Does it have any output? */
1182
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1183
 
                                                         warning from
1184
 
                                                         -Wconversion */
 
1215
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1185
1216
        /* This process had nothing to say at this time */
1186
1217
        proc = proc->next;
1187
1218
        continue;
1188
1219
      }
1189
1220
      /* Before reading, make the process' data buffer large enough */
1190
1221
      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){
 
1222
        char *new_buffer = realloc(proc->buffer, proc->buffer_size
 
1223
                                   + (size_t) BUFFER_SIZE);
 
1224
        if(new_buffer == NULL){
1194
1225
          error(0, errno, "malloc");
1195
1226
          exitstatus = EX_OSERR;
1196
1227
          goto fallback;
1197
1228
        }
 
1229
        proc->buffer = new_buffer;
1198
1230
        proc->buffer_size += BUFFER_SIZE;
1199
1231
      }
1200
1232
      /* Read from the process */
1253
1285
    free(custom_argv);
1254
1286
  }
1255
1287
  
1256
 
  if(dir != NULL){
1257
 
    closedir(dir);
 
1288
  free(direntries);
 
1289
  
 
1290
  if(dir_fd != -1){
 
1291
    close(dir_fd);
1258
1292
  }
1259
1293
  
1260
1294
  /* Kill the processes */
1280
1314
  free_plugin_list();
1281
1315
  
1282
1316
  free(plugindir);
 
1317
  free(pluginhelperdir);
1283
1318
  free(argfile);
1284
1319
  
1285
1320
  return exitstatus;