/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
32 by Teddy Hogeborn
* plugins.d/plugbasedclient.c (set_cloexec_flag): New function.
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
13 by Björn Påhlsson
Added following support:
126
#define BUFFER_SIZE 256
127
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
128
const char *argp_program_version =
129
  "plugbasedclient 0.9";
130
const char *argp_program_bug_address =
131
  "<mandos@fukt.bsnet.se>";
132
13 by Björn Påhlsson
Added following support:
133
int main(int argc, char *argv[]){
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
134
  const char *plugindir = "/conf/conf.d/mandos/plugins.d";
24.1.5 by Björn Påhlsson
plugbasedclient:
135
  size_t d_name_len;
13 by Björn Påhlsson
Added following support:
136
  DIR *dir;
137
  struct dirent *dirst;
138
  struct stat st;
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
139
  fd_set rfds_all;
13 by Björn Påhlsson
Added following support:
140
  int ret, maxfd = 0;
141
  process *process_list = NULL;
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
142
  bool debug = false;
143
  int exitstatus = EXIT_SUCCESS;
144
  
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
145
  /* The options we understand. */
146
  struct argp_option options[] = {
147
    { .name = "global-options", .key = 'g',
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
148
      .arg = "OPTION[,OPTION[,...]]",
149
      .doc = "Options passed to all plugins" },
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
150
    { .name = "options-for", .key = 'o',
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
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 },
24.1.5 by Björn Påhlsson
plugbasedclient:
156
    { .name = "plugin-dir", .key = 128,
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
157
      .arg = "DIRECTORY",
158
      .doc = "Specify a different plugin directory", .group = 2 },
159
    { .name = "debug", .key = 129,
160
      .doc = "Debug mode", .group = 3 },
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
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
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
166
          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
167
    plugin **plugins = state->input;
168
    switch (key) {
169
    case 'g':
170
      if (arg != NULL){
171
	char *p = strtok(arg, ",");
172
	do{
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
173
	  addargument(getplugin(NULL, plugins), p);
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
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, ":");
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
182
	if(p){
183
	  p = strtok(p, ",");
184
	  do{
185
	    addargument(getplugin(name, plugins), p);
186
	    p = strtok(NULL, ",");
187
	  } while (p);
188
	}
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
189
      }
190
      break;
24.1.5 by Björn Påhlsson
plugbasedclient:
191
    case 'd':
192
      if (arg != NULL){
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
193
	getplugin(arg, plugins)->disabled = true;
24.1.5 by Björn Påhlsson
plugbasedclient:
194
      }
195
      break;
196
    case 128:
197
      plugindir = arg;
198
      break;
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
199
    case 129:
200
      debug = true;
201
      break;
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
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
  }
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
212
  
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
213
  plugin *plugin_list = NULL;
214
  
215
  struct argp argp = { .options = options, .parser = parse_opt,
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
216
		       .args_doc = "",
217
		       .doc = "Mandos plugin runner -- Run plugins" };
218
  
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
219
  argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
220
  
221
  if(debug){
222
    for(plugin *p = plugin_list; p != NULL; p=p->next){
223
      fprintf(stderr, "Plugin: %s has %d arguments\n",
32 by Teddy Hogeborn
* plugins.d/plugbasedclient.c (set_cloexec_flag): New function.
224
	      p->name ? p->name : "Global", p->argc - 1);
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
225
      for(char **a = p->argv; *a != NULL; a++){
226
	fprintf(stderr, "\tArg: %s\n", *a);
227
      }
228
    }
229
  }
230
  
13 by Björn Påhlsson
Added following support:
231
  dir = opendir(plugindir);
32 by Teddy Hogeborn
* plugins.d/plugbasedclient.c (set_cloexec_flag): New function.
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;
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
237
  }
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
238
  
13 by Björn Påhlsson
Added following support:
239
  if(dir == NULL){
240
    fprintf(stderr, "Can not open directory\n");
241
    return EXIT_FAILURE;
242
  }
243
  
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
244
  FD_ZERO(&rfds_all);
13 by Björn Påhlsson
Added following support:
245
  
246
  while(true){
247
    dirst = readdir(dir);
248
    
249
    // All directory entries have been processed
250
    if(dirst == NULL){
251
      break;
252
    }
253
    
254
    d_name_len = strlen(dirst->d_name);
255
    
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
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
      }
13 by Björn Påhlsson
Added following support:
299
    }
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
300
    
24.1.5 by Björn Påhlsson
plugbasedclient:
301
    char *filename = malloc(d_name_len + strlen(plugindir) + 2);
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
302
    if (filename == NULL){
303
      perror("malloc");
304
      exitstatus =EXIT_FAILURE;
305
      goto end;
306
    }
