/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: 2021-02-01 19:30:45 UTC
  • Revision ID: teddy@recompile.se-20210201193045-lpg6aprpc4srem6k
Fix issue with french translation

Initial white space was missing in both msgid and msgstr of the french
translation, leading to checking tools reporing an incomplete
translation.  The string is a raw command line command, and therefore
did not need translation, so this was never a user-visible issue.

* debian/po/fr.po: Add missing whitespace to the id and translation
  for msgid " mandos-keygen -F/dev/null|grep ^key_id".

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-2020 Teddy Hogeborn
 
6
 * Copyright © 2008-2020 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
 */
25
26
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
26
27
                                   O_CLOEXEC, pipe2() */
27
28
#include <stddef.h>             /* size_t, NULL */
28
 
#include <stdlib.h>             /* malloc(), exit(), EXIT_SUCCESS,
29
 
                                   realloc() */
 
29
#include <stdlib.h>             /* malloc(), reallocarray(), realloc(),
 
30
                                   EXIT_SUCCESS, exit() */
30
31
#include <stdbool.h>            /* bool, true, false */
31
32
#include <stdio.h>              /* fileno(), fprintf(),
32
33
                                   stderr, STDOUT_FILENO, fclose() */
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() */
47
47
                                   struct stat, fstat(), close(),
48
48
                                   setgid(), setuid(), S_ISREG(),
49
49
                                   faccessat() pipe2(), fork(),
50
 
                                   _exit(), dup2(), fexecve(), read()
51
 
                                */
 
50
                                   _exit(), dup2(), fexecve(), read(),
 
51
                                   lstat(), symlink() */
52
52
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
53
53
                                   FD_CLOEXEC, openat(), scandirat(),
54
54
                                   pipe2() */
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;
178
179
  /* Resize the pointed-to array to hold one more pointer */
179
180
  char **new_array = NULL;
180
181
  do {
181
 
    new_array = realloc(*array, sizeof(char *)
182
 
                        * (size_t) ((*len) + 2));
 
182
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26)
 
183
    new_array = reallocarray(*array, (size_t)((*len) + 2),
 
184
                             sizeof(char *));
 
185
#else
 
186
    if(((size_t)((*len) + 2)) > (SIZE_MAX / sizeof(char *))){
 
187
      /* overflow */
 
188
      new_array = NULL;
 
189
      errno = ENOMEM;
 
190
    } else {
 
191
      new_array = realloc(*array, (size_t)((*len) + 2)
 
192
                          * sizeof(char *));
 
193
    }
 
194
#endif
183
195
  } while(new_array == NULL and errno == EINTR);
184
196
  /* Malloc check */
