/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:
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 */
77
76
#define BUFFER_SIZE 256
78
77
 
79
78
#define PDIR "/lib/mandos/plugins.d"
 
79
#define PHDIR "/lib/mandos/plugin-helpers"
80
80
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
81
81
 
82
82
const char *argp_program_version = "plugin-runner " VERSION;
241
241
  return add_to_char_array(def, &(p->environ), &(p->envc));
242
242
}
243
243
 
 
244
#ifndef O_CLOEXEC
244
245
/*
245
246
 * Based on the example in the GNU LibC manual chapter 13.13 "File
246
247
 * Descriptor Flags".
257
258
  return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
258
259
                                       ret | FD_CLOEXEC));
259
260
}
 
261
#endif  /* not O_CLOEXEC */
260
262
 
261
263
 
262
264
/* Mark processes as completed when they exit, and save their exit
346
348
 
347
349
int main(int argc, char *argv[]){
348
350
  char *plugindir = NULL;
 
351
  char *pluginhelperdir = NULL;
349
352
  char *argfile = NULL;
350
353
  FILE *conffp;
351
 
  DIR *dir = NULL;
352
 
  struct dirent *dirst;
 
354
  struct dirent **direntries = NULL;
353
355
  struct stat st;
354
356
  fd_set rfds_all;
355
357
  int ret, maxfd = 0;
363
365
                                      .sa_flags = SA_NOCLDSTOP };
364
366
  char **custom_argv = NULL;
365
367
  int custom_argc = 0;
366
 
  int dir_fd;
 
368
  int dir_fd = -1;
367
369
  
368
370
  /* Establish a signal handler */
369
371
  sigemptyset(&sigchld_action.sa_mask);
414
416
      .doc = "Group ID the plugins will run as", .group = 3 },
415
417
    { .name = "debug", .key = 132,
416
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 },
417
423
    /*
418
424
     * These reproduce what we would get without ARGP_NO_HELP
419
425
     */
545
551
    case 132:                   /* --debug */
546
552
      debug = true;
547
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;
548
561
      /*
549
562
       * These reproduce what we would get without ARGP_NO_HELP
550
563
       */
601
614
    case 130:                   /* --userid */
602
615
    case 131:                   /* --groupid */
603
616
    case 132:                   /* --debug */
 
617
    case 133:                   /* --plugin-helper-dir */
604
618
    case '?':                   /* --help */
605
619
    case -3:                    /* --usage */
606
620
    case 'V':                   /* --version */
761
775
    goto fallback;
762
776
  }
763
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
  
764
796
  if(debug){
765
797
    for(plugin *p = plugin_list; p != NULL; p=p->next){
766
798
      fprintf(stderr, "Plugin: %s has %d arguments\n",
795
827
          }
796
828
        }
797
829
      }
798
 
      TEMP_FAILURE_RETRY(close(plugindir_fd));
 
830
      close(plugindir_fd);
799
831
    }
800
832
  }
801
833
  
829
861
    ret = set_cloexec_flag(dir_fd);
830
862
    if(ret < 0){
831
863
      error(0, errno, "set_cloexec_flag");
832
 
      TEMP_FAILURE_RETRY(close(dir_fd));
833
864
      exitstatus = EX_OSERR;
834
865
      goto fallback;
835
866
    }
836
867
#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;
 
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
      }
844
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;
845
912
  }
846
913
  
847
914
  FD_ZERO(&rfds_all);
848
915
  
849
916
  /* 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
 
                            );
 
917
  for(int i = 0; i < numplugins; i++){
 
918
    
 
919
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
903
920
    if(plugin_fd == -1){
904
921
      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 */
 
922
      free(direntries[i]);
 
923
      continue;
 
924
    }
916
925
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
917
926
    if(ret == -1){
918
927
      error(0, errno, "stat");
919
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
928
      close(plugin_fd);
 
929
      free(direntries[i]);
920
930
      continue;
921
931
    }
922
932
    
923
933
    /* Ignore non-executable files */
924
934
    if(not S_ISREG(st.st_mode)
925
 
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, dirst->d_name, X_OK,
926
 
                                        0)) != 0)){
 
935
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
 
936
                                        X_OK, 0)) != 0)){
927
937
      if(debug){
928
938
        fprintf(stderr, "Ignoring plugin dir entry \"%s/%s\""
929
939
                " with bad type or mode\n",
930
 
                plugindir != NULL ? plugindir : PDIR, dirst->d_name);
 
940
                plugindir != NULL ? plugindir : PDIR,
 
941
                direntries[i]->d_name);
931
942
      }
932
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
943
      close(plugin_fd);
 
944
      free(direntries[i]);
933
945
      continue;
934
946
    }
935
947
    
936
 
    plugin *p = getplugin(dirst->d_name);
 
948
    plugin *p = getplugin(direntries[i]->d_name);
937
949
    if(p == NULL){
938
950
      error(0, errno, "getplugin");
939
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
951
      close(plugin_fd);
 
952
      free(direntries[i]);
940
953
      continue;
941
954
    }
942
955
    if(p->disabled){
943
956
      if(debug){
944
957
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
945
 
                dirst->d_name);
 
958
                direntries[i]->d_name);
946
959
      }
947
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
960
      close(plugin_fd);
 
