/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

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