/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: 2011-12-31 20:07:11 UTC
  • mfrom: (535.1.9 wireless-network-hook)
  • Revision ID: teddy@recompile.se-20111231200711-6dli3r8drftem57r
Merge new wireless network hook.  Fix bridge network hook to use
hardware addresses instead of interface names.  Implement and document
new "CONNECT" environment variable for network hooks.

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-2011 Teddy Hogeborn
 
6
 * Copyright © 2008-2011 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@fukt.bsnet.se>.
 
22
 * Contact the authors at <mandos@recompile.se>.
23
23
 */
24
24
 
25
25
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
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
 
77
79
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
78
80
 
79
81
const char *argp_program_version = "plugin-runner " VERSION;
80
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
82
const char *argp_program_bug_address = "<mandos@recompile.se>";
81
83
 
82
84
typedef struct plugin{
83
85
  char *name;                   /* can be NULL or any plugin name */
169
171
}
170
172
 
171
173
/* Helper function for add_argument and add_environment */
 
174
__attribute__((nonnull))
172
175
static bool add_to_char_array(const char *new, char ***array,
173
176
                              int *len){
174
177
  /* Resize the pointed-to array to hold one more pointer */
197
200
}
198
201
 
199
202
/* Add to a plugin's argument vector */
 
203
__attribute__((nonnull(2)))
200
204
static bool add_argument(plugin *p, const char *arg){
201
205
  if(p == NULL){
202
206
    return false;
205
209
}
206
210
 
207
211
/* Add to a plugin's environment */
 
212
__attribute__((nonnull(2)))
208
213
static bool add_environment(plugin *p, const char *def, bool replace){
209
214
  if(p == NULL){
210
215
    return false;
266
271
        /* No child processes */
267
272
        break;
268
273
      }
269
 
      perror("waitpid");
 
274
      error(0, errno, "waitpid");
270
275
    }
271
276
    
272
277
    /* A child exited, find it in process_list */
284
289
}
285
290
 
286
291
/* Prints out a password to stdout */
 
