/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to plugin-runner.c

Convert some programs to use the exit codes from <sysexits.h>.  Change
all programs using the "argp" parsing functions to use them correctly;
checking return value, using argp_error() to report parse errors etc.

* plugin-runner.c: Use <sysexits.h> exit codes.  Always use fallback,
                   even on option errors, except for "--help", etc.
  (getplugin): Make sure "errno" is set correctly on return.
  (main): Declare our own "--help", "--usage", and "--version"
          options which do not cause the fallback to be invoked.
          In all other options, use fallback on any error.
  (parse_opt, parse_opt_config_file): Reset errno at start and return
                                      errno.  No need to check "arg"
                                      for NULL.  New "--help",
                                      "--usage", and "--version"
                                      options.
  (parse_opt): Accept empty string as global option.  Do not print
               errors which will be detected and reported later.  Do
               "argp_error()" on parse error or empty plugin names.
* plugins.d/mandos-client.c: Use <sysexits.h> exit codes.  Do not
                             return successful exit code on "--help",
                             etc. since this would give the wrong
                             message to "plugin-runner".
  (main): Declare our own "--help", "--usage", and "--version"
          options which do not return a successful exit code.
  (parse_opt): Reset errno at start and return errno.  Do
               "argp_error()" on parse errors.  New "--help",
               "--usage", and "--version" options.
* plugins.d/password-prompt.c: Use exit codes from <sysexits.h>.  Do
                               not return successful exit code on
                               "--help", etc. since this would give
                               the wrong message to "plugin-runner".
  (main): Declare our own "--help", "--usage", and "--version" options
          which do not return a successful exit code.  Do
          close(STDOUT_FILENO) after writing to check its return code.
  (parse_opt): Reset errno at start and return errno.

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-2011 Teddy Hogeborn
6
 
 * Copyright © 2008-2011 Björn Påhlsson
 
5
 * Copyright © 2008,2009 Teddy Hogeborn
 
6
 * Copyright © 2008,2009 Björn Påhlsson
7
7
 * 
8
8
 * This program is free software: you can redistribute it and/or
9
9
 * modify it under the terms of the GNU General Public License as
19
19
 * along with this program.  If not, see
20
20
 * <http://www.gnu.org/licenses/>.
21
21
 * 
22
 
 * Contact the authors at <mandos@recompile.se>.
 
22
 * Contact the authors at <mandos@fukt.bsnet.se>.
23
23
 */
24
24
 
25
25
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
26
26
                                   asprintf(), O_CLOEXEC */
27
27
#include <stddef.h>             /* size_t, NULL */
28
 
#include <stdlib.h>             /* malloc(), exit(), EXIT_SUCCESS,
29
 
                                   realloc() */
 
28
#include <stdlib.h>             /* malloc(), exit(), EXIT_FAILURE,
 
29
                                   EXIT_SUCCESS, realloc() */
30
30
#include <stdbool.h>            /* bool, true, false */
31
 
#include <stdio.h>              /* fileno(), fprintf(),
 
31
#include <stdio.h>              /* perror, fileno(), fprintf(),
32
32
                                   stderr, STDOUT_FILENO */
33
33
#include <sys/types.h>          /* DIR, fdopendir(), stat(), struct
34
34
                                   stat, waitpid(), WIFEXITED(),
54
54
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
55
55
                                   FD_CLOEXEC */
56
56
#include <string.h>             /* strsep, strlen(), asprintf(),
57
 
                                   strsignal(), strcmp(), strncmp() */
 
57
                                   strsignal() */
58
58
#include <errno.h>              /* errno */
59
59
#include <argp.h>               /* struct argp_option, struct
60
60
                                   argp_state, struct argp,
68
68
                                */
69
69
#include <errno.h>              /* errno, EBADF */
70
70
#include <inttypes.h>           /* intmax_t, PRIdMAX, strtoimax() */
71
 
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_IOERR,
72
 
                                   EX_CONFIG, EX_UNAVAILABLE, EX_OK */
73
 
#include <errno.h>              /* errno */
74
 
#include <error.h>              /* error() */
 
71
#include <sysexits.h>           /* EX_OSERR, EX_USAGE */
75
72
 
76
73
#define BUFFER_SIZE 256
77
74
 
79
76
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
80
77
 
81
78
const char *argp_program_version = "plugin-runner " VERSION;
82
 
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
79
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
83
80
 
