/mandos/trunk

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