log.c revision 1.43
1/* $OpenBSD: log.c,v 1.43 2004/04/15 18:39:26 deraadt Exp $	 */
2/* $EOM: log.c,v 1.30 2000/09/29 08:19:23 niklas Exp $	 */
3
4/*
5 * Copyright (c) 1998, 1999, 2001 Niklas Hallqvist.  All rights reserved.
6 * Copyright (c) 1999, 2000, 2001, 2003 H�kan Olsson.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
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 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30 * This code was written under funding by Ericsson Radio Systems.
31 */
32
33#include <sys/types.h>
34#include <sys/time.h>
35
36#ifdef USE_DEBUG
37#include <sys/socket.h>
38#include <sys/stat.h>
39#include <sys/uio.h>
40#include <netinet/in.h>
41#include <netinet/in_systm.h>
42#include <netinet/ip.h>
43#include <netinet/ip6.h>
44#include <netinet/udp.h>
45#include <arpa/inet.h>
46
47#ifdef HAVE_PCAP
48#include <pcap.h>
49#else
50#include "sysdep/common/pcap.h"
51#endif
52
53#endif				/* USE_DEBUG */
54
55#include <errno.h>
56#include <stdio.h>
57#include <stdlib.h>
58#include <string.h>
59#include <syslog.h>
60#include <stdarg.h>
61#include <unistd.h>
62
63#include "conf.h"
64#include "isakmp_num.h"
65#include "log.h"
66#include "monitor.h"
67#include "util.h"
68
69static void	_log_print(int, int, const char *, va_list, int, int);
70
71static FILE	*log_output;
72
73int		verbose_logging = 0;
74#if defined (USE_DEBUG)
75static int	log_level[LOG_ENDCLASS];
76
77#define TCPDUMP_MAGIC	0xa1b2c3d4
78#define SNAPLEN		(64 * 1024)
79
80struct packhdr {
81	struct pcap_pkthdr pcap;/* pcap file packet header */
82	u_int32_t sa_family;	/* address family */
83	union {
84		struct ip       ip4;	/* IPv4 header (w/o options) */
85		struct ip6_hdr  ip6;	/* IPv6 header */
86	} ip;
87};
88
89struct isakmp_hdr {
90	u_int8_t        icookie[8], rcookie[8];
91	u_int8_t        next, ver, type, flags;
92	u_int32_t       msgid, len;
93};
94
95static char    *pcaplog_file = NULL;
96static FILE    *packet_log;
97static u_int8_t *packet_buf = NULL;
98
99static int      udp_cksum(struct packhdr *, const struct udphdr *, u_int16_t *);
100static u_int16_t in_cksum(const u_int16_t *, int);
101#endif				/* USE_DEBUG */
102
103void
104log_init(int debug)
105{
106	if (debug)
107		log_output = stderr;
108	else
109		log_to(0);	/* syslog */
110}
111
112void
113log_reinit(void)
114{
115	struct conf_list *logging;
116#ifdef USE_DEBUG
117	struct conf_list_node *logclass;
118	int		class, level;
119#endif				/* USE_DEBUG */
120
121	logging = conf_get_list("General", "Logverbose");
122	if (logging) {
123		verbose_logging = 1;
124		conf_free_list(logging);
125	}
126#ifdef USE_DEBUG
127	logging = conf_get_list("General", "Loglevel");
128	if (!logging)
129		return;
130
131	for (logclass = TAILQ_FIRST(&logging->fields); logclass;
132	    logclass = TAILQ_NEXT(logclass, link)) {
133		if (sscanf(logclass->field, "%d=%d", &class, &level) != 2) {
134			if (sscanf(logclass->field, "A=%d", &level) == 1)
135				for (class = 0; class < LOG_ENDCLASS; class++)
136					log_debug_cmd(class, level);
137			else {
138				log_print("init: invalid logging class or level: %s",
139				    logclass->field);
140				continue;
141			}
142		} else
143			log_debug_cmd(class, level);
144	}
145	conf_free_list(logging);
146#endif				/* USE_DEBUG */
147}
148
149void
150log_to(FILE *f)
151{
152	if (!log_output && f)
153		closelog();
154	log_output = f;
155	if (!f)
156		openlog("isakmpd", LOG_PID | LOG_CONS, LOG_DAEMON);
157}
158
159FILE *
160log_current(void)
161{
162	return log_output;
163}
164
165static char *
166_log_get_class(int error_class)
167{
168	/* XXX For test purposes. To be removed later on?  */
169	static char	*class_text[] = LOG_CLASSES_TEXT;
170
171	if (error_class < 0)
172		return "Dflt";
173	else if (error_class >= LOG_ENDCLASS)
174		return "Unkn";
175	else
176		return class_text[error_class];
177}
178
179static void
180_log_print(int error, int syslog_level, const char *fmt, va_list ap,
181    int class, int level)
182{
183	char		buffer[LOG_SIZE], nbuf[LOG_SIZE + 32];
184	static const char fallback_msg[] =
185	    "write to log file failed (errno %d), redirecting output to syslog";
186	int		len;
187	struct tm      *tm;
188	struct timeval  now;
189	time_t          t;
190
191	len = vsnprintf(buffer, sizeof buffer, fmt, ap);
192	if (len > 0 && len < (int) sizeof buffer - 1 && error)
193		snprintf(buffer + len, sizeof buffer - len, ": %s", strerror(errno));
194	if (log_output) {
195		gettimeofday(&now, 0);
196		t = now.tv_sec;
197		tm = localtime(&t);
198		if (class >= 0)
199			snprintf(nbuf, sizeof nbuf, "%02d%02d%02d.%06ld %s %02d ",
200			    tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec,
201			    _log_get_class(class), level);
202		else /* LOG_PRINT (-1) or LOG_REPORT (-2) */
203			snprintf(nbuf, sizeof nbuf, "%02d%02d%02d.%06ld %s ",
204			    tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec,
205			    class == LOG_PRINT ? "Default" : "Report>");
206		strlcat(nbuf, buffer, sizeof nbuf);
207#if defined (USE_PRIVSEP)
208		strlcat(nbuf, getuid() ? "" : " [priv]", LOG_SIZE + 32);
209#endif
210		strlcat(nbuf, "\n", sizeof nbuf);
211
212		if (fwrite(nbuf, strlen(nbuf), 1, log_output) == 0) {
213			/* Report fallback.  */
214			syslog(LOG_ALERT, fallback_msg, errno);
215			fprintf(log_output, fallback_msg, errno);
216
217			/*
218			 * Close log_output to prevent isakmpd from locking the file.
219			 * We may need to explicitly close stdout to do this properly.
220			 * XXX - Figure out how to match two FILE *'s and rewrite.
221			 */
222			if (fileno(log_output) != -1 &&
223			    fileno(stdout) == fileno(log_output))
224				fclose(stdout);
225			fclose(log_output);
226
227			/* Fallback to syslog.  */
228			log_to(0);
229
230			/* (Re)send current message to syslog().  */
231			syslog(class == LOG_REPORT ? LOG_ALERT :
232			    syslog_level, "%s", buffer);
233		}
234	} else
235		syslog(class == LOG_REPORT ? LOG_ALERT : syslog_level,
236		    "%s", buffer);
237}
238
239#ifdef USE_DEBUG
240void
241log_debug(int cls, int level, const char *fmt, ...)
242{
243	va_list         ap;
244
245	/*
246	 * If we are not debugging this class, or the level is too low, just return.
247         */
248	if (cls >= 0 && (log_level[cls] == 0 || level > log_level[cls]))
249		return;
250	va_start(ap, fmt);
251	_log_print(0, LOG_INFO, fmt, ap, cls, level);
252	va_end(ap);
253}
254
255void
256log_debug_buf(int cls, int level, const char *header, const u_int8_t *buf,
257    size_t sz)
258{
259	size_t	i, j;
260	char	s[73];
261
262	/*
263	 * If we are not debugging this class, or the level is too low, just return.
264         */
265	if (cls >= 0 && (log_level[cls] == 0 || level > log_level[cls]))
266		return;
267
268	log_debug(cls, level, "%s:", header);
269	for (i = j = 0; i < sz;) {
270		snprintf(s + j, sizeof s - j, "%02x", buf[i++]);
271		j += 2;
272		if (i % 4 == 0) {
273			if (i % 32 == 0) {
274				s[j] = '\0';
275				log_debug(cls, level, "%s", s);
276				j = 0;
277			} else
278				s[j++] = ' ';
279		}
280	}
281	if (j) {
282		s[j] = '\0';
283		log_debug(cls, level, "%s", s);
284	}
285}
286
287void
288log_debug_cmd(int cls, int level)
289{
290	if (cls < 0 || cls >= LOG_ENDCLASS) {
291		log_print("log_debug_cmd: invalid debugging class %d", cls);
292		return;
293	}
294	if (level < 0) {
295		log_print("log_debug_cmd: invalid debugging level %d for class %d",
296		    level, cls);
297		return;
298	}
299	if (level == log_level[cls])
300		log_print("log_debug_cmd: log level unchanged for class %d", cls);
301	else {
302		log_print("log_debug_cmd: log level changed from %d to %d for class %d",
303		    log_level[cls], level, cls);
304		log_level[cls] = level;
305	}
306}
307
308void
309log_debug_toggle(void)
310{
311	static int	log_level_copy[LOG_ENDCLASS], toggle = 0;
312
313	if (!toggle) {
314		LOG_DBG((LOG_MISC, 50, "log_debug_toggle: debug levels cleared"));
315		memcpy(&log_level_copy, &log_level, sizeof log_level);
316		memset(&log_level, 0, sizeof log_level);
317	} else {
318		memcpy(&log_level, &log_level_copy, sizeof log_level);
319		LOG_DBG((LOG_MISC, 50, "log_debug_toggle: debug levels restored"));
320	}
321	toggle = !toggle;
322}
323#endif				/* USE_DEBUG */
324
325void
326log_print(const char *fmt, ...)
327{
328	va_list         ap;
329
330	va_start(ap, fmt);
331	_log_print(0, LOG_NOTICE, fmt, ap, LOG_PRINT, 0);
332	va_end(ap);
333}
334
335void
336log_verbose(const char *fmt, ...)
337{
338	va_list         ap;
339#ifdef USE_DEBUG
340	int             i;
341#endif				/* USE_DEBUG */
342
343	if (verbose_logging == 0)
344		return;
345
346#ifdef USE_DEBUG
347	for (i = 0; i < LOG_ENDCLASS; i++)
348		if (log_level[i] > 0)
349			return;
350#endif
351
352	va_start(ap, fmt);
353	_log_print(0, LOG_NOTICE, fmt, ap, LOG_PRINT, 0);
354	va_end(ap);
355}
356
357void
358log_error(const char *fmt, ...)
359{
360	va_list         ap;
361
362	va_start(ap, fmt);
363	_log_print(1, LOG_ERR, fmt, ap, LOG_PRINT, 0);
364	va_end(ap);
365}
366
367void
368log_fatal(const char *fmt, ...)
369{
370	va_list         ap;
371
372	va_start(ap, fmt);
373	_log_print(1, LOG_CRIT, fmt, ap, LOG_PRINT, 0);
374	va_end(ap);
375	exit(1);
376}
377
378#ifdef USE_DEBUG
379void
380log_packet_init(char *newname)
381{
382	struct pcap_file_header sf_hdr;
383	struct stat     st;
384	mode_t          old_umask;
385	char           *mode;
386
387	/* Allocate packet buffer first time through.  */
388	if (!packet_buf)
389		packet_buf = malloc(SNAPLEN);
390
391	if (!packet_buf) {
392		log_error("log_packet_init: malloc (%d) failed", SNAPLEN);
393		return;
394	}
395	if (pcaplog_file && strcmp(pcaplog_file, PCAP_FILE_DEFAULT) != 0)
396		free(pcaplog_file);
397
398	pcaplog_file = strdup(newname);
399	if (!pcaplog_file) {
400		log_error("log_packet_init: strdup (\"%s\") failed", newname);
401		return;
402	}
403	/* Does the file already exist?  XXX lstat() or stat()?  */
404#if defined (USE_PRIVSEP)
405	/* XXX This is a fstat! */
406	if (monitor_stat(pcaplog_file, &st) == 0) {
407#else
408	if (lstat(pcaplog_file, &st) == 0) {
409#endif
410		/* Sanity checks.  */
411		if ((st.st_mode & S_IFMT) != S_IFREG) {
412			log_print("log_packet_init: existing capture file is "
413			     "not a regular file");
414			return;
415		}
416		if ((st.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
417			log_print("log_packet_init: existing capture "
418			    "file has bad modes");
419			return;
420		}
421		/*
422		 * XXX It would be nice to check if it actually is a pcap
423		 * file...
424		 */
425
426		mode = "a";
427	} else
428		mode = "w";
429
430	old_umask = umask(S_IRWXG | S_IRWXO);
431	packet_log = monitor_fopen(pcaplog_file, mode);
432	umask(old_umask);
433
434	if (!packet_log) {
435		log_error("log_packet_init: fopen (\"%s\", \"%s\") failed",
436		    pcaplog_file, mode);
437		return;
438	}
439	log_print("log_packet_init: starting IKE packet capture to file \"%s\"",
440	    pcaplog_file);
441
442	/* If this is a new file, we need to write a PCAP header to it.  */
443	if (*mode == 'w') {
444		sf_hdr.magic = TCPDUMP_MAGIC;
445		sf_hdr.version_major = PCAP_VERSION_MAJOR;
446		sf_hdr.version_minor = PCAP_VERSION_MINOR;
447		sf_hdr.thiszone = 0;
448		sf_hdr.snaplen = SNAPLEN;
449		sf_hdr.sigfigs = 0;
450		sf_hdr.linktype = DLT_LOOP;
451
452		fwrite((char *) &sf_hdr, sizeof sf_hdr, 1, packet_log);
453		fflush(packet_log);
454	}
455}
456
457void
458log_packet_restart(char *newname)
459{
460	if (packet_log) {
461		log_print("log_packet_restart: capture already active on file \"%s\"",
462		    pcaplog_file);
463		return;
464	}
465	if (newname)
466		log_packet_init(newname);
467	else if (!pcaplog_file)
468		log_packet_init(PCAP_FILE_DEFAULT);
469	else
470		log_packet_init(pcaplog_file);
471}
472
473void
474log_packet_stop(void)
475{
476	/* Stop capture.  */
477	if (packet_log) {
478		fclose(packet_log);
479		log_print("log_packet_stop: stopped capture");
480	}
481	packet_log = 0;
482}
483
484void
485log_packet_iov(struct sockaddr * src, struct sockaddr * dst, struct iovec * iov,
486    int iovcnt)
487{
488	struct isakmp_hdr *isakmphdr;
489	struct packhdr  hdr;
490	struct udphdr   udp;
491	int             off, datalen, hdrlen, i;
492	struct timeval  tv;
493
494	for (i = 0, datalen = 0; i < iovcnt; i++)
495		datalen += iov[i].iov_len;
496
497	if (!packet_log || datalen > SNAPLEN)
498		return;
499
500	/* copy packet into buffer */
501	for (i = 0, off = 0; i < iovcnt; i++) {
502		memcpy(packet_buf + off, iov[i].iov_base, iov[i].iov_len);
503		off += iov[i].iov_len;
504	}
505
506	memset(&hdr, 0, sizeof hdr);
507	memset(&udp, 0, sizeof udp);
508
509	/* isakmp - turn off the encryption bit in the isakmp hdr */
510	isakmphdr = (struct isakmp_hdr *) packet_buf;
511	isakmphdr->flags &= ~(ISAKMP_FLAGS_ENC);
512
513	/* udp */
514	udp.uh_sport = sockaddr_port(src);
515	udp.uh_dport = sockaddr_port(dst);
516	datalen += sizeof udp;
517	udp.uh_ulen = htons(datalen);
518
519	/* ip */
520	hdr.sa_family = htonl(src->sa_family);
521	switch (src->sa_family) {
522	default:
523		/* Assume IPv4. XXX Can 'default' ever happen here?  */
524		hdr.sa_family = htonl(AF_INET);
525		hdr.ip.ip4.ip_src.s_addr = 0x02020202;
526		hdr.ip.ip4.ip_dst.s_addr = 0x01010101;
527		/* The rest of the setup is common to AF_INET.  */
528		goto setup_ip4;
529
530	case AF_INET:
531		hdr.ip.ip4.ip_src.s_addr = ((struct sockaddr_in *) src)->sin_addr.s_addr;
532		hdr.ip.ip4.ip_dst.s_addr = ((struct sockaddr_in *) dst)->sin_addr.s_addr;
533
534setup_ip4:
535		hdrlen = sizeof hdr.ip.ip4;
536		hdr.ip.ip4.ip_v = 0x4;
537		hdr.ip.ip4.ip_hl = 0x5;
538		hdr.ip.ip4.ip_p = IPPROTO_UDP;
539		hdr.ip.ip4.ip_len = htons(datalen + hdrlen);
540		/* Let's use the IP ID as a "packet counter".  */
541		i = ntohs(hdr.ip.ip4.ip_id) + 1;
542		hdr.ip.ip4.ip_id = htons(i);
543		/* Calculate IP header checksum. */
544		hdr.ip.ip4.ip_sum = in_cksum((u_int16_t *) & hdr.ip.ip4,
545		    hdr.ip.ip4.ip_hl << 2);
546		break;
547
548	case AF_INET6:
549		hdrlen = sizeof(hdr.ip.ip6);
550		hdr.ip.ip6.ip6_vfc = IPV6_VERSION;
551		hdr.ip.ip6.ip6_nxt = IPPROTO_UDP;
552		hdr.ip.ip6.ip6_plen = udp.uh_ulen;
553		memcpy(&hdr.ip.ip6.ip6_src, &((struct sockaddr_in6 *) src)->sin6_addr,
554		    sizeof hdr.ip.ip6.ip6_src);
555		memcpy(&hdr.ip.ip6.ip6_dst, &((struct sockaddr_in6 *) dst)->sin6_addr,
556		    sizeof hdr.ip.ip6.ip6_dst);
557		break;
558	}
559
560	/* Calculate UDP checksum.  */
561	udp.uh_sum = udp_cksum(&hdr, &udp, (u_int16_t *) packet_buf);
562	hdrlen += sizeof hdr.sa_family;
563
564	/* pcap file packet header */
565	gettimeofday(&tv, 0);
566	hdr.pcap.ts.tv_sec = tv.tv_sec;
567	hdr.pcap.ts.tv_usec = tv.tv_usec;
568	hdr.pcap.caplen = datalen + hdrlen;
569	hdr.pcap.len = datalen + hdrlen;
570
571	hdrlen += sizeof(struct pcap_pkthdr);
572	datalen -= sizeof(struct udphdr);
573
574	/* Write to pcap file.  */
575	fwrite(&hdr, hdrlen, 1, packet_log);	/* pcap + IP */
576	fwrite(&udp, sizeof(struct udphdr), 1, packet_log);	/* UDP */
577	fwrite(packet_buf, datalen, 1, packet_log);	/* IKE-data */
578	fflush(packet_log);
579}
580
581/* Copied from tcpdump/print-udp.c, mostly rewritten.  */
582static int
583udp_cksum(struct packhdr *hdr, const struct udphdr *u, u_int16_t *d)
584{
585	struct ip	*ip4;
586	struct ip6_hdr	*ip6;
587	int	i, hdrlen, tlen = ntohs(u->uh_ulen) - sizeof(struct udphdr);
588
589	union phu {
590		struct ip4pseudo {
591			struct in_addr  src;
592			struct in_addr  dst;
593			u_int8_t        z;
594			u_int8_t        proto;
595			u_int16_t       len;
596		} ip4p;
597		struct ip6pseudo {
598			struct in6_addr src;
599			struct in6_addr dst;
600			u_int32_t       plen;
601			u_int16_t       z0;
602			u_int8_t        z1;
603			u_int8_t        nxt;
604		} ip6p;
605		u_int16_t       pa[20];
606	} phu;
607	const u_int16_t *sp;
608	u_int32_t       sum;
609
610	/* Setup pseudoheader.  */
611	memset(phu.pa, 0, sizeof phu);
612	switch (ntohl(hdr->sa_family)) {
613	case AF_INET:
614		ip4 = &hdr->ip.ip4;
615		memcpy(&phu.ip4p.src, &ip4->ip_src, sizeof(struct in_addr));
616		memcpy(&phu.ip4p.dst, &ip4->ip_dst, sizeof(struct in_addr));
617		phu.ip4p.proto = ip4->ip_p;
618		phu.ip4p.len = u->uh_ulen;
619		hdrlen = sizeof phu.ip4p;
620		break;
621
622	case AF_INET6:
623		ip6 = &hdr->ip.ip6;
624		memcpy(&phu.ip6p.src, &ip6->ip6_src, sizeof(phu.ip6p.src));
625		memcpy(&phu.ip6p.dst, &ip6->ip6_dst, sizeof(phu.ip6p.dst));
626		phu.ip6p.plen = u->uh_ulen;
627		phu.ip6p.nxt = ip6->ip6_nxt;
628		hdrlen = sizeof phu.ip6p;
629		break;
630
631	default:
632		return 0;
633	}
634
635	/* IPv6 wants a 0xFFFF checksum "on error", not 0x0.  */
636	if (tlen < 0)
637		return (ntohl(hdr->sa_family) == AF_INET ? 0 : 0xFFFF);
638
639	sum = 0;
640	for (i = 0; i < hdrlen; i += 2)
641		sum += phu.pa[i / 2];
642
643	sp = (u_int16_t *) u;
644	for (i = 0; i < (int) sizeof(struct udphdr); i += 2)
645		sum += *sp++;
646
647	sp = d;
648	for (i = 0; i < (tlen & ~1); i += 2)
649		sum += *sp++;
650
651	if (tlen & 1)
652		sum += htons((*(const char *) sp) << 8);
653
654	while (sum > 0xffff)
655		sum = (sum & 0xffff) + (sum >> 16);
656	sum = ~sum & 0xffff;
657
658	return sum;
659}
660
661/* Copied from tcpdump/print-ip.c, modified.  */
662static u_int16_t
663in_cksum(const u_int16_t *w, int len)
664{
665	int             nleft = len, sum = 0;
666	u_int16_t       answer;
667
668	while (nleft > 1) {
669		sum += *w++;
670		nleft -= 2;
671	}
672	if (nleft == 1)
673		sum += htons(*(u_char *) w << 8);
674
675	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
676	sum += (sum >> 16);	/* add carry */
677	answer = ~sum;		/* truncate to 16 bits */
678	return answer;
679}
680
681#endif				/* USE_DEBUG */
682