/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-08-02 22:16:53 UTC
  • Revision ID: teddy@recompile.se-20190802221653-ic1iko9hbefzwsk7
Fix bug in server Debian package: Fails to start on first install

There has been a very long-standing bug where installation of the
server (the "mandos" Debian package) would fail to start the server
properly right after installation.  It would work on manual (re)start
after installation, or after reboot, and even after package purge and
reinstall, it would then work the first time.  The problem, it turns
out, is when the new "_mandos" user (and corresponding group) is
created, the D-Bus server is not reloaded, and is therefore not aware
of that user, and does not recognize the user and group name in the
/etc/dbus-1/system.d/mandos.conf file.  The Mandos server, when it
tries to start and access the D-Bus, is then not permitted to connect
to its D-Bus bus name, and disables D-Bus use as a fallback measure;
i.e. the server works, but it is not controllable via D-Bus commands
(via mandos-ctl or mandos-monitor).  The next time the D-Bus daemon is
reloaded for any reason, the new user & group would become visible to
the D-Bus daemon and after that, any restart of the Mandos server
would succeed and it would bind to its D-Bus name properly, and
thereby be visible and controllable by mandos-ctl & mandos-monitor.
This was mostly invisible when using sysvinit, but systemd makes the
problem visible since the systemd service file for the Mandos server
is configured to not consider the Mandos server "started" until the
D-Bus name has been bound; this makes the starting of the service wait
for 90 seconds and then fail with a timeout error.

Fixing this should also make the Debian CI autopkgtest tests work.

* debian/mandos.postinst (configure): After creating (or renaming)
                                      user & group, reload D-Bus
                                      daemon (if present).

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() */
313
313
__attribute__((nonnull))
314
314
static void free_plugin(plugin *plugin_node){
315
315
  
316
 
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
 
316
  for(char **arg = (plugin_node->argv)+1; *arg != NULL; arg++){
317
317
    free(*arg);
318
318
  }
 
319
  free(plugin_node->name);
319
320
  free(plugin_node->argv);
320
321
  for(char **env = plugin_node->environ; *env != NULL; env++){
321
322
    free(*env);
564
565
    case '?':                   /* --help */
565
566
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
566
567
      argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
 
568
      __builtin_unreachable();
567
569
    case -3:                    /* --usage */
568
570
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
569
571
      argp_state_help(state, state->out_stream,
570
572
                      ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
 
573
      __builtin_unreachable();
571
574
    case 'V':                   /* --version */
572
575
      fprintf(state->out_stream, "%s\n", argp_program_version);
573
576
      exit(EXIT_SUCCESS);
583
586
      if(arg[0] == '\0'){
584
587
        break;
585
588
      }
 
589
#if __GNUC__ >= 7
 
590
      __attribute__((fallthrough));
 
591
#else
 
592
          /* FALLTHROUGH */
 
593
#endif
586
594
    default:
587
595
      return ARGP_ERR_UNKNOWN;
588
596
    }
701
709
        custom_argc += 1;
702
710
        {
703
711
          char **new_argv = realloc(custom_argv, sizeof(char *)
704
 
                                    * ((unsigned int)
705
 
                                       custom_argc + 1));
 
712
                                    * ((size_t)custom_argc + 1));
706
713
          if(new_argv == NULL){
707
714
            error(0, errno, "realloc");
708
715
            exitstatus = EX_OSERR;
794
801
  }
795
802
  
796
803
  if(debug){
797
 
    for(plugin *p = plugin_list; p != NULL; p=p->next){
 
804
    for(plugin *p = plugin_list; p != NULL; p = p->next){
798
805
      fprintf(stderr, "Plugin: %s has %d arguments\n",
799
806
              p->name ? p->name : "Global", p->argc - 1);
800
807
      for(char **a = p->argv; *a != NULL; a++){
809
816
  
810
817
  if(getuid() == 0){
811
818
    /* Work around Debian bug #633582:
812
 
       <http://bugs.debian.org/633582> */
 
819
       <https://bugs.debian.org/633582> */
813
820
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
814
821
    if(plugindir_fd == -1){
815
822
      if(errno != ENOENT){
827
834
          }
828
835
        }
829
836
      }
830
 
      TEMP_FAILURE_RETRY(close(plugindir_fd));
 
837
      close(plugindir_fd);
831
838
    }
832
839
  }
833
840
  
892
899
    return 1;
893
900
  }
894
901
  
895
 
#ifdef __GLIBC__
896
 
#if __GLIBC_PREREQ(2, 15)
897
902
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
898
903
                             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
904
  if(numplugins == -1){
908
905
    error(0, errno, "Could not scan plugin dir");
909
906
    direntries = NULL;
925
922
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
926
923
    if(ret == -1){
927
924
      error(0, errno, "stat");
928
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
925
      close(plugin_fd);
929
926
      free(direntries[i]);
930
927
      continue;
931
928
    }
940
937
                plugindir != NULL ? plugindir : PDIR,
941
938
                direntries[i]->d_name);
942
939
      }
943
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
940
      close(plugin_fd);
944
941
      free(direntries[i]);
945
942
      continue;
946
943
    }
948
945
    plugin *p = getplugin(direntries[i]->d_name);
949
946
    if(p == NULL){
950
947
      error(0, errno, "getplugin");
951
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
948
      close(plugin_fd);
952
949
      free(direntries[i]);
953
950
      continue;
954
951
    }
957
954
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
958
955
                direntries[i]->d_name);
