/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:
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 */
241
240
  return add_to_char_array(def, &(p->environ), &(p->envc));
242
241
}
243
242
 
 
243
#ifndef O_CLOEXEC
244
244
/*
245
245
 * Based on the example in the GNU LibC manual chapter 13.13 "File
246
246
 * Descriptor Flags".
257
257
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
258
258
                                       ret | FD_CLOEXEC));
259
259
}
 
260
#endif  /* not O_CLOEXEC */
260
261
 
261
262
 
262
263
/* Mark processes as completed when they exit, and save their exit
348
349
  char *plugindir = NULL;
349
350
  char *argfile = NULL;
350
351
  FILE *conffp;
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
 
    /* Ignore dotfiles, backup files and other junk */
866
 
    {
867
 
      bool bad_name = false;
868
 
      const char * const patterns[] = { ".*", "#*#", "*~",
869
 
                                        "*.dpkg-new", "*.dpkg-old",
870
 
                                        "*.dpkg-bak", "*.dpkg-divert",
871
 
                                        NULL };
872
 
#ifdef __GNUC__
873
 
#pragma GCC diagnostic push
874
 
#pragma GCC diagnostic ignored "-Wcast-qual"
875
 
#endif
876
 
      for(const char **pat = (const char **)patterns;
877
 
          *pat != NULL; pat++){
878
 
#ifdef __GNUC__
879
 
#pragma GCC diagnostic pop
880
 
#endif
881
 
        if(fnmatch(*pat, dirst->d_name,
882
 
                   FNM_FILE_NAME | FNM_PERIOD) != FNM_NOMATCH){
883
 
          if(debug){
884
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
885
 
                    " matching pattern %s\n", dirst->d_name, *pat);
886
 
          }
887
 
          bad_name = true;
888
 
          break;
889
 
        }
890
 
      }
891
 
      if(bad_name){
892
 
        continue;
893
 
      }
894
 
    }
895
 
    
896
 
    int plugin_fd = openat(dir_fd, dirst->d_name, O_RDONLY |
897
 
#ifdef O_CLOEXEC
898
 
                            O_CLOEXEC
899
 
#else  /* not O_CLOEXEC */
900
 
                            0
901
 
#endif  /* not O_CLOEXEC */
902
 
                            );
 
885
  for(int i = 0; i < numplugins; i++){
 
886
    
 
887
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
903
888
    if(plugin_fd == -1){
904
889
      error(0, errno, "Could not open plugin");
905
 
      continue;
906
 
    }
907
 
#ifndef O_CLOEXEC
908
 
  /* Set the FD_CLOEXEC flag on the plugin FD */
909
 
    ret = set_cloexec_flag(plugin_fd);
910
 
    if(ret < 0){
911
 
      error(0, errno, "set_cloexec_flag");
912
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
913
 
      continue;
914
 
    }
915
 
#endif  /* O_CLOEXEC */
 
890
      free(direntries[i]);
 
891
      continue;
 
892
    }
916
893
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
917
894
    if(ret == -1){
918
895
      error(0, errno, "stat");
919
896
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
897
      free(direntries[i]);
920
898
      continue;
921
899
    }
922
900
    
923
901
    /* Ignore non-executable files */
924
902
    if(not S_ISREG(st.st_mode)
925
 
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, dirst->d_name, X_OK,
926
 
                                        0)) != 0)){
 
903
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
 
904
                                        X_OK, 0)) != 0)){
927
905
      if(debug){
928
906
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
929
907
                " with bad type or mode\n",
930
 
                plugindir != NULL ? plugindir : PDIR, dirst->d_name);
 
908
                plugindir != NULL ? plugindir : PDIR,
 
909
                direntries[i]->d_name);
931
910
      }
932
911
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
912
      free(direntries[i]);
933
913
      continue;
934
914
    }
935
915
    
936
 
    plugin *p = getplugin(dirst->d_name);
 
916
    plugin *p = getplugin(direntries[i]->d_name);
937
917
    if(p == NULL){
938
918
      error(0, errno, "getplugin");
939
919
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
920
      free(direntries[i]);
940
921
      continue;
941
922
    }
942
923
    if(p->disabled){
943
924
      if(debug){
944
925
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
945
 
                dirst->d_name);
 
926
                direntries[i]->d_name);
946
927
      }
947
928
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
929
      free(direntries[i]);
948
930
      continue;
949
931
    }
950
932
    {
975
957
    }