292
__attribute__((nonnull))
287
293
static bool print_out_password(const char *buffer, size_t length){
288
294
  ssize_t ret;
289
295
  for(size_t written = 0; written < length; written += (size_t)ret){
297
303
}
298
304
 
299
305
/* Removes and free a plugin from the plugin list */
 
306
__attribute__((nonnull))
300
307
static void free_plugin(plugin *plugin_node){
301
308
  
302
309
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
357
364
  sigemptyset(&sigchld_action.sa_mask);
358
365
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
359
366
  if(ret == -1){
360
 
    perror("sigaddset");
 
367
    error(0, errno, "sigaddset");
361
368
    exitstatus = EX_OSERR;
362
369
    goto fallback;
363
370
  }
364
371
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
365
372
  if(ret == -1){
366
 
    perror("sigaction");
 
373
    error(0, errno, "sigaction");
367
374
    exitstatus = EX_OSERR;
368
375
    goto fallback;
369
376
  }
414
421
    { .name = NULL }
415
422
  };
416
423
  
 
424
  __attribute__((nonnull(3)))
417
425
  error_t parse_opt(int key, char *arg, struct argp_state *state){
418
426
    errno = 0;
419
427
    switch(key){
420
428
      char *tmp;
421
 
      intmax_t tmpmax;
 
429
      intmax_t tmp_id;
422
430
    case 'g':                   /* --global-options */
423
431
      {
424
432
        char *plugin_option;
497
505
      /* This is already done by parse_opt_config_file() */
498
506
      break;
499
507
    case 130:                   /* --userid */
500
 
      tmpmax = strtoimax(arg, &tmp, 10);
 
508
      tmp_id = strtoimax(arg, &tmp, 10);
501
509
      if(errno != 0 or tmp == arg or *tmp != '\0'
502
 
         or tmpmax != (uid_t)tmpmax){
 
510
         or tmp_id != (uid_t)tmp_id){
503
511
        argp_error(state, "Bad user ID number: \"%s\", using %"
504
512
                   PRIdMAX, arg, (intmax_t)uid);
505
513
        break;
506
514
      }
507
 
      uid = (uid_t)tmpmax;
 
515
      uid = (uid_t)tmp_id;
508
516
      break;
509
517
    case 131:                   /* --groupid */
510
 
      tmpmax = strtoimax(arg, &tmp, 10);
 
518
      tmp_id = strtoimax(arg, &tmp, 10);
511
519
      if(errno != 0 or tmp == arg or *tmp != '\0'
512
 
         or tmpmax != (gid_t)tmpmax){
 
520
         or tmp_id != (gid_t)tmp_id){
513
521
        argp_error(state, "Bad group ID number: \"%s\", using %"
514
522
                   PRIdMAX, arg, (intmax_t)gid);
515
523
        break;
516
524
      }
517
 
      gid = (gid_t)tmpmax;
 
525
      gid = (gid_t)tmp_id;
518
526
      break;
519
527
    case 132:                   /* --debug */
520
528
      debug = true;
599
607
  case ENOMEM:
600
608
  default:
601
609
    errno = ret;
602
 
    perror("argp_parse");
 
610
    error(0, errno, "argp_parse");
603
611
    exitstatus = EX_OSERR;
604
612
    goto fallback;
605
613
  case EINVAL:
626
634
    custom_argc = 1;
627
635
    custom_argv = malloc(sizeof(char*) * 2);
628
636
    if(custom_argv == NULL){
629
 
      perror("malloc");
 
637
      error(0, errno, "malloc");
630
638
      exitstatus = EX_OSERR;
631
639
      goto fallback;
632
640
    }
649
657
        }
650
658
        new_arg = strdup(p);
651
659
        if(new_arg == NULL){
652
 
          perror("strdup");
 
660
          error(0, errno, "strdup");
653
661
          exitstatus = EX_OSERR;
654
662
          free(org_line);
655
663
          goto fallback;
659
667
        custom_argv = realloc(custom_argv, sizeof(char *)
660
668
                              * ((unsigned int) custom_argc + 1));
661
669
        if(custom_argv == NULL){
662
 
          perror("realloc");
 
670
          error(0, errno, "realloc");
663
671
          exitstatus = EX_OSERR;
664
672
          free(org_line);
665
673
          goto fallback;
672
680
      ret = fclose(conffp);
673
681
    } while(ret == EOF and errno == EINTR);
674
682
    if(ret == EOF){
675
 
      perror("fclose");
 
683
      error(0, errno, "fclose");
676
684
      exitstatus = EX_IOERR;
677
685
      goto fallback;
678
686
    }
681
689
    /* Check for harmful errors and go to fallback. Other errors might
682
690
       not affect opening plugins */
683
691
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
684
 
      perror("fopen");
 
692
      error(0, errno, "fopen");
685
693
      exitstatus = EX_OSERR;
686
694
      goto fallback;
687
695
    }
698
706
    case ENOMEM:
699
707
    default:
700
708
      errno = ret;
701
 
      perror("argp_parse");
 
709
      error(0, errno, "argp_parse");
702
710
      exitstatus = EX_OSERR;
703
711
      goto fallback;
704
712
    case EINVAL:
718
726
  case ENOMEM:
719
727
  default:
720
728
    errno = ret;
721
 
    perror("argp_parse");
 
729
    error(0, errno, "argp_parse");
722
730
    exitstatus = EX_OSERR;
723
731
    goto fallback;
724
732
  case EINVAL:
740
748
    }
741
749
  }
742
750
  
743
 
  /* Strip permissions down to nobody */
 
751
  if(getuid() == 0){
 
752
    /* Work around Debian bug #633582:
 
753
       <http://bugs.debian.org/633582> */
 
754
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
 
755
    if(plugindir_fd == -1){
 
756
      error(0, errno, "open");
 
757
    } else {
 
758
      ret = (int)TEMP_FAILURE_RETRY(fstat(plugindir_fd, &st));
 
759
      if(ret == -1){
 
760
        error(0, errno, "fstat");
 
761
      } else {
 
762
        if(S_ISDIR(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
 
763
          ret = fchown(plugindir_fd, uid, gid);
 
764
          if(ret == -1){
 
765
            error(0, errno, "fchown");
 
766
          }
 
767
        }
 
768
      }
 
769
      TEMP_FAILURE_RETRY(close(plugindir_fd));
 
770
    }
 
771
  }
 
772
  
 
773
  /* Lower permissions */
744
774
  setgid(gid);
745
775
  if(ret == -1){
746
 
    perror("setgid");
 
776
    error(0, errno, "setgid");
747
777
  }
748
778
  ret = setuid(uid);
749
779
  if(ret == -1){
750
 
    perror("setuid");
 
780
    error(0, errno, "setuid");
751
781
  }
752
782
  
753
783
  /* Open plugin directory with close_on_exec flag */
771
801
                    );
772
802
    }
773
803
    if(dir_fd == -1){
774
 
      perror("Could not open plugin dir");
 
804
      error(0, errno, "Could not open plugin dir");
775
805
      exitstatus = EX_UNAVAILABLE;
776
806
      goto fallback;
777
807
    }
780
810
  /* Set the FD_CLOEXEC flag on the directory */
781
811
    ret = set_cloexec_flag(dir_fd);
