/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
99
99
   or if none is found, creates a new one */
100
100
static plugin *getplugin(char *name){
101
101
  /* Check for exiting plugin with that name */
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))){
 
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))){
105
105
      return p;
106
106
    }
107
107
  }
108
108
  /* Create a new plugin */
109
109
  plugin *new_plugin = malloc(sizeof(plugin));
110
 
  if(new_plugin == NULL){
 
110
  if (new_plugin == NULL){
111
111
    return NULL;
112
112
  }
113
113
  char *copy_name = NULL;
124
124
                           .next = plugin_list };
125
125
  
126
126
  new_plugin->argv = malloc(sizeof(char *) * 2);
127
 
  if(new_plugin->argv == NULL){
 
127
  if (new_plugin->argv == NULL){
128
128
    free(copy_name);
129
129
    free(new_plugin);
130
130
    return NULL;
230
230
      break;
231
231
    }
232
232
    if(pid == -1){
233
 
      if(errno != ECHILD){
 
233
      if (errno != ECHILD){
234
234
        perror("waitpid");
235
235
      }
236
236
      /* No child processes */
309
309
  struct stat st;
310
310
  fd_set rfds_all;
311
311
  int ret, maxfd = 0;
312
 
  ssize_t sret;
313
312
  uid_t uid = 65534;
314
313
  gid_t gid = 65534;
315
314
  bool debug = false;
372
371
    { .name = NULL }
373
372
  };
374
373
  
375
 
  error_t parse_opt(int key, char *arg, __attribute__((unused))
376
 
                    struct argp_state *state) {
377
 
    switch(key) {
 
374
  error_t parse_opt (int key, char *arg, __attribute__((unused))
 
375
                     struct argp_state *state) {
 
376
    switch (key) {
378
377
    case 'g':                   /* --global-options */
379
 
      if(arg != NULL){
 
378
      if (arg != NULL){
380
379
        char *p;
381
380
        while((p = strsep(&arg, ",")) != NULL){
382
381
          if(p[0] == '\0'){
398
397
      }
399
398
      break;
400
399
    case 'o':                   /* --options-for */
401
 
      if(arg != NULL){
 
400
      if (arg != NULL){
402
401
        char *p_name = strsep(&arg, ":");
403
402
        if(p_name[0] == '\0' or arg == NULL){
404
403
          break;
435
434
      }
436
435
      break;
437
436
    case 'd':                   /* --disable */
438
 
      if(arg != NULL){
 
437
      if (arg != NULL){
439
438
        plugin *p = getplugin(arg);
440
439
        if(p == NULL){
441
440
          return ARGP_ERR_UNKNOWN;
444
443
      }
445
444
      break;
446
445
    case 'e':                   /* --enable */
447
 
      if(arg != NULL){
 
446
      if (arg != NULL){
448
447
        plugin *p = getplugin(arg);
449
448
        if(p == NULL){
450
449
          return ARGP_ERR_UNKNOWN;
463
462
      /* This is already done by parse_opt_config_file() */
464
463
      break;
465
464
    case 130:                   /* --userid */
466
 
      /* In the GNU C library, uid_t is always unsigned int */
467
 
      ret = sscanf(arg, "%u", &uid);
468
 
      if(ret != 1){
469
 
        fprintf(stderr, "Bad user ID number: \"%s\", using %u\n", arg,
470
 
                uid);
471
 
      }
 
465
      uid = (uid_t)strtol(arg, NULL, 10);
472
466
      break;
473
467
    case 131:                   /* --groupid */
474
 
      /* In the GNU C library, gid_t is always unsigned int */
475
 
      ret = sscanf(arg, "%u", &gid);
476
 
      if(ret != 1){
477
 
        fprintf(stderr, "Bad group ID number: \"%s\", using %u\n",
478
 
                arg, gid);
479
 
      }
 
468
      gid = (gid_t)strtol(arg, NULL, 10);
480
469
      break;
481
470
    case 132:                   /* --debug */
482
471
      debug = true;
483
472
      break;
484
 
/*
485
 
 * When adding more options before this line, remember to also add a
486
 
 * "case" to the "parse_opt_config_file" function below.
487
 
 */
488
473
    case ARGP_KEY_ARG:
489
474
      /* Cryptsetup always passes an argument, which is an empty
490
475
         string if "none" was specified in /etc/crypttab.  So if
503
488
  
504
489
  /* This option parser is the same as parse_opt() above, except it
505
490
     ignores everything but the --config-file option. */
506
 
  error_t parse_opt_config_file(int key, char *arg,
507
 
                                __attribute__((unused))
508
 
                                struct argp_state *state) {
509
 
    switch(key) {
 
491
  error_t parse_opt_config_file (int key, char *arg,
 
492
                                 __attribute__((unused))
 
493
                                 struct argp_state *state) {
 
494
    switch (key) {
510
495
    case 'g':                   /* --global-options */
511
496
    case 'G':                   /* --global-env */
512
497
    case 'o':                   /* --options-for */
539
524
                       .args_doc = "",
540
525
                       .doc = "Mandos plugin runner -- Run plugins" };
541
526
  
542
 
  /* 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
543
528
     config file location, if any. */
544
 
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
545
 
  if(ret == ARGP_ERR_UNKNOWN){
 
529
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
530
  if (ret == ARGP_ERR_UNKNOWN){
546
531
    fprintf(stderr, "Unknown error while parsing arguments\n");
547
532
    exitstatus = EXIT_FAILURE;
548
533
    goto fallback;
552
537
  argp.parser = parse_opt;
553
538
  
554
539
  /* Open the configfile if available */
555
 
  if(argfile == NULL){
 
540
  if (argfile == NULL){
556
541
    conffp = fopen(AFILE, "r");
557
542
  } else {
558
543
    conffp = fopen(argfile, "r");
561
546
    char *org_line = NULL;
562
547
    char *p, *arg, *new_arg, *line;
563
548
    size_t size = 0;
 
549
    ssize_t sret;
564
550
    const char whitespace_delims[] = " \r\t\f\v\n";
565
551
    const char comment_delim[] = "#";
566
552
 
613
599
  } else {
614
600
    /* Check for harmful errors and go to fallback. Other errors might
615
601
       not affect opening plugins */
616
 
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
 
602
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
617
603
      perror("fopen");
618
604
      exitstatus = EXIT_FAILURE;
619
605
      goto fallback;
622
608
  /* If there was any arguments from configuration file,
623
609
     pass them to parser as command arguments */
624
610
  if(custom_argv != NULL){
625
 
    ret = argp_parse(&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
626
 
                     0, NULL);
627
 
    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){
628
614
      fprintf(stderr, "Unknown error while parsing arguments\n");
629
615
      exitstatus = EXIT_FAILURE;
630
616
      goto fallback;
633
619
  
634
620
  /* Parse actual command line arguments, to let them override the
635
621
     config file */
636
 
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
637
 
  if(ret == ARGP_ERR_UNKNOWN){
 
622
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
623
  if (ret == ARGP_ERR_UNKNOWN){
638
624
    fprintf(stderr, "Unknown error while parsing arguments\n");
639
625
    exitstatus = EXIT_FAILURE;
640
626
    goto fallback;
656
642
  
657
643
  /* Strip permissions down to nobody */
658
644
  ret = setuid(uid);
659
 
  if(ret == -1){
 
645
  if (ret == -1){
660
646
    perror("setuid");
661
647
  }  
662
648
  setgid(gid);
663
 
  if(ret == -1){
 
649
  if (ret == -1){
664
650
    perror("setgid");
665
651
  }
666
652
  
667
 
  if(plugindir == NULL){
 
653
  if (plugindir == NULL){
668
654
    dir = opendir(PDIR);
669
655
  } else {
670
656
    dir = opendir(plugindir);
697
683
    
698
684
    /* All directory entries have been processed */
699
685
    if(dirst == NULL){
700
 
      if(errno == EBADF){
 
686
      if (errno == EBADF){
701
687
        perror("readdir");
702
688
        exitstatus = EXIT_FAILURE;
703
689
        goto fallback;
763
749
    }
764
750
    
765
751
    ret = stat(filename, &st);
766
 
    if(ret == -1){
 
752
    if (ret == -1){
767
753
      perror("stat");
768
754
      free(filename);
769
755
      continue;
770
756
    }
771
757
 
772
758
    /* Ignore non-executable files */
773
 
    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)){
774
760
      if(debug){
775
761
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
776
762
                " with bad type or mode\n", filename);
823
809
    
824
810
    int pipefd[2];
825
811
    ret = pipe(pipefd);
826
 
    if(ret == -1){
 
812
    if (ret == -1){
827
813
      perror("pipe");
828
814
      exitstatus = EXIT_FAILURE;
829
815
      goto fallback;
842
828
      goto fallback;
843
829
    }
844
830
    /* Block SIGCHLD until process is safely in process list */
845
 
    ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
 
831
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
846
832
    if(ret < 0){
847
833
      perror("sigprocmask");
848
834
      exitstatus = EXIT_FAILURE;
896
882
    close(pipefd[1]);           /* Close unused write end of pipe */
897
883
    free(filename);
898
884
    plugin *new_plugin = getplugin(dirst->d_name);
899
 
    if(new_plugin == NULL){
 
885
    if (new_plugin == NULL){
900
886
      perror("getplugin");
901
 
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
887
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
902
888
      if(ret < 0){
903
889
        perror("sigprocmask");
904
890
      }
911
897
    
912
898
    /* Unblock SIGCHLD so signal handler can be run if this process
913
899
       has already completed */
914
 
    ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
900
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
915
901
    if(ret < 0){
916
902
      perror("sigprocmask");
917
903
      exitstatus = EXIT_FAILURE;
920
906
    
921
907
    FD_SET(new_plugin->fd, &rfds_all);
922
908
    
923
 
    if(maxfd < new_plugin->fd){
 
909
    if (maxfd < new_plugin->fd){
924
910
      maxfd = new_plugin->fd;
925
911
    }
926
912
  }
943
929
  while(plugin_list){
944
930
    fd_set rfds = rfds_all;
945
931
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
946
 
    if(select_ret == -1){
 
932
    if (select_ret == -1){
947
933
      perror("select");
948
934
      exitstatus = EXIT_FAILURE;
949
935
      goto fallback;
968
954
                      (unsigned int) (proc->pid),
969
955
                      WTERMSIG(proc->status));
970
956
            } else if(WCOREDUMP(proc->status)){
971
 
              fprintf(stderr, "Plugin %u dumped core\n",
 
957
              fprintf(stderr, "Plugin %d dumped core\n",
972
958
                      (unsigned int) (proc->pid));
973
959
            }
974
960
          }
989
975
          proc = next_plugin;
990
976
          
991
977
          /* We are done modifying process list, so unblock signal */
992
 
          ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
993
 
                            NULL);
 
978
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
 
979
                             NULL);
994
980
          if(ret < 0){
995
981
            perror("sigprocmask");
996
982
            exitstatus = EXIT_FAILURE;
1025
1011
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
1026
1012
        proc->buffer = realloc(proc->buffer, proc->buffer_size
1027
1013
                               + (size_t) BUFFER_SIZE);
1028
 
        if(proc->buffer == NULL){
 
1014
        if (proc->buffer == NULL){
1029
1015
          perror("malloc");
1030
1016
          exitstatus = EXIT_FAILURE;
1031
1017
          goto fallback;
1033
1019
        proc->buffer_size += BUFFER_SIZE;
1034
1020
      }
1035
1021
      /* Read from the process */
1036
 
      sret = read(proc->fd, proc->buffer + proc->buffer_length,
1037
 
                  BUFFER_SIZE);
1038
 
      if(sret < 0){
 
1022
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
 
1023
                 BUFFER_SIZE);
 
1024
      if(ret < 0){
1039
1025
        /* Read error from this process; ignore the error */
1040
1026
        proc = proc->next;
1041
1027
        continue;
1042
1028
      }
1043
 
      if(sret == 0){
 
1029
      if(ret == 0){
1044
1030
        /* got EOF */
1045
1031
        proc->eof = true;
1046
1032
      } else {
1047
 
        proc->buffer_length += (size_t) sret;
 
1033
        proc->buffer_length += (size_t) ret;
1048
1034
      }
1049
1035
    }
1050
1036
  }