/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
 
#include <stdio.h>              /* popen(), fileno(), fprintf(),
25
 
                                   stderr, STDOUT_FILENO */
26
 
#include <iso646.h>             /* and, or, not */
27
 
#include <sys/types.h>         /* DIR, opendir(), stat(), struct stat,
28
 
                                  waitpid(), WIFEXITED(),
29
 
                                  WEXITSTATUS(), wait() */
30
 
#include <sys/wait.h>           /* wait() */
31
 
#include <dirent.h>             /* DIR, struct dirent, opendir(),
32
 
                                   readdir(), closedir() */
33
 
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
34
 
#include <unistd.h>             /* struct stat, stat(), S_ISREG(),
35
 
                                   fcntl() */
36
 
#include <fcntl.h>              /* fcntl() */
37
 
#include <stddef.h>             /* NULL */
38
 
#include <stdlib.h>             /* EXIT_FAILURE */
39
 
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
40
 
                                   FD_SET(), FD_ISSET() */
41
 
#include <string.h>             /* strlen(), strcpy(), strcat() */
42
 
#include <stdbool.h>            /* true */
43
 
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
44
 
                                   WEXITSTATUS() */
45
 
#include <errno.h>              /* errno */
46
 
#include <argp.h>               /* struct argp_option,
47
 
                                   struct argp_state, struct argp,
48
 
                                   argp_parse() */
 
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 */
49
15
 
50
16
struct process;
51
17
 
53
19
  pid_t pid;
54
20
  int fd;
55
21
  char *buffer;
56
 
  size_t buffer_size;
57
 
  size_t buffer_length;
 
22
  int buffer_size;
 
23
  int buffer_length;
58
24
  struct process *next;
59
25
} process;
60
26
 
61
 
typedef struct plugin{
62
 
  char *name;                   /* can be NULL or any plugin name */
63
 
  char **argv;
64
 
  int argc;
65
 
  bool disabled;
66
 
  struct plugin *next;
67
 
} plugin;
68
 
 
69
 
plugin *getplugin(char *name, plugin **plugin_list){
70
 
  for (plugin *p = *plugin_list; p != NULL; p = p->next){
71
 
    if ((p->name == name)
72
 
        or (p->name and name and (strcmp(p->name, name) == 0))){
73
 
      return p;
74
 
    }
75
 
  }
76
 
  /* Create a new plugin */
77
 
  plugin *new_plugin = malloc(sizeof(plugin));
78
 
  if (new_plugin == NULL){
79
 
    perror("malloc");
80
 
    exit(EXIT_FAILURE);
81
 
  }
82
 
  new_plugin->name = name;
83
 
  new_plugin->argv = malloc(sizeof(char *) * 2);
84
 
  if (new_plugin->argv == NULL){
85
 
    perror("malloc");
86
 
    exit(EXIT_FAILURE);
87
 
  }
88
 
  new_plugin->argv[0] = name;
89
 
  new_plugin->argv[1] = NULL;
90
 
  new_plugin->argc = 1;
91
 
  new_plugin->disabled = false;
92
 
  new_plugin->next = *plugin_list;
93
 
  /* Append the new plugin to the list */
94
 
  *plugin_list = new_plugin;
95
 
  return new_plugin;
96
 
}
97
 
 
98
 
void addargument(plugin *p, char *arg){
99
 
  p->argv[p->argc] = arg;
100
 
  p->argv = realloc(p->argv, sizeof(char *) * (size_t)(p->argc + 2));
101
 
  if (p->argv == NULL){
102
 
    perror("malloc");
103
 
    exit(EXIT_FAILURE);
104
 
  }
105
 
  p->argc++;
106
 
  p->argv[p->argc] = NULL;
107
 
}
108
 
 
109
 
/*
110
 
 * Based on the example in the GNU LibC manual chapter 13.13 "File
111
 
 * Descriptor Flags".
112
 
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
113
 
 */
114
 
int set_cloexec_flag(int fd)
115
 
{
116
 
  int ret = fcntl(fd, F_GETFD, 0);
117
 
  /* If reading the flags failed, return error indication now. */
118
 
  if(ret < 0){
119
 
    return ret;
120
 
  }
121
 
  /* Store modified flag word in the descriptor. */
122
 
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
123
 
}
124
 
 
125
 
 
126
27
#define BUFFER_SIZE 256
127
28
 
