/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 © 2007-2008 Teddy Hogeborn & Björn Påhlsson
 
5
 * Copyright © 2008 Teddy Hogeborn
 
6
 * Copyright © 2008 Björn Påhlsson
6
7
 * 
7
8
 * This program is free software: you can redistribute it and/or
8
9
 * modify it under the terms of the GNU General Public License as
27
28
#include <stdlib.h>             /* malloc(), exit(), EXIT_FAILURE,
28
29
                                   EXIT_SUCCESS, realloc() */
29
30
#include <stdbool.h>            /* bool, true, false */
30
 
#include <stdio.h>              /* perror, popen(), fileno(),
31
 
                                   fprintf(), stderr, STDOUT_FILENO */
 
31
#include <stdio.h>              /* perror, fileno(), fprintf(),
 
32
                                   stderr, STDOUT_FILENO */
32
33
#include <sys/types.h>          /* DIR, opendir(), stat(), struct
33
34
                                   stat, waitpid(), WIFEXITED(),
34
35
                                   WEXITSTATUS(), wait(), pid_t,
46
47
                                   fcntl(), setuid(), setgid(),
47
48
                                   F_GETFD, F_SETFD, FD_CLOEXEC,
48
49
                                   access(), pipe(), fork(), close()
49
 
                                   dup2, STDOUT_FILENO, _exit(),
 
50
                                   dup2(), STDOUT_FILENO, _exit(),
50
51
                                   execv(), write(), read(),
51
52
                                   close() */
52
53
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
69
70
#define PDIR "/lib/mandos/plugins.d"
70
71
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
71
72
 
72
 
const char *argp_program_version = "plugin-runner 1.0";
 
73
const char *argp_program_version = "plugin-runner " VERSION;
73
74
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
74
75
 
75
 
struct plugin;
76
 
 
77
76
typedef struct plugin{
78
77
  char *name;                   /* can be NULL or any plugin name */
79
78
  char **argv;
96
95
 
97
96
static plugin *plugin_list = NULL;
98
97
 
99
 
/* Gets a existing plugin based on name,
 
98
/* Gets an existing plugin based on name,
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 */
118
117
      return NULL;
119
118
    }
120
119
  }
121
 
 
 
120
  
