/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: 2014-07-25 23:16:04 UTC
  • mto: (237.7.272 trunk)
  • mto: This revision was merged to the branch mainline in revision 321.
  • Revision ID: teddy@recompile.se-20140725231604-f5c4f82rn2o5ll1k
Use the .items() method instead of .iteritems().

This is strictly not a Python 2.7 change, but Python 2.7 backported
the new .viewitems() from Python 3, and instead of changing .items()
to .viewitems() and later having to change them all into .items()
again in Python 3, I opted to just change all .iteritems() to .items()
so the code will work both now and with Python 3.  The slowdown with
Python 2 is not significant, and with Python 3 it will again be fast.

* mandos (Client.__init__): Use .items() instead of .iteritems().
  (DBusObjectWithProperties.Introspect): - '' -
  (alternate_dbus_interfaces/wrapper): - '' -
  (main): - '' -
* mandos-ctl (main): - '' -

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-2013 Teddy Hogeborn
6
 
 * Copyright © 2008-2013 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
 
                                   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
32
                                   stderr, STDOUT_FILENO, fclose() */
33
 
#include <sys/types.h>          /* DIR, fdopendir(), fstat(), struct
34
 
                                   stat, waitpid(), WIFEXITED(),
35
 
                                   WEXITSTATUS(), wait(), pid_t,
36
 
                                   uid_t, gid_t, getuid(), getgid(),
37
 
                                   dirfd() */
 
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(),
42
41
                                   WCOREDUMP() */
43
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() */
 
44
#include <dirent.h>             /* struct dirent, scandirat() */
47
45
#include <unistd.h>             /* fcntl(), F_GETFD, F_SETFD,
48
46
                                   FD_CLOEXEC, write(), STDOUT_FILENO,
49
47
                                   struct stat, fstat(), close(),
50
48
                                   setgid(), setuid(), S_ISREG(),
51
 
                                   faccessat() pipe(), fork(),
 
49
                                   faccessat() pipe2(), fork(),
52
50
                                   _exit(), dup2(), fexecve(), read()
53
51
                                */
54
52
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
55
 
                                   FD_CLOEXEC, openat() */
 
53
                                   FD_CLOEXEC, openat(), scandirat(),
 
54
                                   pipe2() */
56
55
#include <string.h>             /* strsep, strlen(), strsignal(),
57
56
                                   strcmp(), strncmp() */
58
57
#include <errno.h>              /* errno */
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
 
240
240
  return add_to_char_array(def, &(p->environ), &(p->envc));
241
241
}
242
242
 
 
243
#ifndef O_CLOEXEC
243
244
/*
244
245
 * Based on the example in the GNU LibC manual chapter 13.13 "File
245
246
 * Descriptor Flags".
256
257
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
257
258
                                       ret | FD_CLOEXEC));
258
259
}
 
260
#endif  /* not O_CLOEXEC */
259
261
 
260
262
 
261
263
/* Mark processes as completed when they exit, and save their exit
347
349
  char *plugindir = NULL;
348
350
  char *argfile = NULL;
349
351
  FILE *conffp;
350
 
  size_t d_name_len;
351
 
  DIR *dir = NULL;
352
 
  struct dirent *dirst;
 
352
  struct dirent **direntries = NULL;
353
353
  struct stat st;
354
354
  fd_set rfds_all;
355
355
  int ret, maxfd = 0;
363
363
                                      .sa_flags = SA_NOCLDSTOP };
364
364
  char **custom_argv = NULL;
365
365
  int custom_argc = 0;
366
 
  int dir_fd;
 
366
  int dir_fd = -1;
367
367
  
368
368
  /* Establish a signal handler */
369
369
  sigemptyset(&sigchld_action.sa_mask);
829
829
    ret = set_cloexec_flag(dir_fd);
830
830
    if(ret < 0){
831
831
      error(0, errno, "set_cloexec_flag");
832
 
      TEMP_FAILURE_RETRY(close(dir_fd));
833
832
      exitstatus = EX_OSERR;
834
833
      goto fallback;
835
834
    }
836
835
#endif  /* O_CLOEXEC */
837
 
    
838
 
    dir = fdopendir(dir_fd);
839
 
    if(dir == NULL){
840
 
      error(0, errno, "Could not open plugin dir");
841
 
      TEMP_FAILURE_RETRY(close(dir_fd));
842
 
      exitstatus = EX_OSERR;
843
 
      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
      }
844
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;
845
880
  }
846
881
  
847
882
  FD_ZERO(&rfds_all);
848
883
  
849
884
  /* Read and execute any executable in the plugin directory*/