959
956
      }
960
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
957
      close(plugin_fd);
961
958
      free(direntries[i]);
962
959
      continue;
963
960
    }
1003
1000
    if(pipefd[0] >= FD_SETSIZE){
1004
1001
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
1005
1002
              FD_SETSIZE);
1006
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
1007
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1003
      close(pipefd[0]);
 
1004
      close(pipefd[1]);
1008
1005
      exitstatus = EX_OSERR;
1009
1006
      free(direntries[i]);
1010
1007
      goto fallback;
1014
1011
    ret = set_cloexec_flag(pipefd[0]);
1015
1012
    if(ret < 0){
1016
1013
      error(0, errno, "set_cloexec_flag");
1017
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
1018
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1014
      close(pipefd[0]);
 
1015
      close(pipefd[1]);
1019
1016
      exitstatus = EX_OSERR;
1020
1017
      free(direntries[i]);
1021
1018
      goto fallback;
1023
1020
    ret = set_cloexec_flag(pipefd[1]);
1024
1021
    if(ret < 0){
1025
1022
      error(0, errno, "set_cloexec_flag");
1026
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
1027
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1023
      close(pipefd[0]);
 
1024
      close(pipefd[1]);
1028
1025
      exitstatus = EX_OSERR;
1029
1026
      free(direntries[i]);
1030
1027
      goto fallback;
1049
1046
      error(0, errno, "fork");
1050
1047
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1051
1048
                                     &sigchld_action.sa_mask, NULL));
1052
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
1053
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1049
      close(pipefd[0]);
 
1050
      close(pipefd[1]);
1054
1051
      exitstatus = EX_OSERR;
1055
1052
      free(direntries[i]);
1056
1053
      goto fallback;
1084
1081
      /* no return */
1085
1082
    }
1086
1083
    /* Parent process */
1087
 
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1088
 
                                             pipe */
1089
 
    TEMP_FAILURE_RETRY(close(plugin_fd));
 
1084
    close(pipefd[1]);           /* Close unused write end of pipe */
 
1085
    close(plugin_fd);
