/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

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