/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:
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) {
378
337
    /* Get the INPUT argument from `argp_parse', which we know is a
379
338
       pointer to our plugin list pointer. */
 
339
    plugin **plugins = state->input;
380
340
    switch (key) {
381
 
    case 'g':                   /* --global-options */
 
341
    case 'g':
382
342
      if (arg != NULL){
383
343
        char *p;
384
344
        while((p = strsep(&arg, ",")) != NULL){
385
345
          if(p[0] == '\0'){
386
346
            continue;
387
347
          }
388
 
          if(not add_argument(getplugin(NULL), p)){
 
348
          if(not add_argument(getplugin(NULL, plugins), p)){
389
349
            perror("add_argument");
390
350
            return ARGP_ERR_UNKNOWN;
391
351
          }
392
352
        }
393
353
      }
394
354
      break;
395
 
    case 'e':                   /* --global-env */
 
355
    case 'e':
396
356
      if(arg == NULL){
397
357
        break;
398
358
      }
401
361
        if(envdef == NULL){
402
362
          break;
403
363
        }
404
 
        if(not add_environment(getplugin(NULL), envdef, true)){
 
364
        if(not add_environment(getplugin(NULL, plugins), envdef)){
405
365
          perror("add_environment");
406
366
        }
407
367
      }
408
368
      break;
409
 
    case 'o':                   /* --options-for */
 
369
    case 'o':
410
370
      if (arg != NULL){
411
371
        char *p_name = strsep(&arg, ":");
412
372
        if(p_name[0] == '\0'){
422
382
            if(p[0] == '\0'){
423
383
              continue;
424
384
            }
425
 
            if(not add_argument(getplugin(p_name), p)){
 
385
            if(not add_argument(getplugin(p_name, plugins), p)){
426
386
              perror("add_argument");
427
387
              return ARGP_ERR_UNKNOWN;
428
388
            }
430
390
        }
431
391
      }
432
392
      break;
433
 
    case 'f':                   /* --env-for */
 
393
    case 'f':
434
394
      if(arg == NULL){
435
395
        break;
436
396
      }
444
404
          break;
445
405
        }
446
406
        envdef++;
447
 
        if(not add_environment(getplugin(p_name), envdef, true)){
 
407
        if(not add_environment(getplugin(p_name, plugins), envdef)){
448
408
          perror("add_environment");
449
409
        }
450
410
      }
451
411
      break;
452
 
    case 'd':                   /* --disable */
 
412
    case 'd':
453
413
      if (arg != NULL){
454
 
        plugin *p = getplugin(arg);
 
414
        plugin *p = getplugin(arg, plugins);
455
415
        if(p == NULL){
456
416
          return ARGP_ERR_UNKNOWN;
457
417
        }
458
418
        p->disabled = true;
459
419
      }
460
420
      break;
461
 
    case 128:                   /* --plugin-dir */
 
421
    case 128:
462
422
      plugindir = strdup(arg);
463
423
      if(plugindir == NULL){
464
424
        perror("strdup");
465
425
      }      
466
426
      break;
467
 
    case 129:                   /* --config-file */
 
427
    case 129:
468
428
      argfile = strdup(arg);
469
429
      if(argfile == NULL){
470
430
        perror("strdup");
471
431
      }
472
432
      break;      
473
 
    case 130:                   /* --userid */
 
433
    case 130:
474
434
      uid = (uid_t)strtol(arg, NULL, 10);
475
435
      break;
476
 
    case 131:                   /* --groupid */
 
436
    case 131:
477
437
      gid = (gid_t)strtol(arg, NULL, 10);
478
438
      break;
479
 
    case 132:                   /* --debug */
 
439
    case 132:
480
440
      debug = true;
481
441
      break;
482
442
    case ARGP_KEY_ARG:
490
450
    return 0;
491
451
  }
492
452
  
 
453
  plugin *plugin_list = NULL;
 
454
  
493
455
  struct argp argp = { .options = options, .parser = parse_opt,
494
 
                       .args_doc = "",
 
456
                       .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
495
457
                       .doc = "Mandos plugin runner -- Run plugins" };
496
458
  
497
 
  /* Open the configfile if available */
 
459
  ret = argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
 
460
  if (ret == ARGP_ERR_UNKNOWN){
 
461
    fprintf(stderr, "Unknown error while parsing arguments\n");
 
462
    exitstatus = EXIT_FAILURE;
 
463
    goto fallback;
 
464
  }
 
465
 
498
466
  if (argfile == NULL){
499
467
    conffp = fopen(AFILE, "r");
500
468
  } else {
501
469
    conffp = fopen(argfile, "r");
502
 
  }  
 
470
  }
 
471
  
503
472
  if(conffp != NULL){
504
473
    char *org_line = NULL;
505
474
    char *p, *arg, *new_arg, *line;
517
486
    }
518
487
    custom_argv[0] = argv[0];
519
488
    custom_argv[1] = NULL;
520
 
 
521
 
    /* for each line in the config file, strip whitespace and ignore
522
 
       commented text */
 
489
    
523
490
    while(true){
524
491
      sret = getline(&org_line, &size, conffp);
525
492
      if(sret == -1){
554
521
      }
555
522
    }
556
523
    free(org_line);
557
 
  } else {
 
524
  } else{
558
525
    /* Check for harmful errors and go to fallback. Other errors might
559
526
       not affect opening plugins */
560
527
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
563
530
      goto fallback;
564
531
    }
