/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
27
#define BUFFER_SIZE 256
110
28
 
111
 
const char *argp_program_version =
112
 
  "plugbasedclient 0.9";
113
 
const char *argp_program_bug_address =
114
 
  "<mandos@fukt.bsnet.se>";
115
 
 
116
29
int main(int argc, char *argv[]){
117
 
  const char *plugindir = "/conf/conf.d/mandos/plugins.d";
118
 
  size_t d_name_len;
 
30
  char plugindir[] = "plugins.d";
 
31
  size_t d_name_len, plugindir_len = sizeof(plugindir)-1;
119
32
  DIR *dir;
120
33
  struct dirent *dirst;
121
34
  struct stat st;
122
 
  fd_set rfds_all;
 
35
  fd_set rfds_orig;
123
36
  int ret, maxfd = 0;
124
37
  process *process_list = NULL;
125
 
  bool debug = false;
126
 
  int exitstatus = EXIT_SUCCESS;
127
 
  
128
 
  /* The options we understand. */
129
 
  struct argp_option options[] = {
130
 
    { .name = "global-options", .key = 'g',
131
 
      .arg = "OPTION[,OPTION[,...]]",
132
 
      .doc = "Options passed to all plugins" },
133
 
    { .name = "options-for", .key = 'o',
134
 
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
135
 
      .doc = "Options passed only to specified plugin" },
136
 
    { .name = "disable", .key = 'd',
137
 
      .arg = "PLUGIN",
138
 
      .doc = "Disable a specific plugin", .group = 1 },
139
 
    { .name = "plugin-dir", .key = 128,
140
 
      .arg = "DIRECTORY",
141
 
      .doc = "Specify a different plugin directory", .group = 2 },
142
 
    { .name = "debug", .key = 129,
143
 
      .doc = "Debug mode", .group = 3 },
144
 
    { .name = NULL }
145
 
  };
146
 
  
147
 
  error_t parse_opt (int key, char *arg, struct argp_state *state) {
148
 
       /* Get the INPUT argument from `argp_parse', which we
149
 
          know is a pointer to our plugin list pointer. */
150
 
    plugin **plugins = state->input;
151
 
    switch (key) {
152
 
    case 'g':
153
 
      if (arg != NULL){
154
 
        char *p = strtok(arg, ",");
155
 
        do{
156
 
          addargument(getplugin(NULL, plugins), p);
157
 
          p = strtok(NULL, ",");
158
 
        } while (p);
159
 
      }
160
 
      break;
161
 
    case 'o':
162
 
      if (arg != NULL){
163
 
        char *name = strtok(arg, ":");
164
 
        char *p = strtok(NULL, ":");
165
 
        if(p){
166
 
          p = strtok(p, ",");
167
 
          do{
168
 
            addargument(getplugin(name, plugins), p);
169
 
            p = strtok(NULL, ",");
170
 
          } while (p);
171
 
        }
172
 
      }
173
 
      break;
174
 
    case 'd':
175
 
      if (arg != NULL){
176
 
        getplugin(arg, plugins)->disabled = true;
177
 
      }
178
 
      break;
179
 
    case 128:
180
 
      plugindir = arg;
181
 
      break;
182
 
    case 129:
183
 
      debug = true;
184
 
      break;
185
 
    case ARGP_KEY_ARG:
186
 
      argp_usage (state);
187
 
      break;
188
 
    case ARGP_KEY_END:
189
 
      break;
190
 
    default:
191
 
      return ARGP_ERR_UNKNOWN;
192
 
    }
193
 
    return 0;
194
 
  }
195
 
  
196
 
  plugin *plugin_list = NULL;
197
 
  
198
 
  struct argp argp = { .options = options, .parser = parse_opt,
199
 
                       .args_doc = "",
200
 
                       .doc = "Mandos plugin runner -- Run plugins" };
201
 
  
202
 
  argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
203
 
  
204
 
  if(debug){
205
 
    for(plugin *p = plugin_list; p != NULL; p=p->next){
206
 
      fprintf(stderr, "Plugin: %s has %d arguments\n",
207
 
              p->name ? p->name : "Global", p->argc);
208
 
      for(char **a = p->argv; *a != NULL; a++){
209
 
        fprintf(stderr, "\tArg: %s\n", *a);
210
 
      }
211
 
    }
212
 
  }
213
38
  
214
39
  dir = opendir(plugindir);
215
 
  {
216
 
    /* Set the FD_CLOEXEC flag on the directory */
217
 
    int fd = dirfd(dir);
218
 
    ret = fcntl (fd, F_GETFD, 0);
219
 
    if(ret < 0){
220
 
      perror("fcntl F_GETFD");
221
 
      goto end;
222
 
    }
223
 
    ret = fcntl(fd, F_SETFD, FD_CLOEXEC | ret);
224
 
    if(ret < 0){
225
 
      perror("fcntl F_SETFD");
226
 
      goto end;
227
 
    }
228
 
  }
229
 
  
 
40
 
230
41
  if(dir == NULL){
231
42
    fprintf(stderr, "Can not open directory\n");
232
43
    return EXIT_FAILURE;
233
44
  }
234
45
  
235
 
  FD_ZERO(&rfds_all);
 
46
  FD_ZERO(&rfds_orig);
236
47
  
237
48
  while(true){
238
49
    dirst = readdir(dir);
244
55
    
245
56
    d_name_len = strlen(dirst->d_name);
246
57
    
247
 
    // Ignore dotfiles, backup files and other junk
248
 
    {
249
 
      bool bad_name = false;
250
 
      
251
 
      const char const *bad_prefixes[] = { ".", "#", NULL };
252
 
      
253
 
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
254
 
                                           ".dpkg-old",
255
 
                                           ".dpkg-divert", NULL };
256
 
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
257
 
        size_t pre_len = strlen(*pre);
258
 
        if((d_name_len >= pre_len)
259
 
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
260
 
          if(debug){
261
 
            fprintf(stderr, "Ignoring plugin dir entry name \"%s\""
262
 
                    " with bad prefix %s\n", dirst->d_name, *pre);
263
 
          }
264
 
          bad_name = true;
265
 
          break;
266
 
        }
267
 
      }
268
 
      
269
 
      if(bad_name){
270
 
        continue;
271
 
      }
272
 
      
273
 
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
274
 
        size_t suf_len = strlen(*suf);
275
 
        if((d_name_len >= suf_len)
276
 
           and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
277
 
                == 0)){
278
 
          if(debug){
279
 
            fprintf(stderr, "Ignoring plugin dir entry name \"%s\""
280
 
                    " with bad suffix %s\n", dirst->d_name, *suf);
281
 
          }
282
 
          bad_name = true;
283
 
          break;
284
 
        }
