/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 plugbasedclient.c

  • Committer: Björn Påhlsson
  • Date: 2008-07-20 02:52:20 UTC
  • Revision ID: belorn@braxen-20080720025220-r5u0388uy9iu23h6
Added following support:
Pluginbased client handler
rewritten Mandos client
       Avahi instead of udp server discovery
       openpgp encrypted key support
Passprompt stand alone application for direct console input
Added logging for Mandos server

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  -*- coding: utf-8 -*- */
2
 
/*
3
 
 * Mandos plugin runner - Run Mandos plugins
4
 
 *
5
 
 * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
6
 
 * 
7
 
 * This program is free software: you can redistribute it and/or
8
 
 * modify it under the terms of the GNU General Public License as
9
 
 * published by the Free Software Foundation, either version 3 of the
10
 
 * License, or (at your option) any later version.
11
 
 * 
12
 
 * This program is distributed in the hope that it will be useful, but
13
 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 
 * General Public License for more details.
16
 
 * 
17
 
 * You should have received a copy of the GNU General Public License
18
 
 * along with this program.  If not, see
19
 
 * <http://www.gnu.org/licenses/>.
20
 
 * 
21
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
22
 
 */
23
 
 
24
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY() */
25
 
 
26
 
#include <stddef.h>             /* size_t, NULL */
27
 
#include <stdlib.h>             /* malloc(), exit(), EXIT_FAILURE,
28
 
                                   EXIT_SUCCESS, realloc() */
29
 
#include <stdbool.h>            /* bool, true, false */
30
 
#include <stdio.h>              /* perror, popen(), fileno(),
31
 
                                   fprintf(), stderr, STDOUT_FILENO */
32
 
#include <sys/types.h>          /* DIR, opendir(), stat(), struct
33
 
                                   stat, waitpid(), WIFEXITED(),
34
 
                                   WEXITSTATUS(), wait(), pid_t,
35
 
                                   uid_t, gid_t, getuid(), getgid(),
36
 
                                   dirfd() */
37
 
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
38
 
                                   FD_SET(), FD_ISSET(), FD_CLR */
39
 
#include <sys/wait.h>           /* wait(), waitpid(), WIFEXITED(),
40
 
                                   WEXITSTATUS() */
41
 
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
42
 
#include <iso646.h>             /* and, or, not */
43
 
#include <dirent.h>             /* DIR, struct dirent, opendir(),
44
 
                                   readdir(), closedir(), dirfd() */
45
 
#include <unistd.h>             /* struct stat, stat(), S_ISREG(),
46
 
                                   fcntl(), setuid(), setgid(),
47
 
                                   F_GETFD, F_SETFD, FD_CLOEXEC,
48
 
                                   access(), pipe(), fork(), close()
49
 
                                   dup2, STDOUT_FILENO, _exit(),
50
 
                                   execv(), write(), read(),
51
 
                                   close() */
52
 
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
53
 
                                   FD_CLOEXEC */
54
 
#include <string.h>             /* strsep, strlen(), strcpy(),
55
 
                                   strcat() */
56
 
#include <errno.h>              /* errno */
57
 
#include <argp.h>               /* struct argp_option, struct
58
 
                                   argp_state, struct argp,
59
 
                                   argp_parse(), ARGP_ERR_UNKNOWN,
60
 
                                   ARGP_KEY_END, ARGP_KEY_ARG, error_t */
61
 
#include <signal.h>             /* struct sigaction, sigemptyset(),
62
 
                                   sigaddset(), sigaction(),
63
 
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
64
 
                                   SIG_UNBLOCK, kill() */
65
 
#include <errno.h>              /* errno, EBADF */
66
 
 
67
 
#define BUFFER_SIZE 256
68
 
#define CONFFILE "/conf/conf.d/mandos/client.conf"
69
 
 
70
 
const char *argp_program_version = "mandos-client 1.0";
71
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
1
#include <stdio.h>      /* popen, fileno */
 
2
#include <iso646.h>     /* and, or, not */
 
3
#include <sys/types.h>  /* DIR, opendir, stat, struct stat, waitpid,
 
4
                           WIFEXITED, WEXITSTATUS, wait */
 
5
#include <sys/wait.h>   /* wait */
 
6
#include <dirent.h>     /* DIR, opendir */
 
7
#include <sys/stat.h>   /* stat, struct stat */
 
8
#include <unistd.h>     /* stat, struct stat, chdir */
 
9
#include <stdlib.h>     /* EXIT_FAILURE */
 
10
#include <sys/select.h> /* fd_set, select, FD_ZERO, FD_SET, FD_ISSET */
 
11
#include <string.h>     /* strlen, strcpy, strcat */
 
