/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-16 03:29:08 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080816032908-ihw7c05r2mnyk389
Add feature to specify custom environment variables for plugins.

* plugin-runner.c (plugin): New members "environ" and "envc" to
                            contain possible custom environment.
  (getplugin): Return NULL on failure instead of doing exit(); all
               callers changed.
  (add_to_char_array): New helper function for "add_argument" and
                       "add_environment".
  (addargument): Renamed to "add_argument".  Return bool.  Call
                 "add_to_char_array" to actually do things.
  (add_environment): New; analogous to "add_argument".
  (addcustomargument): Renamed to "add_to_argv" to avoid confusion
                       with "add_argument".
  (main): New options "--global-envs" and "--envs-for" to specify
          custom environment for plugins.  Print environment for
          plugins in debug mode.  Use asprintf instead of strcpy and
          strcat.  Use execve() for plugins with custom environments.
          Free environment for plugin when freeing plugin list.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 * Contact the authors at <mandos@fukt.bsnet.se>.
22
22
 */
23
23
 
24
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY() */
25
 
 
 
24
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
 
25
                                   asprintf() */
26
26
#include <stddef.h>             /* size_t, NULL */
27
27
#include <stdlib.h>             /* malloc(), exit(), EXIT_FAILURE,
28
28
                                   EXIT_SUCCESS, realloc() */
51
51
                                   close() */
52
52
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
53
53
                                   FD_CLOEXEC */
54
 
#include <string.h>             /* strtok, strlen(), strcpy(),
55
 
                                   strcat() */
 
54
#include <string.h>             /* strsep, strlen(), asprintf() */
56
55
#include <errno.h>              /* errno */
57
56
#include <argp.h>               /* struct argp_option, struct
58
57
                                   argp_state, struct argp,
65
64
#include <errno.h>              /* errno, EBADF */
66
65
 
67
66
#define BUFFER_SIZE 256
 
67
#define ARGFILE "/conf/conf.d/mandos/plugin-runner.conf"
68
68
 
69
 
const char *argp_program_version = "mandos-client 1.0";
 
69
const char *argp_program_version = "plugin-runner 1.0";
70
70
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
71
71
 
72
72
struct process;
87
87
  char *name;                   /* can be NULL or any plugin name */
88
88
  char **argv;
89
89
  int argc;
 
90
  char **environ;
 
91
  int envc;
90
92
  bool disabled;
91
93
  struct plugin *next;
92
94
} plugin;
101
103
  /* Create a new plugin */
102
104
  plugin *new_plugin = malloc(sizeof(plugin));
103
105
  if (new_plugin == NULL){
104
 
    perror("malloc");
105
 
    exit(EXIT_FAILURE);
 
106
    return NULL;
106
107
  }
107
 
  new_plugin->name = name;
 
108
  *new_plugin = (plugin) { .name = name,
 
109
                           .argc = 1,
 
110
                           .envc = 0,
 
111
                           .disabled = false,
 
112
                           .next = *plugin_list };
 
113
  
108
114
  new_plugin->argv = malloc(sizeof(char *) * 2);
109
115
  if (new_plugin->argv == NULL){
110
 
    perror("malloc");
111
 
    exit(EXIT_FAILURE);
 
116
    free(new_plugin);
 
117
    return NULL;
112
118
  }
113
119
  new_plugin->argv[0] = name;
114
120
  new_plugin->argv[1] = NULL;
115
 
  new_plugin->argc = 1;
116
 
  new_plugin->disabled = false;
117
 
  new_plugin->next = *plugin_list;
 
121
 
 
122
  new_plugin->environ = malloc(sizeof(char *));
 
123
  if(new_plugin->environ == NULL){
 
124
    free(new_plugin->argv);
 
125
    free(new_plugin);
 
126
    return NULL;
 
127
  }
 
128
  new_plugin->environ[0] = NULL;
118
129
  /* Append the new plugin to the list */
119
130
  *plugin_list = new_plugin;
120
131
  return new_plugin;
121
132
}
122
133
 
123
 
static void addargument(plugin *p, char *arg){
124
 
  p->argv[p->argc] = arg;
125
 
  p->argv = realloc(p->argv, sizeof(char *) * (size_t)(p->argc + 2));
126
 
  if (p->argv == NULL){
127
 
    perror("malloc");
128
 
    exit(EXIT_FAILURE);
129
 
  }
130
 
  p->argc++;
131
 
  p->argv[p->argc] = NULL;
132
 
}
 
134
/* Helper function for add_argument and add_environment */
 
