/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:
483
450
    return 0;
484
451
  }
485
452
  
486
 
  /* This option parser is the same as parse_opt() above, except it
487
 
     ignores everything but the --config-file option. */
488
 
  error_t parse_opt_config_file (int key, char *arg,
489
 
                                 __attribute__((unused))
490
 
                                 struct argp_state *state) {
491
 
    switch (key) {
492
 
    case 'g':                   /* --global-options */
493
 
    case 'G':                   /* --global-env */
494
 
    case 'o':                   /* --options-for */
495
 
    case 'E':                   /* --env-for */
496
 
    case 'd':                   /* --disable */
497
 
    case 'e':                   /* --enable */
498
 
    case 128:                   /* --plugin-dir */
499
 
      break;
500
 
    case 129:                   /* --config-file */
501
 
      free(argfile);
502
 
      argfile = strdup(arg);
503
 
      if(argfile == NULL){
504
 
        perror("strdup");
505
 
      }
506
 
      break;      
507
 
    case 130:                   /* --userid */
508
 
    case 131:                   /* --groupid */
509
 
    case 132:                   /* --debug */
510
 
    case ARGP_KEY_ARG:
511
 
    case ARGP_KEY_END:
512
 
      break;
513
 
    default:
514
 
      return ARGP_ERR_UNKNOWN;
515
 
    }
516
 
    return 0;
517
 
  }
 
453
  plugin *plugin_list = NULL;
518
454
  
519
 
  struct argp argp = { .options = options,
520
 
                       .parser = parse_opt_config_file,
521
 
                       .args_doc = "",
 
455
  struct argp argp = { .options = options, .parser = parse_opt,
 
456
                       .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
522
457
                       .doc = "Mandos plugin runner -- Run plugins" };
523
458
  
524
 
  /* Parse using the parse_opt_config_file in order to get the custom
525
 
     config file location, if any. */
526
 
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
459
  ret = argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
527
460
  if (ret == ARGP_ERR_UNKNOWN){
528
461
    fprintf(stderr, "Unknown error while parsing arguments\n");
529
462
    exitstatus = EXIT_FAILURE;
530
463
    goto fallback;
531
464
  }
532
 
  
533
 
  /* Reset to the normal argument parser */
534
 
  argp.parser = parse_opt;
535
 
  
536
 
  /* Open the configfile if available */
 
465
 
537
466
  if (argfile == NULL){
538
467
    conffp = fopen(AFILE, "r");
539
468
  } else {
540
469
    conffp = fopen(argfile, "r");
541
 
  }  
 
470
  }
 
471
  
542
472
  if(conffp != NULL){
543
473
    char *org_line = NULL;
544
474
    char *p, *arg, *new_arg, *line;
556
486
    }
557
487
    custom_argv[0] = argv[0];
558
488
    custom_argv[1] = NULL;
559
 
 
560
 
    /* for each line in the config file, strip whitespace and ignore
561
 
       commented text */
 
489
    
562
490
    while(true){
563
491
      sret = getline(&org_line, &size, conffp);
564
492
      if(sret == -1){
593
521
      }
594
522
    }
595
523
    free(org_line);
596
 
  } else {
 
524
  } else{
597
525
    /* Check for harmful errors and go to fallback. Other errors might
598
526
       not affect opening plugins */
599
527
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
602
530
      goto fallback;
603
531
    }
604
532
  }
605
 
  /* If there was any arguments from configuration file,
606
 
     pass them to parser as command arguments */
 
533
 
607
534
  if(custom_argv != NULL){
608
 
    ret = argp_parse (&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
609
 
                      0, NULL);
 
535
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, &plugin_list);
610
536
    if (ret == ARGP_ERR_UNKNOWN){
611
537
      fprintf(stderr, "Unknown error while parsing arguments\n");
612
538
      exitstatus = EXIT_FAILURE;
614
540
    }
615
541
  }
616
542
  
617
 
  /* Parse actual command line arguments, to let them override the
618
 
     config file */
619
 
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
620
 
  if (ret == ARGP_ERR_UNKNOWN){
621
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
622
 
    exitstatus = EXIT_FAILURE;
623
 
    goto fallback;
624
 
  }
625
 
  
626
543
  if(debug){
627
544
    for(plugin *p = plugin_list; p != NULL; p=p->next){
628
545
      fprintf(stderr, "Plugin: %s has %d arguments\n",
637
554
    }
638
555
  }
639
556
  
640
 
  /* Strip permissions down to nobody */
641
557
  ret = setuid(uid);
642
558
  if (ret == -1){
643
559
    perror("setuid");
644
 
  }  
 
560
  }
 
561
  
645
562
  setgid(gid);
646
563
  if (ret == -1){
647
564
    perror("setgid");
648
565
  }
649
 
  
 
566
 