12
#include <stdbool.h>    /* true */
 
13
#include <sys/wait.h>   /* waitpid, WIFEXITED, WEXITSTATUS */
 
14
#include <errno.h>      /* errno */
72
15
 
73
16
struct process;
74
17
 
76
19
  pid_t pid;
77
20
  int fd;
78
21
  char *buffer;
79
 
  size_t buffer_size;
80
 
  size_t buffer_length;
81
 
  bool eof;
82
 
  bool completed;
83
 
  int status;
 
22
  int buffer_size;
 
23
  int buffer_length;
84
24
  struct process *next;
85
25
} process;
86
26
 
87
 
typedef struct plugin{
88
 
  char *name;                   /* can be NULL or any plugin name */
89
 
  char **argv;
90
 
  int argc;
91
 
  bool disabled;
92
 
  struct plugin *next;
93
 
} plugin;
94
 
 
95
 
static plugin *getplugin(char *name, plugin **plugin_list){
96
 
  for (plugin *p = *plugin_list; p != NULL; p = p->next){
97
 
    if ((p->name == name)
98
 
        or (p->name and name and (strcmp(p->name, name) == 0))){
99
 
      return p;
100
 
    }
101
 
  }
102
 
  /* Create a new plugin */
103
 
  plugin *new_plugin = malloc(sizeof(plugin));
104
 
  if (new_plugin == NULL){
105
 
    perror("malloc");
106
 
    exit(EXIT_FAILURE);
107
 
  }
108
 
  new_plugin->name = name;
109
 
  new_plugin->argv = malloc(sizeof(char *) * 2);
110
 
  if (new_plugin->argv == NULL){
111
 
    perror("malloc");
112
 
    exit(EXIT_FAILURE);
113
 
  }
114
 
  new_plugin->argv[0] = name;
115
 
  new_plugin->argv[1] = NULL;
116
 
  new_plugin->argc = 1;
117
 
  new_plugin->disabled = false;
118
 
  new_plugin->next = *plugin_list;
119
 
  /* Append the new plugin to the list */
120
 
  *plugin_list = new_plugin;
121
 
  return new_plugin;
122
 
}
123
 
 
124
 
static void addargument(plugin *p, char *arg){
125
 
  p->argv[p->argc] = arg;
126
 
  p->argv = realloc(p->argv, sizeof(char *) * (size_t)(p->argc + 2));
127
 
  if (p->argv == NULL){
128
 
    perror("malloc");
129
 
    exit(EXIT_FAILURE);
130
 
  }
131
 
  p->argc++;
132
 
  p->argv[p->argc] = NULL;
133
 
}
134
 
 
135
 
/*
136
 
 * Based on the example in the GNU LibC manual chapter 13.13 "File
137
 
 * Descriptor Flags".
138
 
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
139
 
 */
140
 
static int set_cloexec_flag(int fd)
141
 
{
142
 
  int ret = fcntl(fd, F_GETFD, 0);
143
 
  /* If reading the flags failed, return error indication now. */
144
 
  if(ret < 0){
145
 
    return ret;
146
 
  }
147
 
  /* Store modified flag word in the descriptor. */
148
 
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
149
 
}
150
 
 
151
 
process *process_list = NULL;
152
 
 
153
 
/* Mark a process as completed when it exits, and save its exit
154
 
   status. */
155
 
void handle_sigchld(__attribute__((unused)) int sig){
156
 
  process *proc = process_list;
157
 
  int status;
158
 
  pid_t pid = wait(&status);
159
 
  if(pid == -1){
160
 
    perror("wait");
161
 
    return;
162
 
  }
163
 
  while(proc != NULL and proc->pid != pid){
164
 
    proc = proc->next;
165
 
  }
166
 
  if(proc == NULL){
167
 
    /* Process not found in process list */
168
 
    return;
169
 
  }
170
 
  proc->status = status;
171
 
  proc->completed = true;
172
 
}
173
 
 
174
 
bool print_out_password(const char *buffer, size_t length){
175
 
  ssize_t ret;
176
 
  if(length>0 and buffer[length-1] == '\n'){
177
 
    length--;
178
 
  }
179
 
  for(size_t written = 0; written < length; written += (size_t)ret){
180
 
    ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
181
 
                                   length - written));
182
 
    if(ret < 0){
183
 
      return false;
184
 
    }
185
 
  }
186
 
  return true;
187
 
}
188
 
 
189
 
