/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-08-16 03:29:08 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080816032908-ihw7c05r2mnyk389
Add feature to specify custom environment variables for plugins.

* plugin-runner.c (plugin): New members "environ" and "envc" to
                            contain possible custom environment.
  (getplugin): Return NULL on failure instead of doing exit(); all
               callers changed.
  (add_to_char_array): New helper function for "add_argument" and
                       "add_environment".
  (addargument): Renamed to "add_argument".  Return bool.  Call
                 "add_to_char_array" to actually do things.
  (add_environment): New; analogous to "add_argument".
  (addcustomargument): Renamed to "add_to_argv" to avoid confusion
                       with "add_argument".
  (main): New options "--global-envs" and "--envs-for" to specify
          custom environment for plugins.  Print environment for
          plugins in debug mode.  Use asprintf instead of strcpy and
          strcat.  Use execve() for plugins with custom environments.
          Free environment for plugin when freeing plugin list.

Show diffs side-by-side

added added

removed removed

Lines of Context:
56
56
#include <argp.h>               /* struct argp_option, struct
57
57
                                   argp_state, struct argp,
58
58
                                   argp_parse(), ARGP_ERR_UNKNOWN,
59
 
                                   ARGP_KEY_END, ARGP_KEY_ARG,
60
 
                                   error_t */
 
59
                                   ARGP_KEY_END, ARGP_KEY_ARG, error_t */
61
60
#include <signal.h>             /* struct sigaction, sigemptyset(),
62
61
                                   sigaddset(), sigaction(),
63
62
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
79
78
  size_t buffer_size;
80
79
  size_t buffer_length;
81
80
  bool eof;
82
 
  volatile bool completed;
83
 
  volatile int status;
 
81
  bool completed;
 
82
  int status;
84
83
  struct process *next;
85
84
} process;
86
85
 
106
105
  if (new_plugin == NULL){
107
106
    return NULL;
108
107
  }
109
 
  char *copy_name = NULL;
110
 
  if(name != NULL){
111
 
    copy_name = strdup(name);
112
 
    if(copy_name == NULL){
113
 
      return NULL;
114
 
    }
115
 
  }