84
81
typedef struct plugin{
85
82
  char *name;                   /* can be NULL or any plugin name */
268
265
        /* No child processes */
269
266
        break;
270
267
      }
271
 
      error(0, errno, "waitpid");
 
268
      perror("waitpid");
272
269
    }
273
270
    
274
271
    /* A child exited, find it in process_list */
359
356
  sigemptyset(&sigchld_action.sa_mask);
360
357
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
361
358
  if(ret == -1){
362
 
    error(0, errno, "sigaddset");
363
 
    exitstatus = EX_OSERR;
 
359
    perror("sigaddset");
 
360
    exitstatus = EXIT_FAILURE;
364
361
    goto fallback;
365
362
  }
366
363
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
367
364
  if(ret == -1){
368
 
    error(0, errno, "sigaction");
369
 
    exitstatus = EX_OSERR;
 
365
    perror("sigaction");
 
366
    exitstatus = EXIT_FAILURE;
370
367
    goto fallback;
371
368
  }
372
369
  
601
598
  case ENOMEM:
602
599
  default:
603
600
    errno = ret;
604
 
    error(0, errno, "argp_parse");
 
601
    perror("argp_parse");
605
602
    exitstatus = EX_OSERR;
606
603
    goto fallback;
607
604
  case EINVAL:
628
625
    custom_argc = 1;
629
626
    custom_argv = malloc(sizeof(char*) * 2);
630
627
    if(custom_argv == NULL){
631
 
      error(0, errno, "malloc");
632
 
      exitstatus = EX_OSERR;
 
628
      perror("malloc");
 
629
      exitstatus = EXIT_FAILURE;
633
630
      goto fallback;
634
631
    }
635
632
    custom_argv[0] = argv[0];
651
648
        }
652
649
        new_arg = strdup(p);
653
650
        if(new_arg == NULL){
654
 
          error(0, errno, "strdup");
655
 
          exitstatus = EX_OSERR;
 
651
          perror("strdup");
 
652
          exitstatus = EXIT_FAILURE;
656
653
          free(org_line);
657
654
          goto fallback;
658
655
        }
661
658
        custom_argv = realloc(custom_argv, sizeof(char *)
662
659
                              * ((unsigned int) custom_argc + 1));
663
660
        if(custom_argv == NULL){
664
 
          error(0, errno, "realloc");
665
 
          exitstatus = EX_OSERR;
 
661
          perror("realloc");
 
662
          exitstatus = EXIT_FAILURE;
666
663
          free(org_line);
667
664
          goto fallback;
668
665
        }
674
671
      ret = fclose(conffp);
675
672
    } while(ret == EOF and errno == EINTR);
676
673
    if(ret == EOF){
677
 
      error(0, errno, "fclose");
678
 
      exitstatus = EX_IOERR;
 
674
      perror("fclose");
 
675
      exitstatus = EXIT_FAILURE;
679
676
      goto fallback;
680
677
    }
681
678
    free(org_line);
683
680
    /* Check for harmful errors and go to fallback. Other errors might
684
681
       not affect opening plugins */
685
682
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
686
 
      error(0, errno, "fopen");
687
 
      exitstatus = EX_OSERR;
 
683
      perror("fopen");
 
684
      exitstatus = EXIT_FAILURE;
688
685
      goto fallback;
689
686
    }
690
687
  }
700
697
    case ENOMEM:
701
698
    default:
702
699
      errno = ret;
703
 
      error(0, errno, "argp_parse");
 
700
      perror("argp_parse");
704
701
      exitstatus = EX_OSERR;
705
702
      goto fallback;
706
703
    case EINVAL:
720
717
  case ENOMEM:
721
718
  default:
722
719
    errno = ret;
723
 
    error(0, errno, "argp_parse");
 
720
    perror("argp_parse");
724
721
    exitstatus = EX_OSERR;
725
722
    goto fallback;
726
723
  case EINVAL:
742
739
    }
743
740
  }
744
741
  
