/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
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
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() */
13 by Björn Påhlsson
Added following support:
49
50
struct process;
51
52
typedef struct process{
53
  pid_t pid;
54
  int fd;
55
  char *buffer;
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
56
  size_t buffer_size;
57
  size_t buffer_length;
13 by Björn Påhlsson
Added following support:
58
  struct process *next;
59
} process;
60
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
61
typedef struct plugin{
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
62
  char *name;			/* can be NULL or any plugin name */
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
63
  char **argv;
64
  int argc;
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
65
  bool disabled;
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
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;
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
91
  new_plugin->disabled = false;
24.1.5 by Björn Påhlsson
plugbasedclient:
92
  new_plugin->next = *plugin_list;
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
93
  /* Append the new plugin to the list */
94
  *plugin_list = new_plugin;
95
  return new_plugin;
96
}
97
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
98
void addargument(plugin *p, char *arg){
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
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
}
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
108
13 by Björn Påhlsson
Added following support:
109
#define BUFFER_SIZE 256
110
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
111
const char *argp_program_version =
112
  "plugbasedclient 0.9";
113
const char *argp_program_bug_address =
114
  "<mandos@fukt.bsnet.se>";
115
13 by Björn Påhlsson
Added following support:
116
int main(int argc, char *argv[]){
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
117
  const char *plugindir = "/conf/conf.d/mandos/plugins.d";
24.1.5 by Björn Påhlsson
plugbasedclient:
118
  size_t d_name_len;
13 by Björn Påhlsson
Added following support:
119
  DIR *dir;
120
  struct dirent *dirst;
121
  struct stat st;
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
122
  fd_set rfds_all;
13 by Björn Påhlsson
Added following support:
123
  int ret, maxfd = 0;
124
  process *process_list = NULL;
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
125
  bool debug = false;
126
  int exitstatus = EXIT_SUCCESS;
127
  
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
128
  /* The options we understand. */
129
  struct argp_option options[] = {
130
    { .name = "global-options", .key = 'g',
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
131
      .arg = "OPTION[,OPTION[,...]]",
132
      .doc = "Options passed to all plugins" },
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
133
    { .name = "options-for", .key = 'o',
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
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 },
24.1.5 by Björn Påhlsson
plugbasedclient:
139
    { .name = "plugin-dir", .key = 128,
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
140
      .arg = "DIRECTORY",
141
      .doc = "Specify a different plugin directory", .group = 2 },
142
    { .name = "debug", .key = 129,
143
      .doc = "Debug mode", .group = 3 },
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
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
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
149
          know is a pointer to our plugin list pointer. */
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
150
    plugin **plugins = state->input;
151
    switch (key) {
152
    case 'g':
153
      if (arg != NULL){
154
	char *p = strtok(arg, ",");
155
	do{
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
156
	  addargument(getplugin(NULL, plugins), p);
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
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, ":");
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
165
	if(p){
166
	  p = strtok(p, ",");
167
	  do{
168
	    addargument(getplugin(name, plugins), p);
169
	    p = strtok(NULL, ",");
170
	  } while (p);
171
	}
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
172
      }
173
      break;
24.1.5 by Björn Påhlsson
plugbasedclient:
174
    case 'd':
175
      if (arg != NULL){
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
176
	getplugin(arg, plugins)->disabled = true;
24.1.5 by Björn Påhlsson
plugbasedclient:
177
      }
178
      break;
179
    case 128:
180
      plugindir = arg;
181
      break;
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
182
    case 129:
183
      debug = true;
184
      break;
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
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
  }
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
195
  
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
196
  plugin *plugin_list = NULL;
197
  
198
  struct argp argp = { .options = options, .parser = parse_opt,
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
199
		       .args_doc = "",
200
		       .doc = "Mandos plugin runner -- Run plugins" };
201
  
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
202
  argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
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
  
13 by Björn Påhlsson
Added following support:
214
  dir = opendir(plugindir);
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
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
  }
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
229
  
13 by Björn Påhlsson
Added following support:
230
  if(dir == NULL){
231
    fprintf(stderr, "Can not open directory\n");
232
    return EXIT_FAILURE;
233
  }
234
  
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
235
  FD_ZERO(&rfds_all);
13 by Björn Påhlsson
Added following support:
236
  
237
  while(true){
238
    dirst = readdir(dir);
239
    
240
    // All directory entries have been processed
241
    if(dirst == NULL){
242
      break;
243
    }
244
    
245
    d_name_len = strlen(dirst->d_name);
246
    
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
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
      }
13 by Björn Påhlsson
Added following support:
290
    }
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
291
    