116
 
  
117
 
  *new_plugin = (plugin) { .name = copy_name,
 
108
  *new_plugin = (plugin) { .name = name,
118
109
                           .argc = 1,
119
110
                           .envc = 0,
120
111
                           .disabled = false,
122
113
  
123
114
  new_plugin->argv = malloc(sizeof(char *) * 2);
124
115
  if (new_plugin->argv == NULL){
125
 
    free(copy_name);
126
116
    free(new_plugin);
127
117
    return NULL;
128
118
  }
129
 
  new_plugin->argv[0] = copy_name;
 
119
  new_plugin->argv[0] = name;
130
120
  new_plugin->argv[1] = NULL;
131
121
 
132
122
  new_plugin->environ = malloc(sizeof(char *));
133
123
  if(new_plugin->environ == NULL){
134
 
    free(copy_name);
135
124
    free(new_plugin->argv);
136
125
    free(new_plugin);
137
126
    return NULL;
200
189
 
201
190
process *process_list = NULL;
202
191
 
203
 
/* Mark processes as completed when they exit, and save their exit
 
192
/* Mark a process as completed when it exits, and save its exit
204
193
   status. */
205
194
void handle_sigchld(__attribute__((unused)) int sig){
206
 
  while(true){
207
 
    process *proc = process_list;
208
 
    int status;
209
 
    pid_t pid = waitpid(-1, &status, WNOHANG);
210
 
    if(pid == 0){
211
 
      /* Only still running child processes */
212
 
      break;
213
 
    }
214
 
    if(pid == -1){
215
 
      if (errno != ECHILD){
216
 
        perror("waitpid");
217
 
      }
218
 
      /* No child processes */
219
 
      break;
220
 
    }
221
 
 
222
 
    /* A child exited, find it in process_list */
223
 
    while(proc != NULL and proc->pid != pid){
224
 
      proc = proc->next;
225
 
    }
226
 
    if(proc == NULL){
227
 
      /* Process not found in process list */
228
 
      continue;
229
 
    }
230
 
    proc->status = status;
231
 
    proc->completed = true;
232
 
  }
 
195
  process *proc = process_list;
 
196
  int status;
 
197
  pid_t pid = wait(&status);
 
198
  if(pid == -1){
 
199
    perror("wait");
 
200
    return;
 
201
  }
 
202
  while(proc != NULL and proc->pid != pid){
 
203
    proc = proc->next;
 
204
  }
 
205
  if(proc == NULL){
 
206
    /* Process not found in process list */
 
207
    return;
 
208
  }
 
209
  proc->status = status;
 
210
  proc->completed = true;
233
211
}
234
212
 
235
213
bool print_out_password(const char *buffer, size_t length){
254
232
    if(argv == NULL){
255
233
      return NULL;
256
234
    }
257
 
    argv[0] = NULL;     /* Will be set to argv[0] in main before
258
 
                           parsing */
 
235
    argv[0] = NULL;     /* Will be set to argv[0] in main before parsing */
259
236
    argv[1] = NULL;
260
237
  }
261
238
  *argc += 1;
265
242
    return NULL;
266
243
  }
267
244
  argv[*argc-1] = arg;
268
 
  argv[*argc] = NULL;
 
245
  argv[*argc] = NULL;   
269
246
  return argv;
270
247
}
271
248
 
272
 
static void free_plugin_list(plugin *plugin_list){
273
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
274
 
    next = plugin_list->next;
275
 
    for(char **arg = plugin_list->argv; *arg != NULL; arg++){
276
 
      free(*arg);
277
 
    }
278
 
    free(plugin_list->argv);
279
 
    for(char **env = plugin_list->environ; *env != NULL; env++){
280
 
      free(*env);
281
 
    }
282
 
    free(plugin_list->environ);
283
 
    free(plugin_list);
284
 
  }
285
 
}
286
 
 
287
249
int main(int argc, char *argv[]){
288
250
  const char *plugindir = "/lib/mandos/plugins.d";
289
251
  const char *argfile = ARGFILE;
307
269
  /* Establish a signal handler */
308
270
  sigemptyset(&sigchld_action.sa_mask);
309
271
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
310
 
  if(ret == -1){
 
272
  if(ret < 0){
311
273
    perror("sigaddset");
312
 
    exitstatus = EXIT_FAILURE;
313
 
    goto fallback;
 
274
    exit(EXIT_FAILURE);
314
275
  }
315
276
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
316
 
  if(ret == -1){
 
277
  if(ret < 0){
317
278
    perror("sigaction");
318
 
    exitstatus = EXIT_FAILURE;
319
 
    goto fallback;
 
279
    exit(EXIT_FAILURE);
320
280
  }
321
281
  
322
282
  /* The options we understand. */
468
428
  if (ret == ARGP_ERR_UNKNOWN){
469
429
    fprintf(stderr, "Unknown error while parsing arguments\n");
470
430
    exitstatus = EXIT_FAILURE;
471
 
    goto fallback;
 
431
    goto end;
472
432
  }
473
433
 
474
434
  conffp = fopen(argfile, "r");
475
435
  if(conffp != NULL){
476
436
    char *org_line = NULL;
477
 
    char *p, *arg, *new_arg, *line;
478
437
    size_t size = 0;
479
438
    ssize_t sret;
 
439
    char *p, *arg, *new_arg, *line;
480
440
    const char whitespace_delims[] = " \r\t\f\v\n";
481
441
    const char comment_delim[] = "#";
482
442
 
497
457
        if (custom_argv == NULL){
498
458
          perror("add_to_argv");
499
459
          exitstatus = EXIT_FAILURE;
500
 
          goto fallback;
 
460
          goto end;
501
461
        }
502
462
      }
503
463
    }
508
468
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
509
469
      perror("fopen");
510
470
      exitstatus = EXIT_FAILURE;
511
 
      goto fallback;
 
471
      goto end;
512
472
    }
513
473
  }
