/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 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;
 
239
  if(length>0 and buffer[length-1] == '\n'){
 
240
    length--;
 
241
  }
258
242
  for(size_t written = 0; written < length; written += (size_t)ret){
259
243
    ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
260
244
                                   length - written));
265
249
  return true;
266
250
}
267
251
 
268
 
/* Removes and free a plugin from the plugin list */
269
 
static void free_plugin(plugin *plugin_node){
270
 
  
271
 
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
272
 
    free(*arg);
273
 
  }
274
 
  free(plugin_node->argv);
275
 
  for(char **env = plugin_node->environ; *env != NULL; env++){
276
 
    free(*env);
277
 
  }
278
 
  free(plugin_node->environ);
279
 
  free(plugin_node->buffer);
280
 
 
281
 
  /* Removes the plugin from the singly-linked list */
282
 
  if(plugin_node == plugin_list){
283
 
    /* First one - simple */
284
 
    plugin_list = plugin_list->next;
285
 
  } else {
286
 
    /* Second one or later */
287
 
    for(plugin *p = plugin_list; p != NULL; p = p->next){
288
 
      if(p->next == plugin_node){
289
 
        p->next = plugin_node->next;
290
 
        break;
291
 
      }
292
 
    }
293
 
  }
294
 
  
295
 
  free(plugin_node);
296
 
}
297
 
 
298
 
static void free_plugin_list(void){
299
 
  while(plugin_list != NULL){
300
 
    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);
301
264
  }
302
265
}
303
266
 
341
304
    { .name = "global-options", .key = 'g',
342
305
      .arg = "OPTION[,OPTION[,...]]",
343
306
      .doc = "Options passed to all plugins" },
344
 
    { .name = "global-env", .key = 'G',
 
307
    { .name = "global-envs", .key = 'e',
345
308
      .arg = "VAR=value",
346
309
      .doc = "Environment variable passed to all plugins" },
347
310
    { .name = "options-for", .key = 'o',
348
311
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
349
312
      .doc = "Options passed only to specified plugin" },
350
 
    { .name = "env-for", .key = 'E',
 
313
    { .name = "envs-for", .key = 'f',
351
314
      .arg = "PLUGIN:ENV=value",
352
315
      .doc = "Environment variable passed to specified plugin" },
353
316
    { .name = "disable", .key = 'd',
354
317
      .arg = "PLUGIN",
355
318
      .doc = "Disable a specific plugin", .group = 1 },
356
 
    { .name = "enable", .key = 'e',
357
 
      .arg = "PLUGIN",
358
 
      .doc = "Enable a specific plugin", .group = 1 },
359
319
    { .name = "plugin-dir", .key = 128,
360
320
      .arg = "DIRECTORY",
361
321
      .doc = "Specify a different plugin directory", .group = 2 },
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 'G':                   /* --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
 
        if(p_name[0] == '\0' or arg == NULL){
 
372
        if(p_name[0] == '\0'){
411
373
          break;
412
374
        }
413
375
        char *opt = strsep(&arg, ":");
414
 
        if(opt[0] == '\0' or opt == NULL){
 
376
        if(opt[0] == '\0'){
415
377
          break;
416
378
        }
417
 
        char *p;
418
 
        while((p = strsep(&opt, ",")) != NULL){
419
 
          if(p[0] == '\0'){
420
 
            continue;
421
 
          }
422
 
          if(not add_argument(getplugin(p_name), p)){
423
 
            perror("add_argument");
424
 
            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
            }
425
389
          }
426
390
        }
427
391
      }
428
392
      break;
429
 
    case 'E':                   /* --env-for */
 
393
    case 'f':
430
394
      if(arg == NULL){
431
395
        break;
432
396
      }
440
404
          break;
441
405
        }
442
406
        envdef++;
443
 
        if(not add_environment(getplugin(p_name), envdef, true)){
 
407
        if(not add_environment(getplugin(p_name, plugins), envdef)){
444
408
          perror("add_environment");
445
409
        }
446
410
      }
447
411
      break;
448
 
    case 'd':                   /* --disable */
 
412
    case 'd':
449
413
      if (arg != NULL){
450
 
        plugin *p = getplugin(arg);
 
414
        plugin *p = getplugin(arg, plugins);
451
415
        if(p == NULL){
452
416
          return ARGP_ERR_UNKNOWN;
453
417
        }
454
418
        p->disabled = true;
455
419
      }
456
420
      break;
457
 
    case 'e':                   /* --enable */
458
 
      if (arg != NULL){
459
 
        plugin *p = getplugin(arg);
460
 
        if(p == NULL){
461
 
          return ARGP_ERR_UNKNOWN;
462
 
        }
463
 
        p->disabled = false;
464
 
      }
465
 
      break;
466
 
    case 128:                   /* --plugin-dir */
 
421
    case 128:
467
422
      plugindir = strdup(arg);
468
423
      if(plugindir == NULL){
469
424
        perror("strdup");
470
425
      }      
471
426
      break;
472
 
    case 129:                   /* --config-file */
473
 
      /* This is already done by parse_opt_config_file() */
474
 
      break;
475
 
    case 130:                   /* --userid */
 
427
    case 129:
 
428
      argfile = strdup(arg);
 
429
      if(argfile == NULL){
 
430
        perror("strdup");
 
431
      }
 
432
      break;      
 
433
    case 130:
476
434
      uid = (uid_t)strtol(arg, NULL, 10);
477
435
      break;
478
 
    case 131:                   /* --groupid */
 
436
    case 131:
479
437
      gid = (gid_t)strtol(arg, NULL, 10);
480
438
      break;
481
 
    case 132:                   /* --debug */
 
439
    case 132:
482
440
      debug = true;
483
441
      break;
484
442
    case ARGP_KEY_ARG:
492
450
    return 0;
493
451
  }