745
 
  if(getuid() == 0){
746
 
    /* Work around Debian bug #633582:
747
 
       <http://bugs.debian.org/633582> */
748
 
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
749
 
    if(plugindir_fd == -1){
750
 
      error(0, errno, "open");
751
 
    } else {
752
 
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
753
 
      if(ret == -1){
754
 
        error(0, errno, "fstat");
755
 
      } else {
756
 
        if(S_ISDIR(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
757
 
          ret = fchown(plugindir_fd, uid, gid);
758
 
          if(ret == -1){
759
 
            error(0, errno, "fchown");
760
 
          }
761
 
        }
762
 
      }
763
 
      TEMP_FAILURE_RETRY(close(plugindir_fd));
764
 
    }
765
 
  }
766
 
  
767
 
  /* Lower permissions */
 
742
  /* Strip permissions down to nobody */
768
743
  setgid(gid);
769
744
  if(ret == -1){
770
 
    error(0, errno, "setgid");
 
745
    perror("setgid");
771
746
  }
772
747
  ret = setuid(uid);
773
748
  if(ret == -1){
774
 
    error(0, errno, "setuid");
 
749
    perror("setuid");
775
750
  }
776
751
  
777
752
  /* Open plugin directory with close_on_exec flag */
795
770
                    );
796
771
    }
797
772
    if(dir_fd == -1){
798
 
      error(0, errno, "Could not open plugin dir");
799
 
      exitstatus = EX_UNAVAILABLE;
 
773
      perror("Could not open plugin dir");
 
774
      exitstatus = EXIT_FAILURE;
800
775
      goto fallback;
801
776
    }
802
777
    
804
779
  /* Set the FD_CLOEXEC flag on the directory */
805
780
    ret = set_cloexec_flag(dir_fd);
806
781
    if(ret < 0){
807
 
      error(0, errno, "set_cloexec_flag");
 
782
      perror("set_cloexec_flag");
808
783
      TEMP_FAILURE_RETRY(close(dir_fd));
809
 
      exitstatus = EX_OSERR;
 
784
      exitstatus = EXIT_FAILURE;
810
785
      goto fallback;
811
786
    }
812
787
#endif  /* O_CLOEXEC */
813
788
    
814
789
    dir = fdopendir(dir_fd);
815
790
    if(dir == NULL){
816
 
      error(0, errno, "Could not open plugin dir");
 
791
      perror("Could not open plugin dir");
817
792
      TEMP_FAILURE_RETRY(close(dir_fd));
818
 
      exitstatus = EX_OSERR;
 
793
      exitstatus = EXIT_FAILURE;
819
794
      goto fallback;
820
795
    }
821
796
  }
831
806
    /* All directory entries have been processed */
832
807
    if(dirst == NULL){
833
808
      if(errno == EBADF){
834
 
        error(0, errno, "readdir");
835
 
        exitstatus = EX_IOERR;
 
809
        perror("readdir");
 
810
        exitstatus = EXIT_FAILURE;
836
811
        goto fallback;
837
812
      }
838
813
      break;
868
843
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
869
844
        size_t suf_len = strlen(*suf);
870
845
        if((d_name_len >= suf_len)
871
 
           and (strcmp((dirst->d_name) + d_name_len-suf_len, *suf)
 
846
           and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
872
847
                == 0)){
873
848
          if(debug){
874
849
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
894
869
                                             dirst->d_name));
895
870
    }
896
871
    if(ret < 0){
897
 
      error(0, errno, "asprintf");
 
872
      perror("asprintf");
898
873
      continue;
899
874
    }
900
875
    
901
876
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
902
877
    if(ret == -1){
903
 
      error(0, errno, "stat");
 
878
      perror("stat");
904
879
      free(filename);
905
880
      continue;
906
881
    }
918
893
    
919
894
    plugin *p = getplugin(dirst->d_name);
920
895
    if(p == NULL){
921
 
      error(0, errno, "getplugin");
 
896
      perror("getplugin");
922
897
      free(filename);
923
898
      continue;
924
899
    }
936
911
      if(g != NULL){
937
912
        for(char **a = g->argv + 1; *a != NULL; a++){
938
913
          if(not add_argument(p, *a)){
939
 
            error(0, errno, "add_argument");
 
914
            perror("add_argument");
940
915
          }
941
916
        }
942
917
        /* Add global environment variables */
943
918
        for(char **e = g->environ; *e != NULL; e++){
944
919
          if(not add_environment(p, *e, false)){
945
 
            error(0, errno, "add_environment");
 
920
            perror("add_environment");
946
921
          }
947
922
        }
948
923
      }
953
928
    if(p->environ[0] != NULL){
954
929
      for(char **e = environ; *e != NULL; e++){
955
930
        if(not add_environment(p, *e, false)){
956
 
          error(0, errno, "add_environment");
 
931
          perror("add_environment");
957
932
        }
958
933
      }
959
934
    }
