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