782
812
    if(ret < 0){
783
 
      perror("set_cloexec_flag");
 
813
      error(0, errno, "set_cloexec_flag");
784
814
      TEMP_FAILURE_RETRY(close(dir_fd));
785
815
      exitstatus = EX_OSERR;
786
816
      goto fallback;
789
819
    
790
820
    dir = fdopendir(dir_fd);
791
821
    if(dir == NULL){
792
 
      perror("Could not open plugin dir");
 
822
      error(0, errno, "Could not open plugin dir");
793
823
      TEMP_FAILURE_RETRY(close(dir_fd));
794
824
      exitstatus = EX_OSERR;
795
825
      goto fallback;
807
837
    /* All directory entries have been processed */
808
838
    if(dirst == NULL){
809
839
      if(errno == EBADF){
810
 
        perror("readdir");
 
840
        error(0, errno, "readdir");
811
841
        exitstatus = EX_IOERR;
812
842
        goto fallback;
813
843
      }
870
900
                                             dirst->d_name));
871
901
    }
872
902
    if(ret < 0){
873
 
      perror("asprintf");
 
903
      error(0, errno, "asprintf");
874
904
      continue;
875
905
    }
876
906
    
877
907
    ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
878
908
    if(ret == -1){
879
 
      perror("stat");
 
909
      error(0, errno, "stat");
880
910
      free(filename);
881
911
      continue;
882
912
    }
894
924
    
895
925
    plugin *p = getplugin(dirst->d_name);
896
926
    if(p == NULL){
897
 
      perror("getplugin");
 
927
      error(0, errno, "getplugin");
898
928
      free(filename);
899
929
      continue;
900
930
    }
912
942
      if(g != NULL){
913
943
        for(char **a = g->argv + 1; *a != NULL; a++){
914
944
          if(not add_argument(p, *a)){
915
 
            perror("add_argument");
 
945
            error(0, errno, "add_argument");
916
946
          }
917
947
        }
918
948
        /* Add global environment variables */
919
949
        for(char **e = g->environ; *e != NULL; e++){
920
950
          if(not add_environment(p, *e, false)){
921
 
            perror("add_environment");
 
951
            error(0, errno, "add_environment");
922
952
          }
923
953
        }
924
954
      }
929
959
    if(p->environ[0] != NULL){
930
960
      for(char **e = environ; *e != NULL; e++){
931
961
        if(not add_environment(p, *e, false)){
932
 
          perror("add_environment");
 
962
          error(0, errno, "add_environment");
933
963
        }
934
964
      }
935
965
    }
937
967
    int pipefd[2];
938
968
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
939
969
    if(ret == -1){
940
 
      perror("pipe");
 
970
      error(0, errno, "pipe");
941
971
      exitstatus = EX_OSERR;
942
972
      goto fallback;
943
973
    }
944
974
    /* Ask OS to automatic close the pipe on exec */
945
975
    ret = set_cloexec_flag(pipefd[0]);
946
976
    if(ret < 0){
947
 
      perror("set_cloexec_flag");
 
977
      error(0, errno, "set_cloexec_flag");
948
978
      exitstatus = EX_OSERR;
949
979
      goto fallback;
950
980
    }
951
981
    ret = set_cloexec_flag(pipefd[1]);
952
982
    if(ret < 0){
953
 
      perror("set_cloexec_flag");
 
983
      error(0, errno, "set_cloexec_flag");
954
984
      exitstatus = EX_OSERR;
955
985
      goto fallback;
956
986
    }
959
989
                                              &sigchld_action.sa_mask,
960
990
                                              NULL));
961
991
    if(ret < 0){
962
 
      perror("sigprocmask");
 
992
      error(0, errno, "sigprocmask");
963
993
      exitstatus = EX_OSERR;
964
994
      goto fallback;
965
995
    }
969
999
      pid = fork();
970
1000
    } while(pid == -1 and errno == EINTR);
971
1001
    if(pid == -1){
972
 
      perror("fork");
 
1002
      error(0, errno, "fork");
973
1003
      exitstatus = EX_OSERR;
974
1004
      goto fallback;
975
1005
    }
977
1007
      /* this is the child process */
978
1008
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
979
1009
      if(ret < 0){
980
 
        perror("sigaction");
 
1010
        error(0, errno, "sigaction");
981
1011
        _exit(EX_OSERR);
982
1012
      }
983
1013
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
984
1014
      if(ret < 0){
985
 
        perror("sigprocmask");
 
1015
        error(0, errno, "sigprocmask");
986
1016
        _exit(EX_OSERR);
987
1017
      }
988
1018
      
989
1019
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
990
1020
      if(ret == -1){
991
 
        perror("dup2");
 
1021
        error(0, errno, "dup2");
992
1022
        _exit(EX_OSERR);
993
1023
      }