565
532
  }
566
 
  /* If there was any arguments from configuration file,
567
 
     pass them to parser as command arguments */
 
533
 
568
534
  if(custom_argv != NULL){
569
 
    ret = argp_parse (&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
570
 
                      0, NULL);
 
535
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, &plugin_list);
571
536
    if (ret == ARGP_ERR_UNKNOWN){
572
537
      fprintf(stderr, "Unknown error while parsing arguments\n");
573
538
      exitstatus = EXIT_FAILURE;
575
540
    }
576
541
  }
577
542
  
578
 
  /* Parse actual command line arguments, to let them override the
579
 
     config file */
580
 
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
581
 
  if (ret == ARGP_ERR_UNKNOWN){
582
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
583
 
    exitstatus = EXIT_FAILURE;
584
 
    goto fallback;
585
 
  }
586
 
  
587
543
  if(debug){
588
544
    for(plugin *p = plugin_list; p != NULL; p=p->next){
589
545
      fprintf(stderr, "Plugin: %s has %d arguments\n",
598
554
    }
599
555
  }
600
556
  
601
 
  /* Strip permissions down to nobody */
602
557
  ret = setuid(uid);
603
558
  if (ret == -1){
604
559
    perror("setuid");
605
 
  }  
 
560
  }
 
561
  
606
562
  setgid(gid);
607
563
  if (ret == -1){
608
564
    perror("setgid");
609
565
  }
610
 
  
 
566
 
