/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

Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.

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 and 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 <https://www.fukt.bsnet.se/~belorn/> and
 
22
 * <https://www.fukt.bsnet.se/~teddy/>.
 
23
 */
 
24
 
 
25
#define _FORTIFY_SOURCE 2
 
26
 
 
27
#include <stdio.h>      /* popen, fileno */
 
28
#include <iso646.h>     /* and, or, not */
 
29
#include <sys/types.h>  /* DIR, opendir, stat, struct stat, waitpid,
 
30
                           WIFEXITED, WEXITSTATUS, wait */
 
31
#include <sys/wait.h>   /* wait */
 
32
#include <dirent.h>     /* DIR, opendir */
 
33
#include <sys/stat.h>   /* stat, struct stat */
 
34
#include <unistd.h>     /* stat, struct stat, chdir */
 
35
#include <stdlib.h>     /* EXIT_FAILURE */
 
36
#include <sys/select.h> /* fd_set, select, FD_ZERO, FD_SET,
 
37
                           FD_ISSET */
 
38
#include <string.h>     /* strlen, strcpy, strcat */
 
39
#include <stdbool.h>    /* true */
 
40
#include <sys/wait.h>   /* waitpid, WIFEXITED, WEXITSTATUS */
 
41
#include <errno.h>      /* errno */
 
42
#include <argp.h>       /* argp */
 
43
 
 
44
struct process;
 
45
 
 
46
typedef struct process{
 
47
  pid_t pid;
 
48
  int fd;
 
49
  char *buffer;
 
50
  size_t buffer_size;
 
51
  size_t buffer_length;
 
52
  struct process *next;
 
53
} process;
 
54
 
 
55
typedef struct plugin{
 
56
  char *name;           /* can be "global" and any plugin name */
 
57
  char **argv;
 
58
  int argc;
 
59
  struct plugin *next;
 
60
} plugin;
 
61
 
 
62
plugin *getplugin(char *name, plugin **plugin_list){
 
63
  for (plugin *p = *plugin_list; p != NULL; p = p->next){
 
64
    if ((p->name == name)
 
65
        or (p->name and name and (strcmp(p->name, name) == 0))){
 
66
      return p;
 
67
    }
 
68
  }
 
69
  /* Create a new plugin */
 
70
  plugin *new_plugin = malloc(sizeof(plugin));
 
71
  if (new_plugin == NULL){
 
72
    perror("malloc");
 
73
    exit(EXIT_FAILURE);
 
74
  }
 
75
  new_plugin->name = name;
 
76
  new_plugin->argv = malloc(sizeof(char *) * 2);
 
77
  if (new_plugin->argv == NULL){
 
78
    perror("malloc");
 
79
    exit(EXIT_FAILURE);
 
80
  }
 
81
  new_plugin->argv[0] = name;
 
82
  new_plugin->argv[1] = NULL;
 
83
  new_plugin->argc = 1;
 
84
  /* Append the new plugin to the list */
 
85
  new_plugin->next = *plugin_list;
 
86
  *plugin_list = new_plugin;
 
87
  return new_plugin;
 
88
}
 
89
 
 
90
void addarguments(plugin *p, char *arg){
 
91
  p->argv[p->argc] = arg;
 
92
  p->argv = realloc(p->argv, sizeof(char *) * (size_t)(p->argc + 2));
 
93
  if (p->argv == NULL){
 
94
    perror("malloc");
 
95
    exit(EXIT_FAILURE);
 
96
  }
 
97
  p->argc++;
 
98
  p->argv[p->argc] = NULL;
 
99
}
 
100
        
 
101
#define BUFFER_SIZE 256
 
102
 
 
103
const char *argp_program_version =
 
104
  "plugbasedclient 0.9";
 
105
const char *argp_program_bug_address =
 
106
  "<mandos@fukt.bsnet.se>";
 
107
static char doc[] =
 
108
  "Mandos plugin runner -- Run Mandos plugins";
 
109
/* A description of the arguments we accept. */
 
110
static char args_doc[] = "";
 