494
452
  
495
 
  /* This option parser is the same as parse_opt() above, except it
496
 
     ignores everything but the --config-file option. */
497
 
  error_t parse_opt_config_file (int key, char *arg,
498
 
                                 __attribute__((unused))
499
 
                                 struct argp_state *state) {
500
 
    switch (key) {
501
 
    case 'g':                   /* --global-options */
502
 
    case 'G':                   /* --global-env */
503
 
    case 'o':                   /* --options-for */
504
 
    case 'E':                   /* --env-for */
505
 
    case 'd':                   /* --disable */
506
 
    case 'e':                   /* --enable */
507
 
    case 128:                   /* --plugin-dir */
508
 
      break;
509
 
    case 129:                   /* --config-file */
510
 
      argfile = strdup(arg);
511
 
      if(argfile == NULL){
512
 
        perror("strdup");
513
 
      }
514
 
      break;      
515
 
    case 130:                   /* --userid */
516
 
    case 131:                   /* --groupid */
517
 
    case 132:                   /* --debug */
518
 
    case ARGP_KEY_ARG:
519
 
    case ARGP_KEY_END:
520
 
      break;
521
 
    default:
522
 
      return ARGP_ERR_UNKNOWN;
523
 
    }
524
 
    return 0;
525
 
  }
 
453
  plugin *plugin_list = NULL;
526
454
  
527
 
  struct argp argp = { .options = options,
528
 
                       .parser = parse_opt_config_file,
529
 
                       .args_doc = "",
 
455
  struct argp argp = { .options = options, .parser = parse_opt,
 
456
                       .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
530
457
                       .doc = "Mandos plugin runner -- Run plugins" };
531
458
  
532
 
  /* Parse using the parse_opt_config_file in order to get the custom
533
 
     config file location, if any. */
534
 
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
459
  ret = argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
535
460
  if (ret == ARGP_ERR_UNKNOWN){
536
461
    fprintf(stderr, "Unknown error while parsing arguments\n");
537
462
    exitstatus = EXIT_FAILURE;
538
463
    goto fallback;
539
464
  }
540
 
  
541
 
  /* Reset to the normal argument parser */
542
 
  argp.parser = parse_opt;
543
 
  
544
 
  /* Open the configfile if available */
 
465
 
545
466
  if (argfile == NULL){
546
467
    conffp = fopen(AFILE, "r");
547
468
  } else {
548
469
    conffp = fopen(argfile, "r");
549
 
  }  
 
470
  }
 