128
 
const char *argp_program_version =
129
 
  "plugbasedclient 0.9";
130
 
const char *argp_program_bug_address =
131
 
  "<mandos@fukt.bsnet.se>";
132
 
 
133
29
int main(int argc, char *argv[]){
134
 
  const char *plugindir = "/conf/conf.d/mandos/plugins.d";
135
 
  size_t d_name_len;
 
30
  char plugindir[] = "plugins.d";
 
31
  size_t d_name_len, plugindir_len = sizeof(plugindir)-1;
136
32
  DIR *dir;
137
33
  struct dirent *dirst;
138
34
  struct stat st;
139
 
  fd_set rfds_all;
 
35
  fd_set rfds_orig;
140
36
  int ret, maxfd = 0;
141
37
  process *process_list = NULL;
142
 
  bool debug = false;
143
 
  int exitstatus = EXIT_SUCCESS;
144
 
  
145
 
  /* The options we understand. */
146
 
  struct argp_option options[] = {
147
 
    { .name = "global-options", .key = 'g',
148
 
      .arg = "OPTION[,OPTION[,...]]",
149
 
      .doc = "Options passed to all plugins" },
150
 
    { .name = "options-for", .key = 'o',
151
 
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
152
 
      .doc = "Options passed only to specified plugin" },
153
 
    { .name = "disable", .key = 'd',
154
 
      .arg = "PLUGIN",
155
 
      .doc = "Disable a specific plugin", .group = 1 },
156
 
    { .name = "plugin-dir", .key = 128,
157
 
      .arg = "DIRECTORY",
158
 
      .doc = "Specify a different plugin directory", .group = 2 },
159
 
    { .name = "debug", .key = 129,
160
 
      .doc = "Debug mode", .group = 3 },
161
 
    { .name = NULL }
162
 
  };
163
 
  
164
 
  error_t parse_opt (int key, char *arg, struct argp_state *state) {
165
 
       /* Get the INPUT argument from `argp_parse', which we
166
 
          know is a pointer to our plugin list pointer. */
167
 
    plugin **plugins = state->input;
168
 
    switch (key) {
169
 
    case 'g':
170
 
      if (arg != NULL){
171
 
        char *p = strtok(arg, ",");
172
 
        do{
173
 
          addargument(getplugin(NULL, plugins), p);
174
 
          p = strtok(NULL, ",");
175
 
        } while (p);
176
 
      }
177
 
      break;
178
 
    case 'o':
179
 
      if (arg != NULL){
180
 
        char *name = strtok(arg, ":");
181
 
        char *p = strtok(NULL, ":");
182
 
        if(p){
183
 
          p = strtok(p, ",");
184
 
          do{
185
 
            addargument(getplugin(name, plugins), p);
186
 
            p = strtok(NULL, ",");
187
 
          } while (p);
188
 
        }
189
 
      }
190
 
      break;
191
 
    case 'd':
192
 
      if (arg != NULL){
193
 
        getplugin(arg, plugins)->disabled = true;
194
 
      }
195
 
      break;
196
 
    case 128:
197
 
      plugindir = arg;
198
 
      break;
199
 
    case 129:
200
 
      debug = true;
201
 
      break;
202
 
    case ARGP_KEY_ARG:
203
 
      argp_usage (state);
204
 
      break;
205
 
    case ARGP_KEY_END:
206
 
      break;
207
 
    default:
208
 
      return ARGP_ERR_UNKNOWN;
209
 
    }
210
 
    return 0;
211
 
  }
212
 
  
213
 
  plugin *plugin_list = NULL;
214
 
  
215
 
  struct argp argp = { .options = options, .parser = parse_opt,
216
 
                       .args_doc = "",
217
 
                       .doc = "Mandos plugin runner -- Run plugins" };
218
 
  
219
 
  argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
220
 
  
221
 
  if(debug){
222
 
    for(plugin *p = plugin_list; p != NULL; p=p->next){
223
 
      fprintf(stderr, "Plugin: %s has %d arguments\n",
224
 
              p->name ? p->name : "Global", p->argc - 1);
225
 
      for(char **a = p->argv; *a != NULL; a++){
226
 
        fprintf(stderr, "\tArg: %s\n", *a);
227
 
      }
228
 
    }
229
 
  }
230
38
  
231
39
  dir = opendir(plugindir);
232
 
  /* Set the FD_CLOEXEC flag on the directory */
233
 
  ret = set_cloexec_flag(dirfd(dir));
234
 
  if(ret < 0){
235
 
    perror("set_cloexec_flag");
236
 
    goto end;
237
 
  }
238
 
  
 
40
 
239
41
  if(dir == NULL){
240
42
    fprintf(stderr, "Can not open directory\n");
241
43
    return EXIT_FAILURE;
242
44
  }
243
45
  
244
 
  FD_ZERO(&rfds_all);
 
46
  FD_ZERO(&rfds_orig);
245
47
  
246
48
  while(true){
247
49
    dirst = readdir(dir);
253
55
    
254
56
    d_name_len = strlen(dirst->d_name);
255
57
    
256
 
    // Ignore dotfiles, backup files and other junk
257
 
    {
258
 
      bool bad_name = false;
259
 
      
260
 
      const char const *bad_prefixes[] = { ".", "#", NULL };
261
 
      
262
 
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
263
 
                                           ".dpkg-old",
264
 
                                           ".dpkg-divert", NULL };
265
 
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
266
 
        size_t pre_len = strlen(*pre);
267
 
        if((d_name_len >= pre_len)
268
 
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
269
 
          if(debug){
270
 
            fprintf(stderr, "Ignoring plugin dir entry name \"%s\""
271
 
                    " with bad prefix %s\n", dirst->d_name, *pre);
272
 
          }
273
 
          bad_name = true;
274
 
          break;
275
 
        }
276
 
      }
277
 
      
278
 
      if(bad_name){
279
 
        continue;
280
 
      }
281
 
      
282
 
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
283
 
        size_t suf_len = strlen(*suf);
284
 
        if((d_name_len >= suf_len)
285
 
           and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
286
 
                == 0)){
287
 
          if(debug){
288
 
            fprintf(stderr, "Ignoring plugin dir entry name \"%s\""
289
 
                    " with bad suffix %s\n", dirst->d_name, *suf);
290
 
          }
291
 
          bad_name = true;
292
 
          break;
293
 
        }
