/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
      }
397
 
      if(not add_environment(getplugin(NULL), arg, true)){
398
 
        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
        }
399
367
      }
400
368
      break;
401
 
    case 'o':                   /* --options-for */
 
369
    case 'o':
402
370
      if (arg != NULL){
403
371
        char *p_name = strsep(&arg, ":");
404
 
        if(p_name[0] == '\0' or arg == NULL){
 
372
        if(p_name[0] == '\0'){
405
373
          break;
406
374
        }
407
375
        char *opt = strsep(&arg, ":");
408
 
        if(opt[0] == '\0' or opt == NULL){
 
376
        if(opt[0] == '\0'){
409
377
          break;
410
378
        }
411
 
        char *p;
412
 
        while((p = strsep(&opt, ",")) != NULL){
413
 
          if(p[0] == '\0'){
414
 
            continue;
415
 
          }
416
 
          if(not add_argument(getplugin(p_name), p)){
417
 
            perror("add_argument");
418
 
            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
            }
419
389
          }
420
390
        }
421
391
      }
422
392
      break;
423
 
    case 'E':                   /* --env-for */
 
393
    case 'f':
424
394
      if(arg == NULL){
425
395
        break;
426
396
      }
429
399
        if(envdef == NULL){
430
400
          break;
431
401
        }
432
 
        *envdef = '\0';
433
 
        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)){
434
408
          perror("add_environment");
435
409
        }
436
410
      }
437
411
      break;
438
 
    case 'd':                   /* --disable */
 
412
    case 'd':
439
413
      if (arg != NULL){
440
 
        plugin *p = getplugin(arg);
 
414
        plugin *p = getplugin(arg, plugins);
441
415
        if(p == NULL){
442
416
          return ARGP_ERR_UNKNOWN;
443
417
        }
444
418
        p->disabled = true;
445
419
      }
446
420
      break;
447
 
    case 'e':                   /* --enable */
448
 
      if (arg != NULL){
449
 
        plugin *p = getplugin(arg);
450
 
        if(p == NULL){
451
 
          return ARGP_ERR_UNKNOWN;
452
 
        }
453
 
        p->disabled = false;
454
 
      }
455
 
      break;
456
 
    case 128:                   /* --plugin-dir */
457
 
      free(plugindir);
 
421
    case 128:
458
422
      plugindir = strdup(arg);
459
423
      if(plugindir == NULL){
460
424
        perror("strdup");
461
425
      }      
462
426
      break;
463
 
    case 129:                   /* --config-file */
464
 
      /* This is already done by parse_opt_config_file() */
465
 
      break;
466
 
    case 130:                   /* --userid */
 
427
    case 129:
 
428
      argfile = strdup(arg);
 
429
      if(argfile == NULL){
 
430
        perror("strdup");
 
431
      }
 
432
      break;      
 
433
    case 130:
467
434
      uid = (uid_t)strtol(arg, NULL, 10);
468
435
      break;
469
 
    case 131:                   /* --groupid */
 
436
    case 131:
470
437
      gid = (gid_t)strtol(arg, NULL, 10);
471
438
      break;
472
 
    case 132:                   /* --debug */
 
439
    case 132:
473
440
      debug = true;
474
441
      break;
475
442
    case ARGP_KEY_ARG:
476
 
      /* Cryptsetup always passes an argument, which is an empty
477
 
         string if "none" was specified in /etc/crypttab.  So if
478
 
         argument was empty, we ignore it silently. */
479
 
      if(arg[0] != '\0'){
480
 
        fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
481
 
      }
482
 
      break;
483
 
    case ARGP_KEY_END:
484
 
      break;
485
 
    default:
486
 
      return ARGP_ERR_UNKNOWN;
487
 
    }
488
 
    return 0;
489
 
  }
490
 
  
491
 
  /* This option parser is the same as parse_opt() above, except it
492
 
     ignores everything but the --config-file option. */