471
  
550
472
  if(conffp != NULL){
551
473
    char *org_line = NULL;
552
474
    char *p, *arg, *new_arg, *line;
564
486
    }
565
487
    custom_argv[0] = argv[0];
566
488
    custom_argv[1] = NULL;
567
 
 
568
 
    /* for each line in the config file, strip whitespace and ignore
569
 
       commented text */
 
489
    
570
490
    while(true){
571
491
      sret = getline(&org_line, &size, conffp);
572
492
      if(sret == -1){
601
521
      }
602
522
    }
603
523
    free(org_line);
604
 
  } else {
 
524
  } else{
605
525
    /* Check for harmful errors and go to fallback. Other errors might
606
526
       not affect opening plugins */
607
527
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
610
530
      goto fallback;
611
531
    }
612
532
  }
613
 
  /* If there was any arguments from configuration file,
614
 
     pass them to parser as command arguments */
 
533
 
615
534
  if(custom_argv != NULL){
616
 
    ret = argp_parse (&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
617
 
                      0, NULL);
 
535
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, &plugin_list);
618
536
    if (ret == ARGP_ERR_UNKNOWN){
619
537
      fprintf(stderr, "Unknown error while parsing arguments\n");
620
538
      exitstatus = EXIT_FAILURE;
622
540
    }
623
541
  }
624
542
  
625
 
  /* Parse actual command line arguments, to let them override the
626
 
     config file */
627
 
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
628
 
  if (ret == ARGP_ERR_UNKNOWN){
629
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
630
 
    exitstatus = EXIT_FAILURE;
631
 
    goto fallback;
632
 
  }
633
 
  
634
543
  if(debug){
635
544
    for(plugin *p = plugin_list; p != NULL; p=p->next){
636
545
      fprintf(stderr, "Plugin: %s has %d arguments\n",
645
554
    }
646
555
  }
647
556
  
648
 
  /* Strip permissions down to nobody */
649
557
  ret = setuid(uid);
650
558
  if (ret == -1){
651
559
    perror("setuid");
652
 
  }  
 
560
  }
 
561
  
653
562
  setgid(gid);
654
563
  if (ret == -1){
655
564
    perror("setgid");
656
565
  }
657
 
  
 
566
 