961
      free(direntries[i]);
948
962
      continue;
949
963
    }
950
964
    {
975
989
    }
976
990
    
977
991
    int pipefd[2];
 
992
#ifndef O_CLOEXEC
978
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 */
979
997
    if(ret == -1){
980
998
      error(0, errno, "pipe");
981
999
      exitstatus = EX_OSERR;
 
1000
      free(direntries[i]);
982
1001
      goto fallback;
983
1002
    }
984
1003
    if(pipefd[0] >= FD_SETSIZE){
985
1004
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
986
1005
              FD_SETSIZE);
987
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
988
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1006
      close(pipefd[0]);
 
1007
      close(pipefd[1]);
989
1008
      exitstatus = EX_OSERR;
 
1009
      free(direntries[i]);
990
1010
      goto fallback;
991
1011
    }
 
1012
#ifndef O_CLOEXEC
992
1013
    /* Ask OS to automatic close the pipe on exec */
993
1014
    ret = set_cloexec_flag(pipefd[0]);
994
1015
    if(ret < 0){
995
1016
      error(0, errno, "set_cloexec_flag");
996
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
997
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1017
      close(pipefd[0]);
 
1018
      close(pipefd[1]);
998
1019
      exitstatus = EX_OSERR;
 
1020
      free(direntries[i]);
999
1021
      goto fallback;
1000
1022
    }
1001
1023
    ret = set_cloexec_flag(pipefd[1]);
1002
1024
    if(ret < 0){
1003
1025
      error(0, errno, "set_cloexec_flag");
1004
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
1005
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1026
      close(pipefd[0]);
 
1027
      close(pipefd[1]);
1006
1028
      exitstatus = EX_OSERR;
 
1029
      free(direntries[i]);
1007
1030
      goto fallback;
1008
1031
    }
 
1032
#endif  /* not O_CLOEXEC */
1009
1033
    /* Block SIGCHLD until process is safely in process list */
1010
1034
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
1011
1035
                                              &sigchld_action.sa_mask,
1013
1037
    if(ret < 0){
1014
1038
      error(0, errno, "sigprocmask");
1015
1039
      exitstatus = EX_OSERR;
 
1040
      free(direntries[i]);
1016
1041
      goto fallback;
1017
1042
    }
1018
1043
    /* Starting a new process to be watched */
1024
1049
      error(0, errno, "fork");
1025
1050
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1026
1051
                                     &sigchld_action.sa_mask, NULL));
1027
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
1028
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1052
      close(pipefd[0]);
 
1053
      close(pipefd[1]);
1029
1054
      exitstatus = EX_OSERR;
 
1055
      free(direntries[i]);
1030
1056
      goto fallback;
1031
1057
    }
1032
1058
    if(pid == 0){
1048
1074
        _exit(EX_OSERR);
1049
1075
      }
1050
1076
      
1051
 
      if(dirfd(dir) < 0){
1052
 
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
1053
 
           above and must now close it manually here. */
1054
 
        closedir(dir);
1055
 
      }
1056
1077
      if(fexecve(plugin_fd, p->argv,
1057
1078
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
1058
1079
        error(0, errno, "fexecve for %s/%s",
1059
 
              plugindir != NULL ? plugindir : PDIR, dirst->d_name);
 
1080
              plugindir != NULL ? plugindir : PDIR,
 
1081
              direntries[i]->d_name);
1060
1082
        _exit(EX_OSERR);
1061
1083
      }
1062
1084
      /* no return */
1063
1085
    }
1064
1086
    /* Parent process */
1065
 
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1066
 
                                             pipe */
1067
 
    TEMP_FAILURE_RETRY(close(plugin_fd));
1068
 
    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);
1069
1090
    if(new_plugin == NULL){
1070
1091
      error(0, errno, "getplugin");
1071
1092
      ret = (int)(TEMP_FAILURE_RETRY
1075
1096
        error(0, errno, "sigprocmask");
1076
1097
      }
1077
1098
      exitstatus = EX_OSERR;
 
1099
      free(direntries[i]);
1078
1100
      goto fallback;
1079
1101
    }
 
1102
    free(direntries[i]);
1080
1103
    
1081
1104
    new_plugin->pid = pid;
1082
1105
    new_plugin->fd = pipefd[0];
1112
1135
    }
1113
1136
  }
1114
1137
  
1115
 
  TEMP_FAILURE_RETRY(closedir(dir));
1116
 
  dir = NULL;
 
1138
  free(direntries);
 
1139
  direntries = NULL;
 
1140
  close(dir_fd);
 
1141
  dir_fd = -1;
1117
1142
  free_plugin(getplugin(NULL));
1118
1143
  
1119
1144
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1313
1338
    free(custom_argv);
1314
1339
  }
1315
1340
  
1316
 
  if(dir != NULL){
1317
 
    closedir(dir);
 
1341
  free(direntries);
 
1342
  
 
1343
  if(dir_fd != -1){
 
1344
    close(dir_fd);
1318
1345
  }
1319
1346
  
1320
1347
  /* Kill the processes */
1340
1367
  free_plugin_list();
1341
1368
  
1342
1369
  free(plugindir);
 
1370
  free(pluginhelperdir);
1343
1371
  free(argfile);
1344
1372
  
1345
1373
  return exitstatus;