/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: 2019-03-12 20:13:34 UTC
  • Revision ID: teddy@recompile.se-20190312201334-my3htrprewjosuw5
mandos-ctl: Refactor

* mandos-ctl: Reorder everything into logical order; put main() first,
              and put every subsequent definition as soon as possible
              after its first use, except superclasses which need to
              be placed before the classes inheriting from them.
              Reorder all tests to match.

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-2014 Teddy Hogeborn
6
 
 * Copyright © 2008-2014 Björn Påhlsson
7
 
 * 
8
 
 * This program is free software: you can redistribute it and/or
9
 
 * modify it under the terms of the GNU General Public License as
10
 
 * published by the Free Software Foundation, either version 3 of the
11
 
 * License, or (at your option) any later version.
12
 
 * 
13
 
 * This program is distributed in the hope that it will be useful, but
 
5
 * Copyright © 2008-2018 Teddy Hogeborn
 
6
 * Copyright © 2008-2018 Björn Påhlsson
 
7
 * 
 
8
 * This file is part of Mandos.
 
9
 * 
 
10
 * Mandos is free software: you can redistribute it and/or modify it
 
11
 * under the terms of the GNU General Public License as published by
 
12
 * the Free Software Foundation, either version 3 of the License, or
 
13
 * (at your option) any later version.
 
14
 * 
 
15
 * Mandos is distributed in the hope that it will be useful, but
14
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
18
 * General Public License for more details.
17
19
 * 
18
20
 * You should have received a copy of the GNU General Public License
19
 
 * along with this program.  If not, see
20
 
 * <http://www.gnu.org/licenses/>.
 
21
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
21
22
 * 
22
23
 * Contact the authors at <mandos@recompile.se>.
23
24
 */
37
38
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
38
39
                                   FD_SET(), FD_ISSET(), FD_CLR */
39
40
#include <sys/wait.h>           /* wait(), waitpid(), WIFEXITED(),
40
 
                                   WEXITSTATUS(), WTERMSIG(),
41
 
                                   WCOREDUMP() */
 
41
                                   WEXITSTATUS(), WTERMSIG() */
42
42
#include <sys/stat.h>           /* struct stat, fstat(), S_ISREG() */
43
43
#include <iso646.h>             /* and, or, not */
44
44
#include <dirent.h>             /* struct dirent, scandirat() */
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;
347
348
 
348
349
int main(int argc, char *argv[]){
349
350
  char *plugindir = NULL;
 
351
  char *pluginhelperdir = NULL;
350
352
  char *argfile = NULL;
351
353
  FILE *conffp;
352
354
  struct dirent **direntries = NULL;
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
       */
551
564
    case '?':                   /* --help */
552
565
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
553
566
      argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
 
567
      __builtin_unreachable();
554
568
    case -3:                    /* --usage */
555
569
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
556
570
      argp_state_help(state, state->out_stream,
557
571
                      ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
 
572
      __builtin_unreachable();
558
573
    case 'V':                   /* --version */
559
574
      fprintf(state->out_stream, "%s\n", argp_program_version);
560
575
      exit(EXIT_SUCCESS);
570
585
      if(arg[0] == '\0'){
571
586
        break;
572
587
      }
 
588
      /* FALLTHROUGH */
573
589
    default:
574
590
      return ARGP_ERR_UNKNOWN;
575
591
    }
601
617
    case 130:                   /* --userid */
602
618
    case 131:                   /* --groupid */
603
619
    case 132:                   /* --debug */
 
620
    case 133:                   /* --plugin-helper-dir */
604
621
    case '?':                   /* --help */
605
622
    case -3:                    /* --usage */
606
623
    case 'V':                   /* --version */
687
704
        custom_argc += 1;
688
705
        {
689
706
          char **new_argv = realloc(custom_argv, sizeof(char *)
690
 
                                    * ((unsigned int)
691
 
                                       custom_argc + 1));
 
707
                                    * ((size_t)custom_argc + 1));
692
708
          if(new_argv == NULL){
693
709
            error(0, errno, "realloc");
694
710
            exitstatus = EX_OSERR;
761
777
    goto fallback;
762
778
  }
763
779
  
 
780
  {
 
781
    char *pluginhelperenv;
 
782
    bool bret = true;
 
783
    ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s",
 
784
                   pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
785
    if(ret != -1){
 
786
      bret = add_environment(getplugin(NULL), pluginhelperenv, true);
 
787
    }
 
788
    if(ret == -1 or not bret){
 
789
      error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR"
 
790
            " environment variable to \"%s\" for all plugins\n",
 
791
            pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
792
    }
 
793
    if(ret != -1){
 
794
      free(pluginhelperenv);
 
795
    }
 
796
  }
 
797
  
764
798
  if(debug){
765
 
    for(plugin *p = plugin_list; p != NULL; p=p->next){
 
799
    for(plugin *p = plugin_list; p != NULL; p = p->next){
766
800
      fprintf(stderr, "Plugin: %s has %d arguments\n",
767
801
              p->name ? p->name : "Global", p->argc - 1);
768
802
      for(char **a = p->argv; *a != NULL; a++){
777
811
  
778
812
  if(getuid() == 0){
779
813
    /* Work around Debian bug #633582:
780
 
       <http://bugs.debian.org/633582> */
 
814
       <https://bugs.debian.org/633582> */
781
815
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
782
816
    if(plugindir_fd == -1){
783
817
      if(errno != ENOENT){
795
829
          }
796
830
        }
797
831
      }
798
 
      TEMP_FAILURE_RETRY(close(plugindir_fd));
 
832
      close(plugindir_fd);
799
833
    }
800
834
  }
801
835
  
860
894
    return 1;
861
895
  }
862
896
  
863
 
#ifdef __GLIBC__
864
 
#if __GLIBC_PREREQ(2, 15)
865
897
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
866
898
                             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
899
  if(numplugins == -1){
876
900
    error(0, errno, "Could not scan plugin dir");
877
901
    direntries = NULL;
893
917
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
894
918
    if(ret == -1){
895
919
      error(0, errno, "stat");
896
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
920
      close(plugin_fd);
897
921
      free(direntries[i]);
898
922
      continue;
899
923
    }
908
932
                plugindir != NULL ? plugindir : PDIR,
909
933
                direntries[i]->d_name);
910
934
      }
911
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
935
      close(plugin_fd);
912
936
      free(direntries[i]);
913
937
      continue;
914
938
    }