111
 
 
112
int main(int argc, char *argv[]){
 
113
  char plugindir[] = "plugins.d";
 
114
  size_t d_name_len, plugindir_len = sizeof(plugindir)-1;
 
115
  DIR *dir;
 
116
  struct dirent *dirst;
 
117
  struct stat st;
 
118
  fd_set rfds_orig;
 
119
  int ret, maxfd = 0;
 
120
  process *process_list = NULL;
 
121
  
 
122
  /* The options we understand. */
 
123
  struct argp_option options[] = {
 
124
    { .name = "global-options", .key = 'g',
 
125
      .arg = "option[,option[,...]]", .flags = 0,
 
126
      .doc = "Options effecting all plugins" },
 
127
    { .name = "options-for", .key = 'o',
 
128
      .arg = "plugin:option[,option[,...]]", .flags = 0,
 
129
      .doc = "Options effecting only specified plugins" },
 
130
    { .name = NULL }
 
131
  };
 
132
  
 
133
  error_t parse_opt (int key, char *arg, struct argp_state *state) {
 
134
       /* Get the INPUT argument from `argp_parse', which we
 
135
          know is a pointer to our arguments structure. */
 
136
    plugin **plugins = state->input;
 
137
    switch (key) {
 
138
    case 'g':
 
139
      if (arg != NULL){
 
140
        char *p = strtok(arg, ",");
 
141
        do{
 
142
          addarguments(getplugin(NULL, plugins), p);
 
143
          p = strtok(NULL, ",");
 
144
        } while (p);
 
145
      }
 
146
      break;
 
147
    case 'o':
 
148
      if (arg != NULL){
 
149
        char *name = strtok(arg, ":");
 
150
        char *p = strtok(NULL, ":");
 
151
        p = strtok(p, ",");
 
152
        do{
 
153
          addarguments(getplugin(name, plugins), p);
 
154
          p = strtok(NULL, ",");
 
155
        } while (p);
 
156
      }
 
157
      break;
 
158
    case ARGP_KEY_ARG:
 
159
      argp_usage (state);
 
160
      break;
 
161
    case ARGP_KEY_END:
 
162
      break;
 
163
    default:
 
164
      return ARGP_ERR_UNKNOWN;
 
165
    }
 
166
    return 0;
 
167
  }
 
168
 
 
169
  plugin *plugin_list = NULL;
 
170
  
 
171
  struct argp argp = { .options = options, .parser = parse_opt,
 
172
                       .args_doc = args_doc, .doc = doc };
 
173
 
 
174
  argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
 
175
 
 
176
/*   for(plugin *p = plugin_list; p != NULL; p=p->next){ */
 
177
/*     fprintf(stderr, "Plugin: %s has %d arguments\n", p->name ? p->name : "Global", p->argc); */
 
178
/*     for(char **a = p->argv + 1; *a != NULL; a++){ */
 
179
/*       fprintf(stderr, "\tArg: %s\n", *a); */
 
180
/*     } */
 
181
/*   } */
 
182
  
 
183
/*   return 0; */
 
184
  
 
185
  dir = opendir(plugindir);
 
186
  
 
187
  if(dir == NULL){
 
188
    fprintf(stderr, "Can not open directory\n");
 
189
    return EXIT_FAILURE;
 
190
  }
 
191
  
 
192
  FD_ZERO(&rfds_orig);
 
193
  
 
194
  while(true){
 
195
    dirst = readdir(dir);
 
196
    
 
197
    // All directory entries have been processed
 
198
    if(dirst == NULL){
 
199
      break;
 
200
    }
 
201
    
 
202
    d_name_len = strlen(dirst->d_name);
 
203
    
 
204
    // Ignore dotfiles and backup files
 
205
    if (dirst->d_name[0] == '.'
 
206
        or dirst->d_name[d_name_len - 1] == '~'){
 
207
      continue;
 
208
    }
 
209
 
 
210
    char *filename = malloc(d_name_len + plugindir_len + 2);
 
211
    strcpy(filename, plugindir);
 
212
    strcat(filename, "/");
 
213
    strcat(filename, dirst->d_name);    
 
214
 
 
215
    stat(filename, &st);
 
216
 
 
217
    if (S_ISREG(st.st_mode) and (access(filename, X_OK) == 0)){
 
218
      // Starting a new process to be watched
 
219
      process *new_process = malloc(sizeof(process));
 
220
      int pipefd[2];
 
221
      ret = pipe(pipefd);
 
222
      if (ret == -1){
 
223
        perror(argv[0]);
 
224
        goto end;
 
225
      }
 
226
      new_process->pid = fork();
 
227
      if(new_process->pid == 0){
 
228
        /* this is the child process */
 
229
        closedir(dir);
 
230
        close(pipefd[0]);       /* close unused read end of pipe */
 
231
        dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
 
232
        char *basename;
 
233
        basename = strrchr(filename, '/');
 
234
        if (basename == NULL){
 
235
          basename = filename;
 
236
        } else {
 
237
          basename++;
 
238
        }
 
239
        plugin *p = getplugin(basename, &plugin_list);
 
240
 
 
241
        plugin *g = getplugin(NULL, &plugin_list);
 
242
        for(char **a = g->argv + 1; *a != NULL; a++){
 
243
          addarguments(p, *a);
 
244
        }
 
245
        if(execv(filename, p->argv) < 0){
 
246
          perror(argv[0]);
 
247
          close(pipefd[1]);
 
248
          exit(EXIT_FAILURE);
 
249
        }
 
250
        /* no return */
 
251
      }
 
252
      close(pipefd[1]);         /* close unused write end of pipe */
 
253
      new_process->fd = pipefd[0];
 
254
      new_process->buffer = malloc(BUFFER_SIZE);
 
255
      if (new_process->buffer == NULL){
 
256
        perror(argv[0]);
 
257
        goto end;
 
258
      }
 
259
      new_process->buffer_size = BUFFER_SIZE;
 
260
      new_process->buffer_length = 0;
 
261
      FD_SET(new_process->fd, &rfds_orig);
 
262
      
 
263
      if (maxfd < new_process->fd){
 
264
        maxfd = new_process->fd;
 
265
      }
 
266
      
 
267
      //List handling
 
268
      new_process->next = process_list;
 
269
      process_list = new_process;
 
270
    }
 
271
  }
 
272
  
 
273
  closedir(dir);
 
274
  
 
275
  if (process_list != NULL){
 
276
    while(true){
 
277
      fd_set rfds = rfds_orig;
 
278
      int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
 
279
      if (select_ret == -1){
 
280
        perror(argv[0]);
 
281
        goto end;
 
282
      }else{    
 
283
        for(process *process_itr = process_list; process_itr != NULL;
 
284
            process_itr = process_itr->next){
 
285
          if(FD_ISSET(process_itr->fd, &rfds)){
 
286
            if(process_itr->buffer_length + BUFFER_SIZE
 
287
               > process_itr->buffer_size){
 
288
                process_itr->buffer = realloc(process_itr->buffer,
 
289
                                              process_itr->buffer_size
 
290
                                              + (size_t) BUFFER_SIZE);
 
291
                if (process_itr->buffer == NULL){
 
292
                  perror(argv[0]);
 
293
                  goto end;
 
294
                }
 
295
                process_itr->buffer_size += BUFFER_SIZE;
 
296
            }
 
297
            ret = read(process_itr->fd, process_itr->buffer
 
298
                       + process_itr->buffer_length, BUFFER_SIZE);
 
299
            if(ret < 0){
 
300
              /* Read error from this process; ignore it */
 
301
              continue;
 
302
            }
 
303
            process_itr->buffer_length += (size_t) ret;
 
304
            if(ret == 0){
 
305
              /* got EOF */
 
306
              /* wait for process exit */
 
307
              int status;
 
308
              waitpid(process_itr->pid, &status, 0);
 
309
              if(WIFEXITED(status) and WEXITSTATUS(status) == 0){
 
310
                for(size_t written = 0;
 
311
                    written < process_itr->buffer_length;){
 
312
                  ret = write(STDOUT_FILENO,
 
313
                              process_itr->buffer + written,
 
314
                              process_itr->buffer_length - written);
 
315
                  if(ret < 0){
 
316
                    perror(argv[0]);
 
317
                    goto end;
 
318
                  }
 
319
                  written += (size_t)ret;
 
320
                }
 
321
                goto end;
 
322
              } else {
 
323
                FD_CLR(process_itr->fd, &rfds_orig);
 
324
              }
 
325
            }
 
326
          }
 
327
        }
 
328
      }
 
329
    }
 
330
  }
 
331
  
 
332
 end:
 
333
  for(process *process_itr = process_list; process_itr != NULL;
 
334
      process_itr = process_itr->next){
 
335
    close(process_itr->fd);
 
336
    kill(process_itr->pid, SIGTERM);
 
337
    free(process_itr->buffer);
 
338
  }
 
339
  
 
340
  while(true){
 
341
    int status;
 
342
    ret = wait(&status);
 
343
    if (ret == -1){
 
344
      if(errno != ECHILD){
 
345
        perror("wait");
 
346
      }
 
347
      break;
 
348
    }
 
349
  }  
 
350
  return EXIT_SUCCESS;
 
351
}