1 /*	$CoreSDI: peochk.c,v 1.54 2002/03/01 07:31:03 alejo Exp $	*/
2 
3 /*
4  * Copyright (c) 2001, Core SDI S.A., Argentina
5  * All rights reserved
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither name of the Core SDI S.A. nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * peochk - syslog -- Initial key generator and integrity log file checker
34  *
35  * Author: Claudio Castiglia, Core-SDI SA
36  *
37  *
38  * peochk [-f logfile] [-g] [-i key0file] [-k keyfile] [-l]
39  *        [-m hash_method] [-q] [logfile]
40  *
41  * supported hash_method values:
42  *			md5
43  *			rmd160
44  *			sha1
45  *
46  * defaults:
47  *	logfile: 	/var/log/messages
48  *	keyfile:	/var/ssyslog/.var.log.messages.key
49  *	hash_method:	sha1
50  *
51  * NOTES:
52  *	1) When logfile is specified without the -f switch, the data is
53  *	   read from the standard input
54  *	2) If logfile is specified using both -f switch and without it,
55  *	   the -f argument is used and data is read from that file
56  *	3) If logfile is specified but not the keyfile, this will be
57  *	   /var/ssyslog/xxx.key where xxx is the logfile with all '/'
58  *	   replaced by '.'
59  *	4) If -l switch is specified, peochk detects the line number
60  *	   corrupted on logfile
61  *	5) -q means quiet mode
62  *
63  */
64 
65 #include "config.h"
66 
67 #include <sys/param.h>
68 #include <sys/stat.h>
69 #include <sys/syslog.h>
70 #include <sys/types.h>
71 #include <sys/uio.h>
72 
73 #include <errno.h>
74 #include <fcntl.h>
75 #include <stdarg.h>
76 #include <stdio.h>
77 #include <stdlib.h>
78 #include <string.h>
79 #if TIME_WITH_SYS_TIME
80 # include <sys/time.h>
81 # include <time.h>
82 #else
83 # if HAVE_SYS_TIME_H
84 #  include <sys/time.h>
85 # else
86 #  include <time.h>
87 # endif
88 #endif
89 #include <unistd.h>
90 #include <netdb.h>
91 
92 #include "hash.h"
93 #include "../modules.h"
94 #include "../syslogd.h"
95 
96 #define CHECK	0x01
97 #define QUIET	0x02
98 #define ST_IN	0x04
99 
100 int	 actionf;
101 char	*corrupted = "corrupted";
102 char	*default_logfile = "/var/log/messages";
103 char	*keyfile;
104 char	*key0file;
105 char	*logfile;
106 char	*macfile;
107 int	 method;
108 
109 
110 /*
111  * release allocated memory
112  */
113 void
release(void)114 release(void)
115 {
116 	if (keyfile != default_keyfile)
117 		free(keyfile);
118 	if (key0file)
119 		free(key0file);
120 	if (logfile != default_logfile)
121 		free(logfile);
122 	if (macfile)
123 		free(macfile);
124 }
125 
126 
127 /*
128  * usage
129  */
130 void
usage(void)131 usage(void)
132 {
133 	fprintf (stderr,
134 		"Usage:\n"
135 		"Check mode:\n"
136 		"    peochk [-h] [-l] [-f logfile] [-i key0file] [-k keyfile]\n"
137 		"           [-m hash_method] [-q] [logfile]\n\n"
138 		"Initial key generator mode:\n"
139 		"    peochk -g [-h] [-k keyfile] [-m hash_method] [logfile]"
140 		"\n\n");
141 	exit(-1);
142 }
143 
144 
145 /*
146  * readline()
147  */
148 int
readline(int fd,char * buf,size_t len,FILE ** f)149 readline(int fd, char *buf, size_t len, FILE **f)
150 {
151 	char	*st;
152 	size_t	 i;
153 
154 	if (*f == NULL) {
155 		*f = fdopen(fd, "r");
156 		if (*f == NULL)
157 			return (-1);
158 	}
159 	st = fgets(buf, len, *f);
160 	if (st != NULL) {
161 		i = strlen(buf) - 1;
162 		if (*(buf + i) == '\n')
163 			*(buf + i ) = '\0';
164 		return (i);
165 	}
166 	*buf = '\0';
167 	return (0);
168 }
169 
170 
171 /*
172  * eexit()
173  *	Prints a message on stdout and exits with a status
174  */
175 void
eexit(int status,char * fmt,...)176 eexit(int status, char *fmt, ...)
177 {
178 	va_list ap;
179 	if (fmt) {
180 		va_start(ap, fmt);
181 #ifdef HAVE_VPRINTF
182 		vfprintf(stdout, fmt, ap);
183 #elif defined(HAVE_DOPRNT)
184 		_doprnt(stdout, fmt, ap);
185 #else
186 #error No vfprintf and no doprnt
187 #endif
188 		va_end(ap);
189 	}
190 	exit(status);
191 }
192 
193 
194 /*
195  * check: read logfile and check it
196  */
197 void
check(void)198 check(void)
199 {
200 	FILE *finput;
201 	int   i;
202 	int   input;
203 	int   mfd;
204 	unsigned char  key[41];
205 	int   keylen;
206 	unsigned char  lastkey[21];
207 	int   lastkeylen;
208 	int   line;
209 	unsigned char  mkey1[21];
210 	int   mkey1len;
211 	unsigned char  mkey2[21];
212 	int   mkey2len;
213 	char  msg[MAXLINE];
214 	int   msglen;
215 
216 	/* open logfile */
217 	if (actionf & ST_IN)
218 		input = STDIN_FILENO;
219 	else if ( (input = open(logfile, O_RDONLY, 0)) == -1) {
220 		perror(logfile);
221 		exit(-1);
222 	}
223 
224 	mfd = 0;	/* shutup gcc */
225 
226 	/* open macfile */
227 	if (macfile)
228 		if ( (mfd = open(macfile, O_RDONLY, 0)) == -1) {
229 			perror(macfile);
230 			exit(-1);
231 		}
232 
233 	/* read initial key (as ascii string) and tranlate it to binary */
234 	if ( (i = open(key0file, O_RDONLY, 0)) == -1) {
235 		perror(key0file);
236 		exit(-1);
237 	}
238 	if ( (keylen = read(i, key, 40)) == -1) {
239 		perror(key0file);
240 		exit(-1);
241 	}
242 	if (!keylen) {
243 		if (actionf & QUIET)
244 			eexit(1, "1\n");
245 		else
246 			eexit(1, "(1) %s: %s\n", key0file, corrupted);
247 	}
248 	key[keylen] = 0;
249 	asc2bin(key, key);
250 	keylen >>= 1;
251 	close(i);
252 
253 	/* read last key */
254 	if ( (i = open(keyfile, O_RDONLY, 0)) == -1) {
255 		perror(keyfile);
256 		exit(-1);
257 	}
258 	if ( (lastkeylen = read(i, lastkey, 20)) == -1) {
259 		perror(keyfile);
260 		exit(-1);
261 	}
262 	if (!lastkeylen) {
263 		if (actionf & QUIET)
264 			eexit(1, "1\n");
265 		else
266 			eexit(1, "(1) %s: %s\n", keyfile, corrupted);
267 	}
268 	close(i);
269 
270 	/* test both key lenghts */
271 	if (lastkeylen != keylen) {
272 		if (actionf & QUIET)
273 			eexit(1, "1\n");
274 		else
275 			eexit(1, "(1) %s and/or %s %s\n", key0file, keyfile,
276 			    corrupted);
277 	}
278 
279 	/* check it */
280 	line = 1;
281 	finput = NULL;
282 	while ( (msglen = readline(input, msg, MAXLINE, &finput)) > 0) {
283 		if (macfile) {
284 			if ( ((mkey1len = mac2(key, keylen,
285 			    (unsigned char *) msg, msglen, mkey1)) < 0) ||
286 			    ((mkey2len = read(mfd, mkey2,
287 			    mkey1len)) < 0) ) {
288 				perror(macfile);
289 				exit(-1);
290 			}
291 			if ((mkey2len != mkey1len) || memcmp(mkey2, mkey1,
292 			    mkey1len)) {
293 				if (actionf & QUIET)
294 					eexit(1, "%i\n", line);
295 				else
296 					eexit(1, "(%i) %s %s on line %i\n",
297 					    line, logfile, corrupted, line);
298 			}
299 			line++;
300 		}
301 		if ( (keylen = mac(method, key, keylen,
302 		    (unsigned char *) msg, msglen, key)) == -1) {
303 			perror("fatal");
304 			exit(-1);
305 		}
306 	}
307 
308 	if (finput != NULL)
309 		fclose(finput);
310 
311 	if (macfile != NULL)
312 		close(mfd);
313 
314 	if (i < 0) {
315 		fprintf(stderr, "error reading logs form %s : %s\n",
316 		    (actionf & ST_IN) ? "standard input" : logfile,
317 		    strerror(errno));
318 		exit(-1);
319 	}
320 
321 	if (memcmp(lastkey, key, keylen)) {
322 		if (actionf & QUIET)
323 			eexit(1, "1\n");
324 		else
325 			eexit(1, "(1) %s %s\n", logfile, corrupted);
326 	}
327 	if (actionf & QUIET)
328 		eexit(0, "0\n");
329 	else
330 		eexit(0, "(0) %s file is ok\n", logfile);
331 }
332 
333 
334 /*
335  * generate:
336  *	generate initial key and write it on keyfile and key0file
337  *	in the last file data is written as ascii string
338  */
339 void
generate(void)340 generate(void)
341 {
342 	int	 kfd;
343 	int	 k0fd;
344 	unsigned char	 key[20];
345 	unsigned char	 keyasc[41];
346 	int	 keylen;
347 	unsigned char	 randvalue[20];
348 
349 	if (getrandom(randvalue, 20) < 0) {
350 		release();
351 		perror("getrandom");
352 		exit(-1);
353 	}
354 	if ( (keylen = mac(method, NULL, 0, randvalue, 20, key)) == -1) {
355 		release();
356 		perror("fatal");
357 		exit(-1);
358 	}
359 	if ( (kfd = open(keyfile, O_WRONLY|O_CREAT|O_EXCL,
360 	    S_IRUSR|S_IWUSR)) == -1) {
361 		release();
362 		perror(keyfile);
363 		exit(-1);
364 	}
365 	if ( (k0fd = open(key0file, O_WRONLY|O_CREAT|O_EXCL,
366 	    S_IRUSR|S_IWUSR)) == -1) {
367 		unlink(keyfile);
368 		close(kfd);
369 		release();
370 		perror(key0file);
371 		exit(-1);
372 	}
373 
374 	/* write key 0 */
375 	write(kfd, key, keylen);
376 	write(k0fd, bin2asc(keyasc, key, keylen), keylen << 1);
377 	close(kfd);
378 	close(k0fd);
379 }
380 
381 
382 /*
383  * main
384  */
385 int
main(int argc,char ** argv)386 main(int argc, char **argv)
387 {
388 	int	 ch;
389 	int	 argcnt;
390 	int	 mac;
391 
392 	/* integrity check mode, stdin */
393 	actionf = CHECK | ST_IN;
394 
395 	/* default values */
396 	logfile = default_logfile;
397 	keyfile = default_keyfile;
398 	key0file = NULL;
399 	mac = 0;
400 	macfile = NULL;
401 	method = SHA1;
402 
403 	argcnt = 1;	/* skip program name */
404 
405 	while ((ch = getxopt(argc, argv, "f!logfile: g!generatekey"
406 	    " i!initkeyfile: k!keyfile: l!mac m!method: q!quiet h!help",
407 	    &argcnt)) != -1) {
408 
409 		switch (ch) {
410 			case 'f':
411 				/* log file (intrusion detection mode) */
412 				if (logfile != default_logfile)
413 					free(logfile);
414 
415 				if ((logfile = strrealpath(argv[argcnt]))
416 				    == NULL) {
417 					release();
418 					perror(argv[argcnt]);
419 					exit(-1);
420 				}
421 
422 				actionf &= ~ST_IN;
423 				break;
424 
425 			case 'g':
426 				/* generate new keyfile and initial key */
427 				actionf &= ~CHECK;
428 				break;
429 
430 			case 'i':
431 				/* key 0 file */
432 				if (key0file)
433 					free(key0file);
434 				if ((key0file = strdup(argv[argcnt]))
435 				    == NULL) {
436 					release();
437 					perror(argv[argcnt]);
438 					exit(-1);
439 				}
440 				break;
441 
442 			case 'k':
443 				/* keyfile */
444 				if (keyfile != default_keyfile)
445 					free(keyfile);
446 
447 				if ((keyfile = strdup(argv[argcnt])) == NULL) {
448 					release();
449 					perror(argv[argcnt]);
450 					exit(-1);
451 				}
452 				break;
453 
454 			case 'l':
455 				mac = 1;
456 				break;
457 
458 			case 'm':
459 				/* hash method */
460 				if ( (method = gethash(argv[argcnt])) < 0) {
461 					release();
462 					usage();
463 				}
464 				break;
465 
466 			case 'q':
467 				/* quiet mode */
468 				actionf |= QUIET;
469 				break;
470 			case 'h':
471 			default:
472 				release();
473 				usage();
474 		}
475 
476 		argcnt++;
477 
478 	}
479 
480 	/* check logfile specified without -f switch */
481 	argc -= argc;
482 	argv += argc;
483 	if (argc && (actionf & ST_IN))
484 		if ( (logfile = strrealpath(argv[argc-1])) == NULL) {
485 			release();
486 			perror(argv[argc-1]);
487 			exit(-1);
488 		}
489 
490 	/* if keyfile was not specified converted logfile is used instead */
491 	if (keyfile == default_keyfile && logfile != default_logfile) {
492 		char *tmp;
493 
494 		if ( (tmp = strallocat("/var/ssyslog/", logfile)) == NULL) {
495 			release();
496 			perror("buffer for keyfile");
497 			exit(-1);
498 		}
499 		strdot(tmp+13);
500 		if ( (keyfile = strallocat(tmp, ".key")) == NULL) {
501 			free(tmp);
502 			release();
503 			perror("buffer for keyfile");
504 			exit(-1);
505 		}
506 		free(tmp);
507 	}
508 
509 	/* if key0file was not specified create one */
510 	if (key0file == NULL)
511 		if ( (key0file = strkey0(keyfile)) == NULL) {
512 		release();
513 		perror("creating key0 file");
514 		exit(-1);
515 		}
516 
517 	/* create macfile */
518 	if (mac)
519 		if ( (macfile = strmac(keyfile)) == NULL) {
520 			release();
521 			perror("creating mac file");
522 			exit(-1);
523 		}
524 
525 	/* execute action */
526 	if (actionf & CHECK)
527 		check();
528 	else
529 		generate();
530 
531 	release();
532 	return (0);
533 }
534 
535