650
567
  if (plugindir == NULL){
651
568
    dir = opendir(PDIR);
652
569
  } else {
674
591
  
675
592
  FD_ZERO(&rfds_all);
676
593
  
677
 
  /* Read and execute any executable in the plugin directory*/
678
594
  while(true){
679
595
    dirst = readdir(dir);
680
596
    
681
 
    /* All directory entries have been processed */
 
597
    // All directory entries have been processed
682
598
    if(dirst == NULL){
683
599
      if (errno == EBADF){
684
600
        perror("readdir");
690
606
    
691
607
    d_name_len = strlen(dirst->d_name);
692
608
    
693
 
    /* Ignore dotfiles, backup files and other junk */
 
609
    // Ignore dotfiles, backup files and other junk
694
610
    {
695
611
      bool bad_name = false;
696
612
      
711
627
          break;
712
628
        }
713
629
      }
 
630
      
714
631
      if(bad_name){
715
632
        continue;
716
633
      }
 
634
      
717
635
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
718
636
        size_t suf_len = strlen(*suf);
719
637
        if((d_name_len >= suf_len)
734
652
    }
735
653
 
736
654
    char *filename;
737
 
    if(plugindir == NULL){
738
 
      ret = asprintf(&filename, PDIR "/%s", dirst->d_name);
739
 
    } else {
740
 
      ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
741
 
    }
 
655
    ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
742
656
    if(ret < 0){
743
657
      perror("asprintf");
744
658
      continue;
750
664
      free(filename);
751
665
      continue;
752
666
    }
753
 
 
754
 
    /* Ignore non-executable files */
 
667
    
755
668
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
756
669
      if(debug){
757
670
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
760
673
      free(filename);
761
674
      continue;
762
675
    }
763
 
    
764
 
    plugin *p = getplugin(dirst->d_name);
 
676
    plugin *p = getplugin(dirst->d_name, &plugin_list);
765
677
    if(p == NULL){
766
678
      perror("getplugin");
767
679
      free(filename);
777
689
    }
778
690
    {
779
691
      /* Add global arguments to argument list for this plugin */
780
 
      plugin *g = getplugin(NULL);
 
692
      plugin *g = getplugin(NULL, &plugin_list);
781
693
      if(g != NULL){
782
694
        for(char **a = g->argv + 1; *a != NULL; a++){
783
695
          if(not add_argument(p, *a)){
786
698
        }
787
699
        /* Add global environment variables */
788
700
        for(char **e = g->environ; *e != NULL; e++){
789
 
          if(not add_environment(p, *e, false)){
 
701
          if(not add_environment(p, *e)){
790
702
            perror("add_environment");
791
703
          }
792
704
        }
797
709
       process, too. */
798
710
    if(p->environ[0] != NULL){
799
711
      for(char **e = environ; *e != NULL; e++){
800
 
        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)){
801
718
          perror("add_environment");
802
719
        }
803
720
      }
810
727
      exitstatus = EXIT_FAILURE;
811
728
      goto fallback;
812
729
    }
813
 
    /* Ask OS to automatic close the pipe on exec */
814
730
    ret = set_cloexec_flag(pipefd[0]);
815
731
    if(ret < 0){
816
732
      perror("set_cloexec_flag");
830
746
      exitstatus = EXIT_FAILURE;
831
747
      goto fallback;
832
748
    }
833
 
    /* Starting a new process to be watched */
 
749
    // Starting a new process to be watched
834
750
    pid_t pid = fork();
835
751
    if(pid == -1){
836
752
      perror("fork");
874
790
      }
875
791
      /* no return */
876
792
    }
877
 
    /* Parent process */
878
 
    close(pipefd[1]);           /* Close unused write end of pipe */
 
793
    /* parent process */
879
794
    free(filename);
880
 
    plugin *new_plugin = getplugin(dirst->d_name);
881
 
    if (new_plugin == NULL){
882
 
      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");
883
799
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
884
800
      if(ret < 0){
885
 
        perror("sigprocmask");
 
801
        perror("sigprocmask");
886
802
      }
887
803
      exitstatus = EXIT_FAILURE;
888
804
      goto fallback;
889
805
    }
890
806
    
891
 
    new_plugin->pid = pid;
892
 
    new_plugin->fd = pipefd[0];
893
 
    
 
807
    *new_process = (struct process){ .pid = pid,
 
808
                                     .fd = pipefd[0],
 
809
                                     .next = process_list };
 
810
    // List handling
 
811
    process_list = new_process;
894
812
    /* Unblock SIGCHLD so signal handler can be run if this process
895
813
       has already completed */
896
814
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
900
818
      goto fallback;
901
819
    }
902
820
    
903
 
    FD_SET(new_plugin->fd, &rfds_all);
 
821
    FD_SET(new_process->fd, &rfds_all);
904
822
    
905
 
    if (maxfd < new_plugin->fd){
906
 
      maxfd = new_plugin->fd;
 
823
    if (maxfd < new_process->fd){
 
824
      maxfd = new_process->fd;
907
825
    }
908
826
    
909
827
  }
 
828
 
 
829
  free_plugin_list(plugin_list);
 
830
  plugin_list = NULL;
910
831
  
911
832
  closedir(dir);
912
833
  dir = NULL;
913
 
 
914
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
915
 
    if(p->pid != 0){
916
 
      break;
917
 
    }
918
 
    if(p->next == NULL){
919
 
      fprintf(stderr, "No plugin processes started. Incorrect plugin"
920
 
              " directory?\n");
921
 
      free_plugin_list();
922
 
    }
 
834
    
 
835
  if (process_list == NULL){
 
836
    fprintf(stderr, "No plugin processes started. Incorrect plugin"
 
837
            " directory?\n");
 
838
    process_list = NULL;
923
839
  }
924
 
 
925
 
  /* Main loop while running plugins exist */
926
 
  while(plugin_list){
 
840
  while(process_list){
927
841
    fd_set rfds = rfds_all;
928
842
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
929
843
    if (select_ret == -1){
933
847
    }
934
848
    /* OK, now either a process completed, or something can be read
935
849
       from one of them */
936
 
    for(plugin *proc = plugin_list; proc != NULL;){
 
850
    for(process *proc = process_list; proc ; proc = proc->next){
937
851
      /* Is this process completely done? */
938
852
      if(proc->eof and proc->completed){
939
853
        /* Only accept the plugin output if it exited cleanly */
940
854
        if(not WIFEXITED(proc->status)
941
855
           or WEXITSTATUS(proc->status) != 0){
942
856
          /* Bad exit by plugin */
943
 
 
944
857
          if(debug){
945
858
            if(WIFEXITED(proc->status)){
946
859
              fprintf(stderr, "Plugin %u exited with status %d\n",
955
868
                      (unsigned int) (proc->pid));
956
869
            }
957
870
          }
958
 
          
959
871
          /* Remove the plugin */
960
872
          FD_CLR(proc->fd, &rfds_all);
961
 
 
962
873
          /* Block signal while modifying process_list */
963
874
          ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL);
964
875
          if(ret < 0){
966
877
            exitstatus = EXIT_FAILURE;
967
878
            goto fallback;
968
879
          }
969
 
          
 
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
          }
970
893
          /* We are done modifying process list, so unblock signal */
971
894
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
972
895
                             NULL);
973
896
          if(ret < 0){
974
897
            perror("sigprocmask");
975
 
            exitstatus = EXIT_FAILURE;
976
 
            goto fallback;
977
 
          }
978
 
          
979
 
          if(plugin_list == NULL){
980
 
            break;
981
 
          }
982
 
          
983
 
          plugin *next_plugin = proc->next;
984
 
          free_plugin(proc);
985
 
          proc = next_plugin;
986
 
          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;
987
905
        }
988
 
        
989
906
        /* This process exited nicely, so print its buffer */
990
907
 
991
908
        bool bret = print_out_password(proc->buffer,
996
913
        }
997
914
        goto fallback;
998
915
      }
999
 
      
1000
916
      /* This process has not completed.  Does it have any output? */
1001
917
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1002
918
        /* This process had nothing to say at this time */
1003
 
        proc = proc->next;
1004
919
        continue;
1005
920
      }
1006
921
      /* Before reading, make the process' data buffer large enough */
1019
934
                 BUFFER_SIZE);
1020
935
      if(ret < 0){
1021
936
        /* Read error from this process; ignore the error */
1022
 
        proc = proc->next;
1023
937
        continue;
1024
938
      }
1025
939
      if(ret == 0){
1034
948
 
1035
949
 fallback:
1036
950
  
1037
 
  if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){
 
951
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
1038
952
    /* Fallback if all plugins failed, none are found or an error
1039
953
       occured */
1040
954
    bool bret;
1041
955
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
1042
956
    char *passwordbuffer = getpass("Password: ");
1043
 
    size_t len = strlen(passwordbuffer);
1044
 
    /* Strip trailing newline */
1045
 
    if(len > 0 and passwordbuffer[len-1] == '\n'){
1046
 
      passwordbuffer[len-1] = '\0'; /* not strictly necessary */
1047
 
      len--;
1048
 
    }
1049
 
    bret = print_out_password(passwordbuffer, len);
 
957
    bret = print_out_password(passwordbuffer, strlen(passwordbuffer));
1050
958
    if(not bret){
1051
959
      perror("print_out_password");
1052
960
      exitstatus = EXIT_FAILURE;
1066
974
    }
1067
975
    free(custom_argv);
1068
976
  }
 
977
  free_plugin_list(plugin_list);
1069
978
  
1070
979
  if(dir != NULL){
1071
980
    closedir(dir);
1072
981
  }
1073
982
  
1074
983
  /* Free the process list and kill the processes */
1075
 
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1076
 
    if(p->pid != 0){
1077
 
      close(p->fd);
1078
 
      ret = kill(p->pid, SIGTERM);
1079
 
      if(ret == -1 and errno != ESRCH){
1080
 
        /* Set-uid proccesses might not get closed */
1081
 
        perror("kill");
1082
 
      }
 
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");
1083
991
    }
 
992
    free(process_list->buffer);
 
993
    free(process_list);
1084
994
  }
1085
995
  
1086
996
  /* Wait for any remaining child processes to terminate */
1091
1001
    perror("wait");
1092
1002
  }
1093
1003
 
1094
 
  free_plugin_list();
1095
 
  
1096
1004
  free(plugindir);
1097
1005
  free(argfile);
1098
1006