/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
62
62
#include <signal.h>             /* struct sigaction, sigemptyset(),
63
63
                                   sigaddset(), sigaction(),
64
64
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
65
 
                                   SIG_UNBLOCK, kill(), sig_atomic_t
66
 
                                */
 
65
                                   SIG_UNBLOCK, kill() */
67
66
#include <errno.h>              /* errno, EBADF */
68
 
#include <inttypes.h>           /* intmax_t, SCNdMAX, PRIdMAX,  */
69
67
 
70
68
#define BUFFER_SIZE 256
71
69
 
90
88
  size_t buffer_size;
91
89
  size_t buffer_length;
92
90
  bool eof;
93
 
  volatile sig_atomic_t completed;
94
 
  int status;
 
91
  volatile bool completed;
 
92
  volatile int status;
95
93
  struct plugin *next;
96
94
} plugin;
97
95
 
101
99
   or if none is found, creates a new one */
102
100
static plugin *getplugin(char *name){
103
101
  /* Check for exiting plugin with that name */
104
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
105
 
    if((p->name == name)
106
 
       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))){
107
105
      return p;
108
106
    }
109
107
  }
110
108
  /* Create a new plugin */
111
109
  plugin *new_plugin = malloc(sizeof(plugin));
112
 
  if(new_plugin == NULL){
 
110
  if (new_plugin == NULL){
113
111
    return NULL;
114
112
  }
115
113
  char *copy_name = NULL;
116
114
  if(name != NULL){
117
115
    copy_name = strdup(name);
118
116
    if(copy_name == NULL){
119
 
      free(new_plugin);
120
117
      return NULL;
121
118
    }
122
119
  }
123
120
  
124
 
  *new_plugin = (plugin){ .name = copy_name,
125
 
                          .argc = 1,
126
 
                          .disabled = false,
127
 
                          .next = plugin_list };
 
121
  *new_plugin = (plugin) { .name = copy_name,
 
122
                           .argc = 1,
 
123
                           .disabled = false,
 
124
                           .next = plugin_list };
128
125
  
129
126
  new_plugin->argv = malloc(sizeof(char *) * 2);
130
 
  if(new_plugin->argv == NULL){
 
127
  if (new_plugin->argv == NULL){
131
128
    free(copy_name);
132
129
    free(new_plugin);
133
130
    return NULL;
224
221
/* Mark processes as completed when they exit, and save their exit
225
222
   status. */
226
223
static void handle_sigchld(__attribute__((unused)) int sig){
227
 
  int old_errno = errno;
228
224
  while(true){
229
225
    plugin *proc = plugin_list;
230
226
    int status;
234
230
      break;
235
231
    }
236
232
    if(pid == -1){
237
 
      if(errno == ECHILD){
238
 
        /* No child processes */
239
 
        break;
 
233
      if (errno != ECHILD){
 
234
        perror("waitpid");
240
235
      }
241
 
      perror("waitpid");
 
236
      /* No child processes */
 
237
      break;
242
238
    }
243
239
    
244
240
    /* A child exited, find it in process_list */
250
246
      continue;
251
247
    }
252
248
    proc->status = status;
253
 
    proc->completed = 1;
 
249
    proc->completed = true;
254
250
  }
255
 
  errno = old_errno;
256
251
}
257
252
 
258
253
/* Prints out a password to stdout */
313
308
  struct dirent *dirst;
314
309
  struct stat st;
315
310
  fd_set rfds_all;
316
 
  int ret, numchars, maxfd = 0;
317
 
  ssize_t sret;
318
 
  intmax_t tmpmax;
 
311
  int ret, maxfd = 0;
319
312
  uid_t uid = 65534;
320
313
  gid_t gid = 65534;
321
314
  bool debug = false;
378
371
    { .name = NULL }
379
372
  };
380
373
  
381
 
  error_t parse_opt(int key, char *arg, __attribute__((unused))
382
 
                    struct argp_state *state){
383
 
    switch(key){
 
374
  error_t parse_opt (int key, char *arg, __attribute__((unused))
 
375
                     struct argp_state *state) {
 
376
    switch (key) {
384
377
    case 'g':                   /* --global-options */
385
 
      if(arg != NULL){
386
 
        char *plugin_option;
387
 
        while((plugin_option = strsep(&arg, ",")) != NULL){
388
 
          if(plugin_option[0] == '\0'){
 
378
      if (arg != NULL){
 
379
        char *p;
 
380
        while((p = strsep(&arg, ",")) != NULL){
 
381
          if(p[0] == '\0'){
389
382
            continue;
390
383
          }
391
 
          if(not add_argument(getplugin(NULL), plugin_option)){
 
384
          if(not add_argument(getplugin(NULL), p)){
392
385
            perror("add_argument");
393
386
            return ARGP_ERR_UNKNOWN;
394
387
          }
404
397
      }
405
398
      break;
406
399
    case 'o':                   /* --options-for */
407
 
      if(arg != NULL){
408
 
        char *plugin_name = strsep(&arg, ":");
409
 
        if(plugin_name[0] == '\0'){
410
 
          break;
411
 
        }
412
 
        char *plugin_option;
413
 
        while((plugin_option = strsep(&arg, ",")) != NULL){
414
 
          if(not add_argument(getplugin(plugin_name), plugin_option)){
 
400
      if (arg != NULL){
 
401
        char *p_name = strsep(&arg, ":");
 
402
        if(p_name[0] == '\0' or arg == NULL){
 
403
          break;
 
404
        }
 
405
        char *opt = strsep(&arg, ":");
 
406
        if(opt[0] == '\0' or opt == NULL){
 
407
          break;
 
408
        }
 
409
        char *p;
 
410
        while((p = strsep(&opt, ",")) != NULL){
 
411
          if(p[0] == '\0'){
 
412
            continue;
 
413
          }
 
414
          if(not add_argument(getplugin(p_name), p)){
415
415
            perror("add_argument");
416
416
            return ARGP_ERR_UNKNOWN;
417
417
          }
434
434
      }
435
435
      break;
436
436
    case 'd':                   /* --disable */
437
 
      if(arg != NULL){
 
437
      if (arg != NULL){
438
438
        plugin *p = getplugin(arg);
439
439
        if(p == NULL){
440
440
          return ARGP_ERR_UNKNOWN;
443
443
      }
444
444
      break;
445
445
    case 'e':                   /* --enable */
446
 
      if(arg != NULL){
 
446
      if (arg != NULL){
447
447
        plugin *p = getplugin(arg);
448
448
        if(p == NULL){
449
449
          return ARGP_ERR_UNKNOWN;
462
462
      /* This is already done by parse_opt_config_file() */
463
463
      break;
464
464
    case 130:                   /* --userid */
465
 
      ret = sscanf(arg, "%" SCNdMAX "%n", &tmpmax, &numchars);
466
 
      if(ret < 1 or tmpmax != (uid_t)tmpmax
467
 
         or arg[numchars] != '\0'){
468
 
        fprintf(stderr, "Bad user ID number: \"%s\", using %"
469
 
                PRIdMAX "\n", arg, (intmax_t)uid);
470
 
      } else {
471
 
        uid = (uid_t)tmpmax;
472
 
      }
 
465
      uid = (uid_t)strtol(arg, NULL, 10);
473
466
      break;
474
467
    case 131:                   /* --groupid */
475
 
      ret = sscanf(arg, "%" SCNdMAX "%n", &tmpmax, &numchars);
476
 
      if(ret < 1 or tmpmax != (gid_t)tmpmax
477
 
         or arg[numchars] != '\0'){
478
 
        fprintf(stderr, "Bad group ID number: \"%s\", using %"
479
 
                PRIdMAX "\n", arg, (intmax_t)gid);
480
 
      } else {
481
 
        gid = (gid_t)tmpmax;
482
 
      }
 
468
      gid = (gid_t)strtol(arg, NULL, 10);
483
469
      break;
484
470
    case 132:                   /* --debug */
485
471
      debug = true;
486
472
      break;
487
 
/*
488
 
 * When adding more options before this line, remember to also add a
489
 
 * "case" to the "parse_opt_config_file" function below.
490
 
 */
491
473
    case ARGP_KEY_ARG:
492
474
      /* Cryptsetup always passes an argument, which is an empty
493
475
         string if "none" was specified in /etc/crypttab.  So if
506
488
  
507
489
  /* This option parser is the same as parse_opt() above, except it
508
490
     ignores everything but the --config-file option. */
509
 
  error_t parse_opt_config_file(int key, char *arg,
510
 
                                __attribute__((unused))
511
 
                                struct argp_state *state){
512
 
    switch(key){
 
491
  error_t parse_opt_config_file (int key, char *arg,
 
492
                                 __attribute__((unused))
 
493
                                 struct argp_state *state) {
 
494
    switch (key) {
513
495
    case 'g':                   /* --global-options */
514
496
    case 'G':                   /* --global-env */
515
497
    case 'o':                   /* --options-for */
542
524
                       .args_doc = "",
543
525
                       .doc = "Mandos plugin runner -- Run plugins" };
544
526
  
545
 
  /* 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
546
528
     config file location, if any. */
547
 
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
548
 
  if(ret == ARGP_ERR_UNKNOWN){
 
529
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
530
  if (ret == ARGP_ERR_UNKNOWN){
549
531
    fprintf(stderr, "Unknown error while parsing arguments\n");
550
532
    exitstatus = EXIT_FAILURE;
551
533
    goto fallback;
555
537
  argp.parser = parse_opt;
556
538
  
557
539
  /* Open the configfile if available */
558
 
  if(argfile == NULL){
 
540
  if (argfile == NULL){
559
541
    conffp = fopen(AFILE, "r");
560
542
  } else {
561
543
    conffp = fopen(argfile, "r");
564
546
    char *org_line = NULL;
565
547
    char *p, *arg, *new_arg, *line;
566
548
    size_t size = 0;
 
549
    ssize_t sret;
567
550
    const char whitespace_delims[] = " \r\t\f\v\n";
568
551
    const char comment_delim[] = "#";
569
552
 
616
599
  } else {
617
600
    /* Check for harmful errors and go to fallback. Other errors might
618
601
       not affect opening plugins */
619
 
    if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
 
602
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
620
603
      perror("fopen");
621
604
      exitstatus = EXIT_FAILURE;
622
605
      goto fallback;
625
608
  /* If there was any arguments from configuration file,
626
609
     pass them to parser as command arguments */
627
610
  if(custom_argv != NULL){
628
 
    ret = argp_parse(&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
629
 
                     0, NULL);
630
 
    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){
631
614
      fprintf(stderr, "Unknown error while parsing arguments\n");
632
615
      exitstatus = EXIT_FAILURE;
633
616
      goto fallback;
636
619
  
637
620
  /* Parse actual command line arguments, to let them override the
638
621
     config file */
639
 
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
640
 
  if(ret == ARGP_ERR_UNKNOWN){
 
622
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
623
  if (ret == ARGP_ERR_UNKNOWN){
641
624
    fprintf(stderr, "Unknown error while parsing arguments\n");
642
625
    exitstatus = EXIT_FAILURE;
643
626
    goto fallback;
650
633
      for(char **a = p->argv; *a != NULL; a++){
651
634
        fprintf(stderr, "\tArg: %s\n", *a);
652
635
      }
653
 
      fprintf(stderr, "...and %d environment variables\n", p->envc);
 
636
      fprintf(stderr, "...and %u environment variables\n", p->envc);
654
637
      for(char **a = p->environ; *a != NULL; a++){
655
638
        fprintf(stderr, "\t%s\n", *a);
656
639
      }
658
641
  }
659
642
  
660
643
  /* Strip permissions down to nobody */
 
644
  ret = setuid(uid);
 
645
  if (ret == -1){
 
646
    perror("setuid");
 
647
  }  
661
648
  setgid(gid);
662
 
  if(ret == -1){
 
649
  if (ret == -1){
663
650
    perror("setgid");
664
651
  }
665
 
  ret = setuid(uid);
666
 
  if(ret == -1){
667
 
    perror("setuid");
668
 
  }
669
652
  
670
 
  if(plugindir == NULL){
 
653
  if (plugindir == NULL){
671
654
    dir = opendir(PDIR);
672
655
  } else {
673
656
    dir = opendir(plugindir);
700
683
    
701
684
    /* All directory entries have been processed */
702
685
    if(dirst == NULL){
703
 
      if(errno == EBADF){
 
686
      if (errno == EBADF){
704
687
        perror("readdir");
705
688
        exitstatus = EXIT_FAILURE;
706
689
        goto fallback;
766
749
    }
767
750
    
768
751
    ret = stat(filename, &st);
769
 
    if(ret == -1){
 
752
    if (ret == -1){
770
753
      perror("stat");
771
754
      free(filename);
772
755
      continue;
773
756
    }
774
757
 
775
758
    /* Ignore non-executable files */
776
 
    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)){
777
760
      if(debug){
778
761
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
779
762
                " with bad type or mode\n", filename);
826
809
    
827
810
    int pipefd[2];
828
811
    ret = pipe(pipefd);
829
 
    if(ret == -1){
 
812
    if (ret == -1){
830
813
      perror("pipe");
831
814
      exitstatus = EXIT_FAILURE;
832
815
      goto fallback;
845
828
      goto fallback;
846
829
    }
847
830
    /* Block SIGCHLD until process is safely in process list */
848
 
    ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
 
831
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
849
832
    if(ret < 0){
850
833
      perror("sigprocmask");
851
834
      exitstatus = EXIT_FAILURE;
899
882
    close(pipefd[1]);           /* Close unused write end of pipe */
900
883
    free(filename);
901
884
    plugin *new_plugin = getplugin(dirst->d_name);
902
 
    if(new_plugin == NULL){
 
885
    if (new_plugin == NULL){
903
886
      perror("getplugin");
904
 
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
887
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
905
888
      if(ret < 0){
906
889
        perror("sigprocmask");
907
890
      }
914
897
    
915
898
    /* Unblock SIGCHLD so signal handler can be run if this process
916
899
       has already completed */
917
 
    ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
900
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
918
901
    if(ret < 0){
919
902
      perror("sigprocmask");
920
903
      exitstatus = EXIT_FAILURE;
923
906
    
924
907
    FD_SET(new_plugin->fd, &rfds_all);
925
908
    
926
 
    if(maxfd < new_plugin->fd){
 
909
    if (maxfd < new_plugin->fd){
927
910
      maxfd = new_plugin->fd;
928
911
    }
929
912
  }
930
913
  
931
914
  closedir(dir);
932
915
  dir = NULL;
933
 
  free_plugin(getplugin(NULL));
934
916
  
935
917
  for(plugin *p = plugin_list; p != NULL; p = p->next){
936
918
    if(p->pid != 0){
947
929
  while(plugin_list){
948
930
    fd_set rfds = rfds_all;
949
931
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
950
 
    if(select_ret == -1){
 
932
    if (select_ret == -1){
951
933
      perror("select");
952
934
      exitstatus = EXIT_FAILURE;
953
935
      goto fallback;
956
938
       from one of them */
957
939
    for(plugin *proc = plugin_list; proc != NULL;){
958
940
      /* Is this process completely done? */
959
 
      if(proc->completed and proc->eof){
 
941
      if(proc->eof and proc->completed){
960
942
        /* Only accept the plugin output if it exited cleanly */
961
943
        if(not WIFEXITED(proc->status)
962
944
           or WEXITSTATUS(proc->status) != 0){
964
946
 
965
947
          if(debug){
966
948
            if(WIFEXITED(proc->status)){
967
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
968
 
                      " status %d\n", proc->name,
969
 
                      (intmax_t) (proc->pid),
 
949
              fprintf(stderr, "Plugin %u exited with status %d\n",
 
950
                      (unsigned int) (proc->pid),
970
951
                      WEXITSTATUS(proc->status));
971
 
            } else if(WIFSIGNALED(proc->status)){
972
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] killed by"
973
 
                      " signal %d\n", proc->name,
974
 
                      (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),
975
955
                      WTERMSIG(proc->status));
976
956
            } else if(WCOREDUMP(proc->status)){
977
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
978
 
                      " core\n", proc->name, (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
  }