char ** addcustomargument(char **argv, int *argc, char *arg){
190
 
 
191
 
  if (argv == NULL){
192
 
    *argc = 1;
193
 
    argv = malloc(sizeof(char*) * 2);
194
 
    if(argv == NULL){
195
 
      return NULL;
196
 
    }
197
 
    argv[0] = NULL;     /* Will be set to argv[0] in main before parsing */
198
 
    argv[1] = NULL;
199
 
  }
200
 
  *argc += 1;
201
 
  argv = realloc(argv, sizeof(char *)
202
 
                  * ((unsigned int) *argc + 1));
203
 
  if(argv == NULL){
204
 
    return NULL;
205
 
  }
206
 
  argv[*argc-1] = arg;
207
 
  argv[*argc] = NULL;   
208
 
  return argv;
209
 
}
 
27
#define BUFFER_SIZE 256
210
28
 
211
29
int main(int argc, char *argv[]){
212
 
  const char *plugindir = "/conf/conf.d/mandos/plugins.d";
213
 
  const char *conffile = CONFFILE;
214
 
  FILE *conffp;
215
 
  size_t d_name_len;
216
 
  DIR *dir = NULL;
 
30
  char plugindir[] = "plugins.d";
 
31
  size_t d_name_len, plugindir_len = sizeof(plugindir)-1;
 
32
  DIR *dir;
217
33
  struct dirent *dirst;
218
34
  struct stat st;
219
 
  fd_set rfds_all;
 
35
  fd_set rfds_orig;
220
36
  int ret, maxfd = 0;
221
 
  uid_t uid = 65534;
222
 
  gid_t gid = 65534;
223
 
  bool debug = false;
224
 
  int exitstatus = EXIT_SUCCESS;
225
 
  struct sigaction old_sigchld_action;
226
 
  struct sigaction sigchld_action = { .sa_handler = handle_sigchld,
227
 
                                      .sa_flags = SA_NOCLDSTOP };
228
 
  char **custom_argv = NULL;
229
 
  int custom_argc = 0;
230
 
  
231
 
  /* Establish a signal handler */
232
 
  sigemptyset(&sigchld_action.sa_mask);
233
 
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
234
 
  if(ret < 0){
235
 
    perror("sigaddset");
236
 
    exit(EXIT_FAILURE);
237
 
  }
238
 
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
239
 
  if(ret < 0){
240
 
    perror("sigaction");
241
 
    exit(EXIT_FAILURE);
242
 
  }
243
 
  
244
 
  /* The options we understand. */
245
 
  struct argp_option options[] = {
246
 
    { .name = "global-options", .key = 'g',
247
 
      .arg = "OPTION[,OPTION[,...]]",
248
 
      .doc = "Options passed to all plugins" },
249
 
    { .name = "options-for", .key = 'o',
250
 
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
251
 
      .doc = "Options passed only to specified plugin" },
252
 
    { .name = "disable", .key = 'd',
253
 
      .arg = "PLUGIN",
254
 
      .doc = "Disable a specific plugin", .group = 1 },
255
 
    { .name = "plugin-dir", .key = 128,
256
 
      .arg = "DIRECTORY",
257
 
      .doc = "Specify a different plugin directory", .group = 2 },
258
 
    { .name = "userid", .key = 129,
259
 
      .arg = "ID", .flags = 0,
260
 
      .doc = "User ID the plugins will run as", .group = 2 },
261
 
    { .name = "groupid", .key = 130,
262
 
      .arg = "ID", .flags = 0,
263
 
      .doc = "Group ID the plugins will run as", .group = 2 },
264
 
    { .name = "debug", .key = 131,
265
 
      .doc = "Debug mode", .group = 3 },
266
 
    { .name = NULL }
267
 
  };
268
 
  
269
 
  error_t parse_opt (int key, char *arg, struct argp_state *state) {
270
 
    /* Get the INPUT argument from `argp_parse', which we know is a
271
 
       pointer to our plugin list pointer. */
272
 
    plugin **plugins = state->input;
273
 
    switch (key) {
274
 
    case 'g':
275
 
      if (arg != NULL){
276
 
        char *p;
277
 
        while((p = strsep(&arg, ",")) != NULL){
278
 
          if(p[0] == '\0'){
279
 
            continue;
280
 
          }
281
 
          addargument(getplugin(NULL, plugins), p);
282
 
        }
283
 
      }
284
 
      break;
285
 
    case 'o':
286
 
      if (arg != NULL){
287
 
        char *name = strsep(&arg, ":");
288
 
        if(name[0] == '\0'){
289
 
          break;
290
 
        }
291
 
        char *opt = strsep(&arg, ":");
292
 
        if(opt[0] == '\0'){
293
 
          break;
294
 
        }
295
 
        if(opt != NULL){
296
 
          char *p;
297
 
          while((p = strsep(&opt, ",")) != NULL){
298
 
            if(p[0] == '\0'){
299
 
              continue;
300
 
            }
301
 
            addargument(getplugin(name, plugins), p);
302
 
          }
303
 
        }
304
 
      }
305
 
      break;
306
 
    case 'd':
307
 
      if (arg != NULL){
308
 
        getplugin(arg, plugins)->disabled = true;
309
 
      }
310
 
      break;
311
 
    case 128:
312
 
      plugindir = arg;
313
 
      break;
314
 
    case 129:
315
 
      uid = (uid_t)strtol(arg, NULL, 10);
316
 
      break;
317
 
    case 130:
318
 
      gid = (gid_t)strtol(arg, NULL, 10);
319
 
      break;
320
 
    case 131:
321
 
      debug = true;
322
 
      break;
323
 
    case ARGP_KEY_ARG:
324
 
      fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg); 
325
 
      break;
326
 
    case ARGP_KEY_END:
327
 
      break;
328
 
    default:
329
 
      return ARGP_ERR_UNKNOWN;
330
 
    }
331
 
    return 0;
332
 
  }
