/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-29 05:53:59 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080829055359-wkdasnyxtylmnxus
* mandos.xml (EXAMPLE): Replaced all occurences of command name with
                        "&COMMANDNAME;".

* plugins.d/password-prompt.c (main): Improved some documentation
                                      strings.  Do perror() of
                                      tcgetattr() fails.  Add debug
                                      output if interrupted by signal.
                                      Loop over write() instead of
                                      using fwrite() when outputting
                                      password.  Add debug output if
                                      getline() returns 0, unless it
                                      was caused by a signal.  Add
                                      exit status code to debug
                                      output.

* plugins.d/password-prompt.xml: Changed all single quotes to double
                                 quotes for consistency.  Removed
                                 <?xml-stylesheet>.
  (ENTITY TIMESTAMP): New.  Automatically updated by Emacs time-stamp
                      by using Emacs local variables.
  (/refentry/refentryinfo/title): Changed to "Mandos Manual".
  (/refentry/refentryinfo/productname): Changed to "Mandos".
  (/refentry/refentryinfo/date): New; set to "&TIMESTAMP;".
  (/refentry/refentryinfo/copyright): Split copyright holders.
  (/refentry/refnamediv/refpurpose): Improved wording.
  (SYNOPSIS): Fix to use correct markup.  Add short options.
  (DESCRIPTION, OPTIONS): Improved wording.
  (OPTIONS): Improved wording.  Use more correct markup.  Document
             short options.
  (EXIT STATUS): Add text.
  (ENVIRONMENT): Document use of "cryptsource" and "crypttarget".
  (FILES): REMOVED.
  (BUGS): Add text.
  (EXAMPLE): Added some examples.
  (SECURITY): Added text.
  (SEE ALSO): Remove reference to mandos(8).  Add reference to
              crypttab(5).

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, error_t */
 
59
                                   ARGP_KEY_END, ARGP_KEY_ARG,
 
60
                                   error_t */
60
61
#include <signal.h>             /* struct sigaction, sigemptyset(),
61
62
                                   sigaddset(), sigaction(),
62
63
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
64
65
#include <errno.h>              /* errno, EBADF */
65
66
 
66
67
#define BUFFER_SIZE 256
67
 
#define ARGFILE "/conf/conf.d/mandos/plugin-runner.conf"
 
68
 
 
69
#define PDIR "/lib/mandos/plugins.d"
 
70
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
68
71
 
69
72
const char *argp_program_version = "plugin-runner 1.0";
70
73
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
78
81
  size_t buffer_size;
79
82
  size_t buffer_length;
80
83
  bool eof;
81
 
  bool completed;
82
 
  int status;
 
84
  volatile bool completed;
 
85
  volatile int status;
83
86
  struct process *next;
84
87
} process;
85
88
 
105
108
  if (new_plugin == NULL){
106
109
    return NULL;
107
110
  }