850
 
  while(true){
851
 
    do {
852
 
      dirst = readdir(dir);
853
 
    } while(dirst == NULL and errno == EINTR);
854
 
    
855
 
    /* All directory entries have been processed */
856
 
    if(dirst == NULL){
857
 
      if(errno == EBADF){
858
 
        error(0, errno, "readdir");
859
 
        exitstatus = EX_IOERR;
860
 
        goto fallback;
861
 
      }
862
 
      break;
863
 
    }
864
 
    
865
 
    d_name_len = strlen(dirst->d_name);
866
 
    
867
 
    /* Ignore dotfiles, backup files and other junk */
868
 
    {
869
 
      bool bad_name = false;
870
 
      
871
 
      const char * const bad_prefixes[] = { ".", "#", NULL };
872
 
      
873
 
      const char * const bad_suffixes[] = { "~", "#", ".dpkg-new",
874
 
                                           ".dpkg-old",
875
 
                                           ".dpkg-bak",
876
 
                                           ".dpkg-divert", NULL };
877
 
#ifdef __GNUC__
878
 
#pragma GCC diagnostic push
879
 
#pragma GCC diagnostic ignored "-Wcast-qual"
880
 
#endif
881
 
      for(const char **pre = (const char **)bad_prefixes;
882
 
          *pre != NULL; pre++){
883
 
#ifdef __GNUC__
884
 
#pragma GCC diagnostic pop
885
 
#endif
886
 
        size_t pre_len = strlen(*pre);
887
 
        if((d_name_len >= pre_len)
888
 
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
889
 
          if(debug){
890
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
891
 
                    " with bad prefix %s\n", dirst->d_name, *pre);
892
 
          }
893
 
          bad_name = true;
894
 
          break;
895
 
        }
896
 
      }
897
 
      if(bad_name){
898
 
        continue;
899
 
      }
900
 
#ifdef __GNUC__
901
 
#pragma GCC diagnostic push
902
 
#pragma GCC diagnostic ignored "-Wcast-qual"
903
 
#endif
904
 
      for(const char **suf = (const char **)bad_suffixes;
905
 
          *suf != NULL; suf++){
906
 
#ifdef __GNUC__
907
 
#pragma GCC diagnostic pop
908
 
#endif
909
 
        size_t suf_len = strlen(*suf);
910
 
        if((d_name_len >= suf_len)
911
 
           and (strcmp((dirst->d_name) + d_name_len-suf_len, *suf)
912
 
                == 0)){
913
 
          if(debug){
914
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
915
 
                    " with bad suffix %s\n", dirst->d_name, *suf);
916
 
          }
917
 
          bad_name = true;
918
 
          break;
919
 
        }
920
 
      }
921
 
      
922
 
      if(bad_name){
923
 
        continue;
924
 
      }
925
 
    }
926
 
    
927
 
    int plugin_fd = openat(dir_fd, dirst->d_name, O_RDONLY |
928
 
#ifdef O_CLOEXEC
929
 
                            O_CLOEXEC
930
 
#else  /* not O_CLOEXEC */
931
 
                            0
932
 
#endif  /* not O_CLOEXEC */
933
 
                            );
 
885
  for(int i = 0; i < numplugins; i++){
 
886
    
 
887
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
934
888
    if(plugin_fd == -1){
935
889
      error(0, errno, "Could not open plugin");
936
 
      continue;
937
 
    }
938
 
#ifndef O_CLOEXEC
939
 
  /* Set the FD_CLOEXEC flag on the plugin FD */
940
 
    ret = set_cloexec_flag(plugin_fd);
941
 
    if(ret < 0){
942
 
      error(0, errno, "set_cloexec_flag");
943
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
944
 
      continue;
945
 
    }
946
 
#endif  /* O_CLOEXEC */
 
890
      free(direntries[i]);
 
891
      continue;
 
892
    }
947
893
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
948
894
    if(ret == -1){
949
895
      error(0, errno, "stat");
950
896
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
897
      free(direntries[i]);
951
898
      continue;
952
899
    }
953
900
    
954
901
    /* Ignore non-executable files */
955
902
    if(not S_ISREG(st.st_mode)
956
 
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, dirst->d_name, X_OK,
957
 
                                        0)) != 0)){
 
903
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
 
904
                                        X_OK, 0)) != 0)){
958
905
      if(debug){
959
906
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
960
907
                " with bad type or mode\n",
961
 
                plugindir != NULL ? plugindir : PDIR, dirst->d_name);
 
908
                plugindir != NULL ? plugindir : PDIR,
 
909
                direntries[i]->d_name);
962
910
      }
963
911
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
912
      free(direntries[i]);
964
913
      continue;
965
914
    }
966
915
    
967
 
    plugin *p = getplugin(dirst->d_name);
 
916
    plugin *p = getplugin(direntries[i]->d_name);
968
917
    if(p == NULL){
969
918
      error(0, errno, "getplugin");
970
919
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
920
      free(direntries[i]);
971
921
      continue;
972
922
    }