294
 
      }
295
 
      
296
 
      if(bad_name){
297
 
        continue;
298
 
      }
 
58
    // Ignore dotfiles and backup files
 
59
    if (dirst->d_name[0] == '.'
 
60
        or dirst->d_name[d_name_len - 1] == '~'){
 
61
      continue;
299
62
    }
300
63
    
301
 
    char *filename = malloc(d_name_len + strlen(plugindir) + 2);
302
 
    if (filename == NULL){
303
 
      perror("malloc");
304
 
      exitstatus =EXIT_FAILURE;
305
 
      goto end;
306
 
    }
 
64
    char *filename = malloc(d_name_len + plugindir_len + 1);
307
65
    strcpy(filename, plugindir);
308
66
    strcat(filename, "/");
309
67
    strcat(filename, dirst->d_name);    
310
 
 
 
68
    
311
69
    stat(filename, &st);
312
70
 
313
 
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
314
 
      if(debug){
315
 
        fprintf(stderr, "Ignoring plugin dir entry name \"%s\""
316
 
                " with bad type or mode\n", filename);
317
 
      }
318
 
      continue;
319
 
    }
320
 
    if(getplugin(dirst->d_name, &plugin_list)->disabled){
321
 
      if(debug){
322
 
        fprintf(stderr, "Ignoring disabled plugin \"%s\"",
323
 
                dirst->d_name);
324
 
      }
325
 
      continue;
326
 
    }
327
 
    // Starting a new process to be watched
328
 
    int pipefd[2]; 
329
 
    ret = pipe(pipefd);
330
 
    if (ret == -1){
331
 
      perror("pipe");
332
 
      goto end;
333
 
    }
334
 
    plugin *p = getplugin(dirst->d_name, &plugin_list);
335
 
    {
336
 
      /* Add global arguments to argument list for this plugin */
337
 
      plugin *g = getplugin(NULL, &plugin_list);
338
 
      for(char **a = g->argv + 1; *a != NULL; a++){
339
 
        addargument(p, *a);
340
 
      }
341
 
    }
