/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

merge

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,2009 Teddy Hogeborn
6
 
 * Copyright © 2008,2009 Björn Påhlsson
 
5
 * Copyright © 2008-2010 Teddy Hogeborn
 
6
 * Copyright © 2008-2010 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
28
28
#include <stdlib.h>             /* malloc(), exit(), EXIT_SUCCESS,
29
29
                                   realloc() */
30
30
#include <stdbool.h>            /* bool, true, false */
31
 
#include <stdio.h>              /* perror, fileno(), fprintf(),
 
31
#include <stdio.h>              /* fileno(), fprintf(),
32
32
                                   stderr, STDOUT_FILENO */
33
33
#include <sys/types.h>          /* DIR, fdopendir(), stat(), struct
34
34
                                   stat, waitpid(), WIFEXITED(),
70
70
#include <inttypes.h>           /* intmax_t, PRIdMAX, strtoimax() */
71
71
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_IOERR,
72
72
                                   EX_CONFIG, EX_UNAVAILABLE, EX_OK */
 
73
#include <errno.h>              /* errno */
 
74
#include <error.h>              /* error() */
73
75
 
74
76
#define BUFFER_SIZE 256
75
77
 
266
268
        /* No child processes */
267
269
        break;
268
270
      }
269
 
      perror("waitpid");
 
271
      error(0, errno, "waitpid");
270
272
    }
271
273
    
272
274
    /* A child exited, find it in process_list */
357
359
  sigemptyset(&sigchld_action.sa_mask);
358
360
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
359
361
  if(ret == -1){
360
 
    perror("sigaddset");
 
362
    error(0, errno, "sigaddset");
361
363
    exitstatus = EX_OSERR;
362
364
    goto fallback;
363
365
  }
364
366
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
365
367
  if(ret == -1){
366
 
    perror("sigaction");
 
368
    error(0, errno, "sigaction");
367
369
    exitstatus = EX_OSERR;
368
370
    goto fallback;
369
371
  }
599
601
  case ENOMEM:
600
602
  default:
601
603
    errno = ret;
602
 
    perror("argp_parse");
 
604
    error(0, errno, "argp_parse");
603
605
    exitstatus = EX_OSERR;
604
606
    goto fallback;
605
607
  case EINVAL:
626
628
    custom_argc = 1;
627
629
    custom_argv = malloc(sizeof(char*) * 2);
628
630
    if(custom_argv == NULL){
629
 
      perror("malloc");
 
631
      error(0, errno, "malloc");
630
632
      exitstatus = EX_OSERR;
631
633
      goto fallback;
632
634
    }
649
651
        }
650
652
        new_arg = strdup(p);
651
653
        if(new_arg == NULL){
652
 
          perror("strdup");
 
654
          error(0, errno, "strdup");
653
655
          exitstatus = EX_OSERR;
654
656
          free(org_line);
655
657
          goto fallback;
659
661
        custom_argv = realloc(custom_argv, sizeof(char *)
660
662
                              * ((unsigned int) custom_argc + 1));
661
663
        if(custom_argv == NULL){
662
 
          perror("realloc");
 
664
          error(0, errno, "realloc");
663
665
          exitstatus = EX_OSERR;
664
666
          free(org_line);
665
667
          goto fallback;
672
674
      ret = fclose(conffp);
673
675
    } while(ret == EOF and errno == EINTR);
674
676
    if(ret == EOF){
675
 
      perror("fclose");
 
677
      error(0, errno, "fclose");
676
678
      exitstatus = EX_IOERR;
677
679
      goto fallback;
678
680
    }
681
683
    /* Check for harmful errors and go to fallback. Other errors might
682
684
       not affect opening plugins */
683
685
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
684
 
      perror("fopen");
 
686
      error(0, errno, "fopen");
685
687
      exitstatus = EX_OSERR;
686
688
      goto fallback;
687
689
    }
698
700
    case ENOMEM:
699
701
    default:
700
702
      errno = ret;
701
 
      perror("argp_parse");
 
703
      error(0, errno, "argp_parse");
702
704
      exitstatus = EX_OSERR;
703
705
      goto fallback;
704
706
    case EINVAL:
718
720
  case ENOMEM:
719
721
  default:
720
722
    errno = ret;
721
 
    perror("argp_parse");
 
723
    error(0, errno, "argp_parse");
722
724
    exitstatus = EX_OSERR;
723
725
    goto fallback;
724
726
  case EINVAL:
743
745
  /* Strip permissions down to nobody */
744
746
  setgid(gid);
745
747
  if(ret == -1){
746
 
    perror("setgid");
 
748
    error(0, errno, "setgid");
747
749
  }
748
750
  ret = setuid(uid);
749
751
  if(ret == -1){
750
 
    perror("setuid");
 
752
    error(0, errno, "setuid");
751
753
  }
752
754
  
753
755
  /* Open plugin directory with close_on_exec flag */
