/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-2010 Teddy Hogeborn
6
 
 * Copyright © 2008-2010 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
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
 
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:
745
742
  /* Strip permissions down to nobody */
746
743
  setgid(gid);
747
744
  if(ret == -1){
748
 
    error(0, errno, "setgid");
 
745
    perror("setgid");
749
746
  }
750
747
  ret = setuid(uid);
751
748
  if(ret == -1){
752
 
    error(0, errno, "setuid");
 
749
    perror("setuid");
753
750
  }
754
751
  
755
752
  /* Open plugin directory with close_on_exec flag */
773
770
                    );
774
771
    }
775
772
    if(dir_fd == -1){
776
 
      error(0, errno, "Could not open plugin dir");
777
 
      exitstatus = EX_UNAVAILABLE;
 
773
      perror("Could not open plugin dir");
 
774
      exitstatus = EXIT_FAILURE;
778
775
      goto fallback;
779
776
    }
780
777
    
782
779
  /* Set the FD_CLOEXEC flag on the directory */
783
780
    ret = set_cloexec_flag(dir_fd);
784
781
    if(ret < 0){
785
 
      error(0, errno, "set_cloexec_flag");
 
782
      perror("set_cloexec_flag");
786
783
      TEMP_FAILURE_RETRY(close(dir_fd));
787
 
      exitstatus = EX_OSERR;
 
784
      exitstatus = EXIT_FAILURE;
788
785
      goto fallback;
789
786
    }
790
787
#endif  /* O_CLOEXEC */
791
788
    
792
789
    dir = fdopendir(dir_fd);
793
790
    if(dir == NULL){
794
 
      error(0, errno, "Could not open plugin dir");
 
791
      perror("Could not open plugin dir");
795
792
      TEMP_FAILURE_RETRY(close(dir_fd));
796
 
      exitstatus = EX_OSERR;
 
793
      exitstatus = EXIT_FAILURE;
797
794
      goto fallback;
798
795
    }
799
796
  }
809
806
    /* All directory entries have been processed */
810
807
    if(dirst == NULL){
811
808
      if(errno == EBADF){
812
 
        error(0, errno, "readdir");
813
 
        exitstatus = EX_IOERR;
 
809
        perror("readdir");
 
810
        exitstatus = EXIT_FAILURE;
814
811
        goto fallback;
815
812
      }
816
813
      break;
846
843
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
847
844
        size_t suf_len = strlen(*suf);
848
845
        if((d_name_len >= suf_len)
849
 
           and (strcmp((dirst->d_name) + d_name_len-suf_len, *suf)
 
846
           and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
850
847
                == 0)){
851
848
          if(debug){
852
849
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
872
869
                                             dirst->d_name));
873
870
    }
874
871
    if(ret < 0){
875
 
      error(0, errno, "asprintf");
 
872
      perror("asprintf");
876
873
      continue;
877
874
    }
878
875
    
879
876
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
880
877
    if(ret == -1){
881
 
      error(0, errno, "stat");
 
878
      perror("stat");
882
879
      free(filename);
883
880
      continue;
884
881
    }
896
893
    
897
894
    plugin *p = getplugin(dirst->d_name);
898
895
    if(p == NULL){
899
 
      error(0, errno, "getplugin");
 
896
      perror("getplugin");
900
897
      free(filename);
901
898
      continue;
902
899
    }
914
911
      if(g != NULL){
915
912
        for(char **a = g->argv + 1; *a != NULL; a++){
916
913
          if(not add_argument(p, *a)){
917
 
            error(0, errno, "add_argument");
 
914
            perror("add_argument");
918
915
          }
919
916
        }
920
917
        /* Add global environment variables */
921
918
        for(char **e = g->environ; *e != NULL; e++){
922
919
          if(not add_environment(p, *e, false)){
923
 
            error(0, errno, "add_environment");
 
920
            perror("add_environment");
924
921
          }
925
922
        }
926
923
      }
931
928
    if(p->environ[0] != NULL){
932
929
      for(char **e = environ; *e != NULL; e++){
933
930
        if(not add_environment(p, *e, false)){
934
 
          error(0, errno, "add_environment");
 
931
          perror("add_environment");
935
932
        }
936
933
      }
937
934
    }
939
936
    int pipefd[2];