514
474
 
515
475
  if(custom_argv != NULL){
516
476
    custom_argv[0] = argv[0];
517
 
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0,
518
 
                      &plugin_list);
 
477
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, &plugin_list);
519
478
    if (ret == ARGP_ERR_UNKNOWN){
520
479
      fprintf(stderr, "Unknown error while parsing arguments\n");
521
480
      exitstatus = EXIT_FAILURE;
522
 
      goto fallback;
 
481
      goto end;
523
482
    }
524
483
  }
525
484
  
551
510
  if(dir == NULL){
552
511
    perror("Could not open plugin dir");
553
512
    exitstatus = EXIT_FAILURE;
554
 
    goto fallback;
 
513
    goto end;
555
514
  }
556
515
  
557
516
  /* Set the FD_CLOEXEC flag on the directory, if possible */
562
521
      if(ret < 0){
563
522
        perror("set_cloexec_flag");
564
523
        exitstatus = EXIT_FAILURE;
565
 
        goto fallback;
 
524
        goto end;
566
525
      }
567
526
    }
568
527
  }
577
536
      if (errno == EBADF){
578
537
        perror("readdir");
579
538
        exitstatus = EXIT_FAILURE;
580
 
        goto fallback;
 
539
        goto end;
581
540
      }
582
541
      break;
583
542
    }
698
657
      }
699
658
    }
700
659
    
701
 
    int pipefd[2];
 
660
    int pipefd[2]; 
702
661
    ret = pipe(pipefd);
703
662
    if (ret == -1){
704
663
      perror("pipe");
705
664
      exitstatus = EXIT_FAILURE;
706
 
      goto fallback;
 
665
      goto end;
707
666
    }
708
667
    ret = set_cloexec_flag(pipefd[0]);
709
668
    if(ret < 0){
710
669
      perror("set_cloexec_flag");
711
670
      exitstatus = EXIT_FAILURE;
712
 
      goto fallback;
 
671
      goto end;
713
672
    }
714
673
    ret = set_cloexec_flag(pipefd[1]);
715
674
    if(ret < 0){
716
675
      perror("set_cloexec_flag");
717
676
      exitstatus = EXIT_FAILURE;
718
 
      goto fallback;
 
677
      goto end;
719
678
    }
720
679
    /* Block SIGCHLD until process is safely in process list */
721
680
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
722
681
    if(ret < 0){
723
682
      perror("sigprocmask");
724
683
      exitstatus = EXIT_FAILURE;
725
 
      goto fallback;
 
684
      goto end;
726
685
    }
727
686
    // Starting a new process to be watched
728
687
    pid_t pid = fork();
729
688
    if(pid == -1){
730
689
      perror("fork");
731
690
      exitstatus = EXIT_FAILURE;
732
 
      goto fallback;
 
691
      goto end;
733
692
    }
734
693
    if(pid == 0){
735
694
      /* this is the child process */
779
738
        perror("sigprocmask");
780
739
      }
781
740
      exitstatus = EXIT_FAILURE;
782
 
      goto fallback;
 
741
      goto end;
783
742
    }
784
743
    
785
744
    *new_process = (struct process){ .pid = pid,
793
752
    if(ret < 0){
794
753
      perror("sigprocmask");
795
754
      exitstatus = EXIT_FAILURE;
796
 
      goto fallback;
 
755
      goto end;
797
756
    }
798
757
    
799
758
    FD_SET(new_process->fd, &rfds_all);
804
763
    
805
764
  }
806
765
  
807
 
  free_plugin_list(plugin_list);
808
 
  plugin_list = NULL;
 
766
  /* Free the plugin list */
 