285
 
      }
286
 
      
287
 
      if(bad_name){
288
 
        continue;
289
 
      }
 
58
    // Ignore dotfiles and backup files
 
59
    if (dirst->d_name[0] == '.'
 
60
        or dirst->d_name[d_name_len - 1] == '~'){
 
61
      continue;
290
62
    }
291
63
    
292
 
    char *filename = malloc(d_name_len + strlen(plugindir) + 2);
293
 
    if (filename == NULL){
294
 
      perror("malloc");
295
 
      exitstatus =EXIT_FAILURE;
296
 
      goto end;
297
 
    }
 
64
    char *filename = malloc(d_name_len + plugindir_len + 1);
298
65
    strcpy(filename, plugindir);
299
66
    strcat(filename, "/");
300
67
    strcat(filename, dirst->d_name);    
301
 
 
 
68
    
302
69
    stat(filename, &st);
303
70
 
304
 
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
305
 
      if(debug){
306
 
        fprintf(stderr, "Ignoring plugin dir entry name \"%s\""
307
 
                " with bad type or mode\n", filename);
308
 
      }
309
 
      continue;
310
 
    }
311
 
    if(getplugin(dirst->d_name, &plugin_list)->disabled){
312
 
      if(debug){
313
 
        fprintf(stderr, "Ignoring disabled plugin \"%s\"",
314
 
                dirst->d_name);
315
 
      }
316
 
      continue;
317
 
    }
318
 
    // Starting a new process to be watched
319
 
    int pipefd[2]; 
320
 
    ret = pipe(pipefd);
321
 
    if (ret == -1){
322
 
      perror(argv[0]);
323
 
      goto end;
324
 
    }