973
923
    if(p->disabled){
974
924
      if(debug){
975
925
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
976
 
                dirst->d_name);
 
926
                direntries[i]->d_name);
977
927
      }
978
928
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
929
      free(direntries[i]);
979
930
      continue;
980
931
    }
981
932
    {
1006
957
    }
1007
958
    
1008
959
    int pipefd[2];
 
960
#ifndef O_CLOEXEC
1009
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 */
1010
965
    if(ret == -1){
1011
966
      error(0, errno, "pipe");
1012
967
      exitstatus = EX_OSERR;
1013
 
      goto fallback;
1014
 
    }
 
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
1015
981
    /* Ask OS to automatic close the pipe on exec */
1016
982
    ret = set_cloexec_flag(pipefd[0]);
1017
983
    if(ret < 0){
1018
984
      error(0, errno, "set_cloexec_flag");
 
985
      TEMP_FAILURE_RETRY(close(pipefd[0]));
 
986
      TEMP_FAILURE_RETRY(close(pipefd[1]));
1019
987
      exitstatus = EX_OSERR;
 
988
      free(direntries[i]);
1020
989
      goto fallback;
1021
990
    }
1022
991
    ret = set_cloexec_flag(pipefd[1]);
1023
992
    if(ret < 0){
1024
993
      error(0, errno, "set_cloexec_flag");
 
994
      TEMP_FAILURE_RETRY(close(pipefd[0]));
 
995
      TEMP_FAILURE_RETRY(close(pipefd[1]));
1025
996
      exitstatus = EX_OSERR;
 
997
      free(direntries[i]);
1026
998
      goto fallback;
1027
999
    }
 
1000
#endif  /* not O_CLOEXEC */
1028
1001
    /* Block SIGCHLD until process is safely in process list */
1029
1002
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
1030
1003
                                              &sigchld_action.sa_mask,
1032
1005
    if(ret < 0){
1033
1006
      error(0, errno, "sigprocmask");
1034
1007
      exitstatus = EX_OSERR;
 
1008
      free(direntries[i]);
1035
1009
      goto fallback;
1036
1010
    }
1037
1011
    /* Starting a new process to be watched */
1041
1015
    } while(pid == -1 and errno == EINTR);
1042
1016
    if(pid == -1){
1043
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]));
1044
1022
      exitstatus = EX_OSERR;
 
1023
      free(direntries[i]);
1045
1024
      goto fallback;
1046
1025
    }
1047
1026
    if(pid == 0){
1063
1042
        _exit(EX_OSERR);
1064
1043
      }
1065
1044
      
1066
 
      if(dirfd(dir) < 0){
1067
 
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
1068
 
           above and must now close it manually here. */
1069
 
        closedir(dir);
1070
 
      }
1071
1045
      if(fexecve(plugin_fd, p->argv,
1072
1046
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
1073
1047
        error(0, errno, "fexecve for %s/%s",
1074
 
              plugindir != NULL ? plugindir : PDIR, dirst->d_name);
 
1048
              plugindir != NULL ? plugindir : PDIR,
 
1049
              direntries[i]->d_name);
1075
1050
        _exit(EX_OSERR);
1076
1051
      }
1077
1052
      /* no return */
1080
1055
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1081
1056
                                             pipe */
1082
1057
    TEMP_FAILURE_RETRY(close(plugin_fd));
1083
 
    plugin *new_plugin = getplugin(dirst->d_name);
 
1058
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1084
1059
    if(new_plugin == NULL){
1085
1060
      error(0, errno, "getplugin");
1086
1061
      ret = (int)(TEMP_FAILURE_RETRY
1090
1065
        error(0, errno, "sigprocmask");
1091
1066
      }
1092
1067
      exitstatus = EX_OSERR;
 
1068
      free(direntries[i]);
1093
1069
      goto fallback;
1094
1070
    }
 
1071
    free(direntries[i]);
1095
1072
    
1096
1073
    new_plugin->pid = pid;
1097
1074
    new_plugin->fd = pipefd[0];
1127
1104
    }
1128
1105
  }
1129
1106
  
1130
 
  TEMP_FAILURE_RETRY(closedir(dir));
1131
 
  dir = NULL;
 
1107
  free(direntries);
 
1108
  direntries = NULL;
 
1109
  TEMP_FAILURE_RETRY(close(dir_fd));
 
1110
  dir_fd = -1;
1132
1111
  free_plugin(getplugin(NULL));
1133
1112
  
1134
1113
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1328
1307
    free(custom_argv);
1329
1308
  }
1330
1309
  
1331
 
  if(dir != NULL){
1332
 
    closedir(dir);
 
1310
  free(direntries);
 
1311
  
 
1312
  if(dir_fd != -1){
 
1313
    TEMP_FAILURE_RETRY(close(dir_fd));
1333
1314
  }
1334
1315
  
1335
1316
  /* Kill the processes */