493
 
  error_t parse_opt_config_file (int key, char *arg,
494
 
                                 __attribute__((unused))
495
 
                                 struct argp_state *state) {
496
 
    switch (key) {
497
 
    case 'g':                   /* --global-options */
498
 
    case 'G':                   /* --global-env */
499
 
    case 'o':                   /* --options-for */
500
 
    case 'E':                   /* --env-for */
501
 
    case 'd':                   /* --disable */
502
 
    case 'e':                   /* --enable */
503
 
    case 128:                   /* --plugin-dir */
504
 
      break;
505
 
    case 129:                   /* --config-file */
506
 
      free(argfile);
507
 
      argfile = strdup(arg);
508
 
      if(argfile == NULL){
509
 
        perror("strdup");
510
 
      }
511
 
      break;      
512
 
    case 130:                   /* --userid */
513
 
    case 131:                   /* --groupid */
514
 
    case 132:                   /* --debug */
515
 
    case ARGP_KEY_ARG:
516
 
    case ARGP_KEY_END:
517
 
      break;
518
 
    default:
519
 
      return ARGP_ERR_UNKNOWN;
520
 
    }
521
 
    return 0;
522
 
  }
523
 
  
524
 
  struct argp argp = { .options = options,
525
 
                       .parser = parse_opt_config_file,
526
 
                       .args_doc = "",
 
443
      fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
 
444
      break;
 
445
    case ARGP_KEY_END:
 
446
      break;
 
447
    default:
 
448
      return ARGP_ERR_UNKNOWN;
 
449
    }
 
450
    return 0;
 
451
  }
 
452
  
 
453
  plugin *plugin_list = NULL;
 
454
  
 
455
  struct argp argp = { .options = options, .parser = parse_opt,
 
456
                       .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
527
457
                       .doc = "Mandos plugin runner -- Run plugins" };
528
458
  
529
 
  /* Parse using the parse_opt_config_file in order to get the custom
530
 
     config file location, if any. */
531
 
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
459
  ret = argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
532
460
  if (ret == ARGP_ERR_UNKNOWN){
533
461
    fprintf(stderr, "Unknown error while parsing arguments\n");
534
462
    exitstatus = EXIT_FAILURE;
535
463
    goto fallback;
536
464
  }
537
 
  
538
 
  /* Reset to the normal argument parser */
539
 
  argp.parser = parse_opt;
540
 
  
541
 
  /* Open the configfile if available */
 
465
 
542
466
  if (argfile == NULL){
543
467
    conffp = fopen(AFILE, "r");
544
468
  } else {
545
469
    conffp = fopen(argfile, "r");
546
 
  }  
 
470
  }
 
471
  
547
472
  if(conffp != NULL){
548
473
    char *org_line = NULL;
549
474
    char *p, *arg, *new_arg, *line;
561
486
    }
562
487
    custom_argv[0] = argv[0];
563
488
    custom_argv[1] = NULL;
564
 
 
565
 
    /* for each line in the config file, strip whitespace and ignore
566
 
       commented text */
 
489
    
567
490
    while(true){
568
491
      sret = getline(&org_line, &size, conffp);
569
492
      if(sret == -1){
598
521
      }
599
522
    }
600
523
    free(org_line);
601
 
  } else {
 
524
  } else{
602
525
    /* Check for harmful errors and go to fallback. Other errors might
603
526
       not affect opening plugins */
604
527
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
607
530
      goto fallback;
608
531
    }
609
532
  }
610
 
  /* If there was any arguments from configuration file,
611
 
     pass them to parser as command arguments */
 
533
 
612
534
  if(custom_argv != NULL){
613
 
    ret = argp_parse (&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
614
 
                      0, NULL);
 
535
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, &plugin_list);
615
536
    if (ret == ARGP_ERR_UNKNOWN){
616
537
      fprintf(stderr, "Unknown error while parsing arguments\n");
617
538
      exitstatus = EXIT_FAILURE;
619
540
    }