342
 
    pid_t pid = fork();
343
 
    if(pid == 0){
344
 
      /* this is the child process */
345
 
      closedir(dir);
346
 
      close(pipefd[0]); /* close unused read end of pipe */
347
 
      dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
348
 
      if(pipefd[1] > 2){
349
 
        close(pipefd[1]);
350
 
      }
351
 
      
352
 
      if(execv(filename, p->argv) < 0){
 
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 */
 
79
        closedir(dir);
 
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){
353
100
        perror(argv[0]);
354
 
        close(pipefd[1]);
355
 
        _exit(EXIT_FAILURE);
356
 
      }
357
 
      /* no return */
358
 
    }
359
 
    close(pipefd[1]);           /* close unused write end of pipe */
360
 
    process *new_process = malloc(sizeof(process));
361
 
    if (new_process == NULL){
362
 
      perror("malloc");
363
 
      exitstatus = EXIT_FAILURE;
364
 
      goto end;
365
 
    }
366
 
    
367
 
    new_process->fd = pipefd[0];
368
 
    new_process->buffer = malloc(BUFFER_SIZE);
369
 
    if (new_process->buffer == NULL){
370
 
      perror("malloc");
371
 
      exitstatus = EXIT_FAILURE;
372
 
      goto end;
373
 
    }
374
 
    new_process->buffer_size = BUFFER_SIZE;
375
 
    new_process->buffer_length = 0;
376
 
    FD_SET(new_process->fd, &rfds_all);
377
 
      
378
 
    if (maxfd < new_process->fd){
379
 
      maxfd = new_process->fd;
380
 
    }
381
 
    
382
 
    //List handling
383
 
    new_process->next = process_list;
384
 
    process_list = new_process;
 
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
    }
385
115
  }
386
116
  
387
117
  closedir(dir);
388
118
  
389
119
  if (process_list != NULL){
390
120
    while(true){
391
 
      fd_set rfds = rfds_all;
 
121
      fd_set rfds = rfds_orig;
392
122
      int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
393
123
      if (select_ret == -1){
394
124
        perror(argv[0]);
401
131
               > process_itr->buffer_size){
402
132
                process_itr->buffer = realloc(process_itr->buffer,
403
133
                                              process_itr->buffer_size
404
 
                                              + (size_t) BUFFER_SIZE);
 
134
                                              + BUFFER_SIZE);
405
135
                if (process_itr->buffer == NULL){
406
136
                  perror(argv[0]);
407
137
                  goto end;
410
140
            }
411
141
            ret = read(process_itr->fd, process_itr->buffer
412
142
                       + process_itr->buffer_length, BUFFER_SIZE);
413
 
            if(ret < 0){
414
 
              /* Read error from this process; ignore it */
415
 
              continue;
416
 
            }
417
 
            process_itr->buffer_length += (size_t) ret;
 
143
            process_itr->buffer_length+=ret;
418
144
            if(ret == 0){
419
145
              /* got EOF */
420
146
              /* wait for process exit */
421
147
              int status;
422
148
              waitpid(process_itr->pid, &status, 0);
423
149
              if(WIFEXITED(status) and WEXITSTATUS(status) == 0){
424
 
                for(size_t written = 0;
425
 
                    written < process_itr->buffer_length;){
426
 
                  ret = write(STDOUT_FILENO,
427
 
                              process_itr->buffer + written,
428
 
                              process_itr->buffer_length - written);
429
 
                  if(ret < 0){
430
 
                    perror(argv[0]);
431
 
                    goto end;
432
 
                  }
433
 
                  written += (size_t)ret;
434
 
                }
 
150
                write(STDOUT_FILENO, process_itr->buffer,
 
151
                      process_itr->buffer_length);
435
152
                goto end;
436
153
              } else {
437
 
                FD_CLR(process_itr->fd, &rfds_all);
 
154
                FD_CLR(process_itr->fd, &rfds_orig);
438
155
              }
439
156
            }
440
157
          }
461
178
      break;
462
179
    }
463
180
  }  
464
 
  return exitstatus;
 
181
  return EXIT_SUCCESS;
465
182
}