108
 
  *new_plugin = (plugin) { .name = name,
 
111
  char *copy_name = NULL;
 
112
  if(name != NULL){
 
113
    copy_name = strdup(name);
 
114
    if(copy_name == NULL){
 
115
      return NULL;
 
116
    }
 
117
  }
 
118
  
 
119
  *new_plugin = (plugin) { .name = copy_name,
109
120
                           .argc = 1,
110
121
                           .envc = 0,
111
122
                           .disabled = false,
113
124
  
114
125
  new_plugin->argv = malloc(sizeof(char *) * 2);
115
126
  if (new_plugin->argv == NULL){
 
127
    free(copy_name);
116
128
    free(new_plugin);
117
129
    return NULL;
118
130
  }
119
 
  new_plugin->argv[0] = name;
 
131
  new_plugin->argv[0] = copy_name;
120
132
  new_plugin->argv[1] = NULL;
121
133
 
122
134
  new_plugin->environ = malloc(sizeof(char *));
123
135
  if(new_plugin->environ == NULL){
 
136
    free(copy_name);
124
137
    free(new_plugin->argv);
125
138
    free(new_plugin);
126
139
    return NULL;
189
202
 
190
203
process *process_list = NULL;
191
204
 
192
 
/* Mark a process as completed when it exits, and save its exit
 
205
/* Mark processes as completed when they exit, and save their exit
193
206
   status. */
194
207
void handle_sigchld(__attribute__((unused)) int sig){
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;
 
208
  while(true){
 
209
    process *proc = process_list;
 
210
    int status;
 
211
    pid_t pid = waitpid(-1, &status, WNOHANG);
 
212
    if(pid == 0){
 
213
      /* Only still running child processes */
 
214
      break;
 
215
    }
 
216
    if(pid == -1){
 
217
      if (errno != ECHILD){
 
218
        perror("waitpid");
 
219
      }
 
220
      /* No child processes */
 
221
      break;
 
222
    }
 
223
 
 
224
    /* A child exited, find it in process_list */
 
225
    while(proc != NULL and proc->pid != pid){
 
226
      proc = proc->next;
 
227
    }
 
228
    if(proc == NULL){
 
229
      /* Process not found in process list */
 
230
      continue;
 
231
    }
 
232
    proc->status = status;
 
233
    proc->completed = true;
 
234
  }
211
235
}
212
236
 
213
237
bool print_out_password(const char *buffer, size_t length){
225
249
  return true;
226
250
}
227
251
 
228
 
char **add_to_argv(char **argv, int *argc, char *arg){
229
 
  if (argv == NULL){
230
 
    *argc = 1;
231
 
    argv = malloc(sizeof(char*) * 2);
232
 
    if(argv == NULL){
233
 
      return NULL;
234
 
    }
235
 
    argv[0] = NULL;     /* Will be set to argv[0] in main before parsing */
236
 
    argv[1] = NULL;
237
 
  }
238
 
  *argc += 1;
239
 
  argv = realloc(argv, sizeof(char *)
240
 
                  * ((unsigned int) *argc + 1));
241
 
  if(argv == NULL){
242
 
    return NULL;
243
 
  }
244
 
  argv[*argc-1] = arg;
245
 
  argv[*argc] = NULL;   
246
 
  return argv;
 
252
static void free_plugin_list(plugin *plugin_list){
 
253
  for(plugin *next; plugin_list != NULL; plugin_list = next){
 
254
    next = plugin_list->next;
 
255
    for(char **arg = plugin_list->argv; *arg != NULL; arg++){
 
256
      free(*arg);
 
257
    }
 
258
    free(plugin_list->argv);
 
259
    for(char **env = plugin_list->environ; *env != NULL; env++){
 
260
      free(*env);
 
261
    }
 
262
    free(plugin_list->environ);
 
263
    free(plugin_list);
 
264
  }
247
265
}
248
266
 
249
267
int main(int argc, char *argv[]){
250
 
  const char *plugindir = "/lib/mandos/plugins.d";
251
 
  const char *argfile = ARGFILE;
 
268
  char *plugindir = NULL;
 
269
  char *argfile = NULL;
252
270
  FILE *conffp;
253
271
  size_t d_name_len;
254
272
  DIR *dir = NULL;
269
287
  /* Establish a signal handler */
270
288
  sigemptyset(&sigchld_action.sa_mask);
271
289
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
272
 
  if(ret < 0){
 
290
  if(ret == -1){
273
291
    perror("sigaddset");
274
 
    exit(EXIT_FAILURE);
 
292
    exitstatus = EXIT_FAILURE;
 
293
    goto fallback;
275
294
  }
276
295
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
277
 
  if(ret < 0){
 
296
  if(ret == -1){
278
297
    perror("sigaction");
279
 
    exit(EXIT_FAILURE);
 
298
    exitstatus = EXIT_FAILURE;
 
299
    goto fallback;
280
300
  }
281
301
  
282
302
  /* The options we understand. */
299
319
    { .name = "plugin-dir", .key = 128,
300
320
      .arg = "DIRECTORY",
301
321
      .doc = "Specify a different plugin directory", .group = 2 },
302
 
    { .name = "userid", .key = 129,
303
 
      .arg = "ID", .flags = 0,
304
 
      .doc = "User ID the plugins will run as", .group = 2 },
305
 
    { .name = "groupid", .key = 130,
306
 
      .arg = "ID", .flags = 0,
307
 
      .doc = "Group ID the plugins will run as", .group = 2 },
308
 
    { .name = "debug", .key = 131,
309
 
      .doc = "Debug mode", .group = 3 },
 
322
    { .name = "config-file", .key = 129,
 
323
      .arg = "FILE",
 
324
      .doc = "Specify a different configuration file", .group = 2 },
 
325
    { .name = "userid", .key = 130,
 
326
      .arg = "ID", .flags = 0,
 
327
      .doc = "User ID the plugins will run as", .group = 3 },
 
328
    { .name = "groupid", .key = 131,
 
329
      .arg = "ID", .flags = 0,
 
330
      .doc = "Group ID the plugins will run as", .group = 3 },
 
331
    { .name = "debug", .key = 132,
 
332
      .doc = "Debug mode", .group = 4 },
310
333
    { .name = NULL }
311
334
  };
312
335
  
396
419
      }
397
420
      break;
398
421
    case 128:
399
 
      plugindir = arg;
 
422
      plugindir = strdup(arg);
 
423
      if(plugindir == NULL){
 
424
        perror("strdup");
 
425
      }      
400
426
      break;
401
427
    case 129:
 
428
      argfile = strdup(arg);
 
429
      if(argfile == NULL){
 
430
        perror("strdup");
 
431
      }
 
432
      break;      
 
433
    case 130:
402
434
      uid = (uid_t)strtol(arg, NULL, 10);
403
435
      break;
404
 
    case 130:
 
436
    case 131:
405
437
      gid = (gid_t)strtol(arg, NULL, 10);
406
438
      break;
407
 
    case 131:
 
439
    case 132:
408
440
      debug = true;
409
441
      break;
410
442
    case ARGP_KEY_ARG:
428
460
  if (ret == ARGP_ERR_UNKNOWN){
429
461
    fprintf(stderr, "Unknown error while parsing arguments\n");
430
462
    exitstatus = EXIT_FAILURE;
431
 
    goto end;
 
463
    goto fallback;
432
464
  }
433
465
 
434
 
  conffp = fopen(argfile, "r");
 
466
  if (argfile == NULL){
 
467
    conffp = fopen(AFILE, "r");
 
468
  } else {
 
469
    conffp = fopen(argfile, "r");
 
470
  }
 
471
  
435
472
  if(conffp != NULL){
436
473
    char *org_line = NULL;
 
474
    char *p, *arg, *new_arg, *line;
437
475
    size_t size = 0;
438
476
    ssize_t sret;
439
 
    char *p, *arg, *new_arg, *line;
440
477
    const char whitespace_delims[] = " \r\t\f\v\n";
441
478
    const char comment_delim[] = "#";
442
479
 
 
480
    custom_argc = 1;
 
481
    custom_argv = malloc(sizeof(char*) * 2);
 
482
    if(custom_argv == NULL){
 
483
      perror("malloc");
 
484
      exitstatus = EXIT_FAILURE;
 
485
      goto fallback;
 
486
    }
 
487
    custom_argv[0] = argv[0];
 
488
    custom_argv[1] = NULL;
 
489
    
443
490
    while(true){
444
491
      sret = getline(&org_line, &size, conffp);
445
492
      if(sret == -1){
453
500
          continue;
454
501
        }
455
502
        new_arg = strdup(p);
456
 
        custom_argv = add_to_argv(custom_argv, &custom_argc, new_arg);
457
 
        if (custom_argv == NULL){
458
 
          perror("add_to_argv");
459
 
          exitstatus = EXIT_FAILURE;
460
 
          goto end;
461
 
        }
 
503
        if(new_arg == NULL){
 
504
          perror("strdup");
 
505
          exitstatus = EXIT_FAILURE;
 
506
          free(org_line);
 
507
          goto fallback;
 
508
        }
 
509
        
 
510
        custom_argc += 1;
 
511
        custom_argv = realloc(custom_argv, sizeof(char *)
 
512
                              * ((unsigned int) custom_argc + 1));
 
513
        if(custom_argv == NULL){
 
514
          perror("realloc");
 
515
          exitstatus = EXIT_FAILURE;
 
516
          free(org_line);
 
517
          goto fallback;
 
518
        }
 
519
        custom_argv[custom_argc-1] = new_arg;
 
520
        custom_argv[custom_argc] = NULL;        
462
521
      }
463
522
    }
464
523
    free(org_line);
468
527
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
469
528
      perror("fopen");
470
529
      exitstatus = EXIT_FAILURE;
471
 
      goto end;
 
530
      goto fallback;
472
531
    }
473
532
  }
474
533
 
475
534
  if(custom_argv != NULL){
476
 
    custom_argv[0] = argv[0];
477
535
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, &plugin_list);
478
536
    if (ret == ARGP_ERR_UNKNOWN){
479
537
      fprintf(stderr, "Unknown error while parsing arguments\n");
480
538
      exitstatus = EXIT_FAILURE;
481
 
      goto end;
 
539
      goto fallback;
482
540
    }
483
541
  }
484
542
  
505
563
  if (ret == -1){
506
564
    perror("setgid");
507
565
  }
 
566
 
 
567
  if (plugindir == NULL){
 
568
    dir = opendir(PDIR);
 
569
  } else {
 
570
    dir = opendir(plugindir);
 
571
  }
508
572
  
509
 
  dir = opendir(plugindir);
510
573
  if(dir == NULL){
511
574
    perror("Could not open plugin dir");
512
575
    exitstatus = EXIT_FAILURE;
513
 
    goto end;
 
576
    goto fallback;
514
577
  }
515
578
  
516
579
  /* Set the FD_CLOEXEC flag on the directory, if possible */
521
584
      if(ret < 0){
522
585
        perror("set_cloexec_flag");
523
586
        exitstatus = EXIT_FAILURE;
524
 
        goto end;
 
587
        goto fallback;
525
588
      }
526
589
    }
527
590
  }
