/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-03 23:10:42 UTC
  • Revision ID: teddy@recompile.se-20210203231042-2z3egrvpo1zt7nej
mandos-ctl: Fix bad test for command.Remove and related minor issues

The test for command.Remove removes all clients from the spy server,
and then loops over all clients, looking for the corresponding Remove
command as recorded by the spy server.  But since since there aren't
any clients left after they were removed, no assertions are made, and
the test therefore does nothing.  Fix this.

In tests for command.Approve and command.Deny, add checks that clients
were not somehow removed by the command (in which case, likewise, no
assertions are made).

Add related checks to TestPropertySetterCmd.runTest; i.e. test that a
sequence is not empty before looping over it and making assertions.

* mandos-ctl (TestBaseCommands.test_Remove): Save a copy of the
  original "clients" dict, and loop over those instead.  Add assertion
  that all clients were indeed removed.  Also fix the code which looks
  for the Remove command, which now needs to actually work.
  (TestBaseCommands.test_Approve, TestBaseCommands.test_Deny): Add
  assertion that there are still clients before looping over them.
  (TestPropertySetterCmd.runTest): Add assertion that the list of
  values to get is not empty before looping over them.  Also add check
  that there are still clients before looping over clients.

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-2015 Teddy Hogeborn
6
 
 * Copyright © 2008-2015 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-2021 Teddy Hogeborn
 
6
 * Copyright © 2008-2021 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
 */
24
25
 
25
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
26
 
                                   O_CLOEXEC, pipe2() */
 
26
#define _GNU_SOURCE             /* strchrnul(), TEMP_FAILURE_RETRY(),
 
27
                                   getline(), asprintf(), O_CLOEXEC,
 
28
                                   scandirat(), pipe2() */
 
29
#include <argp.h>               /* argp_program_version,
 
30
                                   argp_program_bug_address,
 
31
                                   struct argp_option,
 
32
                                   struct argp_state, argp_error(),
 
33
                                   ARGP_NO_EXIT, argp_state_help,
 
34
                                   ARGP_HELP_STD_HELP,
 
35
                                   ARGP_HELP_USAGE, ARGP_HELP_EXIT_OK,
 
36
                                   ARGP_KEY_ARG, ARGP_ERR_UNKNOWN,
 
37
                                   struct argp, argp_parse(),
 
38
                                   ARGP_IN_ORDER, ARGP_NO_HELP */
 
39
#include <stdbool.h>            /* bool, false, true */
 
40
#include <sys/types.h>          /* pid_t, sig_atomic_t, uid_t, gid_t,
 
41
                                   getuid(), setgid(), setuid() */
27
42
#include <stddef.h>             /* size_t, NULL */
28
 
#include <stdlib.h>             /* malloc(), exit(), EXIT_SUCCESS,
29
 
                                   realloc() */
30
 
#include <stdbool.h>            /* bool, true, false */
31
 
#include <stdio.h>              /* fileno(), fprintf(),
32
 
                                   stderr, STDOUT_FILENO, fclose() */
33
 
#include <sys/types.h>          /* fstat(), struct stat, waitpid(),
34
 
                                   WIFEXITED(), WEXITSTATUS(), wait(),
35
 
                                   pid_t, uid_t, gid_t, getuid(),
36
 
                                   getgid() */
37
 
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
38
 
                                   FD_SET(), FD_ISSET(), FD_CLR */
39
 
#include <sys/wait.h>           /* wait(), waitpid(), WIFEXITED(),
40
 
                                   WEXITSTATUS(), WTERMSIG(),
41
 
                                   WCOREDUMP() */
42
 
#include <sys/stat.h>           /* struct stat, fstat(), S_ISREG() */
43
 
#include <iso646.h>             /* and, or, not */
44
 
#include <dirent.h>             /* struct dirent, scandirat() */
45
 
#include <unistd.h>             /* fcntl(), F_GETFD, F_SETFD,
46
 
                                   FD_CLOEXEC, write(), STDOUT_FILENO,
47
 
                                   struct stat, fstat(), close(),
48
 
                                   setgid(), setuid(), S_ISREG(),
49
 
                                   faccessat() pipe2(), fork(),
50
 
                                   _exit(), dup2(), fexecve(), read()
51
 
                                */
 