325
 
    pid_t pid = fork();
326
 
    if(pid == 0){
327
 
      /* this is the child process */
328
 
      closedir(dir);
329
 
      close(pipefd[0]); /* close unused read end of pipe */
330
 
      dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
331
 
      
332
 
      plugin *p = getplugin(dirst->d_name, &plugin_list);
333
 
      plugin *g = getplugin(NULL, &plugin_list);
334
 
      for(char **a = g->argv + 1; *a != NULL; a++){
335
 
        addargument(p, *a);
336
 
      }
337
 
      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){
338
100
        perror(argv[0]);
339
 
        close(pipefd[1]);
340
 
        _exit(EXIT_FAILURE);
341
 
      }
342
 
      /* no return */
343
 
    }
344
 
    close(pipefd[1]);           /* close unused write end of pipe */
345
 
    process *new_process = malloc(sizeof(process));
346
 
    if (new_process == NULL){
347
 
      perror("malloc");
348
 
      exitstatus = EXIT_FAILURE;
349
 
      goto end;
350
 
    }
351
 
    
352
 
    new_process->fd = pipefd[0];
353
 
    new_process->buffer = malloc(BUFFER_SIZE);
354
 
    if (new_process->buffer == NULL){
355
 
      perror("malloc");
356
 
      exitstatus = EXIT_FAILURE;
357
 
      goto end;
358
 
    }
359
 
    new_process->buffer_size = BUFFER_SIZE;
360
 
    new_process->buffer_length = 0;
361
 
    FD_SET(new_process->fd, &rfds_all);
362
 
      
363
 
    if (maxfd < new_process->fd){
364
 
      maxfd = new_process->fd;
365
 
    }
366
 
    
367
 
    //List handling
368
 
    new_process->next = process_list;
369
 
    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
    }
370
115
  }
371
116
  
372
117
  closedir(dir);
373
118
  
374
119
  if (process_list != NULL){
375
120
    while(true){
376
 
      fd_set rfds = rfds_all;
 
121
      fd_set rfds = rfds_orig;
377
122
      int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
378
123
      if (select_ret == -1){
379
124
        perror(argv[0]);
386
131
               > process_itr->buffer_size){
387
132
                process_itr->buffer = realloc(process_itr->buffer,
388
133
                                              process_itr->buffer_size
389
 
                                              + (size_t) BUFFER_SIZE);
 
134
                                              + BUFFER_SIZE);
390
135
                if (process_itr->buffer == NULL){
391
136
                  perror(argv[0]);
392
137
                  goto end;
395
140
            }
396
141
            ret = read(process_itr->fd, process_itr->buffer
397
142
                       + process_itr->buffer_length, BUFFER_SIZE);
398
 
            if(ret < 0){
399
 
              /* Read error from this process; ignore it */
400
 
              continue;
401
 
            }
402
 
            process_itr->buffer_length += (size_t) ret;
 
143
            process_itr->buffer_length+=ret;
403
144
            if(ret == 0){
404
145
              /* got EOF */
405
146
              /* wait for process exit */
406
147
              int status;
407
148
              waitpid(process_itr->pid, &status, 0);
408
149
              if(WIFEXITED(status) and WEXITSTATUS(status) == 0){
409
 
                for(size_t written = 0;
410
 
                    written < process_itr->buffer_length;){
411
 
                  ret = write(STDOUT_FILENO,
412
 
                              process_itr->buffer + written,
413
 
                              process_itr->buffer_length - written);
414
 
                  if(ret < 0){
415
 
                    perror(argv[0]);
416
 
                    goto end;
417
 
                  }
418
 
                  written += (size_t)ret;
419
 
                }
 
150
                write(STDOUT_FILENO, process_itr->buffer,
 
151
                      process_itr->buffer_length);
420
152
                goto end;
421
153
              } else {
422
 
                FD_CLR(process_itr->fd, &rfds_all);
 
154
                FD_CLR(process_itr->fd, &rfds_orig);
423
155
              }
424
156
            }
425
157
          }
446
178
      break;
447
179
    }
448
180
  }  
449
 
  return exitstatus;
 
181
  return EXIT_SUCCESS;
450
182
}