333
 
  
334
 
  plugin *plugin_list = NULL;
335
 
  
336
 
  struct argp argp = { .options = options, .parser = parse_opt,
337
 
                       .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
338
 
                       .doc = "Mandos plugin runner -- Run plugins" };
339
 
  
340
 
  ret = argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
341
 
  if (ret == ARGP_ERR_UNKNOWN){
342
 
    fprintf(stderr, "Unknown error while parsing arguments\n");
343
 
    exitstatus = EXIT_FAILURE;
344
 
    goto end;
345
 
  }
346
 
 
347
 
  conffp = fopen(conffile, "r");
348
 
  if(conffp != NULL){
349
 
    char *org_line = NULL;
350
 
    size_t size = 0;
351
 
    ssize_t sret;
352
 
    char *p, *arg, *new_arg, *line;
353
 
    const char whitespace_delims[] = " \r\t\f\v\n";
354
 
    const char comment_delim[] = "#";
355
 
 
356
 
    while(true){
357
 
      sret = getline(&org_line, &size, conffp);
358
 
      if(sret == -1){
359
 
        break;
360
 
      }
361
 
 
362
 
      line = org_line;
363
 
      arg = strsep(&line, comment_delim);
364
 
      while((p = strsep(&arg, whitespace_delims)) != NULL){
365
 
        if(p[0] == '\0'){
366
 
          continue;
367
 
        }
368
 
        new_arg = strdup(p);
369
 
        custom_argv = addcustomargument(custom_argv, &custom_argc, new_arg);
370
 
        if (custom_argv == NULL){
371
 
          perror("addcustomargument");
372
 
          exitstatus = EXIT_FAILURE;
373
 
          goto end;
374
 
        }
375
 
      }
376
 
    }
377
 
    free(org_line);
378
 
  } else{
379
 
    /* check for harmfull errors */
380
 
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
381
 
      perror("fopen");
382
 
      exitstatus = EXIT_FAILURE;
383
 
      goto end;
384
 
    }
385
 
  }
386
 
 
387
 
  if(custom_argv != NULL){
388
 
    custom_argv[0] = argv[0];
389
 
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, &plugin_list);
390
 
    if (ret == ARGP_ERR_UNKNOWN){
391
 
      fprintf(stderr, "Unknown error while parsing arguments\n");
392
 
      exitstatus = EXIT_FAILURE;
393
 
      goto end;
394
 
    }
395
 
  }
396
 
  
397
 
  if(debug){
398
 
    for(plugin *p = plugin_list; p != NULL; p=p->next){
399
 
      fprintf(stderr, "Plugin: %s has %d arguments\n",
400
 
              p->name ? p->name : "Global", p->argc - 1);
401
 
      for(char **a = p->argv; *a != NULL; a++){
402
 
        fprintf(stderr, "\tArg: %s\n", *a);
403
 
      }
404
 
    }
405
 
  }
406
 
  
407
 
  ret = setuid(uid);
408
 
  if (ret == -1){
409
 
    perror("setuid");
410
 
  }
411
 
  
412
 
  setgid(gid);
413
 
  if (ret == -1){
414
 
    perror("setgid");
415
 
  }
 
37
  process *process_list = NULL;
416
38
  
417
39
  dir = opendir(plugindir);
 
40
 
418
41
  if(dir == NULL){
419
 
    perror("Could not open plugin dir");
420
 
    exitstatus = EXIT_FAILURE;
421
 
    goto end;
422
 
  }
423
 
  
424
 
  /* Set the FD_CLOEXEC flag on the directory, if possible */