961
936
    int pipefd[2];
962
937
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
963
938
    if(ret == -1){
964
 
      error(0, errno, "pipe");
965
 
      exitstatus = EX_OSERR;
 
939
      perror("pipe");
 
940
      exitstatus = EXIT_FAILURE;
966
941
      goto fallback;
967
942
    }
968
943
    /* Ask OS to automatic close the pipe on exec */
969
944
    ret = set_cloexec_flag(pipefd[0]);
970
945
    if(ret < 0){
971
 
      error(0, errno, "set_cloexec_flag");
972
 
      exitstatus = EX_OSERR;
 
946
      perror("set_cloexec_flag");
 
947
      exitstatus = EXIT_FAILURE;
973
948
      goto fallback;
974
949
    }
975
950
    ret = set_cloexec_flag(pipefd[1]);
976
951
    if(ret < 0){
977
 
      error(0, errno, "set_cloexec_flag");
978
 
      exitstatus = EX_OSERR;
 
952
      perror("set_cloexec_flag");
 
953
      exitstatus = EXIT_FAILURE;
979
954
      goto fallback;
980
955
    }
981
956
    /* Block SIGCHLD until process is safely in process list */
983
958
                                              &sigchld_action.sa_mask,
984
959
                                              NULL));
985
960
    if(ret < 0){
986
 
      error(0, errno, "sigprocmask");
987
 
      exitstatus = EX_OSERR;
 
961
      perror("sigprocmask");
 
962
      exitstatus = EXIT_FAILURE;
988
963
      goto fallback;
989
964
    }
990
965
    /* Starting a new process to be watched */
993
968
      pid = fork();
994
969
    } while(pid == -1 and errno == EINTR);
995
970
    if(pid == -1){
996
 
      error(0, errno, "fork");
997
 
      exitstatus = EX_OSERR;
 
971
      perror("fork");
 
972
      exitstatus = EXIT_FAILURE;
998
973
      goto fallback;
999
974
    }
1000
975
    if(pid == 0){
1001
976
      /* this is the child process */
1002
977
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1003
978
      if(ret < 0){
1004
 
        error(0, errno, "sigaction");
1005
 
        _exit(EX_OSERR);
 
979
        perror("sigaction");
 
980
        _exit(EXIT_FAILURE);
1006
981
      }
1007
982
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
1008
983
      if(ret < 0){
1009
 
        error(0, errno, "sigprocmask");
1010
 
        _exit(EX_OSERR);
 
984
        perror("sigprocmask");
 
985
        _exit(EXIT_FAILURE);
1011
986
      }
1012
987
      
1013
988
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
1014
989
      if(ret == -1){
1015
 
        error(0, errno, "dup2");
1016
 
        _exit(EX_OSERR);
 
990
        perror("dup2");
 
991
        _exit(EXIT_FAILURE);
1017
992
      }
1018
993
      
1019
994
      if(dirfd(dir) < 0){
1023
998
      }
1024
999
      if(p->environ[0] == NULL){
1025
1000
        if(execv(filename, p->argv) < 0){
1026
 
          error(0, errno, "execv for %s", filename);
1027
 
          _exit(EX_OSERR);
 
1001
          perror("execv");
 
1002
          _exit(EXIT_FAILURE);
1028
1003
        }
1029
1004
      } else {
1030
1005
        if(execve(filename, p->argv, p->environ) < 0){
1031
 
          error(0, errno, "execve for %s", filename);
1032
 
          _exit(EX_OSERR);
 
1006
          perror("execve");
 
1007
          _exit(EXIT_FAILURE);
1033
1008
        }
1034
1009
      }
1035
1010
      /* no return */
1040
1015
    free(filename);
1041
1016
    plugin *new_plugin = getplugin(dirst->d_name);
1042
1017
    if(new_plugin == NULL){
1043
 
      error(0, errno, "getplugin");
 
1018
      perror("getplugin");
1044
1019
      ret = (int)(TEMP_FAILURE_RETRY
1045
1020
                  (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
1046
1021
                               NULL)));
1047
1022
      if(ret < 0){
1048
 
        error(0, errno, "sigprocmask");
 
1023
        perror("sigprocmask");
1049
1024
      }
1050
 
      exitstatus = EX_OSERR;
 
1025
      exitstatus = EXIT_FAILURE;
1051
1026
      goto fallback;
1052
1027
    }
1053
1028
    
1060
1035
                                              &sigchld_action.sa_mask,