916
940
    plugin *p = getplugin(direntries[i]->d_name);
917
941
    if(p == NULL){
918
942
      error(0, errno, "getplugin");
919
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
943
      close(plugin_fd);
920
944
      free(direntries[i]);
921
945
      continue;
922
946
    }
925
949
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
926
950
                direntries[i]->d_name);
927
951
      }
928
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
952
      close(plugin_fd);
929
953
      free(direntries[i]);
930
954
      continue;
931
955
    }
971
995
    if(pipefd[0] >= FD_SETSIZE){
972
996
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
973
997
              FD_SETSIZE);
974
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
975
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
998
      close(pipefd[0]);
 
999
      close(pipefd[1]);
976
1000
      exitstatus = EX_OSERR;
977
1001
      free(direntries[i]);
978
1002
      goto fallback;
982
1006
    ret = set_cloexec_flag(pipefd[0]);
983
1007
    if(ret < 0){
984
1008
      error(0, errno, "set_cloexec_flag");
985
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
986
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1009
      close(pipefd[0]);
 
1010
      close(pipefd[1]);
987
1011
      exitstatus = EX_OSERR;
988
1012
      free(direntries[i]);
989
1013
      goto fallback;
991
1015
    ret = set_cloexec_flag(pipefd[1]);
992
1016
    if(ret < 0){
993
1017
      error(0, errno, "set_cloexec_flag");
994
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
995
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1018
      close(pipefd[0]);
 
1019
      close(pipefd[1]);
996
1020
      exitstatus = EX_OSERR;
997
1021
      free(direntries[i]);
998
1022
      goto fallback;
1017
1041
      error(0, errno, "fork");
1018
1042
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1019
1043
                                     &sigchld_action.sa_mask, NULL));
1020
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
1021
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1044
      close(pipefd[0]);
 
1045
      close(pipefd[1]);
1022
1046
      exitstatus = EX_OSERR;
1023
1047
      free(direntries[i]);
1024
1048
      goto fallback;
1052
1076
      /* no return */
1053
1077
    }
1054
1078
    /* Parent process */
1055
 
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1056
 
                                             pipe */
1057
 
    TEMP_FAILURE_RETRY(close(plugin_fd));
 
1079
    close(pipefd[1]);           /* Close unused write end of pipe */
 
1080
    close(plugin_fd);