425
 
  {
426
 
    int dir_fd = dirfd(dir);
427
 
    if(dir_fd >= 0){
428
 
      ret = set_cloexec_flag(dir_fd);
429
 
      if(ret < 0){
430
 
        perror("set_cloexec_flag");
431
 
        exitstatus = EXIT_FAILURE;
432
 
        goto end;
433
 
      }
434
 
    }
435
 
  }
436
 
  
437
 
  FD_ZERO(&rfds_all);
 
42
    fprintf(stderr, "Can not open directory\n");
 
43
    return EXIT_FAILURE;
 
44
  }
 
45
  
 
46
  FD_ZERO(&rfds_orig);
438
47
  
439
48
  while(true){
440
49
    dirst = readdir(dir);
441
50
    
442
51
    // All directory entries have been processed
443
52
    if(dirst == NULL){
444
 
      if (errno == EBADF){
445
 
        perror("readdir");
446
 
        exitstatus = EXIT_FAILURE;
447
 
        goto end;
448
 
      }
449
53
      break;
450
54
    }
451
55
    
452
56
    d_name_len = strlen(dirst->d_name);
453
57
    
454
 
    // Ignore dotfiles, backup files and other junk
455
 
    {
456
 
      bool bad_name = false;
457
 
      
458
 
      const char const *bad_prefixes[] = { ".", "#", NULL };
459
 
      
460
 
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
461
 
                                           ".dpkg-old",
462
 
                                           ".dpkg-divert", NULL };
463
 
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
464
 
        size_t pre_len = strlen(*pre);
465
 
        if((d_name_len >= pre_len)
466
 
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
467
 
          if(debug){
468
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
469
 
                    " with bad prefix %s\n", dirst->d_name, *pre);
470
 
          }
471
 
          bad_name = true;
472
 
          break;
473
 
        }
474
 
      }
475
 
      
476
 
      if(bad_name){
477
 
        continue;
478
 
      }
479
 
      
480
 
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
481
 
        size_t suf_len = strlen(*suf);
482
 
        if((d_name_len >= suf_len)
483
 
           and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
484
 
                == 0)){
485
 
          if(debug){
486
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
487
 
                    " with bad suffix %s\n", dirst->d_name, *suf);
488
 
          }
489
 
          bad_name = true;
490
 
          break;
491
 
        }
492
 
      }
493
 
      
494
 
      if(bad_name){
495
 
        continue;
496
 
      }
497
 
    }
498
 
    
499
 
    char *filename = malloc(d_name_len + strlen(plugindir) + 2);
500
 
    if (filename == NULL){
501
 
      perror("malloc");
502
 
      continue;
503
 
    }
504
 
    strcpy(filename, plugindir); /* Spurious warning */
505
 
    strcat(filename, "/");      /* Spurious warning */
506
 
    strcat(filename, dirst->d_name); /* Spurious warning */
507
 
    
508
 
    ret = stat(filename, &st);
509
 
    if (ret == -1){
510
 
      perror("stat");
511
 
      free(filename);
512
 
      continue;
513
 
    }
514
 
    
515
 
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
516
 
      if(debug){
517
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
518
 
                " with bad type or mode\n", filename);
519
 
      }
520
 
      free(filename);
521
 
      continue;
522
 
    }
523
 
    if(getplugin(dirst->d_name, &plugin_list)->disabled){
524
 
      if(debug){
525
 
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
526
 
                dirst->d_name);
527
 
      }
528
 
      free(filename);
529
 
      continue;
530
 
    }
531
 
    plugin *p = getplugin(dirst->d_name, &plugin_list);
532
 
    {
533
 
      /* Add global arguments to argument list for this plugin */
534
 
      plugin *g = getplugin(NULL, &plugin_list);
535
 
      for(char **a = g->argv + 1; *a != NULL; a++){
536
 
        addargument(p, *a);
537
 
      }
538
 
    }
539
 
    int pipefd[2]; 
540
 
    ret = pipe(pipefd);
541
 
    if (ret == -1){
542
 
      perror("pipe");
543
 
      exitstatus = EXIT_FAILURE;
544
 
      goto end;
545
 
    }
546
 
    ret = set_cloexec_flag(pipefd[0]);
547
 
    if(ret < 0){
548
 
      perror("set_cloexec_flag");
549
 
      exitstatus = EXIT_FAILURE;
550
 
      goto end;
551
 
    }
552
 
    ret = set_cloexec_flag(pipefd[1]);
553
 
    if(ret < 0){
554
 
      perror("set_cloexec_flag");
555
 
      exitstatus = EXIT_FAILURE;
556
 
      goto end;
557
 
    }