976
958
    
977
959
    int pipefd[2];
 
960
#ifndef O_CLOEXEC
978
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 */
979
965
    if(ret == -1){
980
966
      error(0, errno, "pipe");
981
967
      exitstatus = EX_OSERR;
982
 
      goto fallback;
983
 
    }
 
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
984
981
    /* Ask OS to automatic close the pipe on exec */
985
982
    ret = set_cloexec_flag(pipefd[0]);
986
983
    if(ret < 0){
988
985
      TEMP_FAILURE_RETRY(close(pipefd[0]));
989
986
      TEMP_FAILURE_RETRY(close(pipefd[1]));
990
987
      exitstatus = EX_OSERR;
 
988
      free(direntries[i]);
991
989
      goto fallback;
992
990
    }
993
991
    ret = set_cloexec_flag(pipefd[1]);
996
994
      TEMP_FAILURE_RETRY(close(pipefd[0]));
997
995
      TEMP_FAILURE_RETRY(close(pipefd[1]));
998
996
      exitstatus = EX_OSERR;
 
997
      free(direntries[i]);
999
998
      goto fallback;
1000
999
    }
 
1000
#endif  /* not O_CLOEXEC */
1001
1001
    /* Block SIGCHLD until process is safely in process list */
1002
1002
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
1003
1003
                                              &sigchld_action.sa_mask,
1005
1005
    if(ret < 0){
1006
1006
      error(0, errno, "sigprocmask");
1007
1007
      exitstatus = EX_OSERR;
 
1008
      free(direntries[i]);
1008
1009
      goto fallback;
1009
1010
    }
1010
1011
    /* Starting a new process to be watched */
1019
1020
      TEMP_FAILURE_RETRY(close(pipefd[0]));
1020
1021
      TEMP_FAILURE_RETRY(close(pipefd[1]));
1021
1022
      exitstatus = EX_OSERR;
 
1023
      free(direntries[i]);
1022
1024
      goto fallback;
1023
1025
    }
1024
1026
    if(pid == 0){
1040
1042
        _exit(EX_OSERR);
1041
1043
      }
1042
1044
      
1043
 
      if(dirfd(dir) < 0){
1044
 
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
1045
 
           above and must now close it manually here. */
1046
 
        closedir(dir);
1047
 
      }
1048
1045
      if(fexecve(plugin_fd, p->argv,
1049
1046
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
1050
1047
        error(0, errno, "fexecve for %s/%s",
1051
 
              plugindir != NULL ? plugindir : PDIR, dirst->d_name);
 
1048
              plugindir != NULL ? plugindir : PDIR,
 
1049
              direntries[i]->d_name);
1052
1050
        _exit(EX_OSERR);
1053
1051
      }
1054
1052
      /* no return */
1057
1055
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1058
1056
                                             pipe */
1059
1057
    TEMP_FAILURE_RETRY(close(plugin_fd));
1060
 
    plugin *new_plugin = getplugin(dirst->d_name);
 
1058
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1061
1059
    if(new_plugin == NULL){
1062
1060
      error(0, errno, "getplugin");
1063
1061
      ret = (int)(TEMP_FAILURE_RETRY
1067
1065
        error(0, errno, "sigprocmask");
1068
1066
      }
1069
1067
      exitstatus = EX_OSERR;
 
1068
      free(direntries[i]);
1070
1069
      goto fallback;
1071
1070
    }
 
1071
    free(direntries[i]);
1072
1072
    
1073
1073
    new_plugin->pid = pid;
1074
1074
    new_plugin->fd = pipefd[0];
1104
1104
    }
1105
1105
  }
1106
1106
  
1107
 
  TEMP_FAILURE_RETRY(closedir(dir));
1108
 
  dir = NULL;
 
1107
  free(direntries);
 
1108
  direntries = NULL;
 
1109
  TEMP_FAILURE_RETRY(close(dir_fd));
 
1110
  dir_fd = -1;
1109
1111
  free_plugin(getplugin(NULL));
1110
1112
  
1111
1113
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1305
1307
    free(custom_argv);
1306
1308
  }
1307
1309
  
1308
 
  if(dir != NULL){
1309
 
    closedir(dir);
 
1310
  free(direntries);
 
1311
  
 
1312
  if(dir_fd != -1){
 
1313
    TEMP_FAILURE_RETRY(close(dir_fd));
1310
1314
  }
1311
1315
  
1312
1316
  /* Kill the processes */