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