536
599
      if (errno == EBADF){
537
600
        perror("readdir");
538
601
        exitstatus = EXIT_FAILURE;
539
 
        goto end;
 
602
        goto fallback;
540
603
      }
541
604
      break;
542
605
    }
657
720
      }
658
721
    }
659
722
    
660
 
    int pipefd[2]; 
 
723
    int pipefd[2];
661
724
    ret = pipe(pipefd);
662
725
    if (ret == -1){
663
726
      perror("pipe");
664
727
      exitstatus = EXIT_FAILURE;
665
 
      goto end;
 
728
      goto fallback;
666
729
    }
667
730
    ret = set_cloexec_flag(pipefd[0]);
668
731
    if(ret < 0){
669
732
      perror("set_cloexec_flag");
670
733
      exitstatus = EXIT_FAILURE;
671
 
      goto end;
 
734
      goto fallback;
672
735
    }
673
736
    ret = set_cloexec_flag(pipefd[1]);
674
737
    if(ret < 0){
675
738
      perror("set_cloexec_flag");
676
739
      exitstatus = EXIT_FAILURE;
677
 
      goto end;
 
740
      goto fallback;
678
741
    }
679
742
    /* Block SIGCHLD until process is safely in process list */
680
743
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
681
744
    if(ret < 0){
682
745
      perror("sigprocmask");
683
746
      exitstatus = EXIT_FAILURE;
684
 
      goto end;
 
747
      goto fallback;
685
748
    }