940
937
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
941
938
    if(ret == -1){
942
 
      error(0, errno, "pipe");
943
 
      exitstatus = EX_OSERR;
 
939
      perror("pipe");
 
940
      exitstatus = EXIT_FAILURE;
944
941
      goto fallback;
945
942
    }
946
943
    /* Ask OS to automatic close the pipe on exec */
947
944
    ret = set_cloexec_flag(pipefd[0]);
948
945
    if(ret < 0){
949
 
      error(0, errno, "set_cloexec_flag");
950
 
      exitstatus = EX_OSERR;
 
946
      perror("set_cloexec_flag");
 
947
      exitstatus = EXIT_FAILURE;
951
948
      goto fallback;
952
949
    }
953
950
    ret = set_cloexec_flag(pipefd[1]);
954
951
    if(ret < 0){
955
 
      error(0, errno, "set_cloexec_flag");
956
 
      exitstatus = EX_OSERR;
 
952
      perror("set_cloexec_flag");
 
953
      exitstatus = EXIT_FAILURE;
957
954
      goto fallback;
958
955
    }
959
956
    /* Block SIGCHLD until process is safely in process list */
961
958
                                              &sigchld_action.sa_mask,
962
959
                                              NULL));
963
960
    if(ret < 0){
964
 
      error(0, errno, "sigprocmask");
965
 
      exitstatus = EX_OSERR;
 
961
      perror("sigprocmask");
 
962
      exitstatus = EXIT_FAILURE;
966
963
      goto fallback;
967
964
    }
968
965
    /* Starting a new process to be watched */
971
968
      pid = fork();
972
969
    } while(pid == -1 and errno == EINTR);
973
970
    if(pid == -1){
974
 
      error(0, errno, "fork");
975
 
      exitstatus = EX_OSERR;
 
971
      perror("fork");
 
972
      exitstatus = EXIT_FAILURE;
976
973
      goto fallback;
977
974
    }
978
975
    if(pid == 0){
979
976
      /* this is the child process */
980
977
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
981
978
      if(ret < 0){
982
 
        error(0, errno, "sigaction");
983
 
        _exit(EX_OSERR);
 
979
        perror("sigaction");
 
980
        _exit(EXIT_FAILURE);
984
981
      }
985
982
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
986
983
      if(ret < 0){
987
 
        error(0, errno, "sigprocmask");
988
 
        _exit(EX_OSERR);
 
984
        perror("sigprocmask");
 
985
        _exit(EXIT_FAILURE);
989
986
      }
990
987
      
991
988
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
992
989
      if(ret == -1){
993
 
        error(0, errno, "dup2");
994
 
        _exit(EX_OSERR);
 
990
        perror("dup2");
 
991
        _exit(EXIT_FAILURE);
995
992
      }
996
993
      
997
994
      if(dirfd(dir) < 0){
1001
998
      }
1002
999
      if(p->environ[0] == NULL){
1003
1000
        if(execv(filename, p->argv) < 0){
1004
 
          error(0, errno, "execv for %s", filename);
1005
 
          _exit(EX_OSERR);
 
1001
          perror("execv");
 
1002
          _exit(EXIT_FAILURE);
1006
1003
        }
1007
1004
      } else {
1008
1005
        if(execve(filename, p->argv, p->environ) < 0){
1009
 
          error(0, errno, "execve for %s", filename);
1010
 
          _exit(EX_OSERR);
 
1006
          perror("execve");
 
1007
          _exit(EXIT_FAILURE);
1011
1008
        }
1012
1009
      }
1013
1010
      /* no return */
1018
1015
    free(filename);
1019
1016
    plugin *new_plugin = getplugin(dirst->d_name);
1020
1017
    if(new_plugin == NULL){
1021
 
      error(0, errno, "getplugin");
 
1018
      perror("getplugin");
1022
1019
      ret = (int)(TEMP_FAILURE_RETRY
1023
1020
                  (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
1024
1021
                               NULL)));
1025
1022
      if(ret < 0){
1026
 
        error(0, errno, "sigprocmask");
 
1023
        perror("sigprocmask");
1027
1024
      }
1028
 
      exitstatus = EX_OSERR;
 
1025
      exitstatus = EXIT_FAILURE;
1029
1026
      goto fallback;
1030
1027
    }
1031
1028
    
1038
1035
                                              &sigchld_action.sa_mask,
1039
1036
                                              NULL));