658
567
  if (plugindir == NULL){
659
568
    dir = opendir(PDIR);
660
569
  } else {
682
591
  
683
592
  FD_ZERO(&rfds_all);
684
593
  
685
 
  /* Read and execute any executable in the plugin directory*/
686
594
  while(true){
687
595
    dirst = readdir(dir);
688
596
    
689
 
    /* All directory entries have been processed */
 
597
    // All directory entries have been processed
690
598
    if(dirst == NULL){
691
599
      if (errno == EBADF){
692
600
        perror("readdir");
698
606
    
699
607
    d_name_len = strlen(dirst->d_name);
700
608
    
701
 
    /* Ignore dotfiles, backup files and other junk */
 
609
    // Ignore dotfiles, backup files and other junk
702
610
    {
703
611
      bool bad_name = false;
704
612
      
719
627
          break;
720
628
        }
721
629
      }
 
630
      
722
631
      if(bad_name){
723
632
        continue;
724
633
      }
 
634
      
725
635
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
726
636
        size_t suf_len = strlen(*suf);
727
637
        if((d_name_len >= suf_len)
742
652
    }
743
653
 
744
654
    char *filename;
745
 
    if(plugindir == NULL){
746
 
      ret = asprintf(&filename, PDIR "/%s", dirst->d_name);
747
 
    } else {
748
 
      ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
749
 
    }
 
655
    ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
750
656
    if(ret < 0){
751
657
      perror("asprintf");
752
658
      continue;
758
664
      free(filename);
759
665
      continue;
760
666
    }
761
 
 
762
 
    /* Ignore non-executable files */
 
667
    
763
668
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
764
669
      if(debug){
765
670
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
768
673
      free(filename);
769
674
      continue;
770
675
    }
771
 
    
772
 
    plugin *p = getplugin(dirst->d_name);
 
676
    plugin *p = getplugin(dirst->d_name, &plugin_list);
773
677
    if(p == NULL){
774
678
      perror("getplugin");
775
679
      free(filename);
785
689
    }
786
690
    {
787
691
      /* Add global arguments to argument list for this plugin */
788
 
      plugin *g = getplugin(NULL);
 
692
      plugin *g = getplugin(NULL, &plugin_list);
789
693
      if(g != NULL){
790
694
        for(char **a = g->argv + 1; *a != NULL; a++){
791
695
          if(not add_argument(p, *a)){
794
698
        }
795
699
        /* Add global environment variables */
796
700
        for(char **e = g->environ; *e != NULL; e++){
797
 
          if(not add_environment(p, *e, false)){
 
701
          if(not add_environment(p, *e)){
798
702
            perror("add_environment");
799
703
          }
800
704
        }
805
709
       process, too. */
806
710
    if(p->environ[0] != NULL){
807
711
      for(char **e = environ; *e != NULL; e++){
808
 
        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)){
809
718
          perror("add_environment");
810
719
        }
811
720
      }
818
727
      exitstatus = EXIT_FAILURE;
819
728
      goto fallback;
820
729
    }
821
 
    /* Ask OS to automatic close the pipe on exec */
822
730
    ret = set_cloexec_flag(pipefd[0]);
823
731
    if(ret < 0){
824
732
      perror("set_cloexec_flag");
838
746
      exitstatus = EXIT_FAILURE;
839
747
      goto fallback;
840
748
    }
841
 
    /* Starting a new process to be watched */
 
749
    // Starting a new process to be watched
842
750
    pid_t pid = fork();
843
751
    if(pid == -1){
844
752
      perror("fork");
882
790
      }
883
791
      /* no return */
884
792
    }
885
 
    /* Parent process */
886
 
    close(pipefd[1]);           /* Close unused write end of pipe */
 
793
    /* parent process */
887
794
    free(filename);
888
 
    plugin *new_plugin = getplugin(dirst->d_name);
889
 
    if (new_plugin == NULL){
890
 
      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");
891
799
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
892
800
      if(ret < 0){
893
 
        perror("sigprocmask");
 
801
        perror("sigprocmask");
894
802
      }
895
803
      exitstatus = EXIT_FAILURE;
896
804
      goto fallback;
897
805
    }
898
806
    
899
 
    new_plugin->pid = pid;
900
 
    new_plugin->fd = pipefd[0];
901
 
    
 
807
    *new_process = (struct process){ .pid = pid,
 
808
                                     .fd = pipefd[0],
 
809
                                     .next = process_list };
 
810
    // List handling
 
811
    process_list = new_process;
902
812
    /* Unblock SIGCHLD so signal handler can be run if this process
903
813
       has already completed */
904
814
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
908
818
      goto fallback;
909
819
    }
910
820
    
911
 
    FD_SET(new_plugin->fd, &rfds_all);
 
821
    FD_SET(new_process->fd, &rfds_all);
912
822
    
913
 
    if (maxfd < new_plugin->fd){
914
 
      maxfd = new_plugin->fd;
 
823
    if (maxfd < new_process->fd){
 
824
      maxfd = new_process->fd;
915
825
    }
916
826
    
917
827
  }
 
828
 
 
829
  free_plugin_list(plugin_list);
 
830
  plugin_list = NULL;
918
831
  
919
832
  closedir(dir);
920
833
  dir = NULL;
921
 
 
922
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
923
 
    if(p->pid != 0){
924
 
      break;
925
 
    }
926
 
    if(p->next == NULL){
927
 
      fprintf(stderr, "No plugin processes started. Incorrect plugin"
928
 
              " directory?\n");
929
 
      free_plugin_list();
930
 
    }
 
834
    
 
835
  if (process_list == NULL){
 
836
    fprintf(stderr, "No plugin processes started. Incorrect plugin"
 
837
            " directory?\n");
 
838
    process_list = NULL;
931
839
  }
932
 
 
933
 
  /* Main loop while running plugins exist */
934
 
  while(plugin_list){
 
840
  while(process_list){
935
841
    fd_set rfds = rfds_all;
936
842
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
937
843
    if (select_ret == -1){
941
847
    }
942
848
    /* OK, now either a process completed, or something can be read
943
849
       from one of them */
944
 
    for(plugin *proc = plugin_list; proc != NULL; proc = proc->next){
 
850
    for(process *proc = process_list; proc ; proc = proc->next){
945
851
      /* Is this process completely done? */
946
852
      if(proc->eof and proc->completed){
947
853
        /* Only accept the plugin output if it exited cleanly */
948
854
        if(not WIFEXITED(proc->status)
949
855
           or WEXITSTATUS(proc->status) != 0){
950
856
          /* Bad exit by plugin */
951
 
 
952
857
          if(debug){
953
858
            if(WIFEXITED(proc->status)){
954
859
              fprintf(stderr, "Plugin %u exited with status %d\n",
963
868
                      (unsigned int) (proc->pid));
964
869
            }
965
870
          }
966
 
          
967
871
          /* Remove the plugin */
968
872
          FD_CLR(proc->fd, &rfds_all);
969
 
 
970
873
          /* Block signal while modifying process_list */
971
874
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
972
875
          if(ret < 0){
974
877
            exitstatus = EXIT_FAILURE;
975
878
            goto fallback;
976
879
          }
977
 
          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
          }
978
893
          /* We are done modifying process list, so unblock signal */
979
894
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
980
895
                             NULL);
981
896
          if(ret < 0){
982
897
            perror("sigprocmask");
983
 
            exitstatus = EXIT_FAILURE;
984
 
            goto fallback;
985
 
          }
986
 
          
987
 
          if(plugin_list == NULL){
988
 
            break;
989
 
          }
990
 
          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;
991
905
        }
992
 
        
993
906
        /* This process exited nicely, so print its buffer */
994
907
 
995
908
        bool bret = print_out_password(proc->buffer,
1000
913
        }
1001
914
        goto fallback;
1002
915
      }
1003
 
      
1004
916
      /* This process has not completed.  Does it have any output? */
1005
917
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1006
918
        /* This process had nothing to say at this time */
1036
948
 
1037
949
 fallback:
1038
950
  
1039
 
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
 
951
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
1040
952
    /* Fallback if all plugins failed, none are found or an error
1041
953
       occured */
1042
954
    bool bret;
1043
955
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
1044
956
    char *passwordbuffer = getpass("Password: ");
1045
 
    size_t len = strlen(passwordbuffer);
1046
 
    /* Strip trailing newline */
1047
 
    if(len > 0 and passwordbuffer[len-1] == '\n'){
1048
 
      passwordbuffer[len-1] = '\0'; /* not strictly necessary */
1049
 
      len--;
1050
 
    }
1051
 
    bret = print_out_password(passwordbuffer, len);
 
957
    bret = print_out_password(passwordbuffer, strlen(passwordbuffer));
1052
958
    if(not bret){
1053
959
      perror("print_out_password");
1054
960
      exitstatus = EXIT_FAILURE;
1068
974
    }
1069
975
    free(custom_argv);
1070
976
  }
 
977
  free_plugin_list(plugin_list);
1071
978
  
1072
979
  if(dir != NULL){
1073
980
    closedir(dir);
1074
981
  }
1075
982
  
1076
983
  /* Free the process list and kill the processes */
1077
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1078
 
    if(p->pid != 0){
1079
 
      close(p->fd);
1080
 
      ret = kill(p->pid, SIGTERM);
1081
 
      if(ret == -1 and errno != ESRCH){
1082
 
        /* Set-uid proccesses might not get closed */
1083
 
        perror("kill");
1084
 
      }
 
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");
1085
991
    }
 
992
    free(process_list->buffer);
 
993
    free(process_list);
1086
994
  }
1087
995
  
1088
996
  /* Wait for any remaining child processes to terminate */
1093
1001
    perror("wait");
1094
1002
  }
1095
1003
 
1096
 
  free_plugin_list();
1097
 
  
1098
1004
  free(plugindir);
1099
1005
  free(argfile);
1100
1006