771
773
                    );
772
774
    }
773
775
    if(dir_fd == -1){
774
 
      perror("Could not open plugin dir");
 
776
      error(0, errno, "Could not open plugin dir");
775
777
      exitstatus = EX_UNAVAILABLE;
776
778
      goto fallback;
777
779
    }
780
782
  /* Set the FD_CLOEXEC flag on the directory */
781
783
    ret = set_cloexec_flag(dir_fd);
782
784
    if(ret < 0){
783
 
      perror("set_cloexec_flag");
 
785
      error(0, errno, "set_cloexec_flag");
784
786
      TEMP_FAILURE_RETRY(close(dir_fd));
785
787
      exitstatus = EX_OSERR;
786
788
      goto fallback;
789
791
    
790
792
    dir = fdopendir(dir_fd);
791
793
    if(dir == NULL){
792
 
      perror("Could not open plugin dir");
 
794
      error(0, errno, "Could not open plugin dir");
793
795
      TEMP_FAILURE_RETRY(close(dir_fd));
794
796
      exitstatus = EX_OSERR;
795
797
      goto fallback;
807
809
    /* All directory entries have been processed */
808
810
    if(dirst == NULL){
809
811
      if(errno == EBADF){
810
 
        perror("readdir");
 
812
        error(0, errno, "readdir");
811
813
        exitstatus = EX_IOERR;
812
814
        goto fallback;
813
815
      }
870
872
                                             dirst->d_name));
871
873
    }
872
874
    if(ret < 0){
873
 
      perror("asprintf");
 
875
      error(0, errno, "asprintf");
874
876
      continue;
875
877
    }
876
878
    
877
879
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
878
880
    if(ret == -1){
879
 
      perror("stat");
 
881
      error(0, errno, "stat");
880
882
      free(filename);
881
883
      continue;
882
884
    }
894
896
    
895
897
    plugin *p = getplugin(dirst->d_name);
896
898
    if(p == NULL){
897
 
      perror("getplugin");
 
899
      error(0, errno, "getplugin");
898
900
      free(filename);
899
901
      continue;
900
902
    }
912
914
      if(g != NULL){
913
915
        for(char **a = g->argv + 1; *a != NULL; a++){
914
916
          if(not add_argument(p, *a)){
915
 
            perror("add_argument");
 
917
            error(0, errno, "add_argument");
916
918
          }
917
919
        }
918
920
        /* Add global environment variables */
919
921
        for(char **e = g->environ; *e != NULL; e++){
920
922
          if(not add_environment(p, *e, false)){
921
 
            perror("add_environment");
 
923
            error(0, errno, "add_environment");
922
924
          }
923
925
        }
924
926
      }
929
931
    if(p->environ[0] != NULL){
930
932
      for(char **e = environ; *e != NULL; e++){
931
933
        if(not add_environment(p, *e, false)){
932
 
          perror("add_environment");
 
934
          error(0, errno, "add_environment");
933
935
        }
934
936
      }
935
937
    }
937
939
    int pipefd[2];
938
940
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
939
941
    if(ret == -1){
940
 
      perror("pipe");
 
942
      error(0, errno, "pipe");
941
943
      exitstatus = EX_OSERR;
942
944
      goto fallback;
943
945
    }
944
946
    /* Ask OS to automatic close the pipe on exec */
945
947
    ret = set_cloexec_flag(pipefd[0]);
946
948
    if(ret < 0){
947
 
      perror("set_cloexec_flag");
 
949
      error(0, errno, "set_cloexec_flag");
948
950
      exitstatus = EX_OSERR;
949
951
      goto fallback;
950
952
    }
951
953
    ret = set_cloexec_flag(pipefd[1]);
952
954
    if(ret < 0){
953
 
      perror("set_cloexec_flag");
 
955
      error(0, errno, "set_cloexec_flag");
954
956
      exitstatus = EX_OSERR;
955
957
      goto fallback;
956
958
    }
959
961
                                              &sigchld_action.sa_mask,
960
962
                                              NULL));
961
963
    if(ret < 0){
962
 
      perror("sigprocmask");
 
964
      error(0, errno, "sigprocmask");
963
965
      exitstatus = EX_OSERR;
964
966
      goto fallback;
965
967
    }
969
971
      pid = fork();
970
972
    } while(pid == -1 and errno == EINTR);
971
973
    if(pid == -1){
972
 
      perror("fork");
 
974
      error(0, errno, "fork");
973
975
      exitstatus = EX_OSERR;
974
976
      goto fallback;
975
977
    }
977
979
      /* this is the child process */
978
980
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
979
981
      if(ret < 0){
980
 
        perror("sigaction");
 
982
        error(0, errno, "sigaction");
981
983
        _exit(EX_OSERR);
982
984
      }
983
985
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
984
986
      if(ret < 0){
985
 
        perror("sigprocmask");
 
987
        error(0, errno, "sigprocmask");
986
988
        _exit(EX_OSERR);
987
989
      }
