/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;
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;
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
256
  for(size_t written = 0; written < length; written += (size_t)ret){
259
257
    ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
394
392
      if(arg == NULL){
395
393
        break;
396
394
      }
397
 
      {
398
 
        char *envdef = strdup(arg);
399
 
        if(envdef == NULL){
400
 
          break;
401
 
        }
402
 
        if(not add_environment(getplugin(NULL), envdef, true)){
403
 
          perror("add_environment");
404
 
        }
 
395
      if(not add_environment(getplugin(NULL), arg, true)){
 
396
        perror("add_environment");
405
397
      }
406
398
      break;
407
399
    case 'o':                   /* --options-for */
435
427
        if(envdef == NULL){
436
428
          break;
437
429
        }
438
 
        char *p_name = strndup(arg, (size_t) (envdef-arg));
439
 
        if(p_name == NULL){
440
 
          break;
441
 
        }
442
 
        envdef++;
443
 
        if(not add_environment(getplugin(p_name), envdef, true)){
 
430
        *envdef = '\0';
 
431
        if(not add_environment(getplugin(arg), envdef+1, true)){
444
432
          perror("add_environment");
445
433
        }
446
434
      }
464
452
      }
465
453
      break;
466
454
    case 128:                   /* --plugin-dir */
 
455
      free(plugindir);
467
456
      plugindir = strdup(arg);
468
457
      if(plugindir == NULL){
469
458
        perror("strdup");
482
471
      debug = true;
483
472
      break;
484
473
    case ARGP_KEY_ARG:
485
 
      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
      }
486
480
      break;
487
481
    case ARGP_KEY_END:
488
482
      break;
507
501
    case 128:                   /* --plugin-dir */
508
502
      break;
509
503
    case 129:                   /* --config-file */
 
504
      free(argfile);
510
505
      argfile = strdup(arg);
511
506
      if(argfile == NULL){
512
507
        perror("strdup");
706
701
      
707
702
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
708
703
                                           ".dpkg-old",
 
704
                                           ".dpkg-bak",
709
705
                                           ".dpkg-divert", NULL };
710
706
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
711
707
        size_t pre_len = strlen(*pre);
852
848
        perror("sigaction");
853
849
        _exit(EXIT_FAILURE);
854
850
      }
855
 
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
851
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
856
852
      if(ret < 0){
857
853
        perror("sigprocmask");
858
854
        _exit(EXIT_FAILURE);
859
855
      }
860
 
 
 
856
      
861
857
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
862
858
      if(ret == -1){
863
859
        perror("dup2");
913
909
    if (maxfd < new_plugin->fd){
914
910
      maxfd = new_plugin->fd;
915
911
    }
916
 
    
917
912
  }
918
913
  
919
914
  closedir(dir);
920
915
  dir = NULL;
921
 
 
 
916
  
922
917
  for(plugin *p = plugin_list; p != NULL; p = p->next){
923
918
    if(p->pid != 0){
924
919
      break;
929
924
      free_plugin_list();
930
925
    }
931
926
  }
932
 
 
 
927
  
933
928
  /* Main loop while running plugins exist */
934
929
  while(plugin_list){
935
930
    fd_set rfds = rfds_all;
941
936
    }
942
937
    /* OK, now either a process completed, or something can be read
943
938
       from one of them */
944
 
    for(plugin *proc = plugin_list; proc != NULL; proc = proc->next){
 
939
    for(plugin *proc = plugin_list; proc != NULL;){
945
940
      /* Is this process completely done? */
946
941
      if(proc->eof and proc->completed){
947
942
        /* Only accept the plugin output if it exited cleanly */
974
969
            exitstatus = EXIT_FAILURE;
975
970
            goto fallback;
976
971
          }
 
972
          
 
973
          plugin *next_plugin = proc->next;
977
974
          free_plugin(proc);
 
975
          proc = next_plugin;
 
976
          
978
977
          /* We are done modifying process list, so unblock signal */
979
978
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
980
979
                             NULL);
987
986
          if(plugin_list == NULL){
988
987
            break;
989
988
          }
 
989
          
990
990
          continue;
991
991
        }
992
992
        
993
993
        /* This process exited nicely, so print its buffer */
994
 
 
 
994
        
995
995
        bool bret = print_out_password(proc->buffer,
996
996
                                       proc->buffer_length);
997
997
        if(not bret){
1004
1004
      /* This process has not completed.  Does it have any output? */
1005
1005
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1006
1006
        /* This process had nothing to say at this time */
 
1007
        proc = proc->next;
1007
1008
        continue;
1008
1009
      }
1009
1010
      /* Before reading, make the process' data buffer large enough */
1022
1023
                 BUFFER_SIZE);
1023
1024
      if(ret < 0){
1024
1025
        /* Read error from this process; ignore the error */
 
1026
        proc = proc->next;
1025
1027
        continue;
1026
1028
      }
1027
1029
      if(ret == 0){
1061
1063
    perror("sigaction");
1062
1064
    exitstatus = EXIT_FAILURE;
1063
1065
  }
1064
 
 
 
1066
  
1065
1067
  if(custom_argv != NULL){
1066
1068
    for(char **arg = custom_argv+1; *arg != NULL; arg++){
1067
1069
      free(*arg);
1073
1075
    closedir(dir);
1074
1076
  }
1075
1077
  
1076
 
  /* Free the process list and kill the processes */
 
1078
  /* Kill the processes */
1077
1079
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1078
1080
    if(p->pid != 0){
1079
1081
      close(p->fd);
1092
1094
  if(errno != ECHILD){
1093
1095
    perror("wait");
1094
1096
  }
1095
 
 
 
1097
  
1096
1098
  free_plugin_list();
1097
1099
  
1098
1100
  free(plugindir);