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