620
541
  }
621
542
  
622
 
  /* Parse actual command line arguments, to let them override the
623
 
     config file */
624
 
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
625
 
  if (ret == ARGP_ERR_UNKNOWN){
626
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
627
 
    exitstatus = EXIT_FAILURE;
628
 
    goto fallback;
629
 
  }
630
 
  
631
543
  if(debug){
632
544
    for(plugin *p = plugin_list; p != NULL; p=p->next){
633
545
      fprintf(stderr, "Plugin: %s has %d arguments\n",
642
554
    }
643
555
  }
644
556
  
645
 
  /* Strip permissions down to nobody */
646
557
  ret = setuid(uid);
647
558
  if (ret == -1){
648
559
    perror("setuid");
649
 
  }  
 
560
  }
 
561
  
650
562
  setgid(gid);
651
563
  if (ret == -1){
652
564
    perror("setgid");
653
565
  }
654
 
  
 
566
 
655
567
  if (plugindir == NULL){
656
568
    dir = opendir(PDIR);
657
569
  } else {
679
591
  
680
592
  FD_ZERO(&rfds_all);
681
593
  
682
 
  /* Read and execute any executable in the plugin directory*/
683
594
  while(true){
684
595
    dirst = readdir(dir);
685
596
    
686
 
    /* All directory entries have been processed */
 
597
    // All directory entries have been processed
687
598
    if(dirst == NULL){
688
599
      if (errno == EBADF){
689
600
        perror("readdir");
695
606
    
696
607
    d_name_len = strlen(dirst->d_name);
697
608
    
698
 
    /* Ignore dotfiles, backup files and other junk */
 
609
    // Ignore dotfiles, backup files and other junk
699
610
    {
700
611
      bool bad_name = false;
701
612
      
716
627
          break;
717
628
        }
718
629
      }
 
630
      
719
631
      if(bad_name){
720
632
        continue;
721
633
      }
 
634
      
722
635
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
723
636
        size_t suf_len = strlen(*suf);
724
637
        if((d_name_len >= suf_len)
739
652
    }
740
653
 
741
654
    char *filename;
742
 
    if(plugindir == NULL){
743
 
      ret = asprintf(&filename, PDIR "/%s", dirst->d_name);
744
 
    } else {
745
 
      ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
746
 
    }
 
655
    ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
747
656
    if(ret < 0){
748
657
      perror("asprintf");
749
658
      continue;
755
664
      free(filename);
756
665
      continue;
757
666
    }
758
 
 
759
 
    /* Ignore non-executable files */
 
667
    
760
668
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
761
669
      if(debug){
762
670
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
765
673
      free(filename);
766
674
      continue;
767
675
    }
768
 
    
769
 
    plugin *p = getplugin(dirst->d_name);
 
676
    plugin *p = getplugin(dirst->d_name, &plugin_list);
770
677
    if(p == NULL){
771
678
      perror("getplugin");
772
679
      free(filename);
782
689
    }
783
690
    {
784
691
      /* Add global arguments to argument list for this plugin */
785
 
      plugin *g = getplugin(NULL);
 
692
      plugin *g = getplugin(NULL, &plugin_list);
786
693
      if(g != NULL){
787
694
        for(char **a = g->argv + 1; *a != NULL; a++){
788
695
          if(not add_argument(p, *a)){
791
698
        }
792
699
        /* Add global environment variables */
793
700
        for(char **e = g->environ; *e != NULL; e++){
794
 
          if(not add_environment(p, *e, false)){
 
701
          if(not add_environment(p, *e)){
795
702
            perror("add_environment");
796
703
          }
797
704
        }
802
709
       process, too. */
803
710
    if(p->environ[0] != NULL){
804
711
      for(char **e = environ; *e != NULL; e++){
805
 
        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)){
806
718
          perror("add_environment");
807
719
        }
808
720
      }
815
727
      exitstatus = EXIT_FAILURE;
816
728
      goto fallback;
817
729
    }
818
 
    /* Ask OS to automatic close the pipe on exec */
819
730
    ret = set_cloexec_flag(pipefd[0]);
820
731
    if(ret < 0){
821
732
      perror("set_cloexec_flag");
835
746
      exitstatus = EXIT_FAILURE;
836
747
      goto fallback;
837
748
    }
838
 
    /* Starting a new process to be watched */
 
749
    // Starting a new process to be watched
839
750
    pid_t pid = fork();
840
751
    if(pid == -1){
841
752
      perror("fork");
879
790
      }
880
791
      /* no return */
881
792
    }
882
 
    /* Parent process */
883
 
    close(pipefd[1]);           /* Close unused write end of pipe */
 
793
    /* parent process */
884
794
    free(filename);
885
 
    plugin *new_plugin = getplugin(dirst->d_name);
886
 
    if (new_plugin == NULL){
887
 
      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");
888
799
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
889
800
      if(ret < 0){
890
 
        perror("sigprocmask");
 
801
        perror("sigprocmask");
891
802
      }
892
803
      exitstatus = EXIT_FAILURE;
893
804
      goto fallback;
894
805
    }
895
806
    
896
 
    new_plugin->pid = pid;
897
 
    new_plugin->fd = pipefd[0];
898
 
    
 
807
    *new_process = (struct process){ .pid = pid,
 
808
                                     .fd = pipefd[0],
 
809
                                     .next = process_list };
 
810
    // List handling
 
811
    process_list = new_process;
899
812
    /* Unblock SIGCHLD so signal handler can be run if this process
900
813
       has already completed */
901
814
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
905
818
      goto fallback;
906
819
    }
907
820
    
908
 
    FD_SET(new_plugin->fd, &rfds_all);
 
821
    FD_SET(new_process->fd, &rfds_all);
909
822
    
910
 
    if (maxfd < new_plugin->fd){
911
 
      maxfd = new_plugin->fd;
 
823
    if (maxfd < new_process->fd){
 
824
      maxfd = new_process->fd;
912
825
    }
913
826
    
914
827
  }
 
828
 
 
829
  free_plugin_list(plugin_list);
 
830
  plugin_list = NULL;
915
831
  
916
832
  closedir(dir);
917
833
  dir = NULL;
918
 
 
919
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
920
 
    if(p->pid != 0){
921
 
      break;
922
 
    }
923
 
    if(p->next == NULL){
924
 
      fprintf(stderr, "No plugin processes started. Incorrect plugin"
925
 
              " directory?\n");
926
 
      free_plugin_list();
927
 
    }
 
834
    
 
835
  if (process_list == NULL){
 
836
    fprintf(stderr, "No plugin processes started. Incorrect plugin"
 
837
            " directory?\n");
 
838
    process_list = NULL;
928
839
  }
929
 
 
930
 
  /* Main loop while running plugins exist */
931
 
  while(plugin_list){
 
840
  while(process_list){
932
841
    fd_set rfds = rfds_all;
933
842
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
934
843
    if (select_ret == -1){
938
847
    }
939
848
    /* OK, now either a process completed, or something can be read
940
849
       from one of them */
941
 
    for(plugin *proc = plugin_list; proc != NULL;){
 
850
    for(process *proc = process_list; proc ; proc = proc->next){
942
851
      /* Is this process completely done? */
943
852
      if(proc->eof and proc->completed){
944
853
        /* Only accept the plugin output if it exited cleanly */
945
854
        if(not WIFEXITED(proc->status)
946
855
           or WEXITSTATUS(proc->status) != 0){
947
856
          /* Bad exit by plugin */
948
 
 
949
857
          if(debug){
950
858
            if(WIFEXITED(proc->status)){
951
859
              fprintf(stderr, "Plugin %u exited with status %d\n",
960
868
                      (unsigned int) (proc->pid));
961
869
            }
962
870
          }
963
 
          
964
871
          /* Remove the plugin */
965
872
          FD_CLR(proc->fd, &rfds_all);
966
 
 
967
873
          /* Block signal while modifying process_list */
968
874
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
969
875
          if(ret < 0){
971
877
            exitstatus = EXIT_FAILURE;
972
878
            goto fallback;
973
879
          }
974
 
          
 
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
          }
975
893
          /* We are done modifying process list, so unblock signal */
976
894
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
977
895
                             NULL);
978
896
          if(ret < 0){
979
897
            perror("sigprocmask");
980
 
            exitstatus = EXIT_FAILURE;
981
 
            goto fallback;
982
 
          }
983
 
          
984
 
          if(plugin_list == NULL){
985
 
            break;
986
 
          }
987
 
          
988
 
          plugin *next_plugin = proc->next;
989
 
          free_plugin(proc);
990
 
          proc = next_plugin;
991
 
          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;
992
905
        }
993
 
        
994
906
        /* This process exited nicely, so print its buffer */
995
907
 
996
908
        bool bret = print_out_password(proc->buffer,
1001
913
        }
1002
914
        goto fallback;
1003
915
      }
1004
 
      
1005
916
      /* This process has not completed.  Does it have any output? */
1006
917
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1007
918
        /* This process had nothing to say at this time */
1008
 
        proc = proc->next;
1009
919
        continue;
1010
920
      }
1011
921
      /* Before reading, make the process' data buffer large enough */
1024
934
                 BUFFER_SIZE);
1025
935
      if(ret < 0){
1026
936
        /* Read error from this process; ignore the error */
1027
 
        proc = proc->next;
1028
937
        continue;
1029
938
      }
1030
939
      if(ret == 0){
1039
948
 
1040
949
 fallback:
1041
950
  
1042
 
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
 
951
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
1043
952
    /* Fallback if all plugins failed, none are found or an error
1044
953
       occured */
1045
954
    bool bret;
1046
955
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
1047
956
    char *passwordbuffer = getpass("Password: ");
1048
 
    size_t len = strlen(passwordbuffer);
1049
 
    /* Strip trailing newline */
1050
 
    if(len > 0 and passwordbuffer[len-1] == '\n'){
1051
 
      passwordbuffer[len-1] = '\0'; /* not strictly necessary */
1052
 
      len--;
1053
 
    }
1054
 
    bret = print_out_password(passwordbuffer, len);
 
957
    bret = print_out_password(passwordbuffer, strlen(passwordbuffer));
1055
958
    if(not bret){
1056
959
      perror("print_out_password");
1057
960
      exitstatus = EXIT_FAILURE;
1071
974
    }
1072
975
    free(custom_argv);
1073
976
  }
 
977
  free_plugin_list(plugin_list);
1074
978
  
1075
979
  if(dir != NULL){
1076
980
    closedir(dir);
1077
981
  }
1078
982
  
1079
983
  /* Free the process list and kill the processes */
1080
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1081
 
    if(p->pid != 0){
1082
 
      close(p->fd);
1083
 
      ret = kill(p->pid, SIGTERM);
1084
 
      if(ret == -1 and errno != ESRCH){
1085
 
        /* Set-uid proccesses might not get closed */
1086
 
        perror("kill");
1087
 
      }
 
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");
1088
991
    }
 
992
    free(process_list->buffer);
 
993
    free(process_list);
1089
994
  }
1090
995
  
1091
996
  /* Wait for any remaining child processes to terminate */
1096
1001
    perror("wait");
1097
1002
  }
1098
1003
 
1099
 
  free_plugin_list();
1100
 
  
1101
1004
  free(plugindir);
1102
1005
  free(argfile);
1103
1006