185
197
  if(new_array == NULL){
312
324
__attribute__((nonnull))
313
325
static void free_plugin(plugin *plugin_node){
314
326
  
315
 
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
 
327
  for(char **arg = (plugin_node->argv)+1; *arg != NULL; arg++){
316
328
    free(*arg);
317
329
  }
 
330
  free(plugin_node->name);
318
331
  free(plugin_node->argv);
319
332
  for(char **env = plugin_node->environ; *env != NULL; env++){
320
333
    free(*env);
347
360
 
348
361
int main(int argc, char *argv[]){
349
362
  char *plugindir = NULL;
 
363
  char *pluginhelperdir = NULL;
350
364
  char *argfile = NULL;
351
365
  FILE *conffp;
352
366
  struct dirent **direntries = NULL;
414
428
      .doc = "Group ID the plugins will run as", .group = 3 },
415
429
    { .name = "debug", .key = 132,
416
430
      .doc = "Debug mode", .group = 4 },
 
431
    { .name = "plugin-helper-dir", .key = 133,
 
432
      .arg = "DIRECTORY",
 
433
      .doc = "Specify a different plugin helper directory",
 
434
      .group = 2 },
417
435
    /*
418
436
     * These reproduce what we would get without ARGP_NO_HELP
419
437
     */
545
563
    case 132:                   /* --debug */
546
564
      debug = true;
547
565
      break;
 
566
    case 133:                   /* --plugin-helper-dir */
 
567
      free(pluginhelperdir);
 
568
      pluginhelperdir = strdup(arg);
 
569
      if(pluginhelperdir != NULL){
 
570
        errno = 0;
 
571
      }
 
572
      break;
548
573
      /*
549
574
       * These reproduce what we would get without ARGP_NO_HELP
550
575
       */
551
576
    case '?':                   /* --help */
552
577
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
553
578
      argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
 
579
      __builtin_unreachable();
554
580
    case -3:                    /* --usage */
555
581
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
556
582
      argp_state_help(state, state->out_stream,
557
583
                      ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
 
584
      __builtin_unreachable();
558
585
    case 'V':                   /* --version */
559
586
      fprintf(state->out_stream, "%s\n", argp_program_version);
560
587
      exit(EXIT_SUCCESS);
570
597
      if(arg[0] == '\0'){
571
598
        break;
572
599
      }
 
600
#if __GNUC__ >= 7
 
601
      __attribute__((fallthrough));
 
602
#else
 
603
          /* FALLTHROUGH */
 
604
#endif
573
605
    default:
574
606
      return ARGP_ERR_UNKNOWN;
575
607
    }
601
633
    case 130:                   /* --userid */
602
634
    case 131:                   /* --groupid */
603
635
    case 132:                   /* --debug */
 
636
    case 133:                   /* --plugin-helper-dir */
604
637
    case '?':                   /* --help */
605
638
    case -3:                    /* --usage */
606
639
    case 'V':                   /* --version */
686
719
        
687
720
        custom_argc += 1;
688
721
        {
689
 
          char **new_argv = realloc(custom_argv, sizeof(char *)
690
 
                                    * ((unsigned int)
691
 
                                       custom_argc + 1));
 
722
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26)
 
723
          char **new_argv = reallocarray(custom_argv, (size_t)custom_argc + 1,
 
724
                                         sizeof(char *));
 
725
#else
 
726
          char **new_argv = NULL;
 
727
          if(((size_t)custom_argc + 1) > (SIZE_MAX / sizeof(char *))){
 
728
            /* overflow */
 
729
            errno = ENOMEM;
 
730
          } else {
 
731
            new_argv = realloc(custom_argv, ((size_t)custom_argc + 1)
 
732
                               * sizeof(char *));
 
733
          }
 
734
#endif
692
735
          if(new_argv == NULL){
693
 
            error(0, errno, "realloc");
 
736
            error(0, errno, "reallocarray");
694
737
            exitstatus = EX_OSERR;
695
738
            free(new_arg);
696
739
            free(org_line);
761
804
    goto fallback;
762
805
  }
763
806
  
 
807
  {
 
808
    char *pluginhelperenv;
 
809
    bool bret = true;
 
810
    ret = asprintf(&pluginhelperenv, "MANDOSPLUGINHELPERDIR=%s",
 
811
                   pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
812
    if(ret != -1){
 
813
      bret = add_environment(getplugin(NULL), pluginhelperenv, true);
 
814
    }
 
815
    if(ret == -1 or not bret){
 
816
      error(0, errno, "Failed to set MANDOSPLUGINHELPERDIR"
 
817
            " environment variable to \"%s\" for all plugins\n",
 
818
            pluginhelperdir != NULL ? pluginhelperdir : PHDIR);
 
819
    }
 
820
    if(ret != -1){
 
821
      free(pluginhelperenv);
 
822
    }
 
823
  }
 
824
  
764
825
  if(debug){
765
 
    for(plugin *p = plugin_list; p != NULL; p=p->next){
 
826
    for(plugin *p = plugin_list; p != NULL; p = p->next){
766
827
      fprintf(stderr, "Plugin: %s has %d arguments\n",
767
828
              p->name ? p->name : "Global", p->argc - 1);
768
829
      for(char **a = p->argv; *a != NULL; a++){
777
838
  
778
839
  if(getuid() == 0){
779
840
    /* Work around Debian bug #633582:
780
 
       <http://bugs.debian.org/633582> */
 
841
       <https://bugs.debian.org/633582> */
781
842
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
782
843
    if(plugindir_fd == -1){
783
844
      if(errno != ENOENT){
795
856
          }
796
857
        }
797
858
      }
798
 
      TEMP_FAILURE_RETRY(close(plugindir_fd));
 
859
      close(plugindir_fd);
 
860
    }
 
861
 
 
862
    /* Work around Debian bug #981302
 
863
       <https://bugs.debian.org/981302> */
 
864
    if(lstat("/dev/fd", &st) != 0 and errno == ENOENT){
 
865
      ret = symlink("/proc/self/fd", "/dev/fd");
 
866
      if(ret == -1){
 
867
        error(0, errno, "Failed to create /dev/fd symlink");
 
868
      }
799
869
    }
800
870
  }
801
871
  
860
930
    return 1;
861
931
  }
862
932
  
863
 
#ifdef __GLIBC__
864
 
#if __GLIBC_PREREQ(2, 15)
865
933
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
866
934
                             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
935
  if(numplugins == -1){
876
936
    error(0, errno, "Could not scan plugin dir");
877
937
    direntries = NULL;
887
947
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
888
948
    if(plugin_fd == -1){
889
949
      error(0, errno, "Could not open plugin");
 
950
      free(direntries[i]);
890
951
      continue;
891
952
    }
892
953
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
893
954
    if(ret == -1){
894
955
      error(0, errno, "stat");
895
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
956
      close(plugin_fd);
 
957
      free(direntries[i]);
896
958
      continue;
897
959
    }
898
960
    
906
968
                plugindir != NULL ? plugindir : PDIR,
907
969
                direntries[i]->d_name);
908
970
      }
909
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
971
      close(plugin_fd);
 
972
      free(direntries[i]);
910
973
      continue;
911
974
    }
912
975
    
913
976
    plugin *p = getplugin(direntries[i]->d_name);
914
977
    if(p == NULL){
915
978
      error(0, errno, "getplugin");
916
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
979
      close(plugin_fd);
 
980
      free(direntries[i]);
917
981
      continue;
918
982
    }
919
983
    if(p->disabled){
921
985
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
922
986
                direntries[i]->d_name);
923
987
      }
924
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
988
      close(plugin_fd);
 
989
      free(direntries[i]);
925
990
      continue;
926
991
    }
927
992
    {
960
1025
    if(ret == -1){
961
1026
      error(0, errno, "pipe");
962
1027
      exitstatus = EX_OSERR;
 
1028
      free(direntries[i]);
963
1029
      goto fallback;
964
1030
    }
965
1031
    if(pipefd[0] >= FD_SETSIZE){
966
1032
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
967
1033
              FD_SETSIZE);
968
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
969
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1034
      close(pipefd[0]);
 
1035
      close(pipefd[1]);
970
1036
      exitstatus = EX_OSERR;
 
1037
      free(direntries[i]);
971
1038
      goto fallback;
972
1039
    }
973
1040
#ifndef O_CLOEXEC
975
1042
    ret = set_cloexec_flag(pipefd[0]);
976
1043
    if(ret < 0){
977
1044
      error(0, errno, "set_cloexec_flag");
978
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
979
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1045
      close(pipefd[0]);
 
1046
      close(pipefd[1]);
980
1047
      exitstatus = EX_OSERR;
 
1048
      free(direntries[i]);
981
1049
      goto fallback;
982
1050
    }
983
1051
    ret = set_cloexec_flag(pipefd[1]);
984
1052
    if(ret < 0){
985
1053
      error(0, errno, "set_cloexec_flag");
986
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
987
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1054
      close(pipefd[0]);
 
1055
      close(pipefd[1]);
988
1056
      exitstatus = EX_OSERR;
 
1057
      free(direntries[i]);
989
1058
      goto fallback;
990
1059
    }
991
1060
#endif  /* not O_CLOEXEC */
996
1065
    if(ret < 0){
997
1066
      error(0, errno, "sigprocmask");
998
1067
      exitstatus = EX_OSERR;
 
1068
      free(direntries[i]);
999
1069
      goto fallback;
1000
1070
    }
1001
1071
    /* Starting a new process to be watched */
1007
1077
      error(0, errno, "fork");
1008
1078
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1009
1079
                                     &sigchld_action.sa_mask, NULL));