686
749
    // Starting a new process to be watched
687
750
    pid_t pid = fork();
688
751
    if(pid == -1){
689
752
      perror("fork");
690
753
      exitstatus = EXIT_FAILURE;
691
 
      goto end;
 
754
      goto fallback;
692
755
    }
693
756
    if(pid == 0){
694
757
      /* this is the child process */
738
801
        perror("sigprocmask");
739
802
      }
740
803
      exitstatus = EXIT_FAILURE;
741
 
      goto end;
 
804
      goto fallback;
742
805
    }
743
806
    
744
807
    *new_process = (struct process){ .pid = pid,
752
815
    if(ret < 0){
753
816
      perror("sigprocmask");
754
817
      exitstatus = EXIT_FAILURE;
755
 
      goto end;
 
818
      goto fallback;
756
819
    }
757
820
    
758
821
    FD_SET(new_process->fd, &rfds_all);
762
825
    }
763
826
    
764
827
  }
765
 
  
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
 
  }
 
828
 
 
829
  free_plugin_list(plugin_list);
 
830
  plugin_list = NULL;
777
831
  
778
832
  closedir(dir);
779
833
  dir = NULL;
789
843
    if (select_ret == -1){
790
844
      perror("select");
791
845
      exitstatus = EXIT_FAILURE;
792
 
      goto end;
 
846
      goto fallback;
793
847
    }
