log.c revision 1.44
1/* $OpenBSD: log.c,v 1.44 2004/05/23 18:17:56 hshoexer 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 "
139				    "level: %s", 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",
194		    strerror(errno));
195	if (log_output) {
196		gettimeofday(&now, 0);
197		t = now.tv_sec;
198		tm = localtime(&t);
199		if (class >= 0)
200			snprintf(nbuf, sizeof nbuf,
201			    "%02d%02d%02d.%06ld %s %02d ",
202			    tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec,
203			    _log_get_class(class), level);
204		else /* LOG_PRINT (-1) or LOG_REPORT (-2) */
205			snprintf(nbuf, sizeof nbuf, "%02d%02d%02d.%06ld %s ",
206			    tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec,
207			    class == LOG_PRINT ? "Default" : "Report>");
208		strlcat(nbuf, buffer, sizeof nbuf);
209#if defined (USE_PRIVSEP)
210		strlcat(nbuf, getuid() ? "" : " [priv]", LOG_SIZE + 32);
211#endif
212		strlcat(nbuf, "\n", sizeof nbuf);
213
214		if (fwrite(nbuf, strlen(nbuf), 1, log_output) == 0) {
215			/* Report fallback.  */
216			syslog(LOG_ALERT, fallback_msg, errno);
217			fprintf(log_output, fallback_msg, errno);
218
219			/*
220			 * Close log_output to prevent isakmpd from locking
221			 * the file.  We may need to explicitly close stdout
222			 * to do this properly.
223			 * XXX - Figure out how to match two FILE *'s and
224			 * rewrite.
225			 */
226			if (fileno(log_output) != -1 &&
227			    fileno(stdout) == fileno(log_output))
228				fclose(stdout);
229			fclose(log_output);
230
231			/* Fallback to syslog.  */
232			log_to(0);
233
234			/* (Re)send current message to syslog().  */
235			syslog(class == LOG_REPORT ? LOG_ALERT :
236			    syslog_level, "%s", buffer);
237		}
238	} else
239		syslog(class == LOG_REPORT ? LOG_ALERT : syslog_level, "%s",
240		    buffer);
241}
242
243#ifdef USE_DEBUG
244void
245log_debug(int cls, int level, const char *fmt, ...)
246{
247	va_list         ap;
248
249	/*
250	 * If we are not debugging this class, or the level is too low, just
251	 * return.
252         */
253	if (cls >= 0 && (log_level[cls] == 0 || level > log_level[cls]))
254		return;
255	va_start(ap, fmt);
256	_log_print(0, LOG_INFO, fmt, ap, cls, level);
257	va_end(ap);
258}
259
260void
261log_debug_buf(int cls, int level, const char *header, const u_int8_t *buf,
262    size_t sz)
263{
264	size_t	i, j;
265	char	s[73];
266
267	/*
268	 * If we are not debugging this class, or the level is too low, just
269	 * return.
270         */
271	if (cls >= 0 && (log_level[cls] == 0 || level > log_level[cls]))
272		return;
273
274	log_debug(cls, level, "%s:", header);
275	for (i = j = 0; i < sz;) {
276		snprintf(s + j, sizeof s - j, "%02x", buf[i++]);
277		j += 2;
278		if (i % 4 == 0) {
279			if (i % 32 == 0) {
280				s[j] = '\0';
281				log_debug(cls, level, "%s", s);
282				j = 0;
283			} else
284				s[j++] = ' ';
285		}
286	}
287	if (j) {
288		s[j] = '\0';
289		log_debug(cls, level, "%s", s);
290	}
291}
292
293void
294log_debug_cmd(int cls, int level)
295{
296	if (cls < 0 || cls >= LOG_ENDCLASS) {
297		log_print("log_debug_cmd: invalid debugging class %d", cls);
298		return;
299	}
300	if (level < 0) {
301		log_print("log_debug_cmd: invalid debugging level %d for "
302		    "class %d", level, cls);
303		return;
304	}
305	if (level == log_level[cls])
306		log_print("log_debug_cmd: log level unchanged for class %d",
307		    cls);
308	else {
309		log_print("log_debug_cmd: log level changed from %d to %d "
310		    "for class %d", log_level[cls], level, cls);
311		log_level[cls] = level;
312	}
313}
314
315void
316log_debug_toggle(void)
317{
318	static int	log_level_copy[LOG_ENDCLASS], toggle = 0;
319
320	if (!toggle) {
321		LOG_DBG((LOG_MISC, 50, "log_debug_toggle: debug levels cleared"));
322		memcpy(&log_level_copy, &log_level, sizeof log_level);
323		memset(&log_level, 0, sizeof log_level);
324	} else {
325		memcpy(&log_level, &log_level_copy, sizeof log_level);
326		LOG_DBG((LOG_MISC, 50, "log_debug_toggle: debug levels restored"));
327	}
328	toggle = !toggle;
329}
330#endif				/* USE_DEBUG */
331
332void
333log_print(const char *fmt, ...)
334{
335	va_list	ap;
336
337	va_start(ap, fmt);
338	_log_print(0, LOG_NOTICE, fmt, ap, LOG_PRINT, 0);
339	va_end(ap);
340}
341
342void
343log_verbose(const char *fmt, ...)
344{
345	va_list	ap;
346#ifdef USE_DEBUG
347	int	i;
348#endif				/* USE_DEBUG */
349
350	if (verbose_logging == 0)
351		return;
352
353#ifdef USE_DEBUG
354	for (i = 0; i < LOG_ENDCLASS; i++)
355		if (log_level[i] > 0)
356			return;
357#endif
358
359	va_start(ap, fmt);
360	_log_print(0, LOG_NOTICE, fmt, ap, LOG_PRINT, 0);
361	va_end(ap);
362}
363
364void
365log_error(const char *fmt, ...)
366{
367	va_list	ap;
368
369	va_start(ap, fmt);
370	_log_print(1, LOG_ERR, fmt, ap, LOG_PRINT, 0);
371	va_end(ap);
372}
373
374void
375log_fatal(const char *fmt, ...)
376{
377	va_list	ap;
378
379	va_start(ap, fmt);
380	_log_print(1, LOG_CRIT, fmt, ap, LOG_PRINT, 0);
381	va_end(ap);
382	exit(1);
383}
384
385#ifdef USE_DEBUG
386void
387log_packet_init(char *newname)
388{
389	struct pcap_file_header sf_hdr;
390	struct stat     st;
391	mode_t          old_umask;
392	char           *mode;
393
394	/* Allocate packet buffer first time through.  */
395	if (!packet_buf)
396		packet_buf = malloc(SNAPLEN);
397
398	if (!packet_buf) {
399		log_error("log_packet_init: malloc (%d) failed", SNAPLEN);
400		return;
401	}
402	if (pcaplog_file && strcmp(pcaplog_file, PCAP_FILE_DEFAULT) != 0)
403		free(pcaplog_file);
404
405	pcaplog_file = strdup(newname);
406	if (!pcaplog_file) {
407		log_error("log_packet_init: strdup (\"%s\") failed", newname);
408		return;
409	}
410	/* Does the file already exist?  XXX lstat() or stat()?  */
411#if defined (USE_PRIVSEP)
412	/* XXX This is a fstat! */
413	if (monitor_stat(pcaplog_file, &st) == 0) {
414#else
415	if (lstat(pcaplog_file, &st) == 0) {
416#endif
417		/* Sanity checks.  */
418		if ((st.st_mode & S_IFMT) != S_IFREG) {
419			log_print("log_packet_init: existing capture file is "
420			     "not a regular file");
421			return;
422		}
423		if ((st.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
424			log_print("log_packet_init: existing capture "
425			    "file has bad modes");
426			return;
427		}
428		/*
429		 * XXX It would be nice to check if it actually is a pcap
430		 * file...
431		 */
432
433		mode = "a";
434	} else
435		mode = "w";
436
437	old_umask = umask(S_IRWXG | S_IRWXO);
438	packet_log = monitor_fopen(pcaplog_file, mode);
439	umask(old_umask);
440
441	if (!packet_log) {
442		log_error("log_packet_init: fopen (\"%s\", \"%s\") failed",
443		    pcaplog_file, mode);
444		return;
445	}
446	log_print("log_packet_init: starting IKE packet capture to file \"%s\"",
447	    pcaplog_file);
448
449	/* If this is a new file, we need to write a PCAP header to it.  */
450	if (*mode == 'w') {
451		sf_hdr.magic = TCPDUMP_MAGIC;
452		sf_hdr.version_major = PCAP_VERSION_MAJOR;
453		sf_hdr.version_minor = PCAP_VERSION_MINOR;
454		sf_hdr.thiszone = 0;
455		sf_hdr.snaplen = SNAPLEN;
456		sf_hdr.sigfigs = 0;
457		sf_hdr.linktype = DLT_LOOP;
458
459		fwrite((char *) &sf_hdr, sizeof sf_hdr, 1, packet_log);
460		fflush(packet_log);
461	}
462}
463
464void
465log_packet_restart(char *newname)
466{
467	if (packet_log) {
468		log_print("log_packet_restart: capture already active on "
469		    "file \"%s\"", pcaplog_file);
470		return;
471	}
472	if (newname)
473		log_packet_init(newname);
474	else if (!pcaplog_file)
475		log_packet_init(PCAP_FILE_DEFAULT);
476	else
477		log_packet_init(pcaplog_file);
478}
479
480void
481log_packet_stop(void)
482{
483	/* Stop capture.  */
484	if (packet_log) {
485		fclose(packet_log);
486		log_print("log_packet_stop: stopped capture");
487	}
488	packet_log = 0;
489}
490
491void
492log_packet_iov(struct sockaddr *src, struct sockaddr *dst, struct iovec *iov,
493    int iovcnt)
494{
495	struct isakmp_hdr *isakmphdr;
496	struct packhdr  hdr;
497	struct udphdr   udp;
498	int             off, datalen, hdrlen, i;
499	struct timeval  tv;
500
501	for (i = 0, datalen = 0; i < iovcnt; i++)
502		datalen += iov[i].iov_len;
503
504	if (!packet_log || datalen > SNAPLEN)
505		return;
506
507	/* copy packet into buffer */
508	for (i = 0, off = 0; i < iovcnt; i++) {
509		memcpy(packet_buf + off, iov[i].iov_base, iov[i].iov_len);
510		off += iov[i].iov_len;
511	}
512
513	memset(&hdr, 0, sizeof hdr);
514	memset(&udp, 0, sizeof udp);
515
516	/* isakmp - turn off the encryption bit in the isakmp hdr */
517	isakmphdr = (struct isakmp_hdr *) packet_buf;
518	isakmphdr->flags &= ~(ISAKMP_FLAGS_ENC);
519
520	/* udp */
521	udp.uh_sport = sockaddr_port(src);
522	udp.uh_dport = sockaddr_port(dst);
523	datalen += sizeof udp;
524	udp.uh_ulen = htons(datalen);
525
526	/* ip */
527	hdr.sa_family = htonl(src->sa_family);
528	switch (src->sa_family) {
529	default:
530		/* Assume IPv4. XXX Can 'default' ever happen here?  */
531		hdr.sa_family = htonl(AF_INET);
532		hdr.ip.ip4.ip_src.s_addr = 0x02020202;
533		hdr.ip.ip4.ip_dst.s_addr = 0x01010101;
534		/* The rest of the setup is common to AF_INET.  */
535		goto setup_ip4;
536
537	case AF_INET:
538		hdr.ip.ip4.ip_src.s_addr =
539		    ((struct sockaddr_in *)src)->sin_addr.s_addr;
540		hdr.ip.ip4.ip_dst.s_addr =
541		    ((struct sockaddr_in *)dst)->sin_addr.s_addr;
542
543setup_ip4:
544		hdrlen = sizeof hdr.ip.ip4;
545		hdr.ip.ip4.ip_v = 0x4;
546		hdr.ip.ip4.ip_hl = 0x5;
547		hdr.ip.ip4.ip_p = IPPROTO_UDP;
548		hdr.ip.ip4.ip_len = htons(datalen + hdrlen);
549		/* Let's use the IP ID as a "packet counter".  */
550		i = ntohs(hdr.ip.ip4.ip_id) + 1;
551		hdr.ip.ip4.ip_id = htons(i);
552		/* Calculate IP header checksum. */
553		hdr.ip.ip4.ip_sum = in_cksum((u_int16_t *) & hdr.ip.ip4,
554		    hdr.ip.ip4.ip_hl << 2);
555		break;
556
557	case AF_INET6:
558		hdrlen = sizeof(hdr.ip.ip6);
559		hdr.ip.ip6.ip6_vfc = IPV6_VERSION;
560		hdr.ip.ip6.ip6_nxt = IPPROTO_UDP;
561		hdr.ip.ip6.ip6_plen = udp.uh_ulen;
562		memcpy(&hdr.ip.ip6.ip6_src,
563		    &((struct sockaddr_in6 *)src)->sin6_addr,
564		    sizeof hdr.ip.ip6.ip6_src);
565		memcpy(&hdr.ip.ip6.ip6_dst,
566		    &((struct sockaddr_in6 *)dst)->sin6_addr,
567		    sizeof hdr.ip.ip6.ip6_dst);
568		break;
569	}
570
571	/* Calculate UDP checksum.  */
572	udp.uh_sum = udp_cksum(&hdr, &udp, (u_int16_t *) packet_buf);
573	hdrlen += sizeof hdr.sa_family;
574
575	/* pcap file packet header */
576	gettimeofday(&tv, 0);
577	hdr.pcap.ts.tv_sec = tv.tv_sec;
578	hdr.pcap.ts.tv_usec = tv.tv_usec;
579	hdr.pcap.caplen = datalen + hdrlen;
580	hdr.pcap.len = datalen + hdrlen;
581
582	hdrlen += sizeof(struct pcap_pkthdr);
583	datalen -= sizeof(struct udphdr);
584
585	/* Write to pcap file.  */
586	fwrite(&hdr, hdrlen, 1, packet_log);	/* pcap + IP */
587	fwrite(&udp, sizeof(struct udphdr), 1, packet_log);	/* UDP */
588	fwrite(packet_buf, datalen, 1, packet_log);	/* IKE-data */
589	fflush(packet_log);
590}
591
592/* Copied from tcpdump/print-udp.c, mostly rewritten.  */
593static int
594udp_cksum(struct packhdr *hdr, const struct udphdr *u, u_int16_t *d)
595{
596	struct ip	*ip4;
597	struct ip6_hdr	*ip6;
598	int	i, hdrlen, tlen = ntohs(u->uh_ulen) - sizeof(struct udphdr);
599
600	union phu {
601		struct ip4pseudo {
602			struct in_addr  src;
603			struct in_addr  dst;
604			u_int8_t        z;
605			u_int8_t        proto;
606			u_int16_t       len;
607		} ip4p;
608		struct ip6pseudo {
609			struct in6_addr src;
610			struct in6_addr dst;
611			u_int32_t       plen;
612			u_int16_t       z0;
613			u_int8_t        z1;
614			u_int8_t        nxt;
615		} ip6p;
616		u_int16_t       pa[20];
617	} phu;
618	const u_int16_t *sp;
619	u_int32_t       sum;
620
621	/* Setup pseudoheader.  */
622	memset(phu.pa, 0, sizeof phu);
623	switch (ntohl(hdr->sa_family)) {
624	case AF_INET:
625		ip4 = &hdr->ip.ip4;
626		memcpy(&phu.ip4p.src, &ip4->ip_src, sizeof(struct in_addr));
627		memcpy(&phu.ip4p.dst, &ip4->ip_dst, sizeof(struct in_addr));
628		phu.ip4p.proto = ip4->ip_p;
629		phu.ip4p.len = u->uh_ulen;
630		hdrlen = sizeof phu.ip4p;
631		break;
632
633	case AF_INET6:
634		ip6 = &hdr->ip.ip6;
635		memcpy(&phu.ip6p.src, &ip6->ip6_src, sizeof(phu.ip6p.src));
636		memcpy(&phu.ip6p.dst, &ip6->ip6_dst, sizeof(phu.ip6p.dst));
637		phu.ip6p.plen = u->uh_ulen;
638		phu.ip6p.nxt = ip6->ip6_nxt;
639		hdrlen = sizeof phu.ip6p;
640		break;
641
642	default:
643		return 0;
644	}
645
646	/* IPv6 wants a 0xFFFF checksum "on error", not 0x0.  */
647	if (tlen < 0)
648		return (ntohl(hdr->sa_family) == AF_INET ? 0 : 0xFFFF);
649
650	sum = 0;
651	for (i = 0; i < hdrlen; i += 2)
652		sum += phu.pa[i / 2];
653
654	sp = (u_int16_t *) u;
655	for (i = 0; i < (int)sizeof(struct udphdr); i += 2)
656		sum += *sp++;
657
658	sp = d;
659	for (i = 0; i < (tlen & ~1); i += 2)
660		sum += *sp++;
661
662	if (tlen & 1)
663		sum += htons((*(const char *)sp) << 8);
664
665	while (sum > 0xffff)
666		sum = (sum & 0xffff) + (sum >> 16);
667	sum = ~sum & 0xffff;
668
669	return sum;
670}
671
672/* Copied from tcpdump/print-ip.c, modified.  */
673static u_int16_t
674in_cksum(const u_int16_t *w, int len)
675{
676	int		nleft = len, sum = 0;
677	u_int16_t       answer;
678
679	while (nleft > 1) {
680		sum += *w++;
681		nleft -= 2;
682	}
683	if (nleft == 1)
684		sum += htons(*(u_char *) w << 8);
685
686	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
687	sum += (sum >> 16);	/* add carry */
688	answer = ~sum;		/* truncate to 16 bits */
689	return answer;
690}
691
692#endif				/* USE_DEBUG */
693