/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 an 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;
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) + 1);
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){
238
220
      /* No child processes */
239
221
      break;
240
222
    }
241
 
    
 
223
 
242
224
    /* A child exited, find it in process_list */
243
225
    while(proc != NULL and proc->pid != pid){
244
226
      proc = proc->next;
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 = 'G',
 
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 = 'E',
 
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',
357
317
      .arg = "PLUGIN",
358
318
      .doc = "Disable a specific plugin", .group = 1 },
359
 
    { .name = "enable", .key = 'e',
360
 
      .arg = "PLUGIN",
361
 
      .doc = "Enable a specific plugin", .group = 1 },
362
319
    { .name = "plugin-dir", .key = 128,
363
320
      .arg = "DIRECTORY",
364
321
      .doc = "Specify a different plugin directory", .group = 2 },
376
333
    { .name = NULL }
377
334
  };
378
335
  
379
 
  error_t parse_opt (int key, char *arg, __attribute__((unused))
380
 
                     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;
381
340
    switch (key) {
382
 
    case 'g':                   /* --global-options */
 
341
    case 'g':
383
342
      if (arg != NULL){
384
343
        char *p;
385
344
        while((p = strsep(&arg, ",")) != NULL){
386
345
          if(p[0] == '\0'){
387
346
            continue;
388
347
          }
389
 
          if(not add_argument(getplugin(NULL), p)){
 
348
          if(not add_argument(getplugin(NULL, plugins), p)){
390
349
            perror("add_argument");
391
350
            return ARGP_ERR_UNKNOWN;
392
351
          }
393
352
        }
394
353
      }
395
354
      break;
396
 
    case 'G':                   /* --global-env */
 
355
    case 'e':
397
356
      if(arg == NULL){
398
357
        break;
399
358
      }
400
 
      if(not add_environment(getplugin(NULL), arg, true)){
401
 
        perror("add_environment");
 
359
      {
 
360
        char *envdef = strdup(arg);
 
361
        if(envdef == NULL){
 
362
          break;
 
363
        }
 
364
        if(not add_environment(getplugin(NULL, plugins), envdef)){
 
365
          perror("add_environment");
 
366
        }
402
367
      }
403
368
      break;
404
 
    case 'o':                   /* --options-for */
 
369
    case 'o':
405
370
      if (arg != NULL){
406
371
        char *p_name = strsep(&arg, ":");
407
 
        if(p_name[0] == '\0' or arg == NULL){
 
372
        if(p_name[0] == '\0'){
408
373
          break;
409
374
        }
410
375
        char *opt = strsep(&arg, ":");
411
 
        if(opt[0] == '\0' or opt == NULL){
 
376
        if(opt[0] == '\0'){
412
377
          break;
413
378
        }
414
 
        char *p;
415
 
        while((p = strsep(&opt, ",")) != NULL){
416
 
          if(p[0] == '\0'){
417
 
            continue;
418
 
          }
419
 
          if(not add_argument(getplugin(p_name), p)){
420
 
            perror("add_argument");
421
 
            return ARGP_ERR_UNKNOWN;
 
379
        if(opt != NULL){
 
380
          char *p;
 
381
          while((p = strsep(&opt, ",")) != NULL){
 
382
            if(p[0] == '\0'){
 
383
              continue;
 
384
            }
 
385
            if(not add_argument(getplugin(p_name, plugins), p)){
 
386
              perror("add_argument");
 
387
              return ARGP_ERR_UNKNOWN;
 
388
            }
422
389
          }
423
390
        }
424
391
      }
425
392
      break;
426
 
    case 'E':                   /* --env-for */
 
393
    case 'f':
427
394
      if(arg == NULL){
428
395
        break;
429
396
      }
432
399
        if(envdef == NULL){
433
400
          break;
434
401
        }
435
 
        *envdef = '\0';
436
 
        if(not add_environment(getplugin(arg), envdef+1, true)){
 
402
        char *p_name = strndup(arg, (size_t) (envdef-arg));
 
403
        if(p_name == NULL){
 
404
          break;
 
405
        }
 
406
        envdef++;
 
407
        if(not add_environment(getplugin(p_name, plugins), envdef)){
437
408
          perror("add_environment");
438
409
        }
439
410
      }
440
411
      break;
441
 
    case 'd':                   /* --disable */
 
412
    case 'd':
442
413
      if (arg != NULL){
443
 
        plugin *p = getplugin(arg);
 
414
        plugin *p = getplugin(arg, plugins);
444
415
        if(p == NULL){
445
416
          return ARGP_ERR_UNKNOWN;
446
417
        }
447
418
        p->disabled = true;
448
419
      }
449
420
      break;
450
 
    case 'e':                   /* --enable */
451
 
      if (arg != NULL){
452
 
        plugin *p = getplugin(arg);
453
 
        if(p == NULL){
454
 
          return ARGP_ERR_UNKNOWN;
455
 
        }
456
 
        p->disabled = false;
457
 
      }
458
 
      break;
459
 
    case 128:                   /* --plugin-dir */
460
 
      free(plugindir);
 
421
    case 128:
461
422
      plugindir = strdup(arg);
462
423
      if(plugindir == NULL){
463
424
        perror("strdup");
464
425
      }      
465
426
      break;
466
 
    case 129:                   /* --config-file */
467
 
      /* This is already done by parse_opt_config_file() */
468
 
      break;
469
 
    case 130:                   /* --userid */
 
427
    case 129:
 
428
      argfile = strdup(arg);
 
429
      if(argfile == NULL){
 
430
        perror("strdup");
 
431
      }
 
432
      break;      
 
433
    case 130:
470
434
      uid = (uid_t)strtol(arg, NULL, 10);
471
435
      break;
472
 
    case 131:                   /* --groupid */
 
436
    case 131:
473
437
      gid = (gid_t)strtol(arg, NULL, 10);
474
438
      break;
475
 
    case 132:                   /* --debug */
 
439
    case 132:
476
440
      debug = true;
477
441
      break;
478
442
    case ARGP_KEY_ARG:
486
450
    return 0;
487
451
  }
488
452
  
489
 
  /* This option parser is the same as parse_opt() above, except it
490
 
     ignores everything but the --config-file option. */
491
 
  error_t parse_opt_config_file (int key, char *arg,
492
 
                                 __attribute__((unused))
493
 
                                 struct argp_state *state) {
494
 
    switch (key) {
495
 
    case 'g':                   /* --global-options */
496
 
    case 'G':                   /* --global-env */
497
 
    case 'o':                   /* --options-for */
498
 
    case 'E':                   /* --env-for */
499
 
    case 'd':                   /* --disable */
500
 
    case 'e':                   /* --enable */
501
 
    case 128:                   /* --plugin-dir */
502
 
      break;
503
 
    case 129:                   /* --config-file */
504
 
      free(argfile);
505
 
      argfile = strdup(arg);
506
 
      if(argfile == NULL){
507
 
        perror("strdup");
508
 
      }
509
 
      break;      
510
 
    case 130:                   /* --userid */
511
 
    case 131:                   /* --groupid */
512
 
    case 132:                   /* --debug */
513
 
    case ARGP_KEY_ARG:
514
 
    case ARGP_KEY_END:
515
 
      break;
516
 
    default:
517
 
      return ARGP_ERR_UNKNOWN;
518
 
    }
519
 
    return 0;
520
 
  }
 
453
  plugin *plugin_list = NULL;
521
454
  
522
 
  struct argp argp = { .options = options,
523
 
                       .parser = parse_opt_config_file,
524
 
                       .args_doc = "",
 
455
  struct argp argp = { .options = options, .parser = parse_opt,
 
456
                       .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
525
457
                       .doc = "Mandos plugin runner -- Run plugins" };
526
458
  
527
 
  /* Parse using the parse_opt_config_file in order to get the custom
528
 
     config file location, if any. */
529
 
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
459
  ret = argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
530
460
  if (ret == ARGP_ERR_UNKNOWN){
531
461
    fprintf(stderr, "Unknown error while parsing arguments\n");
532
462
    exitstatus = EXIT_FAILURE;
533
463
    goto fallback;
534
464
  }
535
 
  
536
 
  /* Reset to the normal argument parser */
537
 
  argp.parser = parse_opt;
538
 
  
539
 
  /* Open the configfile if available */
 
465
 
540
466
  if (argfile == NULL){
541
467
    conffp = fopen(AFILE, "r");
542
468
  } else {
543
469
    conffp = fopen(argfile, "r");
544
 
  }  
 
470
  }
 
471
  
545
472
  if(conffp != NULL){
546
473
    char *org_line = NULL;
547
474
    char *p, *arg, *new_arg, *line;
559
486
    }
560
487
    custom_argv[0] = argv[0];
561
488
    custom_argv[1] = NULL;
562
 
 
563
 
    /* for each line in the config file, strip whitespace and ignore
564
 
       commented text */
 
489
    
565
490
    while(true){
566
491
      sret = getline(&org_line, &size, conffp);
567
492
      if(sret == -1){
596
521
      }
597
522
    }
598
523
    free(org_line);
599
 
  } else {
 
524
  } else{
600
525
    /* Check for harmful errors and go to fallback. Other errors might
601
526
       not affect opening plugins */
602
527
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
605
530
      goto fallback;
606
531
    }
607
532
  }
608
 
  /* If there was any arguments from configuration file,
609
 
     pass them to parser as command arguments */
 
533
 
610
534
  if(custom_argv != NULL){
611
 
    ret = argp_parse (&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
612
 
                      0, NULL);
 
535
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, &plugin_list);
613
536
    if (ret == ARGP_ERR_UNKNOWN){
614
537
      fprintf(stderr, "Unknown error while parsing arguments\n");
615
538
      exitstatus = EXIT_FAILURE;
617
540
    }
618
541
  }
619
542
  
620
 
  /* Parse actual command line arguments, to let them override the
621
 
     config file */
622
 
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
623
 
  if (ret == ARGP_ERR_UNKNOWN){
624
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
625
 
    exitstatus = EXIT_FAILURE;
626
 
    goto fallback;
627
 
  }
628
 
  
629
543
  if(debug){
630
544
    for(plugin *p = plugin_list; p != NULL; p=p->next){
631
545
      fprintf(stderr, "Plugin: %s has %d arguments\n",
640
554
    }
641
555
  }
642
556
  
643
 
  /* Strip permissions down to nobody */
644
557
  ret = setuid(uid);
645
558
  if (ret == -1){
646
559
    perror("setuid");
647
 
  }  
 
560
  }
 
561
  
648
562
  setgid(gid);
649
563
  if (ret == -1){
650
564
    perror("setgid");
651
565
  }
652
 
  
 
566
 
653
567
  if (plugindir == NULL){
654
568
    dir = opendir(PDIR);
655
569
  } else {
677
591
  
678
592
  FD_ZERO(&rfds_all);
679
593
  
680
 
  /* Read and execute any executable in the plugin directory*/
681
594
  while(true){
682
595
    dirst = readdir(dir);
683
596
    
684
 
    /* All directory entries have been processed */
 
597
    // All directory entries have been processed
685
598
    if(dirst == NULL){
686
599
      if (errno == EBADF){
687
600
        perror("readdir");
693
606
    
694
607
    d_name_len = strlen(dirst->d_name);
695
608
    
696
 
    /* Ignore dotfiles, backup files and other junk */
 
609
    // Ignore dotfiles, backup files and other junk
697
610
    {
698
611
      bool bad_name = false;
699
612
      
714
627
          break;
715
628
        }
716
629
      }
 
630
      
717
631
      if(bad_name){
718
632
        continue;
719
633
      }
 
634
      
720
635
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
721
636
        size_t suf_len = strlen(*suf);
722
637
        if((d_name_len >= suf_len)
749
664
      free(filename);
750
665
      continue;
751
666
    }
752
 
 
753
 
    /* Ignore non-executable files */
 
667
    
754
668
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
755
669
      if(debug){
756
670
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
759
673
      free(filename);
760
674
      continue;
761
675
    }
762
 
    
763
 
    plugin *p = getplugin(dirst->d_name);
 
676
    plugin *p = getplugin(dirst->d_name, &plugin_list);
764
677
    if(p == NULL){
765
678
      perror("getplugin");
766
679
      free(filename);
776
689
    }
777
690
    {
778
691
      /* Add global arguments to argument list for this plugin */
779
 
      plugin *g = getplugin(NULL);
 
692
      plugin *g = getplugin(NULL, &plugin_list);
780
693
      if(g != NULL){
781
694
        for(char **a = g->argv + 1; *a != NULL; a++){
782
695
          if(not add_argument(p, *a)){
785
698
        }
786
699
        /* Add global environment variables */
787
700
        for(char **e = g->environ; *e != NULL; e++){
788
 
          if(not add_environment(p, *e, false)){
 
701
          if(not add_environment(p, *e)){
789
702
            perror("add_environment");
790
703
          }
791
704
        }
796
709
       process, too. */
797
710
    if(p->environ[0] != NULL){
798
711
      for(char **e = environ; *e != NULL; e++){
799
 
        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)){
800
718
          perror("add_environment");
801
719
        }
802
720
      }
809
727
      exitstatus = EXIT_FAILURE;
810
728
      goto fallback;
811
729
    }
812
 
    /* Ask OS to automatic close the pipe on exec */
813
730
    ret = set_cloexec_flag(pipefd[0]);
814
731
    if(ret < 0){
815
732
      perror("set_cloexec_flag");
829
746
      exitstatus = EXIT_FAILURE;
830
747
      goto fallback;
831
748
    }
832
 
    /* Starting a new process to be watched */
 
749
    // Starting a new process to be watched
833
750
    pid_t pid = fork();
834
751
    if(pid == -1){
835
752
      perror("fork");
873
790
      }
874
791
      /* no return */
875
792
    }
876
 
    /* Parent process */
877
 
    close(pipefd[1]);           /* Close unused write end of pipe */
 
793
    /* parent process */
878
794
    free(filename);
879
 
    plugin *new_plugin = getplugin(dirst->d_name);
880
 
    if (new_plugin == NULL){
881
 
      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");
882
799
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
883
800
      if(ret < 0){
884
 
        perror("sigprocmask");
 
801
        perror("sigprocmask");
885
802
      }
886
803
      exitstatus = EXIT_FAILURE;
887
804
      goto fallback;
888
805
    }
889
806
    
890
 
    new_plugin->pid = pid;
891
 
    new_plugin->fd = pipefd[0];
892
 
    
 
807
    *new_process = (struct process){ .pid = pid,
 
808
                                     .fd = pipefd[0],
 
809
                                     .next = process_list };
 
810
    // List handling
 
811
    process_list = new_process;
893
812
    /* Unblock SIGCHLD so signal handler can be run if this process
894
813
       has already completed */
895
814
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
899
818
      goto fallback;
900
819
    }
901
820
    
902
 
    FD_SET(new_plugin->fd, &rfds_all);
 
821
    FD_SET(new_process->fd, &rfds_all);
903
822
    
904
 
    if (maxfd < new_plugin->fd){
905
 
      maxfd = new_plugin->fd;
 
823
    if (maxfd < new_process->fd){
 
824
      maxfd = new_process->fd;
906
825
    }
907
826
    
908
827
  }
 
828
 
 
829
  free_plugin_list(plugin_list);
 
830
  plugin_list = NULL;
909
831
  
910
832
  closedir(dir);
911
833
  dir = NULL;
912
 
 
913
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
914
 
    if(p->pid != 0){
915
 
      break;
916
 
    }
917
 
    if(p->next == NULL){
918
 
      fprintf(stderr, "No plugin processes started. Incorrect plugin"
919
 
              " directory?\n");
920
 
      free_plugin_list();
921
 
    }
 
834
    
 
835
  if (process_list == NULL){
 
836
    fprintf(stderr, "No plugin processes started. Incorrect plugin"
 
837
            " directory?\n");
 
838
    process_list = NULL;
922
839
  }
923
 
 
924
 
  /* Main loop while running plugins exist */
925
 
  while(plugin_list){
 
840
  while(process_list){
926
841
    fd_set rfds = rfds_all;
927
842
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
928
843
    if (select_ret == -1){
932
847
    }
933
848
    /* OK, now either a process completed, or something can be read
934
849
       from one of them */
935
 
    for(plugin *proc = plugin_list; proc != NULL;){
 
850
    for(process *proc = process_list; proc ; proc = proc->next){
936
851
      /* Is this process completely done? */
937
852
      if(proc->eof and proc->completed){
938
853
        /* Only accept the plugin output if it exited cleanly */
939
854
        if(not WIFEXITED(proc->status)
940
855
           or WEXITSTATUS(proc->status) != 0){
941
856
          /* Bad exit by plugin */
942
 
 
943
857
          if(debug){
944
858
            if(WIFEXITED(proc->status)){
945
859
              fprintf(stderr, "Plugin %u exited with status %d\n",
954
868
                      (unsigned int) (proc->pid));
955
869
            }
956
870
          }
957
 
          
958
871
          /* Remove the plugin */
959
872
          FD_CLR(proc->fd, &rfds_all);
960
 
 
961
873
          /* Block signal while modifying process_list */
962
874
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
963
875
          if(ret < 0){
965
877
            exitstatus = EXIT_FAILURE;
966
878
            goto fallback;
967
879
          }
968
 
          
 
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
          }
969
893
          /* We are done modifying process list, so unblock signal */
970
894
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
971
895
                             NULL);
972
896
          if(ret < 0){
973
897
            perror("sigprocmask");
974
 
            exitstatus = EXIT_FAILURE;
975
 
            goto fallback;
976
 
          }
977
 
          
978
 
          if(plugin_list == NULL){
979
 
            break;
980
 
          }
981
 
          
982
 
          plugin *next_plugin = proc->next;
983
 
          free_plugin(proc);
984
 
          proc = next_plugin;
985
 
          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;
986
905
        }
987
 
        
988
906
        /* This process exited nicely, so print its buffer */
989
907
 
990
908
        bool bret = print_out_password(proc->buffer,
995
913
        }
996
914
        goto fallback;
997
915
      }
998
 
      
999
916
      /* This process has not completed.  Does it have any output? */
1000
917
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1001
918
        /* This process had nothing to say at this time */
1002
 
        proc = proc->next;
1003
919
        continue;
1004
920
      }
1005
921
      /* Before reading, make the process' data buffer large enough */
1018
934
                 BUFFER_SIZE);
1019
935
      if(ret < 0){
1020
936
        /* Read error from this process; ignore the error */
1021
 
        proc = proc->next;
1022
937
        continue;
1023
938
      }
1024
939
      if(ret == 0){
1033
948
 
1034
949
 fallback:
1035
950
  
1036
 
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
 
951
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
1037
952
    /* Fallback if all plugins failed, none are found or an error
1038
953
       occured */
1039
954
    bool bret;
1059
974
    }
1060
975
    free(custom_argv);
1061
976
  }
 
977
  free_plugin_list(plugin_list);
1062
978
  
1063
979
  if(dir != NULL){
1064
980
    closedir(dir);
1065
981
  }
1066
982
  
1067
983
  /* Free the process list and kill the processes */
1068
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1069
 
    if(p->pid != 0){
1070
 
      close(p->fd);
1071
 
      ret = kill(p->pid, SIGTERM);
1072
 
      if(ret == -1 and errno != ESRCH){
1073
 
        /* Set-uid proccesses might not get closed */
1074
 
        perror("kill");
1075
 
      }
 
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");
1076
991
    }
 
992
    free(process_list->buffer);
 
993
    free(process_list);
1077
994
  }
1078
995
  
1079
996
  /* Wait for any remaining child processes to terminate */
1084
1001
    perror("wait");
1085
1002
  }
1086
1003
 
1087
 
  free_plugin_list();
1088
 
  
1089
1004
  free(plugindir);
1090
1005
  free(argfile);
1091
1006