1090
1086
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1091
1087
    if(new_plugin == NULL){
1092
1088
      error(0, errno, "getplugin");
1104
1100
    
1105
1101
    new_plugin->pid = pid;
1106
1102
    new_plugin->fd = pipefd[0];
1107
 
    
 
1103
 
 
1104
    if(debug){
 
1105
      fprintf(stderr, "Plugin %s started (PID %" PRIdMAX ")\n",
 
1106
              new_plugin->name, (intmax_t) (new_plugin->pid));
 
1107
    }
 
1108
 
1108
1109
    /* Unblock SIGCHLD so signal handler can be run if this process
1109
1110
       has already completed */
1110
1111
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1116
1117
      goto fallback;
1117
1118
    }
1118
1119
    
1119
 
#if defined (__GNUC__) and defined (__GLIBC__)
1120
 
#if not __GLIBC_PREREQ(2, 16)
1121
 
#pragma GCC diagnostic push
1122
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1123
 
#endif
1124
 
#endif
1125
 
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
1126
 
                                          -Wconversion in GNU libc
1127
 
                                          before 2.16 */
1128
 
#if defined (__GNUC__) and defined (__GLIBC__)
1129
 
#if not __GLIBC_PREREQ(2, 16)
1130
 
#pragma GCC diagnostic pop
1131
 
#endif
1132
 
#endif
 
1120
    FD_SET(new_plugin->fd, &rfds_all);
1133
1121
    
1134
1122
    if(maxfd < new_plugin->fd){
1135
1123
      maxfd = new_plugin->fd;
1138
1126
  
1139
1127
  free(direntries);
1140
1128
  direntries = NULL;
1141
 
  TEMP_FAILURE_RETRY(close(dir_fd));
 
1129
  close(dir_fd);
1142
1130
  dir_fd = -1;
1143
1131
  free_plugin(getplugin(NULL));
1144
1132
  
1184
1172
                      (intmax_t) (proc->pid),
1185
1173
                      WTERMSIG(proc->status),
1186
1174
                      strsignal(WTERMSIG(proc->status)));
1187
 
            } else if(WCOREDUMP(proc->status)){
1188
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
1189
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
1190
1175
            }
1191
1176
          }
1192
1177
          
1193
1178
          /* Remove the plugin */
1194
 
#if defined (__GNUC__) and defined (__GLIBC__)
1195
 
#if not __GLIBC_PREREQ(2, 16)
1196
 
#pragma GCC diagnostic push
1197
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1198
 
#endif
1199
 
#endif
1200
 
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1201
 
                                          -Wconversion in GNU libc
1202
 
                                          before 2.16 */
1203
 
#if defined (__GNUC__) and defined (__GLIBC__)
1204
 
#if not __GLIBC_PREREQ(2, 16)
1205
 
#pragma GCC diagnostic pop
1206
 
#endif
1207
 
#endif
 
1179
          FD_CLR(proc->fd, &rfds_all);
1208
1180
          
1209
1181
          /* Block signal while modifying process_list */
1210
1182
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1250
1222
      }
1251
1223
      
1252
1224
      /* This process has not completed.  Does it have any output? */
1253
 
#if defined (__GNUC__) and defined (__GLIBC__)
1254
 
#if not __GLIBC_PREREQ(2, 16)
1255
 
#pragma GCC diagnostic push
1256
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1257
 
#endif
1258
 
#endif
1259
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1260
 
                                                         warning from
1261
 
                                                         -Wconversion
1262
 
                                                         in GNU libc
1263
 
                                                         before
1264
 
                                                         2.16 */
1265
 
#if defined (__GNUC__) and defined (__GLIBC__)
1266
 
#if not __GLIBC_PREREQ(2, 16)
1267
 
#pragma GCC diagnostic pop
1268
 
#endif
1269
 
#endif
 
1225
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1270
1226
        /* This process had nothing to say at this time */
1271
1227
        proc = proc->next;
1272
1228
        continue;
1342
1298
  free(direntries);
1343
1299
  
1344
1300
  if(dir_fd != -1){
1345
 
    TEMP_FAILURE_RETRY(close(dir_fd));
 
1301
    close(dir_fd);
1346
1302
  }
1347
1303
  
1348
1304
  /* Kill the processes */