43
#include <iso646.h>             /* or, and, not */
 
44
#include <string.h>             /* strcmp(), strdup(), strchrnul(),
 
45
                                   strncmp(), strlen(), strcpy(),
 
46
                                   strsep(), strchr(), strsignal() */
 
47
#include <stdlib.h>             /* malloc(), free(), reallocarray(),
 
48
                                   realloc(), EXIT_SUCCESS */
 
49
#include <errno.h>              /* errno, EINTR, ENOMEM, ECHILD,
 
50
                                   error_t, EINVAL, EMFILE, ENFILE,
 
51
                                   ENOENT, ESRCH */
 
52
#include <stdint.h>             /* SIZE_MAX */
 
53
#define _GNU_SOURCE             /* strchrnul(), TEMP_FAILURE_RETRY(),
 
54
                                   getline(), asprintf(), O_CLOEXEC,
 
55
                                   scandirat(), pipe2() */
 
56
#include <unistd.h>             /* TEMP_FAILURE_RETRY(), ssize_t,
 
57
                                   write(), STDOUT_FILENO, uid_t,
 
58
                                   gid_t, getuid(), fchown(), close(),
 
59
                                   symlink(), setgid(), setuid(),
 
60
                                   faccessat(), X_OK, pipe(), pipe2(),
 
61
                                   fork(), _exit(), dup2(), fexecve(),
 
62
                                   read(), getpass() */
52
63
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
53
 
                                   FD_CLOEXEC, openat(), scandirat(),
54
 
                                   pipe2() */
55
 
#include <string.h>             /* strsep, strlen(), strsignal(),
56
 
                                   strcmp(), strncmp() */
57
 
#include <errno.h>              /* errno */
58
 
#include <argp.h>               /* struct argp_option, struct
59
 
                                   argp_state, struct argp,
60
 
                                   argp_parse(), ARGP_ERR_UNKNOWN,
61
 
                                   ARGP_KEY_END, ARGP_KEY_ARG,
62
 
                                   error_t */
63
 
#include <signal.h>             /* struct sigaction, sigemptyset(),
64
 
                                   sigaddset(), sigaction(),
65
 
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
66
 
                                   SIG_UNBLOCK, kill(), sig_atomic_t
67
 
                                */
68
 
#include <errno.h>              /* errno, EBADF */
69
 
#include <inttypes.h>           /* intmax_t, PRIdMAX, strtoimax() */
 
64
                                   FD_CLOEXEC, open(), O_RDONLY,
 
65
                                   O_CLOEXEC, openat() */
 
66
#include <sys/wait.h>           /* waitpid(), WNOHANG, WIFEXITED(),
 
67
                                   WEXITSTATUS(), WIFSIGNALED(),
 
68
                                   WTERMSIG(), wait() */
 
69
#include <error.h>              /* error() */
 
70
#include <stdio.h>              /* FILE, fprintf(), fopen(),
 
71
                                   getline(), fclose(), EOF,
 
72
                                   asprintf(), stderr */
 
73
#include <dirent.h>             /* struct dirent, scandirat(),
 
74
                                   alphasort() */
 
75
#include <sys/stat.h>           /* struct stat, fstat(), S_ISDIR(),
 
76
                                   lstat(), S_ISREG() */
 
77
#include <sys/select.h>         /* fd_set, FD_ZERO(), FD_SETSIZE,
 
78
                                   FD_SET(), select(), FD_CLR(),
 
79
                                   FD_ISSET() */
 
80
#include <signal.h>             /* struct sigaction, SA_NOCLDSTOP,
 
81
                                   sigemptyset(), sigaddset(),
 
82
                                   SIGCHLD, sigprocmask(), SIG_BLOCK,
 
83
                                   SIG_UNBLOCK, kill(), SIGTERM */
70
84
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_IOERR,
71
85
                                   EX_CONFIG, EX_UNAVAILABLE, EX_OK */
72
 
#include <errno.h>              /* errno */
73
 
#include <error.h>              /* error() */
74
 
#include <fnmatch.h>            /* fnmatch() */
 
86
#include <inttypes.h>           /* intmax_t, strtoimax(), PRIdMAX */
 
87
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME,
 
88
                                   FNM_PERIOD, FNM_NOMATCH */
75
89
 