24.1.5 by Björn Påhlsson
plugbasedclient:
292
    char *filename = malloc(d_name_len + strlen(plugindir) + 2);
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
293
    if (filename == NULL){
294
      perror("malloc");
295
      exitstatus =EXIT_FAILURE;
296
      goto end;
297
    }
13 by Björn Påhlsson
Added following support:
298
    strcpy(filename, plugindir);
299
    strcat(filename, "/");
300
    strcat(filename, dirst->d_name);    
14 by Björn Påhlsson
Fixed a overbufferflow bug, thanks to a forgotten \0
301
13 by Björn Påhlsson
Added following support:
302
    stat(filename, &st);
303
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
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){
338
	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;
13 by Björn Påhlsson
Added following support:
370
  }
371
  
372
  closedir(dir);
373
  
374
  if (process_list != NULL){
375
    while(true){
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
376
      fd_set rfds = rfds_all;
13 by Björn Påhlsson
Added following support:
377
      int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
378
      if (select_ret == -1){
379
	perror(argv[0]);
380
	goto end;
381
      }else{	
382
	for(process *process_itr = process_list; process_itr != NULL;
383
	    process_itr = process_itr->next){
384
	  if(FD_ISSET(process_itr->fd, &rfds)){
385
	    if(process_itr->buffer_length + BUFFER_SIZE
386
	       > process_itr->buffer_size){
387
		process_itr->buffer = realloc(process_itr->buffer,
388
					      process_itr->buffer_size
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
389
					      + (size_t) BUFFER_SIZE);
13 by Björn Påhlsson
Added following support:
390
		if (process_itr->buffer == NULL){
391
		  perror(argv[0]);
392
		  goto end;
393
		}
394
		process_itr->buffer_size += BUFFER_SIZE;
395
	    }
396
	    ret = read(process_itr->fd, process_itr->buffer
397
		       + process_itr->buffer_length, BUFFER_SIZE);
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
398
	    if(ret < 0){
399
	      /* Read error from this process; ignore it */
400
	      continue;
401
	    }
402
	    process_itr->buffer_length += (size_t) ret;
13 by Björn Påhlsson
Added following support:
403
	    if(ret == 0){
404
	      /* got EOF */
405
	      /* wait for process exit */
406
	      int status;
407
	      waitpid(process_itr->pid, &status, 0);
408
	      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
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
		}
13 by Björn Påhlsson
Added following support:
420
		goto end;
421
	      } else {
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
422
		FD_CLR(process_itr->fd, &rfds_all);
13 by Björn Påhlsson
Added following support:
423
	      }
424
	    }
425
	  }
426
	}
427
      }
428
    }
429
  }
430
  
431
 end:
432
  for(process *process_itr = process_list; process_itr != NULL;
433
      process_itr = process_itr->next){
434
    close(process_itr->fd);
435
    kill(process_itr->pid, SIGTERM);
436
    free(process_itr->buffer);
437
  }
438
  
439
  while(true){
440
    int status;
441
    ret = wait(&status);
442
    if (ret == -1){
443
      if(errno != ECHILD){
444
	perror("wait");
445
      }
446
      break;
447
    }
448
  }  
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
449
  return exitstatus;
13 by Björn Påhlsson
Added following support:
450
}