/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: 2008-12-10 01:26:02 UTC
  • mfrom: (237.1.2 mandos)
  • Revision ID: teddy@fukt.bsnet.se-20081210012602-vhz3h75xkj24t340
First version of a somewhat complete D-Bus server interface.  Also
change user/group name to "_mandos".

* debian/mandos.postinst: Rename old "mandos" user and group to
                          "_mandos"; create "_mandos" user and group
                          if none exist.
* debian/mandos-client.postinst: - '' -

* initramfs-tools-hook: Try "_mandos" before "mandos" as user and
                        group name.

* mandos (_datetime_to_dbus_struct): New; was previously local.
  (Client.started): Renamed to "last_started".  All users changed.
  (Client.started): New; boolean.
  (Client.dbus_object_path): New.
  (Client.check_command): Renamed to "checker_command".  All users
                          changed.
  (Client.__init__): Set and use "self.dbus_object_path".  Set
                     "self.started".
  (Client.start): Update "self.started".  Emit "self.PropertyChanged"
                  signals for both "started" and "last_started".
  (Client.stop): Update "self.started".  Emit "self.PropertyChanged"
                 signal for "started".
  (Client.checker_callback): Take additional "command" argument.  All
                             callers changed. Emit
                             "self.PropertyChanged" signal.
  (Client.bump_timeout): Emit "self.PropertyChanged" signal for
                         "last_checked_ok".
  (Client.start_checker): Emit "self.PropertyChanged" signal for
                          "checker_running".
  (Client.stop_checker): Emit "self.PropertyChanged" signal for
                         "checker_running".
  (Client.still_valid): Bug fix: use "getattr(self, started, False)"
                        instead of "self.started" in case this client
                        object is so new that the "started" attribute
                        has not been created yet.
  (Client.IntervalChanged, Client.CheckerIsRunning, Client.GetChecker,
  Client.GetCreated, Client.GetFingerprint, Client.GetHost,
  Client.GetInterval, Client.GetName, Client.GetStarted,
  Client.GetTimeout, Client.StateChanged, Client.TimeoutChanged):
  Removed; all callers changed.
  (Client.CheckerCompleted): Add "condition" and "command" arguments.
                             All callers changed.
  (Client.GetAllProperties, Client.PropertyChanged): New.
  (Client.StillValid): Renamed to "IsStillValid".
  (Client.StartChecker): Changed to its own function to avoid the
                         return value from "Client.start_checker()".
  (Client.Stop): Changed to its own function to avoid the return value
                 from "Client.stop()".
  (main): Try "_mandos" before "mandos" as user and group name.
          Removed inner function "remove_from_clients".  New inner
          class "MandosServer".

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 Teddy Hogeborn
 
6
 * Copyright © 2008 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
64
64
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
65
65
                                   SIG_UNBLOCK, kill() */
66
66
#include <errno.h>              /* errno, EBADF */
67
 
#include <inttypes.h>           /* intmax_t, SCNdMAX, PRIdMAX,  */
68
67
 
69
68
#define BUFFER_SIZE 256
70
69
 
100
99
   or if none is found, creates a new one */