135
static bool add_to_char_array(const char *new, char ***array,
 
136
                              int *len){
 
137
  /* Resize the pointed-to array to hold one more pointer */
 
138
  *array = realloc(*array, sizeof(char *)
 
139
                   * (size_t) ((*len) + 2));
 
140
  /* Malloc check */
 
141
  if(*array == NULL){
 
142
    return false;
 
143
  }
 
144
  /* Make a copy of the new string */
 
145
  char *copy = strdup(new);
 
146
  if(copy == NULL){
 
147
    return false;
 
148
  }
 
149
  /* Insert the copy */
 
150
  (*array)[*len] = copy;
 
151
  (*len)++;
 
152
  /* Add a new terminating NULL pointer to the last element */
 
153
  (*array)[*len] = NULL;
 
154
  return true;
 
155
}
 
156
 
 
157
/* Add to a plugin's argument vector */
 
158
static bool add_argument(plugin *p, const char *arg){
 
159
  if(p == NULL){
 
160
    return false;
 
161
  }
 
162
  return add_to_char_array(arg, &(p->argv), &(p->argc));
 
163
}
 
164
 
 
165
/* Add to a plugin's environment */
 
166
static bool add_environment(plugin *p, const char *def){
 
167
  if(p == NULL){
 
168
    return false;
 
169
  }
 
170
  return add_to_char_array(def, &(p->environ), &(p->envc));
 
171
}
 
172
 
133
173
 
134
174
/*
135
175
 * Based on the example in the GNU LibC manual chapter 13.13 "File
171
211
}
172
212
 
173
213
bool print_out_password(const char *buffer, size_t length){
174
 
  size_t ret;
175
 
  for(size_t written = 0; written < length; written += ret){
 
214
  ssize_t ret;
 
215
  if(length>0 and buffer[length-1] == '\n'){
 
216
    length--;
 
217
  }
 
218
  for(size_t written = 0; written < length; written += (size_t)ret){
176
219
    ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
177
220
                                   length - written));
178
221
    if(ret < 0){
182
225
  return true;
183
226
}
184
227
 
 
228
char **add_to_argv(char **argv, int *argc, char *arg){
 
229
  if (argv == NULL){
 
230
    *argc = 1;
 
231
    argv = malloc(sizeof(char*) * 2);
 
232
    if(argv == NULL){
 
233
      return NULL;
 
234
    }
 
235
    argv[0] = NULL;     /* Will be set to argv[0] in main before parsing */
 
236
    argv[1] = NULL;
 
237
  }
 
238
  *argc += 1;
 
239
  argv = realloc(argv, sizeof(char *)
 
240
                  * ((unsigned int) *argc + 1));
 
241
  if(argv == NULL){
 
242
    return NULL;
 
243
  }
 
244
  argv[*argc-1] = arg;
 
245
  argv[*argc] = NULL;   
 
246
  return argv;
 
247
}
 
248
 