122
121
  *new_plugin = (plugin) { .name = copy_name,
123
122
                           .argc = 1,
124
123
                           .disabled = false,
187
186
  size_t namelen = (size_t)(strchrnul(def, '=') - def);
188
187
  /* Search for this environment variable */
189
188
  for(char **e = p->environ; *e != NULL; e++){
190
 
    if(strncmp(*e, def, namelen+1) == 0){
 
189
    if(strncmp(*e, def, namelen + 1) == 0){
191
190
      /* It already exists */
192
191
      if(replace){
193
 
        char *new = realloc(*e, strlen(def));
 
192
        char *new = realloc(*e, strlen(def) + 1);
194
193
        if(new == NULL){
195
194
          return false;
196
195
        }
208
207
 * Descriptor Flags".
209
208
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
210
209
 */
211
 
static int set_cloexec_flag(int fd)
212
 
{
 
210
static int set_cloexec_flag(int fd){
213
211
  int ret = fcntl(fd, F_GETFD, 0);
214
212
  /* If reading the flags failed, return error indication now. */
215
213
  if(ret < 0){
222
220
 
223
221
/* Mark processes as completed when they exit, and save their exit
224
222
   status. */
225
 
void handle_sigchld(__attribute__((unused)) int sig){
 
223
static void handle_sigchld(__attribute__((unused)) int sig){
226
224
  while(true){
227
225
    plugin *proc = plugin_list;
228
226
    int status;
238
236
      /* No child processes */
239
237
      break;
240
238
    }
241
 
 
 
239
    
242
240
    /* A child exited, find it in process_list */
243
241
    while(proc != NULL and proc->pid != pid){
244
242
      proc = proc->next;
253
251
}
254
252
 
255
253
/* Prints out a password to stdout */
256
 
bool print_out_password(const char *buffer, size_t length){
 
254
static bool print_out_password(const char *buffer, size_t length){
257
255
  ssize_t ret;
258
 
  if(length>0 and buffer[length-1] == '\n'){
259
 
    length--;
260
 
  }
261
256
  for(size_t written = 0; written < length; written += (size_t)ret){
262
257
    ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
263
258
                                   length - written));
397
392
      if(arg == NULL){
398
393
        break;
399
394
      }
400
 
      {
401
 
        char *envdef = strdup(arg);
402
 
        if(envdef == NULL){
403
 
          break;
404
 
        }
405
 
        if(not add_environment(getplugin(NULL), envdef, true)){
406
 
          perror("add_environment");
407
 
        }
 
395
      if(not add_environment(getplugin(NULL), arg, true)){
 
396
        perror("add_environment");
408
397
      }
409
398
      break;
410
399
    case 'o':                   /* --options-for */
411
400
      if (arg != NULL){
412
401
        char *p_name = strsep(&arg, ":");
413
 
        if(p_name[0] == '\0'){
 
402
        if(p_name[0] == '\0' or arg == NULL){
414
403
          break;
415
404
        }
416
405
        char *opt = strsep(&arg, ":");
417
 
        if(opt[0] == '\0'){
 
406
        if(opt[0] == '\0' or opt == NULL){
418
407
          break;
419
408
        }
420
 
        if(opt != NULL){
421
 
          char *p;
422
 
          while((p = strsep(&opt, ",")) != NULL){
423
 
            if(p[0] == '\0'){
424
 
              continue;
425
 
            }
426
 
            if(not add_argument(getplugin(p_name), p)){
427
 
              perror("add_argument");
428
 
              return ARGP_ERR_UNKNOWN;
429
 
            }
 
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
            perror("add_argument");
 
416
            return ARGP_ERR_UNKNOWN;
430
417
          }
431
418
        }
432
419
      }
440
427
        if(envdef == NULL){
441
428
          break;
442
429
        }
443
 
        char *p_name = strndup(arg, (size_t) (envdef-arg));
444
 
        if(p_name == NULL){
445
 
          break;
446
 
        }
447
 
        envdef++;
448
 
        if(not add_environment(getplugin(p_name), envdef, true)){
 
430
        *envdef = '\0';
 
431
        if(not add_environment(getplugin(arg), envdef+1, true)){
449
432
          perror("add_environment");
450
433
        }
451
434
      }
469
452
      }
470
453
      break;
471
454
    case 128:                   /* --plugin-dir */
 
455
      free(plugindir);
472
456
      plugindir = strdup(arg);
473
457
      if(plugindir == NULL){
474
458
        perror("strdup");
487
471
      debug = true;
488
472
      break;
489
473
    case ARGP_KEY_ARG:
490
 
      fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
 
474
      /* Cryptsetup always passes an argument, which is an empty
 
475
         string if "none" was specified in /etc/crypttab.  So if
 
476
         argument was empty, we ignore it silently. */
 
477
      if(arg[0] != '\0'){
 
478
        fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
 
479
      }
491
480
      break;
492
481
    case ARGP_KEY_END:
493
482
      break;
512
501
    case 128:                   /* --plugin-dir */
513
502
      break;
514
503
    case 129:                   /* --config-file */
 
504
      free(argfile);
515
505
      argfile = strdup(arg);
516
506
      if(argfile == NULL){
517
507
        perror("strdup");
711
701
      
712
702
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
713
703
                                           ".dpkg-old",
 
704
                                           ".dpkg-bak",
714
705
                                           ".dpkg-divert", NULL };
715
706
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
716
707
        size_t pre_len = strlen(*pre);
747
738
    }
748
739
 
749
740
    char *filename;
750
 
    ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
 
741
    if(plugindir == NULL){
 
742
      ret = asprintf(&filename, PDIR "/%s", dirst->d_name);
 
743
    } else {
 
744
      ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
 
745
    }
751
746
    if(ret < 0){
752
747
      perror("asprintf");
753
748
      continue;
853
848
        perror("sigaction");
854
849
        _exit(EXIT_FAILURE);
855
850
      }
856
 
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
851
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
857
852
      if(ret < 0){
858
853
        perror("sigprocmask");
859
854
        _exit(EXIT_FAILURE);
860
855
      }
861
 
 
 
856
      
862
857
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
863
858
      if(ret == -1){
864
859
        perror("dup2");
914
909
    if (maxfd < new_plugin->fd){
915
910
      maxfd = new_plugin->fd;
916
911
    }
917
 
    
918
912
  }
919
913
  
920
914
  closedir(dir);
921
915
  dir = NULL;
922
 
 
 
916
  
923
917
  for(plugin *p = plugin_list; p != NULL; p = p->next){
924
918
    if(p->pid != 0){
925
919
      break;
930
924
      free_plugin_list();
931
925
    }
932
926
  }
933
 
 
 
927
  
934
928
  /* Main loop while running plugins exist */
935
929
  while(plugin_list){
936
930
    fd_set rfds = rfds_all;
942
936
    }
943
937
    /* OK, now either a process completed, or something can be read
944
938
       from one of them */
945
 
    for(plugin *proc = plugin_list; proc != NULL; proc = proc->next){
 
939
    for(plugin *proc = plugin_list; proc != NULL;){
946
940
      /* Is this process completely done? */
947
941
      if(proc->eof and proc->completed){
948
942
        /* Only accept the plugin output if it exited cleanly */
975
969
            exitstatus = EXIT_FAILURE;
976
970
            goto fallback;
977
971
          }
 
972
          
 
973
          plugin *next_plugin = proc->next;
978
974
          free_plugin(proc);
 
975
          proc = next_plugin;
 
976
          
979
977
          /* We are done modifying process list, so unblock signal */
980
978
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
981
979
                             NULL);
988
986
          if(plugin_list == NULL){
989
987
            break;
990
988
          }
 
989
          
991
990
          continue;
992
991
        }
993
992
        
994
993
        /* This process exited nicely, so print its buffer */
995
 
 
 
994
        
996
995
        bool bret = print_out_password(proc->buffer,
997
996
                                       proc->buffer_length);
998
997
        if(not bret){
1005
1004
      /* This process has not completed.  Does it have any output? */
1006
1005
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1007
1006
        /* This process had nothing to say at this time */
 
1007
        proc = proc->next;
1008
1008
        continue;
1009
1009
      }
1010
1010
      /* Before reading, make the process' data buffer large enough */
1023
1023
                 BUFFER_SIZE);
1024
1024
      if(ret < 0){
1025
1025
        /* Read error from this process; ignore the error */
 
1026
        proc = proc->next;
1026
1027
        continue;
1027
1028
      }
1028
1029
      if(ret == 0){
1043
1044
    bool bret;
1044
1045
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
1045
1046
    char *passwordbuffer = getpass("Password: ");
1046
 
    bret = print_out_password(passwordbuffer, strlen(passwordbuffer));
 
1047
    size_t len = strlen(passwordbuffer);
 
1048
    /* Strip trailing newline */
 
1049
    if(len > 0 and passwordbuffer[len-1] == '\n'){
 
1050
      passwordbuffer[len-1] = '\0'; /* not strictly necessary */
 
1051
      len--;
 
1052
    }
 
1053
    bret = print_out_password(passwordbuffer, len);
1047
1054
    if(not bret){
1048
1055
      perror("print_out_password");
1049
1056
      exitstatus = EXIT_FAILURE;
1056
1063
    perror("sigaction");
1057
1064
    exitstatus = EXIT_FAILURE;
1058
1065
  }
1059
 
 
 
1066
  
1060
1067
  if(custom_argv != NULL){
1061
1068
    for(char **arg = custom_argv+1; *arg != NULL; arg++){
1062
1069
      free(*arg);
1068
1075
    closedir(dir);
1069
1076
  }
1070
1077
  
1071
 
  /* Free the process list and kill the processes */
 
1078
  /* Kill the processes */
1072
1079
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1073
1080
    if(p->pid != 0){
1074
1081
      close(p->fd);
1087
1094
  if(errno != ECHILD){
1088
1095
    perror("wait");
1089
1096
  }
1090
 
 
 
1097
  
1091
1098
  free_plugin_list();
1092
1099
  
1093
1100
  free(plugindir);