101
100
static plugin *getplugin(char *name){
102
101
  /* Check for exiting plugin with that name */
103
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
104
 
    if((p->name == name)
105
 
       or (p->name and name and (strcmp(p->name, name) == 0))){
 
102
  for (plugin *p = plugin_list; p != NULL; p = p->next){
 
103
    if ((p->name == name)
 
104
        or (p->name and name and (strcmp(p->name, name) == 0))){
106
105
      return p;
107
106
    }
108
107
  }
109
108
  /* Create a new plugin */
110
109
  plugin *new_plugin = malloc(sizeof(plugin));
111
 
  if(new_plugin == NULL){
 
110
  if (new_plugin == NULL){
112
111
    return NULL;
113
112
  }
114
113
  char *copy_name = NULL;
125
124
                           .next = plugin_list };
126
125
  
127
126
  new_plugin->argv = malloc(sizeof(char *) * 2);
128
 
  if(new_plugin->argv == NULL){
 
127
  if (new_plugin->argv == NULL){
129
128
    free(copy_name);
130
129
    free(new_plugin);
131
130
    return NULL;
231
230
      break;
232
231
    }
233
232
    if(pid == -1){
234
 
      if(errno != ECHILD){
 
233
      if (errno != ECHILD){
235
234
        perror("waitpid");
236
235
      }
237
236
      /* No child processes */
309
308
  struct dirent *dirst;
310
309
  struct stat st;
311
310
  fd_set rfds_all;
312
 
  int ret, numchars, maxfd = 0;
313
 
  ssize_t sret;
314
 
  intmax_t tmpmax;
 
311
  int ret, maxfd = 0;
315
312
  uid_t uid = 65534;
316
313
  gid_t gid = 65534;
317
314
  bool debug = false;
374
371
    { .name = NULL }
375
372
  };
376
373
  
377
 
  error_t parse_opt(int key, char *arg, __attribute__((unused))
378
 
                    struct argp_state *state) {
379
 
    switch(key) {
 
374
  error_t parse_opt (int key, char *arg, __attribute__((unused))
 
375
                     struct argp_state *state) {
 
376
    switch (key) {
380
377
    case 'g':                   /* --global-options */
381
 
      if(arg != NULL){
 
378
      if (arg != NULL){
382
379
        char *p;
383
380
        while((p = strsep(&arg, ",")) != NULL){
384
381
          if(p[0] == '\0'){
400
397
      }
401
398
      break;
402
399
    case 'o':                   /* --options-for */
403
 
      if(arg != NULL){
 
400
      if (arg != NULL){
404
401
        char *p_name = strsep(&arg, ":");
405
402
        if(p_name[0] == '\0' or arg == NULL){
406
403
          break;
437
434
      }
438
435
      break;
439
436
    case 'd':                   /* --disable */
440
 
      if(arg != NULL){
 
437
      if (arg != NULL){
441
438
        plugin *p = getplugin(arg);
442
439
        if(p == NULL){
443
440
          return ARGP_ERR_UNKNOWN;
446
443
      }
447
444
      break;
448
445
    case 'e':                   /* --enable */
449
 
      if(arg != NULL){
 
446
      if (arg != NULL){
450
447
        plugin *p = getplugin(arg);
451
448
        if(p == NULL){
452
449
          return ARGP_ERR_UNKNOWN;
465
462
      /* This is already done by parse_opt_config_file() */
466
463
      break;
467
464
    case 130:                   /* --userid */
468
 
      ret = sscanf(arg, "%" SCNdMAX "%n", &tmpmax, &numchars);
469
 
      if(ret < 1 or tmpmax != (uid_t)tmpmax
470
 
         or arg[numchars] != '\0'){
471
 
        fprintf(stderr, "Bad user ID number: \"%s\", using %"
472
 
                PRIdMAX "\n", arg, (intmax_t)uid);
473
 
      } else {
474
 
        uid = (uid_t)tmpmax;
475
 
      }
 
465
      uid = (uid_t)strtol(arg, NULL, 10);
476
466
      break;
477
467
    case 131:                   /* --groupid */
478
 
      ret = sscanf(arg, "%" SCNdMAX "%n", &tmpmax, &numchars);
479
 
      if(ret < 1 or tmpmax != (gid_t)tmpmax
480
 
         or arg[numchars] != '\0'){
481
 
        fprintf(stderr, "Bad group ID number: \"%s\", using %"
482
 
                PRIdMAX "\n", arg, (intmax_t)gid);
483
 
      } else {
484
 
        gid = (gid_t)tmpmax;
485
 
      }
 
468
      gid = (gid_t)strtol(arg, NULL, 10);
486
469
      break;
487
470
    case 132:                   /* --debug */
488
471
      debug = true;
489
472
      break;
490
 
/*
491
 
 * When adding more options before this line, remember to also add a
492
 
 * "case" to the "parse_opt_config_file" function below.
493
 
 */
494
473
    case ARGP_KEY_ARG:
495
474
      /* Cryptsetup always passes an argument, which is an empty
496
475
         string if "none" was specified in /etc/crypttab.  So if
509
488
  
510
489
  /* This option parser is the same as parse_opt() above, except it
511
490
     ignores everything but the --config-file option. */
512
 
  error_t parse_opt_config_file(int key, char *arg,
513
 
                                __attribute__((unused))
514
 
                                struct argp_state *state) {
515
 
    switch(key) {
 
491
  error_t parse_opt_config_file (int key, char *arg,
 
492
                                 __attribute__((unused))
 
493
                                 struct argp_state *state) {
 
494
    switch (key) {
516
495
    case 'g':                   /* --global-options */
517
496
    case 'G':                   /* --global-env */
518
497
    case 'o':                   /* --options-for */
545
524
                       .args_doc = "",
546
525
                       .doc = "Mandos plugin runner -- Run plugins" };
547
526
  
548
 
  /* Parse using parse_opt_config_file() in order to get the custom
 
527
  /* Parse using the parse_opt_config_file in order to get the custom
549
528
     config file location, if any. */
550
 
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
551
 
  if(ret == ARGP_ERR_UNKNOWN){
 
529
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
530
  if (ret == ARGP_ERR_UNKNOWN){
552
531
    fprintf(stderr, "Unknown error while parsing arguments\n");
553
532
    exitstatus = EXIT_FAILURE;
554
533
    goto fallback;
558
537
  argp.parser = parse_opt;
559
538
  
560
539
  /* Open the configfile if available */
561
 
  if(argfile == NULL){
 
540
  if (argfile == NULL){
562
541
    conffp = fopen(AFILE, "r");
563
542
  } else {
564
543
    conffp = fopen(argfile, "r");
567
546
    char *org_line = NULL;
568
547
    char *p, *arg, *new_arg, *line;
569
548
    size_t size = 0;
 
549
    ssize_t sret;
570
550
    const char whitespace_delims[] = " \r\t\f\v\n";
571
551
    const char comment_delim[] = "#";
572
552
 
619
599
  } else {
620
600
    /* Check for harmful errors and go to fallback. Other errors might
621
601
       not affect opening plugins */
622
 
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
 
602
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
623
603
      perror("fopen");
624
604
      exitstatus = EXIT_FAILURE;
625
605
      goto fallback;
628
608
  /* If there was any arguments from configuration file,
629
609
     pass them to parser as command arguments */
630
610
  if(custom_argv != NULL){
631
 
    ret = argp_parse(&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
632
 
                     0, NULL);
633
 
    if(ret == ARGP_ERR_UNKNOWN){
 
611
    ret = argp_parse (&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
 
612
                      0, NULL);
 
613
    if (ret == ARGP_ERR_UNKNOWN){
634
614
      fprintf(stderr, "Unknown error while parsing arguments\n");
635
615
      exitstatus = EXIT_FAILURE;
636
616
      goto fallback;
639
619
  
640
620
  /* Parse actual command line arguments, to let them override the
641
621
     config file */
642
 
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
643
 
  if(ret == ARGP_ERR_UNKNOWN){
 
622
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
623
  if (ret == ARGP_ERR_UNKNOWN){
644
624
    fprintf(stderr, "Unknown error while parsing arguments\n");
645
625
    exitstatus = EXIT_FAILURE;
646
626
    goto fallback;
653
633
      for(char **a = p->argv; *a != NULL; a++){
654
634
        fprintf(stderr, "\tArg: %s\n", *a);
655
635
      }
656
 
      fprintf(stderr, "...and %d environment variables\n", p->envc);
 
636
      fprintf(stderr, "...and %u environment variables\n", p->envc);
657
637
      for(char **a = p->environ; *a != NULL; a++){
658
638
        fprintf(stderr, "\t%s\n", *a);
659
639
      }
662
642
  
663
643
  /* Strip permissions down to nobody */
664
644
  ret = setuid(uid);
665
 
  if(ret == -1){
 
645
  if (ret == -1){
666
646
    perror("setuid");
667
647
  }  
668
648
  setgid(gid);
669
 
  if(ret == -1){
 
649
  if (ret == -1){
670
650
    perror("setgid");
671
651
  }
672
652
  
673
 
  if(plugindir == NULL){
 
653
  if (plugindir == NULL){
674
654
    dir = opendir(PDIR);
675
655
  } else {
676
656
    dir = opendir(plugindir);
703
683
    
704
684
    /* All directory entries have been processed */
705
685
    if(dirst == NULL){
706
 
      if(errno == EBADF){
 
686
      if (errno == EBADF){
707
687
        perror("readdir");
708
688
        exitstatus = EXIT_FAILURE;
709
689
        goto fallback;
769
749
    }
770
750
    
771
751
    ret = stat(filename, &st);
772
 
    if(ret == -1){
 
752
    if (ret == -1){
773
753
      perror("stat");
774
754
      free(filename);
775
755
      continue;
776
756
    }
777
757
 
778
758
    /* Ignore non-executable files */
779
 
    if(not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
 
759
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
780
760
      if(debug){
781
761
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
782
762
                " with bad type or mode\n", filename);
829
809
    
830
810
    int pipefd[2];
831
811
    ret = pipe(pipefd);
832
 
    if(ret == -1){
 
812
    if (ret == -1){
833
813
      perror("pipe");
834
814
      exitstatus = EXIT_FAILURE;
835
815
      goto fallback;
848
828
      goto fallback;
849
829
    }
850
830
    /* Block SIGCHLD until process is safely in process list */
851
 
    ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
 
831
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
852
832
    if(ret < 0){
853
833
      perror("sigprocmask");
854
834
      exitstatus = EXIT_FAILURE;
902
882
    close(pipefd[1]);           /* Close unused write end of pipe */
903
883
    free(filename);
904
884
    plugin *new_plugin = getplugin(dirst->d_name);
905
 
    if(new_plugin == NULL){
 
885
    if (new_plugin == NULL){
906
886
      perror("getplugin");
907
 
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
887
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
908
888
      if(ret < 0){
909
889
        perror("sigprocmask");
910
890
      }
917
897
    
918
898
    /* Unblock SIGCHLD so signal handler can be run if this process
919
899
       has already completed */
920
 
    ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
900
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
921
901
    if(ret < 0){
922
902
      perror("sigprocmask");
923
903
      exitstatus = EXIT_FAILURE;
926
906
    
927
907
    FD_SET(new_plugin->fd, &rfds_all);
928
908
    
929
 
    if(maxfd < new_plugin->fd){
 
909
    if (maxfd < new_plugin->fd){
930
910
      maxfd = new_plugin->fd;
931
911
    }
932
912
  }
949
929
  while(plugin_list){
950
930
    fd_set rfds = rfds_all;
951
931
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
952
 
    if(select_ret == -1){
 
932
    if (select_ret == -1){
953
933
      perror("select");
954
934
      exitstatus = EXIT_FAILURE;
955
935
      goto fallback;
966
946
 
967
947
          if(debug){
968
948
            if(WIFEXITED(proc->status)){
969
 
              fprintf(stderr, "Plugin %" PRIdMAX " exited with status"
970
 
                      " %d\n", (intmax_t) (proc->pid),
 
949
              fprintf(stderr, "Plugin %u exited with status %d\n",
 
950
                      (unsigned int) (proc->pid),
971
951
                      WEXITSTATUS(proc->status));
972
952
            } else if(WIFSIGNALED(proc->status)) {
973
 
              fprintf(stderr, "Plugin %" PRIdMAX " killed by signal"
974
 
                      " %d\n", (intmax_t) (proc->pid),
 
953
              fprintf(stderr, "Plugin %u killed by signal %d\n",
 
954
                      (unsigned int) (proc->pid),
975
955
                      WTERMSIG(proc->status));
976
956
            } else if(WCOREDUMP(proc->status)){
977
 
              fprintf(stderr, "Plugin %" PRIdMAX " dumped core\n",
978
 
                      (intmax_t) (proc->pid));
 
957
              fprintf(stderr, "Plugin %d dumped core\n",
 
958
                      (unsigned int) (proc->pid));
979
959
            }
980
960
          }
981
961
          
995
975
          proc = next_plugin;
996
976
          
997
977
          /* We are done modifying process list, so unblock signal */
998
 
          ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
999
 
                            NULL);
 
978
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
 
979
                             NULL);
1000
980
          if(ret < 0){
1001
981
            perror("sigprocmask");
1002
982
            exitstatus = EXIT_FAILURE;
1031
1011
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1032
1012
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1033
1013
                               + (size_t) BUFFER_SIZE);
1034
 
        if(proc->buffer == NULL){
 
1014
        if (proc->buffer == NULL){
1035
1015
          perror("malloc");
1036
1016
          exitstatus = EXIT_FAILURE;
1037
1017
          goto fallback;
1039
1019
        proc->buffer_size += BUFFER_SIZE;
1040
1020
      }
1041
1021
      /* Read from the process */
1042
 
      sret = read(proc->fd, proc->buffer + proc->buffer_length,
1043
 
                  BUFFER_SIZE);
1044
 
      if(sret < 0){
 
1022
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
 
1023
                 BUFFER_SIZE);
 
1024
      if(ret < 0){
1045
1025
        /* Read error from this process; ignore the error */
1046
1026
        proc = proc->next;
1047
1027
        continue;
1048
1028
      }
1049
 
      if(sret == 0){
 
1029
      if(ret == 0){
1050
1030
        /* got EOF */
1051
1031
        proc->eof = true;
1052
1032
      } else {
1053
 
        proc->buffer_length += (size_t) sret;
 
1033
        proc->buffer_length += (size_t) ret;
1054
1034
      }
1055
1035
    }
1056
1036
  }