/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;
893
953
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
894
954
    if(ret == -1){
895
955
      error(0, errno, "stat");
896
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
956
      close(plugin_fd);
897
957
      free(direntries[i]);
898
958
      continue;
899
959
    }
908
968
                plugindir != NULL ? plugindir : PDIR,
909
969
                direntries[i]->d_name);
910
970
      }
911
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
971
      close(plugin_fd);
912
972
      free(direntries[i]);
913
973
      continue;
914
974
    }
916
976
    plugin *p = getplugin(direntries[i]->d_name);
917
977
    if(p == NULL){
918
978
      error(0, errno, "getplugin");
919
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
979
      close(plugin_fd);
920
980
      free(direntries[i]);
921
981
      continue;
922
982
    }
925
985
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
926
986
                direntries[i]->d_name);
927
987
      }
928
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
988
      close(plugin_fd);
929
989
      free(direntries[i]);
930
990
      continue;
931
991
    }
971
1031
    if(pipefd[0] >= FD_SETSIZE){
972
1032
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
973
1033
              FD_SETSIZE);
974
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
975
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1034
      close(pipefd[0]);
 
1035
      close(pipefd[1]);
976
1036
      exitstatus = EX_OSERR;
977
1037
      free(direntries[i]);
978
1038
      goto fallback;
982
1042
    ret = set_cloexec_flag(pipefd[0]);
983
1043
    if(ret < 0){
984
1044
      error(0, errno, "set_cloexec_flag");
985
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
986
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1045
      close(pipefd[0]);
 
1046
      close(pipefd[1]);
987
1047
      exitstatus = EX_OSERR;
988
1048
      free(direntries[i]);
989
1049
      goto fallback;
991
1051
    ret = set_cloexec_flag(pipefd[1]);
992
1052
    if(ret < 0){
993
1053
      error(0, errno, "set_cloexec_flag");
994
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
995
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1054
      close(pipefd[0]);
 
1055
      close(pipefd[1]);
996
1056
      exitstatus = EX_OSERR;
997
1057
      free(direntries[i]);
998
1058
      goto fallback;
1017
1077
      error(0, errno, "fork");
1018
1078
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1019
1079
                                     &sigchld_action.sa_mask, NULL));
1020
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
1021
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1080
      close(pipefd[0]);
 
1081
      close(pipefd[1]);
1022
1082
      exitstatus = EX_OSERR;
1023
1083
      free(direntries[i]);
1024
1084
      goto fallback;
1052
1112
      /* no return */
1053
1113
    }
1054
1114
    /* Parent process */
1055
 
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1056
 
                                             pipe */
1057
 
    TEMP_FAILURE_RETRY(close(plugin_fd));
 
1115
    close(pipefd[1]);           /* Close unused write end of pipe */
 
1116
    close(plugin_fd);
1058
1117
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1059
1118
    if(new_plugin == NULL){
1060
1119
      error(0, errno, "getplugin");
1072
1131
    
1073
1132
    new_plugin->pid = pid;
1074
1133
    new_plugin->fd = pipefd[0];
1075
 
    
 
1134
 
 
1135
    if(debug){
 
1136
      fprintf(stderr, "Plugin %s started (PID %" PRIdMAX ")\n",
 
1137
              new_plugin->name, (intmax_t) (new_plugin->pid));
 
1138
    }
 
1139
 
1076
1140
    /* Unblock SIGCHLD so signal handler can be run if this process
1077
1141
       has already completed */
1078
1142
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1084
1148
      goto fallback;
1085
1149
    }
1086
1150
    
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
 
1151
    FD_SET(new_plugin->fd, &rfds_all);
1101
1152
    
1102
1153
    if(maxfd < new_plugin->fd){
1103
1154
      maxfd = new_plugin->fd;
1106
1157
  
1107
1158
  free(direntries);
1108
1159
  direntries = NULL;
1109
 
  TEMP_FAILURE_RETRY(close(dir_fd));
 
1160
  close(dir_fd);
1110
1161
  dir_fd = -1;
1111
1162
  free_plugin(getplugin(NULL));
1112
1163
  
1152
1203
                      (intmax_t) (proc->pid),
1153
1204
                      WTERMSIG(proc->status),
1154
1205
                      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
1206
            }
1159
1207
          }
1160
1208
          
1161
1209
          /* 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
 
1210
          FD_CLR(proc->fd, &rfds_all);
1176
1211
          
1177
1212
          /* Block signal while modifying process_list */
1178
1213
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1218
1253
      }
1219
1254
      
1220
1255
      /* 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
 
1256
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1238
1257
        /* This process had nothing to say at this time */
1239
1258
        proc = proc->next;
1240
1259
        continue;
1310
1329
  free(direntries);
1311
1330
  
1312
1331
  if(dir_fd != -1){
1313
 
    TEMP_FAILURE_RETRY(close(dir_fd));
 
1332
    close(dir_fd);
1314
1333
  }
1315
1334
  
1316
1335
  /* Kill the processes */
1336
1355
  free_plugin_list();
1337
1356
  
1338
1357
  free(plugindir);
 
1358
  free(pluginhelperdir);
1339
1359
  free(argfile);
1340
1360
  
1341
1361
  return exitstatus;