767
  for(plugin *next; plugin_list != NULL; plugin_list = next){
 
768
    next = plugin_list->next;
 
769
    free(plugin_list->argv);
 
770
    if(plugin_list->environ[0] != NULL){
 
771
      for(char **e = plugin_list->environ; *e != NULL; e++){
 
772
        free(*e);
 
773
      }
 
774
    }
 
775
    free(plugin_list);
 
776
  }
809
777
  
810
778
  closedir(dir);
811
779
  dir = NULL;
821
789
    if (select_ret == -1){
822
790
      perror("select");
823
791
      exitstatus = EXIT_FAILURE;
824
 
      goto fallback;
 
792
      goto end;
825
793
    }
826
794
    /* OK, now either a process completed, or something can be read
827
795
       from one of them */
849
817
          /* Remove the plugin */
850
818
          FD_CLR(proc->fd, &rfds_all);
851
819
          /* Block signal while modifying process_list */
852
 
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
 
820
          ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
853
821
          if(ret < 0){
854
822
            perror("sigprocmask");
855
823
            exitstatus = EXIT_FAILURE;
856
 
            goto fallback;
 
824
            goto end;
857
825
          }
858
826
          /* Delete this process entry from the list */
859
827
          if(process_list == proc){
883
851
        }
884
852
        /* This process exited nicely, so print its buffer */
885
853
 
886
 
        bool bret = print_out_password(proc->buffer,
887
 
                                       proc->buffer_length);
 
854
        bool bret = print_out_password(proc->buffer, proc->buffer_length);
888
855
        if(not bret){
889
856
          perror("print_out_password");
890
857
          exitstatus = EXIT_FAILURE;
891
858
        }
892
 
        goto fallback;
 
859
        goto end;
893
860
      }
894
861
      /* This process has not completed.  Does it have any output? */
895
862
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
903
870
        if (proc->buffer == NULL){
904
871
          perror("malloc");
905
872
          exitstatus = EXIT_FAILURE;
906
 
          goto fallback;
 
873
          goto end;
907
874
        }
908
875
        proc->buffer_size += BUFFER_SIZE;
909
876
      }
924
891
  }
925
892
 
926
893
 
927
 
 fallback:
 
894
 end:
928
895
  
929
896
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
930
 
    /* Fallback if all plugins failed, none are found or an error
931
 
       occured */
 
897
    /* Fallback if all plugins failed, none are found or an error occured */
932
898
    bool bret;
933
899
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
934
900
    char *passwordbuffer = getpass("Password: ");
936
902
    if(not bret){
937
903
      perror("print_out_password");
938
904
      exitstatus = EXIT_FAILURE;
 
905
      goto end;
939
906
    }
940
907
  }
941
908
  
942
909
  /* Restore old signal handler */
943
 
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
944
 
  if(ret == -1){
945
 
    perror("sigaction");
946
 
    exitstatus = EXIT_FAILURE;
947
 
  }
948
 
 
949
 
  if(custom_argv != NULL){
950
 
    for(char **arg = custom_argv; *arg != NULL; arg++){
951
 
      free(*arg);
 
910
  sigaction(SIGCHLD, &old_sigchld_action, NULL);
 
911
  
 
912
  free(custom_argv);
 
913
  
 
914
  /* Free the plugin list */
 
915
  for(plugin *next; plugin_list != NULL; plugin_list = next){
 
916
    next = plugin_list->next;
 
917
    free(plugin_list->argv);
 
918
    if(plugin_list->environ[0] != NULL){
 
919
      for(char **e = plugin_list->environ; *e != NULL; e++){
 
920
        free(*e);
 
921
      }
952
922
    }
953
 
    free(custom_argv);
 
923
    free(plugin_list->environ);
 
924
    free(plugin_list);
954
925
  }
955
 
  free_plugin_list(plugin_list);
956
926
  
957
927
  if(dir != NULL){
958
928
    closedir(dir);