1010
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
1011
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1080
      close(pipefd[0]);
 
1081
      close(pipefd[1]);
1012
1082
      exitstatus = EX_OSERR;
 
1083
      free(direntries[i]);
1013
1084
      goto fallback;
1014
1085
    }
1015
1086
    if(pid == 0){
1041
1112
      /* no return */
1042
1113
    }
1043
1114
    /* Parent process */
1044
 
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1045
 
                                             pipe */
1046
 
    TEMP_FAILURE_RETRY(close(plugin_fd));
 
1115
    close(pipefd[1]);           /* Close unused write end of pipe */
 
1116
    close(plugin_fd);
1047
1117
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1048
1118
    if(new_plugin == NULL){
1049
1119
      error(0, errno, "getplugin");
1054
1124
        error(0, errno, "sigprocmask");
1055
1125
      }
1056
1126
      exitstatus = EX_OSERR;
 
1127
      free(direntries[i]);
1057
1128
      goto fallback;
1058
1129
    }
 
1130
    free(direntries[i]);
1059
1131
    
1060
1132
    new_plugin->pid = pid;
1061
1133
    new_plugin->fd = pipefd[0];
1062
 
    
 
1134
 
 
1135
    if(debug){
 
1136
      fprintf(stderr, "Plugin %s started (PID %" PRIdMAX ")\n",
 
1137
              new_plugin->name, (intmax_t) (new_plugin->pid));
 
1138
    }
 
