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