1058
1081
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1059
1082
    if(new_plugin == NULL){
1060
1083
      error(0, errno, "getplugin");
1072
1095
    
1073
1096
    new_plugin->pid = pid;
1074
1097
    new_plugin->fd = pipefd[0];
1075
 
    
 
1098
 
 
1099
    if(debug){
 
1100
      fprintf(stderr, "Plugin %s started (PID %" PRIdMAX ")\n",
 
1101
              new_plugin->name, (intmax_t) (new_plugin->pid));
 
1102
    }
 
1103
 
1076
1104
    /* Unblock SIGCHLD so signal handler can be run if this process
1077
1105
       has already completed */
1078
1106
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1084
1112
      goto fallback;
1085
1113
    }
1086
1114
    
1087
 
#if defined (__GNUC__) and defined (__GLIBC__)
1088
 
#if not __GLIBC_PREREQ(2, 16)
1089
 
#pragma GCC diagnostic push
1090
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1091
 
#endif
1092
 
#endif
1093
 
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
1094
 
                                          -Wconversion in GNU libc
1095
 
                                          before 2.16 */
1096
 
#if defined (__GNUC__) and defined (__GLIBC__)
1097
 
#if not __GLIBC_PREREQ(2, 16)
1098
 
#pragma GCC diagnostic pop
1099
 
#endif
1100
 
#endif
 
1115
    FD_SET(new_plugin->fd, &rfds_all);
1101
1116
    
1102
1117
    if(maxfd < new_plugin->fd){
1103
1118
      maxfd = new_plugin->fd;
1106
1121
  
1107
1122
  free(direntries);
1108
1123
  direntries = NULL;
1109
 
  TEMP_FAILURE_RETRY(close(dir_fd));
 
1124
  close(dir_fd);
1110
1125
  dir_fd = -1;
1111
1126
  free_plugin(getplugin(NULL));
1112
1127
  
1152
1167
                      (intmax_t) (proc->pid),
1153
1168
                      WTERMSIG(proc->status),
1154
1169
                      strsignal(WTERMSIG(proc->status)));
1155
 
            } else if(WCOREDUMP(proc->status)){
1156
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
1157
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
1158
1170
            }
1159
1171
          }
1160
1172
          
1161
1173
          /* Remove the plugin */
1162
 
#if defined (__GNUC__) and defined (__GLIBC__)
1163
 
#if not __GLIBC_PREREQ(2, 16)
1164
 
#pragma GCC diagnostic push
1165
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1166
 
#endif
1167
 
#endif
1168
 
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1169
 
                                          -Wconversion in GNU libc
1170
 
                                          before 2.16 */
1171
 
#if defined (__GNUC__) and defined (__GLIBC__)
1172
 
#if not __GLIBC_PREREQ(2, 16)
1173
 
#pragma GCC diagnostic pop
1174
 
#endif
1175
 
#endif
 
1174
          FD_CLR(proc->fd, &rfds_all);
1176
1175
          
1177
1176
          /* Block signal while modifying process_list */
1178
1177
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1218
1217
      }
1219
1218
      
1220
1219
      /* This process has not completed.  Does it have any output? */
1221
 
#if defined (__GNUC__) and defined (__GLIBC__)
1222
 
#if not __GLIBC_PREREQ(2, 16)
1223
 
#pragma GCC diagnostic push
1224
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1225
 
#endif
1226
 
#endif
1227
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1228
 
                                                         warning from
1229
 
                                                         -Wconversion
1230
 
                                                         in GNU libc
1231
 
                                                         before
1232
 
                                                         2.16 */
1233
 
#if defined (__GNUC__) and defined (__GLIBC__)
1234
 
#if not __GLIBC_PREREQ(2, 16)
1235
 
#pragma GCC diagnostic pop
1236
 
#endif
1237
 
#endif
 
1220
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1238
1221
        /* This process had nothing to say at this time */
1239
1222
        proc = proc->next;
1240
1223
        continue;
1310
1293
  free(direntries);
1311
1294
  
1312
1295
  if(dir_fd != -1){
1313
 
    TEMP_FAILURE_RETRY(close(dir_fd));
 
1296
    close(dir_fd);
1314
1297
  }
1315
1298
  
1316
1299
  /* Kill the processes */
1336
1319
  free_plugin_list();
1337
1320
  
1338
1321
  free(plugindir);
 
1322
  free(pluginhelperdir);
1339
1323
  free(argfile);
1340
1324
  
1341
1325
  return exitstatus;