1139
 
1063
1140
    /* Unblock SIGCHLD so signal handler can be run if this process
1064
1141
       has already completed */
1065
1142
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1071
1148
      goto fallback;
1072
1149
    }
1073
1150
    
1074
 
#if defined (__GNUC__) and defined (__GLIBC__)
1075
 
#if not __GLIBC_PREREQ(2, 16)
1076
 
#pragma GCC diagnostic push
1077
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1078
 
#endif
1079
 
#endif
1080
 
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
1081
 
                                          -Wconversion in GNU libc
1082
 
                                          before 2.16 */
1083
 
#if defined (__GNUC__) and defined (__GLIBC__)
1084
 
#if not __GLIBC_PREREQ(2, 16)
1085
 
#pragma GCC diagnostic pop
1086
 
#endif
1087
 
#endif
 
1151
    FD_SET(new_plugin->fd, &rfds_all);
1088
1152
    
1089
1153
    if(maxfd < new_plugin->fd){
1090
1154
      maxfd = new_plugin->fd;
1093
1157
  
1094
1158
  free(direntries);
1095
1159
  direntries = NULL;
1096
 
  TEMP_FAILURE_RETRY(close(dir_fd));
 
1160
  close(dir_fd);
1097
1161
  dir_fd = -1;
1098
1162
  free_plugin(getplugin(NULL));
1099
1163
  
1139
1203
                      (intmax_t) (proc->pid),
1140
1204
                      WTERMSIG(proc->status),
1141
1205
                      strsignal(WTERMSIG(proc->status)));
1142
 
            } else if(WCOREDUMP(proc->status)){
1143
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
1144
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
1145
1206
            }
1146
1207
          }
1147
1208
          
1148
1209
          /* Remove the plugin */
1149
 
#if defined (__GNUC__) and defined (__GLIBC__)
1150
 
#if not __GLIBC_PREREQ(2, 16)
1151
 
#pragma GCC diagnostic push
1152
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1153
 
#endif
1154
 
#endif
1155
 
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1156
 
                                          -Wconversion in GNU libc
1157
 
                                          before 2.16 */
1158
 
#if defined (__GNUC__) and defined (__GLIBC__)
1159
 
#if not __GLIBC_PREREQ(2, 16)
1160
 
#pragma GCC diagnostic pop
1161
 
#endif
1162
 
#endif
 
1210
          FD_CLR(proc->fd, &rfds_all);
1163
1211
          
1164
1212
          /* Block signal while modifying process_list */
1165
1213
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1205
1253
      }
1206
1254
      
1207
1255
      /* This process has not completed.  Does it have any output? */
1208
 
#if defined (__GNUC__) and defined (__GLIBC__)
1209
 
#if not __GLIBC_PREREQ(2, 16)
1210
 
#pragma GCC diagnostic push
1211
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1212
 
#endif
1213
 
#endif
1214
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1215
 
                                                         warning from
1216
 
                                                         -Wconversion
1217
 
                                                         in GNU libc
1218
 
                                                         before
1219
 
                                                         2.16 */
1220
 
#if defined (__GNUC__) and defined (__GLIBC__)
1221
 
#if not __GLIBC_PREREQ(2, 16)
1222
 
#pragma GCC diagnostic pop
1223
 
#endif
1224
 
#endif
 
1256
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1225
1257
        /* This process had nothing to say at this time */
1226
1258
        proc = proc->next;
1227
1259
        continue;
1297
1329
  free(direntries);
1298
1330
  
1299
1331
  if(dir_fd != -1){
1300
 
    TEMP_FAILURE_RETRY(close(dir_fd));
 
1332
    close(dir_fd);
1301
1333
  }
1302
1334
  
1303
1335
  /* Kill the processes */
1323
1355
  free_plugin_list();
1324
1356
  
1325
1357
  free(plugindir);
 
1358
  free(pluginhelperdir);
1326
1359
  free(argfile);
1327
1360
  
1328
1361
  return exitstatus;