76
90
#define BUFFER_SIZE 256
77
91
 
179
193
  /* Resize the pointed-to array to hold one more pointer */
180
194
  char **new_array = NULL;
181
195
  do {
182
 
    new_array = realloc(*array, sizeof(char *)
183
 
                        * (size_t) ((*len) + 2));
 
196
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26)
 
197
    new_array = reallocarray(*array, (size_t)((*len) + 2),
 
198
                             sizeof(char *));
 
199
#else
 
200
    if(((size_t)((*len) + 2)) > (SIZE_MAX / sizeof(char *))){
 
201
      /* overflow */
 
202
      new_array = NULL;
 
203
      errno = ENOMEM;
 
204
    } else {
 
205
      new_array = realloc(*array, (size_t)((*len) + 2)
 
206
                          * sizeof(char *));
 
207
    }
 
208
#endif
184
209
  } while(new_array == NULL and errno == EINTR);
185
210
  /* Malloc check */
186
211
  if(new_array == NULL){
313
338
__attribute__((nonnull))
314
339
static void free_plugin(plugin *plugin_node){
315
340
  
316
 
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
 
341
  for(char **arg = (plugin_node->argv)+1; *arg != NULL; arg++){
317
342
    free(*arg);
318
343
  }
 
344
  free(plugin_node->name);
319
345
  free(plugin_node->argv);
320
346
  for(char **env = plugin_node->environ; *env != NULL; env++){
321
347
    free(*env);
564
590
    case '?':                   /* --help */
565
591
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
566
592
      argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
 
593
      __builtin_unreachable();
567
594
    case -3:                    /* --usage */
568
595
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
569
596
      argp_state_help(state, state->out_stream,
570
597
                      ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
 
598
      __builtin_unreachable();
571
599
    case 'V':                   /* --version */
572
600
      fprintf(state->out_stream, "%s\n", argp_program_version);
573
601
      exit(EXIT_SUCCESS);
583
611
      if(arg[0] == '\0'){
584
612
        break;
585
613
      }
 
614
#if __GNUC__ >= 7
 
615
      __attribute__((fallthrough));
 
616
#else
 
617
          /* FALLTHROUGH */
 
618
#endif
586
619
    default:
587
620
      return ARGP_ERR_UNKNOWN;
588
621
    }
700
733
        
701
734
        custom_argc += 1;
702
735
        {
703
 
          char **new_argv = realloc(custom_argv, sizeof(char *)
704
 
                                    * ((unsigned int)
705
 
                                       custom_argc + 1));
 
736
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26)
 
737
          char **new_argv = reallocarray(custom_argv, (size_t)custom_argc + 1,
 
738
                                         sizeof(char *));
 
739
#else
 
740
          char **new_argv = NULL;
 
741
          if(((size_t)custom_argc + 1) > (SIZE_MAX / sizeof(char *))){
 
742
            /* overflow */
 
743
            errno = ENOMEM;
 
744
          } else {
 
745
            new_argv = realloc(custom_argv, ((size_t)custom_argc + 1)
 
746
                               * sizeof(char *));
 
747
          }
 
748
#endif
706
749
          if(new_argv == NULL){
707
 
            error(0, errno, "realloc");
 
750
            error(0, errno, "reallocarray");
708
751
            exitstatus = EX_OSERR;
709
752
            free(new_arg);
710
753
            free(org_line);
794
837
  }
795
838
  
796
839
  if(debug){
797
 
    for(plugin *p = plugin_list; p != NULL; p=p->next){
 
840
    for(plugin *p = plugin_list; p != NULL; p = p->next){
798
841
      fprintf(stderr, "Plugin: %s has %d arguments\n",
799
842
              p->name ? p->name : "Global", p->argc - 1);
800
843
      for(char **a = p->argv; *a != NULL; a++){
809
852
  
810
853
  if(getuid() == 0){
811
854
    /* Work around Debian bug #633582:
812
 
       <http://bugs.debian.org/633582> */
 
855
       <https://bugs.debian.org/633582> */
813
856
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
814
857
    if(plugindir_fd == -1){
815
858
      if(errno != ENOENT){
829
872
      }
830
873
      close(plugindir_fd);
831
874
    }
 
875
 
 
876
    /* Work around Debian bug #981302
 
877
       <https://bugs.debian.org/981302> */
 
878
    if(lstat("/dev/fd", &st) != 0 and errno == ENOENT){
 
879
      ret = symlink("/proc/self/fd", "/dev/fd");
 
880
      if(ret == -1){
 
881
        error(0, errno, "Failed to create /dev/fd symlink");
 
882
      }
 
883
    }
832
884
  }
833
885
  
834
886
  /* Lower permissions */
892
944
    return 1;
893
945
  }
894
946
  
895
 
#ifdef __GLIBC__
896
 
#if __GLIBC_PREREQ(2, 15)
897
947
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
898
948
                             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
949
  if(numplugins == -1){
908
950
    error(0, errno, "Could not scan plugin dir");
909
951
    direntries = NULL;
1103
1145
    
1104
1146
    new_plugin->pid = pid;
1105
1147
    new_plugin->fd = pipefd[0];
1106
 
    
 
1148
 
 
1149
    if(debug){
 
1150
      fprintf(stderr, "Plugin %s started (PID %" PRIdMAX ")\n",
 
1151
              new_plugin->name, (intmax_t) (new_plugin->pid));
 
1152
    }
 
1153
 
1107
1154
    /* Unblock SIGCHLD so signal handler can be run if this process
1108
1155
       has already completed */
1109
1156
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1115
1162
      goto fallback;
1116
1163
    }
1117
1164
    
1118
 
#if defined (__GNUC__) and defined (__GLIBC__)
1119
 
#if not __GLIBC_PREREQ(2, 16)
1120
 
#pragma GCC diagnostic push
1121
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1122
 
#endif
1123
 
#endif
1124
 
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
1125
 
                                          -Wconversion in GNU libc
1126
 
                                          before 2.16 */
1127
 
#if defined (__GNUC__) and defined (__GLIBC__)
1128
 
#if not __GLIBC_PREREQ(2, 16)
1129
 
#pragma GCC diagnostic pop
1130
 
#endif
1131
 
#endif
 
1165
    FD_SET(new_plugin->fd, &rfds_all);
1132
1166
    
1133
1167
    if(maxfd < new_plugin->fd){
1134
1168
      maxfd = new_plugin->fd;
1183
1217
                      (intmax_t) (proc->pid),
1184
1218
                      WTERMSIG(proc->status),
1185
1219
                      strsignal(WTERMSIG(proc->status)));
1186
 
            } else if(WCOREDUMP(proc->status)){
1187
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
1188
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
1189
1220
            }
1190
1221
          }
1191
1222
          
1192
1223
          /* Remove the plugin */
1193
 
#if defined (__GNUC__) and defined (__GLIBC__)
1194
 
#if not __GLIBC_PREREQ(2, 16)
1195
 
#pragma GCC diagnostic push
1196
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1197
 
#endif
1198
 
#endif
1199
 
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1200
 
                                          -Wconversion in GNU libc
1201
 
                                          before 2.16 */
1202
 
#if defined (__GNUC__) and defined (__GLIBC__)
1203
 
#if not __GLIBC_PREREQ(2, 16)
1204
 
#pragma GCC diagnostic pop
1205
 
#endif
1206
 
#endif
 
1224
          FD_CLR(proc->fd, &rfds_all);
1207
1225
          
1208
1226
          /* Block signal while modifying process_list */
1209
1227
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1249
1267
      }
1250
1268
      
1251
1269
      /* This process has not completed.  Does it have any output? */
1252
 
#if defined (__GNUC__) and defined (__GLIBC__)
1253
 
#if not __GLIBC_PREREQ(2, 16)
1254
 
#pragma GCC diagnostic push
1255
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1256
 
#endif
1257
 
#endif
1258
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1259
 
                                                         warning from
1260
 
                                                         -Wconversion
1261
 
                                                         in GNU libc
1262
 
                                                         before
1263
 
                                                         2.16 */
1264
 
#if defined (__GNUC__) and defined (__GLIBC__)
1265
 
#if not __GLIBC_PREREQ(2, 16)
1266
 
#pragma GCC diagnostic pop
1267
 
#endif
1268
 
#endif
 
1270
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1269
1271
        /* This process had nothing to say at this time */
1270
1272
        proc = proc->next;
1271
1273
        continue;