1040
1037
    if(ret < 0){
1041
 
      error(0, errno, "sigprocmask");
1042
 
      exitstatus = EX_OSERR;
 
1038
      perror("sigprocmask");
 
1039
      exitstatus = EXIT_FAILURE;
1043
1040
      goto fallback;
1044
1041
    }
1045
1042
    
1071
1068
    fd_set rfds = rfds_all;
1072
1069
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
1073
1070
    if(select_ret == -1 and errno != EINTR){
1074
 
      error(0, errno, "select");
1075
 
      exitstatus = EX_OSERR;
 
1071
      perror("select");
 
1072
      exitstatus = EXIT_FAILURE;
1076
1073
      goto fallback;
1077
1074
    }
1078
1075
    /* OK, now either a process completed, or something can be read
1113
1110
                                         &sigchld_action.sa_mask,
1114
1111
                                         NULL));
1115
1112
          if(ret < 0){
1116
 
            error(0, errno, "sigprocmask");
1117
 
            exitstatus = EX_OSERR;
 
1113
            perror("sigprocmask");
 
1114
            exitstatus = EXIT_FAILURE;
1118
1115
            goto fallback;
1119
1116
          }
1120
1117
          
1127
1124
                      (sigprocmask(SIG_UNBLOCK,
1128
1125
                                   &sigchld_action.sa_mask, NULL)));
1129
1126
          if(ret < 0){
1130
 
            error(0, errno, "sigprocmask");
1131
 
            exitstatus = EX_OSERR;
 
1127
            perror("sigprocmask");
 
1128
            exitstatus = EXIT_FAILURE;
1132
1129
            goto fallback;
1133
1130
          }
1134
1131
          
1144
1141
        bool bret = print_out_password(proc->buffer,
1145
1142
                                       proc->buffer_length);
1146
1143
        if(not bret){
1147
 
          error(0, errno, "print_out_password");
1148
 
          exitstatus = EX_IOERR;
 
1144
          perror("print_out_password");
 
1145
          exitstatus = EXIT_FAILURE;
1149
1146
        }
1150
1147
        goto fallback;
1151
1148
      }
1163
1160
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1164
1161
                               + (size_t) BUFFER_SIZE);
1165
1162
        if(proc->buffer == NULL){
1166
 
          error(0, errno, "malloc");
1167
 
          exitstatus = EX_OSERR;
 
1163
          perror("malloc");
 
1164
          exitstatus = EXIT_FAILURE;
1168
1165
          goto fallback;
1169
1166
        }
1170
1167
        proc->buffer_size += BUFFER_SIZE;
1191
1188
  
1192
1189
 fallback:
1193
1190
  
1194
 
  if(plugin_list == NULL or (exitstatus != EXIT_SUCCESS
1195
 
                             and exitstatus != EX_OK)){
 
1191
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
1196
1192
    /* Fallback if all plugins failed, none are found or an error
1197
1193
       occured */
1198
1194
    bool bret;
1206
1202
    }
1207
1203
    bret = print_out_password(passwordbuffer, len);
1208
1204
    if(not bret){
1209
 
      error(0, errno, "print_out_password");
1210
 
      exitstatus = EX_IOERR;
 
1205
      perror("print_out_password");
 
1206
      exitstatus = EXIT_FAILURE;
1211
1207
    }
1212
1208
  }
1213
1209
  
1214
1210
  /* Restore old signal handler */
1215
1211
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1216
1212
  if(ret == -1){
1217
 
    error(0, errno, "sigaction");
1218
 
    exitstatus = EX_OSERR;
 
1213
    perror("sigaction");
 
1214
    exitstatus = EXIT_FAILURE;
1219
1215
  }
1220
1216
  
1221
1217
  if(custom_argv != NULL){
1236
1232
      ret = kill(p->pid, SIGTERM);
1237
1233
      if(ret == -1 and errno != ESRCH){
1238
1234
        /* Set-uid proccesses might not get closed */
1239
 
        error(0, errno, "kill");
 
1235
        perror("kill");
1240
1236
      }
1241
1237
    }
1242
1238
  }
1246
1242
    ret = wait(NULL);
1247
1243
  } while(ret >= 0);
1248
1244
  if(errno != ECHILD){
1249
 
    error(0, errno, "wait");
 
1245
    perror("wait");
1250
1246
  }
1251
1247
  
1252
1248
  free_plugin_list();