185
249
int main(int argc, char *argv[]){
186
 
  const char *plugindir = "/conf/conf.d/mandos/plugins.d";
 
250
  const char *plugindir = "/lib/mandos/plugins.d";
 
251
  const char *argfile = ARGFILE;
 
252
  FILE *conffp;
187
253
  size_t d_name_len;
188
254
  DIR *dir = NULL;
189
255
  struct dirent *dirst;
197
263
  struct sigaction old_sigchld_action;
198
264
  struct sigaction sigchld_action = { .sa_handler = handle_sigchld,
199
265
                                      .sa_flags = SA_NOCLDSTOP };
200
 
  char *plus_options = NULL;
201
 
  char **plus_argv = NULL;
 
266
  char **custom_argv = NULL;
 
267
  int custom_argc = 0;
202
268
  
203
269
  /* Establish a signal handler */
204
270
  sigemptyset(&sigchld_action.sa_mask);
218
284
    { .name = "global-options", .key = 'g',
219
285
      .arg = "OPTION[,OPTION[,...]]",
220
286
      .doc = "Options passed to all plugins" },
 
287
    { .name = "global-envs", .key = 'e',
 
288
      .arg = "VAR=value",
 
289
      .doc = "Environment variable passed to all plugins" },
221
290
    { .name = "options-for", .key = 'o',
222
291
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
223
292
      .doc = "Options passed only to specified plugin" },
 
293
    { .name = "envs-for", .key = 'f',
 
294
      .arg = "PLUGIN:ENV=value",
 
295
      .doc = "Environment variable passed to specified plugin" },
224
296
    { .name = "disable", .key = 'd',
225
297
      .arg = "PLUGIN",
226
298
      .doc = "Disable a specific plugin", .group = 1 },
245
317
    switch (key) {
246
318
    case 'g':
247
319
      if (arg != NULL){
248
 
        char *p = strtok(arg, ",");
249
 
        do{
250
 
          addargument(getplugin(NULL, plugins), p);
251
 
          p = strtok(NULL, ",");
252
 
        } while (p != NULL);
 
320
        char *p;
 
321
        while((p = strsep(&arg, ",")) != NULL){
 
322
          if(p[0] == '\0'){
 
323
            continue;
 
324
          }
 
325
          if(not add_argument(getplugin(NULL, plugins), p)){
 
326
            perror("add_argument");
 
327
            return ARGP_ERR_UNKNOWN;
 
328
          }
 
329
        }
 
330
      }
 
331
      break;
 
332
    case 'e':
 
333
      if(arg == NULL){
 
334
        break;
 
335
      }
 
336
      {
 
337
        char *envdef = strdup(arg);
 
338
        if(envdef == NULL){
 
339
          break;
 
340
        }
 
341
        if(not add_environment(getplugin(NULL, plugins), envdef)){
 
342
          perror("add_environment");
 
343
        }
253
344
      }
254
345
      break;
255
346
    case 'o':
256
347
      if (arg != NULL){
257
 
        char *name = strtok(arg, ":");
258
 
        char *p = strtok(NULL, ":");
259
 
        if(p != NULL){
260
 
          p = strtok(p, ",");
261
 
          do{
262
 
            addargument(getplugin(name, plugins), p);
263
 
            p = strtok(NULL, ",");
264
 
          } while (p != NULL);
 
348
        char *p_name = strsep(&arg, ":");
 
349
        if(p_name[0] == '\0'){
 
350
          break;
 
351
        }
 
352
        char *opt = strsep(&arg, ":");
 
353
        if(opt[0] == '\0'){
 
354
          break;
 
355
        }
 
356
        if(opt != NULL){
 
357
          char *p;
 
358
          while((p = strsep(&opt, ",")) != NULL){
 
359
            if(p[0] == '\0'){
 
360
              continue;
 
361
            }
 
362
            if(not add_argument(getplugin(p_name, plugins), p)){
 
363
              perror("add_argument");
 
364
              return ARGP_ERR_UNKNOWN;
 
365
            }
 
366
          }
 
367
        }
 
368
      }
 
369
      break;
 
370
    case 'f':
 
371
      if(arg == NULL){
 
372
        break;
 
373
      }
 
374
      {
 
375
        char *envdef = strchr(arg, ':');
 
376
        if(envdef == NULL){
 
377
          break;
 
378
        }
 
379
        char *p_name = strndup(arg, (size_t) (envdef-arg));
 
380
        if(p_name == NULL){
 
381
          break;
 
382
        }
 
383
        envdef++;
 
384
        if(not add_environment(getplugin(p_name, plugins), envdef)){
 
385
          perror("add_environment");
265
386
        }
266
387
      }
267
388
      break;
268
389
    case 'd':
269
390
      if (arg != NULL){
270
 
        getplugin(arg, plugins)->disabled = true;
 
391
        plugin *p = getplugin(arg, plugins);
 
392
        if(p == NULL){
 
393
          return ARGP_ERR_UNKNOWN;
 
394
        }
 
395
        p->disabled = true;
271
396
      }
272
397
      break;
273
398
    case 128:
283
408
      debug = true;
284
409
      break;
285
410
    case ARGP_KEY_ARG:
286
 
      if(plus_options != NULL or arg == NULL or arg[0] != '+'){
287
 
        argp_usage (state);
288
 
      }
289
 
      plus_options = arg;
 
411
      fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
290
412
      break;
291
413
    case ARGP_KEY_END:
292
414
      break;
304
426
  
305
427
  ret = argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
306
428
  if (ret == ARGP_ERR_UNKNOWN){
307
 
    fprintf(stderr, "Unkown error while parsing arguments\n");
 
429
    fprintf(stderr, "Unknown error while parsing arguments\n");
308
430
    exitstatus = EXIT_FAILURE;
309
431
    goto end;
310
432
  }
311
 
  
312
 
  if(plus_options){
313
 
    /* This is a mangled argument in the form of
314
 
     "+--option+--other-option=parameter+--yet-another-option", etc */
315
 
    /* Make new argc and argv vars, and call argp_parse() again. */
316
 
    plus_options++;             /* skip the first '+' character */
317
 
    const char delims[] = "+";
318
 
    char *arg;
319
 
    int new_argc = 1;
320
 
    plus_argv = malloc(sizeof(char*) * 2);
321
 
    if(plus_argv == NULL){
322
 
      perror("malloc");
 
433
 
 
434
  conffp = fopen(argfile, "r");
 
435
  if(conffp != NULL){
 
436
    char *org_line = NULL;
 
437
    size_t size = 0;
 
438
    ssize_t sret;
 
439
    char *p, *arg, *new_arg, *line;
 
440
    const char whitespace_delims[] = " \r\t\f\v\n";
 
441
    const char comment_delim[] = "#";
 
442
 
 
443
    while(true){
 
444
      sret = getline(&org_line, &size, conffp);
 
445
      if(sret == -1){
 
446
        break;
 
447
      }
 
448
 
 
449
      line = org_line;
 
450
      arg = strsep(&line, comment_delim);
 
451
      while((p = strsep(&arg, whitespace_delims)) != NULL){
 
452
        if(p[0] == '\0'){
 
453
          continue;
 
454
        }
 
455
        new_arg = strdup(p);
 
456
        custom_argv = add_to_argv(custom_argv, &custom_argc, new_arg);
 
457
        if (custom_argv == NULL){
 
458
          perror("add_to_argv");
 
459
          exitstatus = EXIT_FAILURE;
 
460
          goto end;
 
461
        }
 
462
      }
 
463
    }
 
464
    free(org_line);
 
465
  } else{
 
466
    /* Check for harmful errors and go to fallback. Other errors might
 
467
       not affect opening plugins */
 
468
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
 
469
      perror("fopen");
323
470
      exitstatus = EXIT_FAILURE;
324
471
      goto end;
325
472
    }
326
 
    plus_argv[0] = argv[0];
327
 
    plus_argv[1] = NULL;
328
 
    arg = strtok(plus_options, delims); /* Get first argument */
329
 
    while(arg != NULL){
330
 
      new_argc++;
331
 
      plus_argv = realloc(plus_argv, sizeof(char *)
332
 
                         * ((unsigned int) new_argc + 1));
333
 
      if(plus_argv == NULL){
334
 
        perror("realloc");
335
 
        exitstatus = EXIT_FAILURE;
336
 
        goto end;
337
 
      }
338
 
      plus_argv[new_argc-1] = arg;
339
 
      plus_argv[new_argc] = NULL;
340
 
      arg = strtok(NULL, delims); /* Get next argument */
341
 
    }
342
 
    ret = argp_parse (&argp, new_argc, plus_argv, 0, 0, &plugin_list);
 
473
  }
 
474
 
 
475
  if(custom_argv != NULL){
 
476
    custom_argv[0] = argv[0];
 
477
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, &plugin_list);
343
478
    if (ret == ARGP_ERR_UNKNOWN){
344
 
      fprintf(stderr, "Unkown error while parsing arguments\n");
 
479
      fprintf(stderr, "Unknown error while parsing arguments\n");
345
480
      exitstatus = EXIT_FAILURE;
346
481
      goto end;
347
482
    }
354
489
      for(char **a = p->argv; *a != NULL; a++){
355
490
        fprintf(stderr, "\tArg: %s\n", *a);
356
491
      }
 
492
      fprintf(stderr, "...and %u environment variables\n", p->envc);
 
493
      for(char **a = p->environ; *a != NULL; a++){
 
494
        fprintf(stderr, "\t%s\n", *a);
 
495
      }
357
496
    }
358
497
  }
359
498
  
448
587
        continue;
449
588
      }
450
589
    }