611
567
  if (plugindir == NULL){
612
568
    dir = opendir(PDIR);
613
569
  } else {
635
591
  
636
592
  FD_ZERO(&rfds_all);
637
593
  
638
 
  /* Read and execute any executable in the plugin directory*/
639
594
  while(true){
640
595
    dirst = readdir(dir);
641
596
    
642
 
    /* All directory entries have been processed */
 
597
    // All directory entries have been processed
643
598
    if(dirst == NULL){
644
599
      if (errno == EBADF){
645
600
        perror("readdir");
651
606
    
652
607
    d_name_len = strlen(dirst->d_name);
653
608
    
654
 
    /* Ignore dotfiles, backup files and other junk */
 
609
    // Ignore dotfiles, backup files and other junk
655
610
    {
656
611
      bool bad_name = false;
657
612
      
672
627
          break;
673
628
        }
674
629
      }
 
630
      
675
631
      if(bad_name){
676
632
        continue;
677
633
      }
 
634
      
678
635
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
679
636
        size_t suf_len = strlen(*suf);
680
637
        if((d_name_len >= suf_len)
707
664
      free(filename);
708
665
      continue;
709
666
    }
710
 
 
711
 
    /* Ignore non-executable files */
 
667
    
712
668
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
713
669
      if(debug){
714
670
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
717
673
      free(filename);
718
674
      continue;
719
675
    }
720
 
    
721
 
    plugin *p = getplugin(dirst->d_name);
 
676
    plugin *p = getplugin(dirst->d_name, &plugin_list);
722
677
    if(p == NULL){
723
678
      perror("getplugin");
724
679
      free(filename);
734
689
    }
735
690
    {
736
691
      /* Add global arguments to argument list for this plugin */
737
 
      plugin *g = getplugin(NULL);
 
692
      plugin *g = getplugin(NULL, &plugin_list);
738
693
      if(g != NULL){
739
694
        for(char **a = g->argv + 1; *a != NULL; a++){
740
695
          if(not add_argument(p, *a)){
743
698
        }
744
699
        /* Add global environment variables */
745
700
        for(char **e = g->environ; *e != NULL; e++){
746
 
          if(not add_environment(p, *e, false)){
 
701
          if(not add_environment(p, *e)){
747
702
            perror("add_environment");
748
703
          }
749
704
        }
754
709
       process, too. */
755
710
    if(p->environ[0] != NULL){
756
711
      for(char **e = environ; *e != NULL; e++){
757
 
        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)){
758
718
          perror("add_environment");
759
719
        }
760
720
      }
767
727
      exitstatus = EXIT_FAILURE;
768
728
      goto fallback;
769
729
    }
770
 
    /* Ask OS to automatic close the pipe on exec */
771
730
    ret = set_cloexec_flag(pipefd[0]);
772
731
    if(ret < 0){
773
732
      perror("set_cloexec_flag");
787
746
      exitstatus = EXIT_FAILURE;
788
747
      goto fallback;
789
748
    }
790
 
    /* Starting a new process to be watched */
 
749
    // Starting a new process to be watched
791
750
    pid_t pid = fork();
792
751
    if(pid == -1){
793
752
      perror("fork");
831
790
      }
832
791
      /* no return */
833
792
    }
834
 
    /* Parent process */
835
 
    close(pipefd[1]);           /* Close unused write end of pipe */
 
793
    /* parent process */
836
794
    free(filename);
837
 
    plugin *new_plugin = getplugin(dirst->d_name);
838
 
    if (new_plugin == NULL){
839
 
      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");
840
799
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
841
800
      if(ret < 0){
842
 
        perror("sigprocmask");
 
801
        perror("sigprocmask");
843
802
      }
844
803
      exitstatus = EXIT_FAILURE;
845
804
      goto fallback;
846
805
    }
847
806
    
848
 
    new_plugin->pid = pid;
849
 
    new_plugin->fd = pipefd[0];
850
 
    
 
807
    *new_process = (struct process){ .pid = pid,
 
808
                                     .fd = pipefd[0],
 
809
                                     .next = process_list };
 
810
    // List handling
 
811
    process_list = new_process;
851
812
    /* Unblock SIGCHLD so signal handler can be run if this process
852
813
       has already completed */
853
814
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
857
818
      goto fallback;
858
819
    }
859
820
    
860
 
    FD_SET(new_plugin->fd, &rfds_all);
 
821
    FD_SET(new_process->fd, &rfds_all);
861
822
    
862
 
    if (maxfd < new_plugin->fd){
863
 
      maxfd = new_plugin->fd;
 
823
    if (maxfd < new_process->fd){
 
824
      maxfd = new_process->fd;
864
825
    }
865
826
    
866
827
  }
 
828
 
 
829
  free_plugin_list(plugin_list);
 
830
  plugin_list = NULL;
867
831
  
868
832
  closedir(dir);
869
833
  dir = NULL;
870
 
 
871
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
872
 
    if(p->pid != 0){
873
 
      break;
874
 
    }
875
 
    if(p->next == NULL){
876
 
      fprintf(stderr, "No plugin processes started. Incorrect plugin"
877
 
              " directory?\n");
878
 
      free_plugin_list();
879
 
    }
 
834
    
 
835
  if (process_list == NULL){
 
836
    fprintf(stderr, "No plugin processes started. Incorrect plugin"
 
837
            " directory?\n");
 
838
    process_list = NULL;
880
839
  }
881
 
 
882
 
  /* Main loop while running plugins exist */
883
 
  while(plugin_list){
 
840
  while(process_list){
884
841
    fd_set rfds = rfds_all;
885
842
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
886
843
    if (select_ret == -1){
890
847
    }
891
848
    /* OK, now either a process completed, or something can be read
892
849
       from one of them */
893
 
    for(plugin *proc = plugin_list; proc != NULL; proc = proc->next){
 
850
    for(process *proc = process_list; proc ; proc = proc->next){
894
851
      /* Is this process completely done? */
895
852
      if(proc->eof and proc->completed){
896
853
        /* Only accept the plugin output if it exited cleanly */
897
854
        if(not WIFEXITED(proc->status)
898
855
           or WEXITSTATUS(proc->status) != 0){
899
856
          /* Bad exit by plugin */
900
 
 
901
857
          if(debug){
902
858
            if(WIFEXITED(proc->status)){
903
859
              fprintf(stderr, "Plugin %u exited with status %d\n",
912
868
                      (unsigned int) (proc->pid));
913
869
            }
914
870
          }
915
 
          
916
871
          /* Remove the plugin */
917
872
          FD_CLR(proc->fd, &rfds_all);
918
 
 
919
873
          /* Block signal while modifying process_list */
920
874
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
921
875
          if(ret < 0){
923
877
            exitstatus = EXIT_FAILURE;
924
878
            goto fallback;
925
879
          }
926
 
          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
          }
927
893
          /* We are done modifying process list, so unblock signal */
928
894
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
929
895
                             NULL);
930
896
          if(ret < 0){
931
897
            perror("sigprocmask");
932
 
            exitstatus = EXIT_FAILURE;
933
 
            goto fallback;
934
 
          }
935
 
          
936
 
          if(plugin_list == NULL){
937
 
            break;
938
 
          }
939
 
          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;
940
905
        }
941
 
        
942
906
        /* This process exited nicely, so print its buffer */
943
907
 
944
908
        bool bret = print_out_password(proc->buffer,
949
913
        }
950
914
        goto fallback;
951
915
      }
952
 
      
953
916
      /* This process has not completed.  Does it have any output? */
954
917
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
955
918
        /* This process had nothing to say at this time */
985
948
 
986
949
 fallback:
987
950
  
988
 
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
 
951
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
989
952
    /* Fallback if all plugins failed, none are found or an error
990
953
       occured */
991
954
    bool bret;
1011
974
    }
1012
975
    free(custom_argv);
1013
976
  }
 
977
  free_plugin_list(plugin_list);
1014
978
  
1015
979
  if(dir != NULL){
1016
980
    closedir(dir);
1017
981
  }
1018
982
  
1019
983
  /* Free the process list and kill the processes */
1020
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1021
 
    if(p->pid != 0){
1022
 
      close(p->fd);
1023
 
      ret = kill(p->pid, SIGTERM);
1024
 
      if(ret == -1 and errno != ESRCH){
1025
 
        /* Set-uid proccesses might not get closed */
1026
 
        perror("kill");
1027
 
      }
 
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");
1028
991
    }
 
992
    free(process_list->buffer);
 
993
    free(process_list);
1029
994
  }
1030
995
  
1031
996
  /* Wait for any remaining child processes to terminate */
1036
1001
    perror("wait");
1037
1002
  }
1038
1003
 
1039
 
  free_plugin_list();
1040
 
  
1041
1004
  free(plugindir);
1042
1005
  free(argfile);
1043
1006