794
848
    /* OK, now either a process completed, or something can be read
795
849
       from one of them */
817
871
          /* Remove the plugin */
818
872
          FD_CLR(proc->fd, &rfds_all);
819
873
          /* Block signal while modifying process_list */
820
 
          ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
 
874
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
821
875
          if(ret < 0){
822
876
            perror("sigprocmask");
823
877
            exitstatus = EXIT_FAILURE;
824
 
            goto end;
 
878
            goto fallback;
825
879
          }
826
880
          /* Delete this process entry from the list */
827
881
          if(process_list == proc){
851
905
        }
852
906
        /* This process exited nicely, so print its buffer */
853
907
 
854
 
        bool bret = print_out_password(proc->buffer, proc->buffer_length);
 
908
        bool bret = print_out_password(proc->buffer,
 
909
                                       proc->buffer_length);
855
910
        if(not bret){
856
911
          perror("print_out_password");
857
912
          exitstatus = EXIT_FAILURE;
858
913
        }
859
 
        goto end;
 
914
        goto fallback;
860
915
      }
861
916
      /* This process has not completed.  Does it have any output? */
862
917
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
870
925
        if (proc->buffer == NULL){
871
926
          perror("malloc");
872
927
          exitstatus = EXIT_FAILURE;
873
 
          goto end;
 
928
          goto fallback;
874
929
        }
875
930
        proc->buffer_size += BUFFER_SIZE;
876
931
      }
891
946
  }
892
947
 
893
948
 
894
 
 end:
 
949
 fallback:
895
950
  
896
951
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
897
 
    /* Fallback if all plugins failed, none are found or an error occured */
 
952
    /* Fallback if all plugins failed, none are found or an error
 
953
       occured */
898
954
    bool bret;
899
955
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
900
956
    char *passwordbuffer = getpass("Password: ");
902
958
    if(not bret){
903
959
      perror("print_out_password");
904
960
      exitstatus = EXIT_FAILURE;
905
 
      goto end;
906
961
    }
907
962
  }
908
963
  
909
964
  /* Restore old signal handler */
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
 
      }
 
965
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
 
966
  if(ret == -1){
 
967
    perror("sigaction");
 
968
    exitstatus = EXIT_FAILURE;
 
969
  }
 
970
 
 
971
  if(custom_argv != NULL){
 
972
    for(char **arg = custom_argv+1; *arg != NULL; arg++){
 
973
      free(*arg);
922
974
    }
923
 
    free(plugin_list->environ);
924
 
    free(plugin_list);
 
975
    free(custom_argv);
925
976
  }
 
977
  free_plugin_list(plugin_list);
926
978
  
927
979
  if(dir != NULL){
928
980
    closedir(dir);
948
1000
  if(errno != ECHILD){
949
1001
    perror("wait");
950
1002
  }
 
1003
 
 
1004
  free(plugindir);
 
1005
  free(argfile);
951
1006
  
952
1007
  return exitstatus;
953
1008
}