/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

  • 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:
72
72
const char *argp_program_version = "plugin-runner 1.0";
73
73
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
74
74
 
75
 
struct plugin;
76
 
 
77
 
typedef struct plugin{
78
 
  char *name;                   /* can be NULL or any plugin name */
79
 
  char **argv;
80
 
  int argc;
81
 
  char **environ;
82
 
  int envc;
83
 
  bool disabled;
84
 
 
85
 
  /* Variables used for running processes*/
 
75
struct process;
 
76
 
 
77
typedef struct process{
86
78
  pid_t pid;
87
79
  int fd;
88
80
  char *buffer;
91
83
  bool eof;
92
84
  volatile bool completed;
93
85
  volatile int status;
 
86
  struct process *next;
 
87
} process;
 
88
 
 
89
typedef struct plugin{
 
90
  char *name;                   /* can be NULL or any plugin name */
 
91
  char **argv;
 
92
  int argc;
 
93
  char **environ;
 
94
  int envc;
 
95
  bool disabled;
94
96
  struct plugin *next;
95
97
} plugin;
96
98
 
97
 
static plugin *plugin_list = NULL;
98
 
 
99
 
/* Gets a existing plugin based on name,
100
 
   or if none is found, creates a new one */
101
 
static plugin *getplugin(char *name){
102
 
  /* Check for exiting plugin with that name */
103
 
  for (plugin *p = plugin_list; p != NULL; p = p->next){
 
99
static plugin *getplugin(char *name, plugin **plugin_list){
 
100
  for (plugin *p = *plugin_list; p != NULL; p = p->next){
104
101
    if ((p->name == name)
105
102
        or (p->name and name and (strcmp(p->name, name) == 0))){
106
103
      return p;
118
115
      return NULL;
119
116
    }
120
117
  }
121
 
 
 
118
  
122
119
  *new_plugin = (plugin) { .name = copy_name,
123
120
                           .argc = 1,
 
121
                           .envc = 0,
124
122
                           .disabled = false,
125
 
                           .next = plugin_list };
 
123
                           .next = *plugin_list };
126
124
  
127
125
  new_plugin->argv = malloc(sizeof(char *) * 2);
128
126
  if (new_plugin->argv == NULL){
132
130
  }
133
131
  new_plugin->argv[0] = copy_name;
134
132
  new_plugin->argv[1] = NULL;
135
 
  
 
133
 
136
134
  new_plugin->environ = malloc(sizeof(char *));
137
135
  if(new_plugin->environ == NULL){
138
136
    free(copy_name);
141
139
    return NULL;
142
140
  }
143
141
  new_plugin->environ[0] = NULL;
144
 
  
145
142
  /* Append the new plugin to the list */
146
 
  plugin_list = new_plugin;
 
143
  *plugin_list = new_plugin;
147
144
  return new_plugin;
148
145
}
149
146
 
179
176
}
180
177
 
181
178
/* Add to a plugin's environment */
182
 
static bool add_environment(plugin *p, const char *def, bool replace){
 
179
static bool add_environment(plugin *p, const char *def){
183
180
  if(p == NULL){
184
181
    return false;
185
182
  }
186
 
  /* namelen = length of name of environment variable */
187
 
  size_t namelen = (size_t)(strchrnul(def, '=') - def);
188
 
  /* Search for this environment variable */
189
 
  for(char **e = p->environ; *e != NULL; e++){
190
 
    if(strncmp(*e, def, namelen+1) == 0){
191
 
      /* It already exists */
192
 
      if(replace){
193
 
        char *new = realloc(*e, strlen(def));
194
 
        if(new == NULL){
195
 
          return false;
196
 
        }
197
 
        *e = new;
198
 
        strcpy(*e, def);
199
 
      }
200
 
      return true;
201
 
    }
202
 
  }
203
183
  return add_to_char_array(def, &(p->environ), &(p->envc));
204
184
}
205
185
 
 
186
 
206
187
/*
207
188
 * Based on the example in the GNU LibC manual chapter 13.13 "File
208
189
 * Descriptor Flags".
219
200
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
220
201
}
221
202
 
 
203
process *process_list = NULL;
222
204
 
223
205
/* Mark processes as completed when they exit, and save their exit
224
206
   status. */
225
207
void handle_sigchld(__attribute__((unused)) int sig){
226
208
  while(true){
227
 
    plugin *proc = plugin_list;
 
209
    process *proc = process_list;
228
210
    int status;
229
211
    pid_t pid = waitpid(-1, &status, WNOHANG);
230
212
    if(pid == 0){
252
234
  }
253
235
}
254
236
 
255
 
/* Prints out a password to stdout */
256
237
bool print_out_password(const char *buffer, size_t length){
257
238
  ssize_t ret;
258
239
  if(length>0 and buffer[length-1] == '\n'){
268
249
  return true;
269
250
}
270
251
 
271
 
/* Removes and free a plugin from the plugin list */
272
 
static void free_plugin(plugin *plugin_node){
273
 
  
274
 
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
275
 
    free(*arg);
276
 
  }
277
 
  free(plugin_node->argv);
278
 
  for(char **env = plugin_node->environ; *env != NULL; env++){
279
 
    free(*env);
280
 
  }
281
 
  free(plugin_node->environ);
282
 
  free(plugin_node->buffer);
283
 
 
284
 
  /* Removes the plugin from the singly-linked list */
285
 
  if(plugin_node == plugin_list){
286
 
    /* First one - simple */
287
 
    plugin_list = plugin_list->next;
288
 
  } else {
289
 
    /* Second one or later */
290
 
    for(plugin *p = plugin_list; p != NULL; p = p->next){
291
 
      if(p->next == plugin_node){
292
 
        p->next = plugin_node->next;
293
 
        break;
294
 
      }
295
 
    }
296
 
  }
297
 
  
298
 
  free(plugin_node);
299
 
}
300
 
 
301
 
static void free_plugin_list(void){
302
 
  while(plugin_list != NULL){
303
 
    free_plugin(plugin_list);
 
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);
304
264
  }
305
265
}
306
266
 
344
304
    { .name = "global-options", .key = 'g',
345
305
      .arg = "OPTION[,OPTION[,...]]",
346
306
      .doc = "Options passed to all plugins" },
347
 
    { .name = "global-env", .key = 'e',
 
307
    { .name = "global-envs", .key = 'e',
348
308
      .arg = "VAR=value",
349
309
      .doc = "Environment variable passed to all plugins" },
350
310
    { .name = "options-for", .key = 'o',
351
311
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
352
312
      .doc = "Options passed only to specified plugin" },
353
 
    { .name = "env-for", .key = 'f',
 
313
    { .name = "envs-for", .key = 'f',
354
314
      .arg = "PLUGIN:ENV=value",
355
315
      .doc = "Environment variable passed to specified plugin" },
356
316
    { .name = "disable", .key = 'd',
373
333
    { .name = NULL }
374
334
  };
375
335
  
376
 
  error_t parse_opt (int key, char *arg, __attribute__((unused))
377
 
                     struct argp_state *state) {
 
336
  error_t parse_opt (int key, char *arg, struct argp_state *state) {
 
337
    /* Get the INPUT argument from `argp_parse', which we know is a
 
338
       pointer to our plugin list pointer. */
 
339
    plugin **plugins = state->input;
378
340
    switch (key) {
379
 
    case 'g':                   /* --global-options */
 
341
    case 'g':
380
342
      if (arg != NULL){
381
343
        char *p;
382
344
        while((p = strsep(&arg, ",")) != NULL){
383
345
          if(p[0] == '\0'){
384
346
            continue;
385
347
          }
386
 
          if(not add_argument(getplugin(NULL), p)){
 
348
          if(not add_argument(getplugin(NULL, plugins), p)){
387
349
            perror("add_argument");
388
350
            return ARGP_ERR_UNKNOWN;
389
351
          }
390
352
        }
391
353
      }
392
354
      break;
393
 
    case 'e':                   /* --global-env */
 
355
    case 'e':
394
356
      if(arg == NULL){
395
357
        break;
396
358
      }
399
361
        if(envdef == NULL){
400
362
          break;
401
363
        }
402
 
        if(not add_environment(getplugin(NULL), envdef, true)){
 
364
        if(not add_environment(getplugin(NULL, plugins), envdef)){
403
365
          perror("add_environment");
404
366
        }
405
367
      }
406
368
      break;
407
 
    case 'o':                   /* --options-for */
 
369
    case 'o':
408
370
      if (arg != NULL){
409
371
        char *p_name = strsep(&arg, ":");
410
372
        if(p_name[0] == '\0'){
420
382
            if(p[0] == '\0'){
421
383
              continue;
422
384
            }
423
 
            if(not add_argument(getplugin(p_name), p)){
 
385
            if(not add_argument(getplugin(p_name, plugins), p)){
424
386
              perror("add_argument");
425
387
              return ARGP_ERR_UNKNOWN;
426
388
            }
428
390
        }
429
391
      }
430
392
      break;
431
 
    case 'f':                   /* --env-for */
 
393
    case 'f':
432
394
      if(arg == NULL){
433
395
        break;
434
396
      }
442
404
          break;
443
405
        }
444
406
        envdef++;
445
 
        if(not add_environment(getplugin(p_name), envdef, true)){
 
407
        if(not add_environment(getplugin(p_name, plugins), envdef)){
446
408
          perror("add_environment");
447
409
        }
448
410
      }
449
411
      break;
450
 
    case 'd':                   /* --disable */
 
412
    case 'd':
451
413
      if (arg != NULL){
452
 
        plugin *p = getplugin(arg);
 
414
        plugin *p = getplugin(arg, plugins);
453
415
        if(p == NULL){
454
416
          return ARGP_ERR_UNKNOWN;
455
417
        }
456
418
        p->disabled = true;
457
419
      }
458
420
      break;
459
 
    case 128:                   /* --plugin-dir */
 
421
    case 128:
460
422
      plugindir = strdup(arg);
461
423
      if(plugindir == NULL){
462
424
        perror("strdup");
463
425
      }      
464
426
      break;
465
 
    case 129:                   /* --config-file */
466
 
      /* This is already done by parse_opt_config_file() */
467
 
      break;
468
 
    case 130:                   /* --userid */
 
427
    case 129:
 
428
      argfile = strdup(arg);
 
429
      if(argfile == NULL){
 
430
        perror("strdup");
 
431
      }
 
432
      break;      
 
433
    case 130:
469
434
      uid = (uid_t)strtol(arg, NULL, 10);
470
435
      break;
471
 
    case 131:                   /* --groupid */
 
436
    case 131:
472
437
      gid = (gid_t)strtol(arg, NULL, 10);
473
438
      break;
474
 
    case 132:                   /* --debug */
 
439
    case 132:
475
440
      debug = true;
476
441
      break;
477
442
    case ARGP_KEY_ARG:
485
450
    return 0;
486
451
  }
487
452
  
488
 
  /* This option parser is the same as parse_opt() above, except it
489
 
     ignores everything but the --config-file option. */
490
 
  error_t parse_opt_config_file (int key, char *arg,
491
 
                                 __attribute__((unused))
492
 
                                 struct argp_state *state) {
493
 
    switch (key) {
494
 
    case 'g':                   /* --global-options */
495
 
    case 'e':                   /* --global-env */
496
 
    case 'o':                   /* --options-for */
497
 
    case 'f':                   /* --env-for */
498
 
    case 'd':                   /* --disable */
499
 
    case 128:                   /* --plugin-dir */
500
 
      break;
501
 
    case 129:                   /* --config-file */
502
 
      argfile = strdup(arg);
503
 
      if(argfile == NULL){
504
 
        perror("strdup");
505
 
      }
506
 
      break;      
507
 
    case 130:                   /* --userid */
508
 
    case 131:                   /* --groupid */
509
 
    case 132:                   /* --debug */
510
 
    case ARGP_KEY_ARG:
511
 
    case ARGP_KEY_END:
512
 
      break;
513
 
    default:
514
 
      return ARGP_ERR_UNKNOWN;
515
 
    }
516
 
    return 0;
517
 
  }
 
453
  plugin *plugin_list = NULL;
518
454
  
519
 
  struct argp argp = { .options = options,
520
 
                       .parser = parse_opt_config_file,
521
 
                       .args_doc = "",
 
455
  struct argp argp = { .options = options, .parser = parse_opt,
 
456
                       .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
522
457
                       .doc = "Mandos plugin runner -- Run plugins" };
523
458
  
524
 
  /* Parse using the parse_opt_config_file in order to get the custom
525
 
     config file location, if any. */
526
 
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
459
  ret = argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
527
460
  if (ret == ARGP_ERR_UNKNOWN){
528
461
    fprintf(stderr, "Unknown error while parsing arguments\n");
529
462
    exitstatus = EXIT_FAILURE;
530
463
    goto fallback;
531
464
  }
532
 
  
533
 
  /* Reset to the normal argument parser */
534
 
  argp.parser = parse_opt;
535
 
  
536
 
  /* Open the configfile if available */
 
465
 
537
466
  if (argfile == NULL){
538
467
    conffp = fopen(AFILE, "r");
539
468
  } else {
540
469
    conffp = fopen(argfile, "r");
541
 
  }  
 
470
  }
 
471
  
542
472
  if(conffp != NULL){
543
473
    char *org_line = NULL;
544
474
    char *p, *arg, *new_arg, *line;
556
486
    }
557
487
    custom_argv[0] = argv[0];
558
488
    custom_argv[1] = NULL;
559
 
 
560
 
    /* for each line in the config file, strip whitespace and ignore
561
 
       commented text */
 
489
    
562
490
    while(true){
563
491
      sret = getline(&org_line, &size, conffp);
564
492
      if(sret == -1){
593
521
      }
594
522
    }
595
523
    free(org_line);
596
 
  } else {
 
524
  } else{
597
525
    /* Check for harmful errors and go to fallback. Other errors might
598
526
       not affect opening plugins */
599
527
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
602
530
      goto fallback;
603
531
    }
604
532
  }
605
 
  /* If there was any arguments from configuration file,
606
 
     pass them to parser as command arguments */
 
533
 
607
534
  if(custom_argv != NULL){
608
 
    ret = argp_parse (&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
609
 
                      0, NULL);
 
535
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, &plugin_list);
610
536
    if (ret == ARGP_ERR_UNKNOWN){
611
537
      fprintf(stderr, "Unknown error while parsing arguments\n");
612
538
      exitstatus = EXIT_FAILURE;
614
540
    }
615
541
  }
616
542
  
617
 
  /* Parse actual command line arguments, to let them override the
618
 
     config file */
619
 
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
620
 
  if (ret == ARGP_ERR_UNKNOWN){
621
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
622
 
    exitstatus = EXIT_FAILURE;
623
 
    goto fallback;
624
 
  }
625
 
  
626
543
  if(debug){
627
544
    for(plugin *p = plugin_list; p != NULL; p=p->next){
628
545
      fprintf(stderr, "Plugin: %s has %d arguments\n",
637
554
    }
638
555
  }
639
556
  
640
 
  /* Strip permissions down to nobody */
641
557
  ret = setuid(uid);
642
558
  if (ret == -1){
643
559
    perror("setuid");
644
 
  }  
 
560
  }
 
561
  
645
562
  setgid(gid);
646
563
  if (ret == -1){
647
564
    perror("setgid");
648
565
  }
649
 
  
 
566
 
650
567
  if (plugindir == NULL){
651
568
    dir = opendir(PDIR);
652
569
  } else {
674
591
  
675
592
  FD_ZERO(&rfds_all);
676
593
  
677
 
  /* Read and execute any executable in the plugin directory*/
678
594
  while(true){
679
595
    dirst = readdir(dir);
680
596
    
681
 
    /* All directory entries have been processed */
 
597
    // All directory entries have been processed
682
598
    if(dirst == NULL){
683
599
      if (errno == EBADF){
684
600
        perror("readdir");
690
606
    
691
607
    d_name_len = strlen(dirst->d_name);
692
608
    
693
 
    /* Ignore dotfiles, backup files and other junk */
 
609
    // Ignore dotfiles, backup files and other junk
694
610
    {
695
611
      bool bad_name = false;
696
612
      
711
627
          break;
712
628
        }
713
629
      }
 
630
      
714
631
      if(bad_name){
715
632
        continue;
716
633
      }
 
634
      
717
635
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
718
636
        size_t suf_len = strlen(*suf);
719
637
        if((d_name_len >= suf_len)
746
664
      free(filename);
747
665
      continue;
748
666
    }
749
 
 
750
 
    /* Ignore non-executable files */
 
667
    
751
668
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
752
669
      if(debug){
753
670
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
756
673
      free(filename);
757
674
      continue;
758
675
    }
759
 
    
760
 
    plugin *p = getplugin(dirst->d_name);
 
676
    plugin *p = getplugin(dirst->d_name, &plugin_list);
761
677
    if(p == NULL){
762
678
      perror("getplugin");
763
679
      free(filename);
773
689
    }
774
690
    {
775
691
      /* Add global arguments to argument list for this plugin */
776
 
      plugin *g = getplugin(NULL);
 
692
      plugin *g = getplugin(NULL, &plugin_list);
777
693
      if(g != NULL){
778
694
        for(char **a = g->argv + 1; *a != NULL; a++){
779
695
          if(not add_argument(p, *a)){
782
698
        }
783
699
        /* Add global environment variables */
784
700
        for(char **e = g->environ; *e != NULL; e++){
785
 
          if(not add_environment(p, *e, false)){
 
701
          if(not add_environment(p, *e)){
786
702
            perror("add_environment");
787
703
          }
788
704
        }
793
709
       process, too. */
794
710
    if(p->environ[0] != NULL){
795
711
      for(char **e = environ; *e != NULL; e++){
796
 
        if(not add_environment(p, *e, false)){
 
712
        char *copy = strdup(*e);
 
713
        if(copy == NULL){
 
714
          perror("strdup");
 
715
          continue;
 
716
        }
 
717
        if(not add_environment(p, copy)){
797
718
          perror("add_environment");
798
719
        }
799
720
      }
806
727
      exitstatus = EXIT_FAILURE;
807
728
      goto fallback;
808
729
    }
809
 
    /* Ask OS to automatic close the pipe on exec */
810
730
    ret = set_cloexec_flag(pipefd[0]);
811
731
    if(ret < 0){
812
732
      perror("set_cloexec_flag");
826
746
      exitstatus = EXIT_FAILURE;
827
747
      goto fallback;
828
748
    }
829
 
    /* Starting a new process to be watched */
 
749
    // Starting a new process to be watched
830
750
    pid_t pid = fork();
831
751
    if(pid == -1){
832
752
      perror("fork");
870
790
      }
871
791
      /* no return */
872
792
    }
873
 
    /* Parent process */
874
 
    close(pipefd[1]);           /* Close unused write end of pipe */
 
793
    /* parent process */
875
794
    free(filename);
876
 
    plugin *new_plugin = getplugin(dirst->d_name);
877
 
    if (new_plugin == NULL){
878
 
      perror("getplugin");
 
795
    close(pipefd[1]);           /* close unused write end of pipe */
 
796
    process *new_process = malloc(sizeof(process));
 
797
    if (new_process == NULL){
 
798
      perror("malloc");
879
799
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
880
800
      if(ret < 0){
881
 
        perror("sigprocmask");
 
801
        perror("sigprocmask");
882
802
      }
883
803
      exitstatus = EXIT_FAILURE;
884
804
      goto fallback;
885
805
    }
886
806
    
887
 
    new_plugin->pid = pid;
888
 
    new_plugin->fd = pipefd[0];
889
 
    
 
807
    *new_process = (struct process){ .pid = pid,
 
808
                                     .fd = pipefd[0],
 
809
                                     .next = process_list };
 
810
    // List handling
 
811
    process_list = new_process;
890
812
    /* Unblock SIGCHLD so signal handler can be run if this process
891
813
       has already completed */
892
814
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
896
818
      goto fallback;
897
819
    }
898
820
    
899
 
    FD_SET(new_plugin->fd, &rfds_all);
 
821
    FD_SET(new_process->fd, &rfds_all);
900
822
    
901
 
    if (maxfd < new_plugin->fd){
902
 
      maxfd = new_plugin->fd;
 
823
    if (maxfd < new_process->fd){
 
824
      maxfd = new_process->fd;
903
825
    }
904
826
    
905
827
  }
 
828
 
 
829
  free_plugin_list(plugin_list);
 
830
  plugin_list = NULL;
906
831
  
907
832
  closedir(dir);
908
833
  dir = NULL;
909
 
 
910
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
911
 
    if(p->pid != 0){
912
 
      break;
913
 
    }
914
 
    if(p->next == NULL){
915
 
      fprintf(stderr, "No plugin processes started. Incorrect plugin"
916
 
              " directory?\n");
917
 
      free_plugin_list();
918
 
    }
 
834
    
 
835
  if (process_list == NULL){
 
836
    fprintf(stderr, "No plugin processes started. Incorrect plugin"
 
837
            " directory?\n");
 
838
    process_list = NULL;
919
839
  }
920
 
 
921
 
  /* Main loop while running plugins exist */
922
 
  while(plugin_list){
 
840
  while(process_list){
923
841
    fd_set rfds = rfds_all;
924
842
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
925
843
    if (select_ret == -1){
929
847
    }
930
848
    /* OK, now either a process completed, or something can be read
931
849
       from one of them */
932
 
    for(plugin *proc = plugin_list; proc != NULL; proc = proc->next){
 
850
    for(process *proc = process_list; proc ; proc = proc->next){
933
851
      /* Is this process completely done? */
934
852
      if(proc->eof and proc->completed){
935
853
        /* Only accept the plugin output if it exited cleanly */
936
854
        if(not WIFEXITED(proc->status)
937
855
           or WEXITSTATUS(proc->status) != 0){
938
856
          /* Bad exit by plugin */
939
 
 
940
857
          if(debug){
941
858
            if(WIFEXITED(proc->status)){
942
859
              fprintf(stderr, "Plugin %u exited with status %d\n",
951
868
                      (unsigned int) (proc->pid));
952
869
            }
953
870
          }
954
 
          
955
871
          /* Remove the plugin */
956
872
          FD_CLR(proc->fd, &rfds_all);
957
 
 
958
873
          /* Block signal while modifying process_list */
959
874
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
960
875
          if(ret < 0){
962
877
            exitstatus = EXIT_FAILURE;
963
878
            goto fallback;
964
879
          }
965
 
          free_plugin(proc);
 
880
          /* Delete this process entry from the list */
 
881
          if(process_list == proc){
 
882
            /* First one - simple */
 
883
            process_list = proc->next;
 
884
          } else {
 
885
            /* Second one or later */
 
886
            for(process *p = process_list; p != NULL; p = p->next){
 
887
              if(p->next == proc){
 
888
                p->next = proc->next;
 
889
                break;
 
890
              }
 
891
            }
 
892
          }
966
893
          /* We are done modifying process list, so unblock signal */
967
894
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
968
895
                             NULL);
969
896
          if(ret < 0){
970
897
            perror("sigprocmask");
971
 
            exitstatus = EXIT_FAILURE;
972
 
            goto fallback;
973
 
          }
974
 
          
975
 
          if(plugin_list == NULL){
976
 
            break;
977
 
          }
978
 
          continue;
 
898
          }
 
899
          free(proc->buffer);
 
900
          free(proc);
 
901
          /* We deleted this process from the list, so we can't go
 
902
             proc->next.  Therefore, start over from the beginning of
 
903
             the process list */
 
904
          break;
979
905
        }
980
 
        
981
906
        /* This process exited nicely, so print its buffer */
982
907
 
983
908
        bool bret = print_out_password(proc->buffer,
988
913
        }
989
914
        goto fallback;
990
915
      }
991
 
      
992
916
      /* This process has not completed.  Does it have any output? */
993
917
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
994
918
        /* This process had nothing to say at this time */
1024
948
 
1025
949
 fallback:
1026
950
  
1027
 
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
 
951
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
1028
952
    /* Fallback if all plugins failed, none are found or an error
1029
953
       occured */
1030
954
    bool bret;
1050
974
    }
1051
975
    free(custom_argv);
1052
976
  }
 
977
  free_plugin_list(plugin_list);
1053
978
  
1054
979
  if(dir != NULL){
1055
980
    closedir(dir);
1056
981
  }
1057
982
  
1058
983
  /* Free the process list and kill the processes */
1059
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1060
 
    if(p->pid != 0){
1061
 
      close(p->fd);
1062
 
      ret = kill(p->pid, SIGTERM);
1063
 
      if(ret == -1 and errno != ESRCH){
1064
 
        /* Set-uid proccesses might not get closed */
1065
 
        perror("kill");
1066
 
      }
 
984
  for(process *next; process_list != NULL; process_list = next){
 
985
    next = process_list->next;
 
986
    close(process_list->fd);
 
987
    ret = kill(process_list->pid, SIGTERM);
 
988
    if(ret == -1 and errno != ESRCH){
 
989
      /* set-uid proccesses migth not get closed */
 
990
      perror("kill");
1067
991
    }
 
992
    free(process_list->buffer);
 
993
    free(process_list);
1068
994
  }
1069
995
  
1070
996
  /* Wait for any remaining child processes to terminate */
1075
1001
    perror("wait");
1076
1002
  }
1077
1003
 
1078
 
  free_plugin_list();
1079
 
  
1080
1004
  free(plugindir);
1081
1005
  free(argfile);
1082
1006