13 by Björn Påhlsson
Added following support:
307
    strcpy(filename, plugindir);
308
    strcat(filename, "/");
309
    strcat(filename, dirst->d_name);    
14 by Björn Påhlsson
Fixed a overbufferflow bug, thanks to a forgotten \0
310
13 by Björn Påhlsson
Added following support:
311
    stat(filename, &st);
312
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
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(argv[0]);
332
      goto end;
333
    }
32 by Teddy Hogeborn
* plugins.d/plugbasedclient.c (set_cloexec_flag): New function.
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
    }
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
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
      
349
      if(execv(filename, p->argv) < 0){
350
	perror(argv[0]);
351
	close(pipefd[1]);
352
	_exit(EXIT_FAILURE);
353
      }
354
      /* no return */
355
    }
356
    close(pipefd[1]);		/* close unused write end of pipe */
357
    process *new_process = malloc(sizeof(process));
358
    if (new_process == NULL){
359
      perror("malloc");
360
      exitstatus = EXIT_FAILURE;
361
      goto end;
362
    }
363
    
364
    new_process->fd = pipefd[0];
365
    new_process->buffer = malloc(BUFFER_SIZE);
366
    if (new_process->buffer == NULL){
367
      perror("malloc");
368
      exitstatus = EXIT_FAILURE;
369
      goto end;
370
    }
371
    new_process->buffer_size = BUFFER_SIZE;
372
    new_process->buffer_length = 0;
373
    FD_SET(new_process->fd, &rfds_all);
374
      
375
    if (maxfd < new_process->fd){
376
      maxfd = new_process->fd;
377
    }
378
    
379
    //List handling
380
    new_process->next = process_list;
381
    process_list = new_process;
13 by Björn Påhlsson
Added following support:
382
  }
383
  
384
  closedir(dir);
385
  
386
  if (process_list != NULL){
387
    while(true){
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
388
      fd_set rfds = rfds_all;
13 by Björn Påhlsson
Added following support:
389
      int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
390
      if (select_ret == -1){
391
	perror(argv[0]);
392
	goto end;
393
      }else{	
394
	for(process *process_itr = process_list; process_itr != NULL;
395
	    process_itr = process_itr->next){
396
	  if(FD_ISSET(process_itr->fd, &rfds)){
397
	    if(process_itr->buffer_length + BUFFER_SIZE
398
	       > process_itr->buffer_size){
399
		process_itr->buffer = realloc(process_itr->buffer,
400
					      process_itr->buffer_size
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
401
					      + (size_t) BUFFER_SIZE);
13 by Björn Påhlsson
Added following support:
402
		if (process_itr->buffer == NULL){
403
		  perror(argv[0]);
404
		  goto end;
405
		}
406
		process_itr->buffer_size += BUFFER_SIZE;
407
	    }
408
	    ret = read(process_itr->fd, process_itr->buffer
409
		       + process_itr->buffer_length, BUFFER_SIZE);
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
410
	    if(ret < 0){
411
	      /* Read error from this process; ignore it */
412
	      continue;
413
	    }
414
	    process_itr->buffer_length += (size_t) ret;
13 by Björn Påhlsson
Added following support:
415
	    if(ret == 0){
416
	      /* got EOF */
417
	      /* wait for process exit */
418
	      int status;
419
	      waitpid(process_itr->pid, &status, 0);
420
	      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
421
		for(size_t written = 0;
422
		    written < process_itr->buffer_length;){
423
		  ret = write(STDOUT_FILENO,
424
			      process_itr->buffer + written,
425
			      process_itr->buffer_length - written);
426
		  if(ret < 0){
427
		    perror(argv[0]);
428
		    goto end;
429
		  }
430
		  written += (size_t)ret;
431
		}
13 by Björn Påhlsson
Added following support:
432
		goto end;
433
	      } else {
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
434
		FD_CLR(process_itr->fd, &rfds_all);
13 by Björn Påhlsson
Added following support:
435
	      }
436
	    }
437
	  }
438
	}
439
      }
440
    }
441
  }
442
  
443
 end:
444
  for(process *process_itr = process_list; process_itr != NULL;
445
      process_itr = process_itr->next){
446
    close(process_itr->fd);
447
    kill(process_itr->pid, SIGTERM);
448
    free(process_itr->buffer);
449
  }
450
  
451
  while(true){
452
    int status;
453
    ret = wait(&status);
454
    if (ret == -1){
455
      if(errno != ECHILD){
456
	perror("wait");
457
      }
458
      break;
459
    }
460
  }  
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
461
  return exitstatus;
13 by Björn Påhlsson
Added following support:
462
}