988
990
      
989
991
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
990
992
      if(ret == -1){
991
 
        perror("dup2");
 
993
        error(0, errno, "dup2");
992
994
        _exit(EX_OSERR);
993
995
      }
994
996
      
999
1001
      }
1000
1002
      if(p->environ[0] == NULL){
1001
1003
        if(execv(filename, p->argv) < 0){
1002
 
          perror("execv");
 
1004
          error(0, errno, "execv for %s", filename);
1003
1005
          _exit(EX_OSERR);
1004
1006
        }
1005
1007
      } else {
1006
1008
        if(execve(filename, p->argv, p->environ) < 0){
1007
 
          perror("execve");
 
1009
          error(0, errno, "execve for %s", filename);
1008
1010
          _exit(EX_OSERR);
1009
1011
        }
1010
1012
      }
1016
1018
    free(filename);
1017
1019
    plugin *new_plugin = getplugin(dirst->d_name);
1018
1020
    if(new_plugin == NULL){
1019
 
      perror("getplugin");
 
1021
      error(0, errno, "getplugin");
1020
1022
      ret = (int)(TEMP_FAILURE_RETRY
1021
1023
                  (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
1022
1024
                               NULL)));
1023
1025
      if(ret < 0){
1024
 
        perror("sigprocmask");
 
1026
        error(0, errno, "sigprocmask");
1025
1027
      }
1026
1028
      exitstatus = EX_OSERR;
1027
1029
      goto fallback;
1036
1038
                                              &sigchld_action.sa_mask,
1037
1039
                                              NULL));
1038
1040
    if(ret < 0){
1039
 
      perror("sigprocmask");
 
1041
      error(0, errno, "sigprocmask");
1040
1042
      exitstatus = EX_OSERR;
1041
1043
      goto fallback;
1042
1044
    }
1069
1071
    fd_set rfds = rfds_all;
1070
1072
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
1071
1073
    if(select_ret == -1 and errno != EINTR){
1072
 
      perror("select");
 
1074
      error(0, errno, "select");
1073
1075
      exitstatus = EX_OSERR;
1074
1076
      goto fallback;
1075
1077
    }
1111
1113
                                         &sigchld_action.sa_mask,
1112
1114
                                         NULL));
1113
1115
          if(ret < 0){
1114
 
            perror("sigprocmask");
 
1116
            error(0, errno, "sigprocmask");
1115
1117
            exitstatus = EX_OSERR;
1116
1118
            goto fallback;
1117
1119
          }
1125
1127
                      (sigprocmask(SIG_UNBLOCK,
1126
1128
                                   &sigchld_action.sa_mask, NULL)));
1127
1129
          if(ret < 0){
1128
 
            perror("sigprocmask");
 
1130
            error(0, errno, "sigprocmask");
1129
1131
            exitstatus = EX_OSERR;
1130
1132
            goto fallback;
1131
1133
          }
1142
1144
        bool bret = print_out_password(proc->buffer,
1143
1145
                                       proc->buffer_length);
1144
1146
        if(not bret){
1145
 
          perror("print_out_password");
 
1147
          error(0, errno, "print_out_password");
1146
1148
          exitstatus = EX_IOERR;
1147
1149
        }
1148
1150
        goto fallback;
1161
1163
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1162
1164
                               + (size_t) BUFFER_SIZE);
1163
1165
        if(proc->buffer == NULL){
1164
 
          perror("malloc");
 
1166
          error(0, errno, "malloc");
1165
1167
          exitstatus = EX_OSERR;
1166
1168
          goto fallback;
1167
1169
        }
1204
1206
    }
1205
1207
    bret = print_out_password(passwordbuffer, len);
1206
1208
    if(not bret){
1207
 
      perror("print_out_password");
 
1209
      error(0, errno, "print_out_password");
1208
1210
      exitstatus = EX_IOERR;
1209
1211
    }
1210
1212
  }
1212
1214
  /* Restore old signal handler */
1213
1215
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1214
1216
  if(ret == -1){
1215
 
    perror("sigaction");
 
1217
    error(0, errno, "sigaction");
1216
1218
    exitstatus = EX_OSERR;
1217
1219
  }
1218
1220
  
1234
1236
      ret = kill(p->pid, SIGTERM);
1235
1237
      if(ret == -1 and errno != ESRCH){
1236
1238
        /* Set-uid proccesses might not get closed */
1237
 
        perror("kill");
 
1239
        error(0, errno, "kill");
1238
1240
      }
1239
1241
    }
1240
1242
  }
1244
1246
    ret = wait(NULL);
1245
1247
  } while(ret >= 0);
1246
1248
  if(errno != ECHILD){
1247
 
    perror("wait");
 
1249
    error(0, errno, "wait");
1248
1250
  }
1249
1251
  
1250
1252
  free_plugin_list();