451
 
    
452
 
    char *filename = malloc(d_name_len + strlen(plugindir) + 2);
453
 
    if (filename == NULL){
454
 
      perror("malloc");
455
 
      exitstatus = EXIT_FAILURE;
456
 
      goto end;
 
590
 
 
591
    char *filename;
 
592
    ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
 
593
    if(ret < 0){
 
594
      perror("asprintf");
 
595
      continue;
457
596
    }
458
 
    strcpy(filename, plugindir); /* Spurious warning */
459
 
    strcat(filename, "/");      /* Spurious warning */
460
 
    strcat(filename, dirst->d_name); /* Spurious warning */
461
597
    
462
598
    ret = stat(filename, &st);
463
599
    if (ret == -1){
464
600
      perror("stat");
465
 
      exitstatus = EXIT_FAILURE;
466
 
      goto end;
 
601
      free(filename);
 
602
      continue;
467
603
    }
468
604
    
469
605
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
474
610
      free(filename);
475
611
      continue;
476
612
    }
477
 
    if(getplugin(dirst->d_name, &plugin_list)->disabled){
 
613
    plugin *p = getplugin(dirst->d_name, &plugin_list);
 
614
    if(p == NULL){
 
615
      perror("getplugin");
 
616
      free(filename);
 
617
      continue;
 
618
    }
 
619
    if(p->disabled){
478
620
      if(debug){
479
621
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
480
622
                dirst->d_name);
482
624
      free(filename);
483
625
      continue;
484
626
    }
485
 
    plugin *p = getplugin(dirst->d_name, &plugin_list);
486
627
    {
487
628
      /* Add global arguments to argument list for this plugin */
488
629
      plugin *g = getplugin(NULL, &plugin_list);
489
 
      for(char **a = g->argv + 1; *a != NULL; a++){
490
 
        addargument(p, *a);
491
 
      }
492
 
    }
 
630
      if(g != NULL){
 
631
        for(char **a = g->argv + 1; *a != NULL; a++){
 
632
          if(not add_argument(p, *a)){
 
633
            perror("add_argument");
 
634
          }
 
635
        }
 
636
        /* Add global environment variables */
 
637
        for(char **e = g->environ; *e != NULL; e++){
 
638
          if(not add_environment(p, *e)){
 
639
            perror("add_environment");
 
640
          }
 
641
        }
 
642
      }
 
643
    }
 