1061
1036
                                              NULL));
1062
1037
    if(ret < 0){
1063
 
      error(0, errno, "sigprocmask");
1064
 
      exitstatus = EX_OSERR;
 
1038
      perror("sigprocmask");
 
1039
      exitstatus = EXIT_FAILURE;
1065
1040
      goto fallback;
1066
1041
    }
1067
1042
    
1093
1068
    fd_set rfds = rfds_all;
1094
1069
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
1095
1070
    if(select_ret == -1 and errno != EINTR){
1096
 
      error(0, errno, "select");
1097
 
      exitstatus = EX_OSERR;
 
1071
      perror("select");
 
1072
      exitstatus = EXIT_FAILURE;
1098
1073
      goto fallback;
1099
1074
    }
1100
1075
    /* OK, now either a process completed, or something can be read
1135
1110
                                         &sigchld_action.sa_mask,
1136
1111
                                         NULL));
1137
1112
          if(ret < 0){
1138
 
            error(0, errno, "sigprocmask");
1139
 
            exitstatus = EX_OSERR;
 
1113
            perror("sigprocmask");
 
1114
            exitstatus = EXIT_FAILURE;
1140
1115
            goto fallback;
1141
1116
          }
1142
1117
          
1149
1124
                      (sigprocmask(SIG_UNBLOCK,
1150
1125
                                   &sigchld_action.sa_mask, NULL)));
1151
1126
          if(ret < 0){
1152
 
            error(0, errno, "sigprocmask");
1153
 
            exitstatus = EX_OSERR;
 
1127
            perror("sigprocmask");
 
1128
            exitstatus = EXIT_FAILURE;
1154
1129
            goto fallback;
1155
1130
          }
1156
1131
          
1166
1141
        bool bret = print_out_password(proc->buffer,
1167
1142
                                       proc->buffer_length);
1168
1143
        if(not bret){
1169
 
          error(0, errno, "print_out_password");
1170
 
          exitstatus = EX_IOERR;
 
1144
          perror("print_out_password");
 
1145
          exitstatus = EXIT_FAILURE;
1171
1146
        }
1172
1147
        goto fallback;
1173
1148
      }
1185
1160
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1186
1161
                               + (size_t) BUFFER_SIZE);
1187
1162
        if(proc->buffer == NULL){
1188
 
          error(0, errno, "malloc");
1189
 
          exitstatus = EX_OSERR;
 
1163
          perror("malloc");
 
1164
          exitstatus = EXIT_FAILURE;
1190
1165
          goto fallback;
1191
1166
        }
1192
1167
        proc->buffer_size += BUFFER_SIZE;
1213
1188
  
1214
1189
 fallback:
1215
1190
  
1216
 
  if(plugin_list == NULL or (exitstatus != EXIT_SUCCESS
1217
 
                             and exitstatus != EX_OK)){
 
1191
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
1218
1192
    /* Fallback if all plugins failed, none are found or an error
1219
1193
       occured */
1220
1194
    bool bret;
1228
1202
    }
1229
1203
    bret = print_out_password(passwordbuffer, len);
1230
1204
    if(not bret){
1231
 
      error(0, errno, "print_out_password");
1232
 
      exitstatus = EX_IOERR;
 
1205
      perror("print_out_password");
 
1206
      exitstatus = EXIT_FAILURE;
1233
1207
    }
1234
1208
  }
1235
1209
  
1236
1210
  /* Restore old signal handler */
1237
1211
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1238
1212
  if(ret == -1){
1239
 
    error(0, errno, "sigaction");
1240
 
    exitstatus = EX_OSERR;
 
1213
    perror("sigaction");
 
1214
    exitstatus = EXIT_FAILURE;
1241
1215
  }
1242
1216
  
1243
1217
  if(custom_argv != NULL){
1258
1232
      ret = kill(p->pid, SIGTERM);
1259
1233
      if(ret == -1 and errno != ESRCH){
1260
1234
        /* Set-uid proccesses might not get closed */
1261
 
        error(0, errno, "kill");
 
1235
        perror("kill");
1262
1236
      }
1263
1237
    }
1264
1238
  }
1268
1242
    ret = wait(NULL);
1269
1243
  } while(ret >= 0);
1270
1244
  if(errno != ECHILD){
1271
 
    error(0, errno, "wait");
 
1245
    perror("wait");
1272
1246
  }
1273
1247
  
1274
1248
  free_plugin_list();