994
1024
      
999
1029
      }
1000
1030
      if(p->environ[0] == NULL){
1001
1031
        if(execv(filename, p->argv) < 0){
1002
 
          perror("execv");
 
1032
          error(0, errno, "execv for %s", filename);
1003
1033
          _exit(EX_OSERR);
1004
1034
        }
1005
1035
      } else {
1006
1036
        if(execve(filename, p->argv, p->environ) < 0){
1007
 
          perror("execve");
 
1037
          error(0, errno, "execve for %s", filename);
1008
1038
          _exit(EX_OSERR);
1009
1039
        }
1010
1040
      }
1016
1046
    free(filename);
1017
1047
    plugin *new_plugin = getplugin(dirst->d_name);
1018
1048
    if(new_plugin == NULL){
1019
 
      perror("getplugin");
 
1049
      error(0, errno, "getplugin");
1020
1050
      ret = (int)(TEMP_FAILURE_RETRY
1021
1051
                  (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
1022
1052
                               NULL)));
1023
1053
      if(ret < 0){
1024
 
        perror("sigprocmask");
 
1054
        error(0, errno, "sigprocmask");
1025
1055
      }
1026
1056
      exitstatus = EX_OSERR;
1027
1057
      goto fallback;
1036
1066
                                              &sigchld_action.sa_mask,
1037
1067
                                              NULL));
1038
1068
    if(ret < 0){
1039
 
      perror("sigprocmask");
 
1069
      error(0, errno, "sigprocmask");
1040
1070
      exitstatus = EX_OSERR;
1041
1071
      goto fallback;
1042
1072
    }
1069
1099
    fd_set rfds = rfds_all;
1070
1100
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
1071
1101
    if(select_ret == -1 and errno != EINTR){
1072
 
      perror("select");
 
1102
      error(0, errno, "select");
1073
1103
      exitstatus = EX_OSERR;
1074
1104
      goto fallback;
1075
1105
    }
1111
1141
                                         &sigchld_action.sa_mask,
1112
1142
                                         NULL));
1113
1143
          if(ret < 0){
1114
 
            perror("sigprocmask");
 
1144
            error(0, errno, "sigprocmask");
1115
1145
            exitstatus = EX_OSERR;
1116
1146
            goto fallback;
1117
1147
          }
1125
1155
                      (sigprocmask(SIG_UNBLOCK,
1126
1156
                                   &sigchld_action.sa_mask, NULL)));
1127
1157
          if(ret < 0){
1128
 
            perror("sigprocmask");
 
1158
            error(0, errno, "sigprocmask");
1129
1159
            exitstatus = EX_OSERR;
1130
1160
            goto fallback;
1131
1161
          }
1142
1172
        bool bret = print_out_password(proc->buffer,
1143
1173
                                       proc->buffer_length);
1144
1174
        if(not bret){
1145
 
          perror("print_out_password");
 
1175
          error(0, errno, "print_out_password");
1146
1176
          exitstatus = EX_IOERR;
1147
1177
        }
1148
1178
        goto fallback;
1161
1191
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1162
1192
                               + (size_t) BUFFER_SIZE);
1163
1193
        if(proc->buffer == NULL){
1164
 
          perror("malloc");
 
1194
          error(0, errno, "malloc");
1165
1195
          exitstatus = EX_OSERR;
1166
1196
          goto fallback;
1167
1197
        }
1204
1234
    }
1205
1235
    bret = print_out_password(passwordbuffer, len);
1206
1236
    if(not bret){
1207
 
      perror("print_out_password");
 
1237
      error(0, errno, "print_out_password");
1208
1238
      exitstatus = EX_IOERR;
1209
1239
    }
1210
1240
  }
1212
1242
  /* Restore old signal handler */
1213
1243
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1214
1244
  if(ret == -1){
1215
 
    perror("sigaction");
 
1245
    error(0, errno, "sigaction");
1216
1246
    exitstatus = EX_OSERR;
1217
1247
  }
1218
1248
  
1234
1264
      ret = kill(p->pid, SIGTERM);
1235
1265
      if(ret == -1 and errno != ESRCH){
1236
1266
        /* Set-uid proccesses might not get closed */
1237
 
        perror("kill");
 
1267
        error(0, errno, "kill");
1238
1268
      }
1239
1269
    }
1240
1270
  }
1244
1274
    ret = wait(NULL);
1245
1275
  } while(ret >= 0);
1246
1276
  if(errno != ECHILD){
1247
 
    perror("wait");
 
1277
    error(0, errno, "wait");
1248
1278
  }
1249
1279
  
1250
1280
  free_plugin_list();