644
    /* If this plugin has any environment variables, we will call
 
645
       using execve and need to duplicate the environment from this
 
646
       process, too. */
 
647
    if(p->environ[0] != NULL){
 
648
      for(char **e = environ; *e != NULL; e++){
 
649
        char *copy = strdup(*e);
 
650
        if(copy == NULL){
 
651
          perror("strdup");
 
652
          continue;
 
653
        }
 
654
        if(not add_environment(p, copy)){
 
655
          perror("add_environment");
 
656
        }
 
657
      }
 
658
    }
 
659
    
493
660
    int pipefd[2]; 
494
661
    ret = pipe(pipefd);
495
662
    if (ret == -1){
518
685
    }
519
686
    // Starting a new process to be watched
520
687
    pid_t pid = fork();
 
688
    if(pid == -1){
 
689
      perror("fork");
 
690
      exitstatus = EXIT_FAILURE;
 
691
      goto end;
 
692
    }
521
693
    if(pid == 0){
522
694
      /* this is the child process */
523
695
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
542
714
           above and must now close it manually here. */
543
715
        closedir(dir);
544
716
      }
545
 
      if(execv(filename, p->argv) < 0){
546
 
        perror("execv");
547
 
        _exit(EXIT_FAILURE);
 
717
      if(p->environ[0] == NULL){
 
718
        if(execv(filename, p->argv) < 0){
 
719
          perror("execv");
 
720
          _exit(EXIT_FAILURE);
 
721
        }
 
722
      } else {
 
723
        if(execve(filename, p->argv, p->environ) < 0){
 
724
          perror("execve");
 
725
          _exit(EXIT_FAILURE);
 
726
        }
548
727
      }
549
728
      /* no return */
550
729
    }
588
767
  for(plugin *next; plugin_list != NULL; plugin_list = next){
589
768
    next = plugin_list->next;
590
769
    free(plugin_list->argv);
 
770
    if(plugin_list->environ[0] != NULL){
 
771
      for(char **e = plugin_list->environ; *e != NULL; e++){
 
772
        free(*e);
 
773
      }
 
774
    }
591
775
    free(plugin_list);
592
776
  }
593
777
  
705
889
      }
706
890
    }
707
891
  }
 
892
 
 
893
 
 
894
 end:
708
895
  
709
 
  if(process_list == NULL){
 
896
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
 
897
    /* Fallback if all plugins failed, none are found or an error occured */
710
898
    bool bret;
711
899
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
712
900
    char *passwordbuffer = getpass("Password: ");
716
904
      exitstatus = EXIT_FAILURE;
717
905
      goto end;
718
906
    }
719
 
    bret = print_out_password("\n", 1);
720
 
    if(not bret){
721
 
      perror("print_out_password");
722
 
      exitstatus = EXIT_FAILURE;
723
 
    }
724
907
  }
725
 
 
726
 
 end:
727
908
  
728
909
  /* Restore old signal handler */
729
910
  sigaction(SIGCHLD, &old_sigchld_action, NULL);
730
911
  
731
 
  free(plus_argv);
 
912
  free(custom_argv);
732
913
  
733
914
  /* Free the plugin list */
734
915
  for(plugin *next; plugin_list != NULL; plugin_list = next){
735
916
    next = plugin_list->next;
736
917
    free(plugin_list->argv);
 
918
    if(plugin_list->environ[0] != NULL){
 
919
      for(char **e = plugin_list->environ; *e != NULL; e++){
 
920
        free(*e);
 
921
      }
 
922
    }
 
923
    free(plugin_list->environ);
737
924
    free(plugin_list);
738
925
  }
739
926