558
 
    /* Block SIGCHLD until process is safely in process list */
559
 
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
560
 
    if(ret < 0){
561
 
      perror("sigprocmask");
562
 
      exitstatus = EXIT_FAILURE;
563
 
      goto end;
564
 
    }
565
 
    // Starting a new process to be watched
566
 
    pid_t pid = fork();
567
 
    if(pid == -1){
568
 
      perror("fork");
569
 
      exitstatus = EXIT_FAILURE;
570
 
      goto end;
571
 
    }
572
 
    if(pid == 0){
573
 
      /* this is the child process */
574
 
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
575
 
      if(ret < 0){
576
 
        perror("sigaction");
577
 
        _exit(EXIT_FAILURE);
578
 
      }
579
 
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
580
 
      if(ret < 0){
581
 
        perror("sigprocmask");
582
 
        _exit(EXIT_FAILURE);
583
 
      }
 
58
    // Ignore dotfiles and backup files
 
59
    if (dirst->d_name[0] == '.'
 
60
        or dirst->d_name[d_name_len - 1] == '~'){
 
61
      continue;
 
62
    }
 
63
    
 
64
    char *filename = malloc(d_name_len + plugindir_len + 1);
 
65
    strcpy(filename, plugindir);
 
66
    strcat(filename, "/");
 
67
    strcat(filename, dirst->d_name);    
 
68
    
 
69
    stat(filename, &st);
584
70
 
585
 
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
586
 
      if(ret == -1){
587
 
        perror("dup2");
588
 
        _exit(EXIT_FAILURE);
589
 
      }
590
 
      
591
 
      if(dirfd(dir) < 0){
592
 
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
593
 
           above and must now close it manually here. */
 
71
    if (S_ISREG(st.st_mode) and (access(filename, X_OK) == 0)){
 
72
      // Starting a new process to be watched
 
73
      process *new_process = malloc(sizeof(process));
 
74
      int pipefd[2];
 
75
      pipe(pipefd);
 
76
      new_process->pid = fork();
 
77
      if(new_process->pid == 0){
 
78
        /* this is the child process */
594
79
        closedir(dir);
595
 
      }
596
 
      if(execv(filename, p->argv) < 0){
597
 
        perror("execv");
598
 
        _exit(EXIT_FAILURE);
599
 
      }
600
 
      /* no return */
601
 
    }
602
 
    /* parent process */
603
 
    free(filename);
604
 
    close(pipefd[1]);           /* close unused write end of pipe */
605
 
    process *new_process = malloc(sizeof(process));
606
 
    if (new_process == NULL){
607
 
      perror("malloc");
608
 
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
609
 
      if(ret < 0){
610
 
        perror("sigprocmask");
611
 
      }
612
 
      exitstatus = EXIT_FAILURE;
613
 
      goto end;
614
 
    }
615
 
    
616
 
    *new_process = (struct process){ .pid = pid,
617
 
                                     .fd = pipefd[0],
618
 
                                     .next = process_list };
619
 
    // List handling
620
 
    process_list = new_process;
621
 
    /* Unblock SIGCHLD so signal handler can be run if this process
622
 
       has already completed */
623
 
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
624
 
    if(ret < 0){
625
 
      perror("sigprocmask");
626
 
      exitstatus = EXIT_FAILURE;
627
 
      goto end;
628
 
    }
629
 
    
630
 
    FD_SET(new_process->fd, &rfds_all);
631
 
    
632
 
    if (maxfd < new_process->fd){
633
 
      maxfd = new_process->fd;
634
 
    }
635
 
    
636
 
  }
637
 
  
638
 
  /* Free the plugin list */
639
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
640
 
    next = plugin_list->next;
641
 
    free(plugin_list->argv);
642
 
    free(plugin_list);
 
80
        close(pipefd[0]);       /* close unused read end of pipe */
 
81
        dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
 
82
        /* create a new modified argument list */
 
83
        char **new_argv = malloc(sizeof(char *) * argc + 1);
 
84
        new_argv[0] = filename;
 
85
        for(int i = 1; i < argc; i++){
 
86
          new_argv[i] = argv[i];
 
87
        }
 
88
        new_argv[argc] = NULL;
 
89
        if(execv(filename, new_argv) < 0){
 
90
          perror(argv[0]);
 
91
          close(pipefd[1]);
 
92
          exit(EXIT_FAILURE);
 
93
        }
 
94
        /* no return */
 
95
      }
 
96
      close(pipefd[1]);         /* close unused write end of pipe */
 
97
      new_process->fd = pipefd[0];
 
98
      new_process->buffer = malloc(BUFFER_SIZE);
 
99
      if (new_process->buffer == NULL){
 
100
        perror(argv[0]);
 
101
        goto end;
 
102
      }
 
103
      new_process->buffer_size = BUFFER_SIZE;
 
104
      new_process->buffer_length = 0;
 
105
      FD_SET(new_process->fd, &rfds_orig);
 
106
      
 
107
      if (maxfd < new_process->fd){
 
108
        maxfd = new_process->fd;
 
109
      }
 
110
      
 
111
      //List handling
 
112
      new_process->next = process_list;
 
113
      process_list = new_process;
 
114
    }
643
115
  }
644
116
  
645
117
  closedir(dir);
646
 
  dir = NULL;
647
 
    
648
 
  if (process_list == NULL){
649
 
    fprintf(stderr, "No plugin processes started. Incorrect plugin"
650
 
            " directory?\n");
651
 
    process_list = NULL;
652
 
  }
653
 
  while(process_list){
654
 
    fd_set rfds = rfds_all;
655
 
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
656
 
    if (select_ret == -1){
657
 
      perror("select");
658
 
      exitstatus = EXIT_FAILURE;
659
 
      goto end;
660
 
    }
661
 
    /* OK, now either a process completed, or something can be read
662
 
       from one of them */
663
 
    for(process *proc = process_list; proc ; proc = proc->next){
664
 
      /* Is this process completely done? */
665
 
      if(proc->eof and proc->completed){
666
 
        /* Only accept the plugin output if it exited cleanly */
667
 
        if(not WIFEXITED(proc->status)
668
 
           or WEXITSTATUS(proc->status) != 0){
669
 
          /* Bad exit by plugin */
670
 
          if(debug){
671
 
            if(WIFEXITED(proc->status)){
672
 
              fprintf(stderr, "Plugin %u exited with status %d\n",
673
 
                      (unsigned int) (proc->pid),
674
 
                      WEXITSTATUS(proc->status));
675
 
            } else if(WIFSIGNALED(proc->status)) {
676
 
              fprintf(stderr, "Plugin %u killed by signal %d\n",
677
 
                      (unsigned int) (proc->pid),
678
 
                      WTERMSIG(proc->status));
679
 
            } else if(WCOREDUMP(proc->status)){
680
 
              fprintf(stderr, "Plugin %d dumped core\n",
681
 
                      (unsigned int) (proc->pid));
 
118
  
 
119
  if (process_list != NULL){
 
120
    while(true){
 
121
      fd_set rfds = rfds_orig;
 
122
      int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
 
123
      if (select_ret == -1){
 
124
        perror(argv[0]);
 
125
        goto end;
 
126
      }else{    
 
127
        for(process *process_itr = process_list; process_itr != NULL;
 
128
            process_itr = process_itr->next){
 
129
          if(FD_ISSET(process_itr->fd, &rfds)){
 
130
            if(process_itr->buffer_length + BUFFER_SIZE
 
131
               > process_itr->buffer_size){
 
132
                process_itr->buffer = realloc(process_itr->buffer,
 
133
                                              process_itr->buffer_size
 
134
                                              + BUFFER_SIZE);
 
135
                if (process_itr->buffer == NULL){
 
136
                  perror(argv[0]);
 
137
                  goto end;
 
138
                }
 
139
                process_itr->buffer_size += BUFFER_SIZE;
682
140
            }
683
 
          }
684
 
          /* Remove the plugin */
685
 
          FD_CLR(proc->fd, &rfds_all);
686
 
          /* Block signal while modifying process_list */
687
 
          ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
688
 
          if(ret < 0){
689
 
            perror("sigprocmask");
690
 
            exitstatus = EXIT_FAILURE;
691
 
            goto end;
692
 
          }
693
 
          /* Delete this process entry from the list */
694
 
          if(process_list == proc){
695
 
            /* First one - simple */
696
 
            process_list = proc->next;
697
 
          } else {
698
 
            /* Second one or later */
699
 
            for(process *p = process_list; p != NULL; p = p->next){
700
 
              if(p->next == proc){
701
 
                p->next = proc->next;
702
 
                break;
 
141
            ret = read(process_itr->fd, process_itr->buffer
 
142
                       + process_itr->buffer_length, BUFFER_SIZE);
 
143
            process_itr->buffer_length+=ret;
 
144
            if(ret == 0){
 
145
              /* got EOF */
 
146
              /* wait for process exit */
 
147
              int status;
 
148
              waitpid(process_itr->pid, &status, 0);
 
149
              if(WIFEXITED(status) and WEXITSTATUS(status) == 0){
 
150
                write(STDOUT_FILENO, process_itr->buffer,
 
151
                      process_itr->buffer_length);
 
152
                goto end;
 
153
              } else {
 
154
                FD_CLR(process_itr->fd, &rfds_orig);
703
155
              }
704
156
            }
705
157
          }
706
 
          /* We are done modifying process list, so unblock signal */
707
 
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
708
 
                             NULL);
709
 
          if(ret < 0){
710
 
            perror("sigprocmask");
711
 
          }
712
 
          free(proc->buffer);
713
 
          free(proc);
714
 
          /* We deleted this process from the list, so we can't go
715
 
             proc->next.  Therefore, start over from the beginning of
716
 
             the process list */
717
 
          break;
718
 
        }
719
 
        /* This process exited nicely, so print its buffer */
720
 
 
721
 
        bool bret = print_out_password(proc->buffer, proc->buffer_length);
722
 
        if(not bret){
723
 
          perror("print_out_password");
724
 
          exitstatus = EXIT_FAILURE;
725
 
        }
726
 
        goto end;
727
 
      }
728
 
      /* This process has not completed.  Does it have any output? */
729
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
730
 
        /* This process had nothing to say at this time */
731
 
        continue;
732
 
      }
733
 
      /* Before reading, make the process' data buffer large enough */
734
 
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
735
 
        proc->buffer = realloc(proc->buffer, proc->buffer_size
736
 
                               + (size_t) BUFFER_SIZE);
737
 
        if (proc->buffer == NULL){
738
 
          perror("malloc");
739
 
          exitstatus = EXIT_FAILURE;
740
 
          goto end;
741
 
        }
742
 
        proc->buffer_size += BUFFER_SIZE;
743
 
      }
744
 
      /* Read from the process */
745
 
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
746
 
                 BUFFER_SIZE);
747
 
      if(ret < 0){
748
 
        /* Read error from this process; ignore the error */
749
 
        continue;
750
 
      }
751
 
      if(ret == 0){
752
 
        /* got EOF */
753
 
        proc->eof = true;
754
 
      } else {
755
 
        proc->buffer_length += (size_t) ret;
 
158
        }
756
159
      }
757
160
    }
758
161
  }
759
 
 
760
 
 
 
162
  
761
163
 end:
762
 
  
763
 
  if(process_list == NULL or exitstatus != EXIT_SUCCESS){
764
 
    /* Fallback if all plugins failed, none are found or an error occured */
765
 
    bool bret;
766
 
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
767
 
    char *passwordbuffer = getpass("Password: ");
768
 
    bret = print_out_password(passwordbuffer, strlen(passwordbuffer));
769
 
    if(not bret){
770
 
      perror("print_out_password");
771
 
      exitstatus = EXIT_FAILURE;
772
 
      goto end;
773
 
    }
774
 
  }
775
 
  
776
 
  /* Restore old signal handler */
777
 
  sigaction(SIGCHLD, &old_sigchld_action, NULL);
778
 
  
779
 
  free(custom_argv);
780
 
  
781
 
  /* Free the plugin list */
782
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
783
 
    next = plugin_list->next;
784
 
    free(plugin_list->argv);
785
 
    free(plugin_list);
786
 
  }
787
 
  
788
 
  if(dir != NULL){
789
 
    closedir(dir);
790
 
  }
791
 
  
792
 
  /* Free the process list and kill the processes */
793
 
  for(process *next; process_list != NULL; process_list = next){
794
 
    next = process_list->next;
795
 
    close(process_list->fd);
796
 
    ret = kill(process_list->pid, SIGTERM);
797
 
    if(ret == -1 and errno != ESRCH){
798
 
      /* set-uid proccesses migth not get closed */
799
 
      perror("kill");
800
 
    }
801
 
    free(process_list->buffer);
802
 
    free(process_list);
803
 
  }
804
 
  
805
 
  /* Wait for any remaining child processes to terminate */
806
 
  do{
807
 
    ret = wait(NULL);
808
 
  } while(ret >= 0);
809
 
  if(errno != ECHILD){
810
 
    perror("wait");
811
 
  }
812
 
  
813
 
  return exitstatus;
 
164
  for(process *process_itr = process_list; process_itr != NULL;
 
165
      process_itr = process_itr->next){
 
166
    close(process_itr->fd);
 
167
    kill(process_itr->pid, SIGTERM);
 
168
    free(process_itr->buffer);
 
169
  }
 
170
  
 
171
  while(true){
 
172
    int status;
 
173
    ret = wait(&status);
 
174
    if (ret == -1){
 
175
      if(errno != ECHILD){
 
176
        perror("wait");
 
177
      }
 
178
      break;
 
179
    }
 
180
  }  
 
181
  return EXIT_SUCCESS;
814
182
}