log.c revision 1.17
1/* $OpenBSD: log.c,v 1.17 2001/04/09 21:21:57 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 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/udp.h> 49#include <arpa/inet.h> 50 51#ifdef HAVE_PCAP 52#include <pcap.h> 53#else 54#include "sysdep/common/pcap.h" 55#endif 56 57#endif /* USE_DEBUG */ 58 59#include <errno.h> 60#include <stdio.h> 61#include <stdlib.h> 62#include <string.h> 63#include <syslog.h> 64 65#ifdef __STDC__ 66#include <stdarg.h> 67#else 68#include <varargs.h> 69#endif 70 71#include "isakmp_num.h" 72#include "log.h" 73 74static void _log_print (int, int, const char *, va_list, int, int); 75 76static FILE *log_output; 77 78#ifdef USE_DEBUG 79static int log_level[LOG_ENDCLASS]; 80 81#define TCPDUMP_MAGIC 0xa1b2c3d4 82#define SNAPLEN (64 * 1024) 83 84struct packhdr { 85 struct pcap_pkthdr pcap; /* pcap file packet header */ 86 struct { 87 u_int32_t null_family; /* NULL encapsulation */ 88 } null; 89 struct ip ip; /* IP header (w/o options) */ 90 struct udphdr udp; /* UDP header */ 91}; 92 93struct isakmp_hdr { 94 u_int8_t icookie[8], rcookie[8]; 95 u_int8_t next, ver, type, flags; 96 u_int32_t msgid, len; 97}; 98 99static char *pcaplog_file = NULL; 100static FILE *packet_log; 101static u_int8_t pack[SNAPLEN + sizeof (struct packhdr)]; 102static struct packhdr *hdr; 103 104static int udp_cksum (const struct ip *, const struct udphdr *, int); 105static u_int16_t in_cksum (const struct ip *, 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 < 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 sprintf (nbuf, "%02d%02d%02d.%06ld %s %02d ", tm->tm_hour, 166 tm->tm_min, tm->tm_sec, now.tv_usec, _log_get_class (class), 167 level); 168 else /* LOG_PRINT (-1) or LOG_REPORT (-2) */ 169 sprintf (nbuf, "%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 strcat (nbuf, buffer); 173 strcat (nbuf, "\n"); 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 sprintf (s + 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#endif /* USE_DEBUG */ 295 296void 297#ifdef __STDC__ 298log_print (const char *fmt, ...) 299#else 300log_print (fmt, va_alist) 301 const char *fmt; 302 va_dcl 303#endif 304{ 305 va_list ap; 306 307#ifdef __STDC__ 308 va_start (ap, fmt); 309#else 310 va_start (ap); 311 fmt = va_arg (ap, const char *); 312#endif 313 _log_print (0, LOG_NOTICE, fmt, ap, LOG_PRINT, 0); 314 va_end (ap); 315} 316 317void 318#ifdef __STDC__ 319log_error (const char *fmt, ...) 320#else 321log_error (fmt, va_alist) 322 const char *fmt; 323 va_dcl 324#endif 325{ 326 va_list ap; 327 328#ifdef __STDC__ 329 va_start (ap, fmt); 330#else 331 va_start (ap); 332 fmt = va_arg (ap, const char *); 333#endif 334 _log_print (1, LOG_ERR, fmt, ap, LOG_PRINT, 0); 335 va_end (ap); 336} 337 338void 339#ifdef __STDC__ 340log_fatal (const char *fmt, ...) 341#else 342log_fatal (fmt, va_alist) 343 const char *fmt; 344 va_dcl 345#endif 346{ 347 va_list ap; 348 349#ifdef __STDC__ 350 va_start (ap, fmt); 351#else 352 va_start (ap); 353 fmt = va_arg (ap, const char *); 354#endif 355 _log_print (1, LOG_CRIT, fmt, ap, LOG_PRINT, 0); 356 va_end (ap); 357 exit (1); 358} 359 360#ifdef USE_DEBUG 361void 362log_packet_init (char *newname) 363{ 364 struct pcap_file_header sf_hdr; 365 mode_t old_umask; 366 367 if (pcaplog_file && strcmp (pcaplog_file, PCAP_FILE_DEFAULT) != 0) 368 free (pcaplog_file); 369 370 pcaplog_file = strdup (newname); 371 if (!pcaplog_file) 372 { 373 log_error ("log_packet_init: strdup (\"%s\") failed", newname); 374 return; 375 } 376 377 old_umask = umask (S_IRWXG | S_IRWXO); 378 packet_log = fopen (pcaplog_file, "w"); 379 umask (old_umask); 380 381 if (!packet_log) 382 { 383 log_error ("log_packet_init: fopen (\"%s\", \"w\") failed", 384 pcaplog_file); 385 return; 386 } 387 388 log_print ("log_packet_init: starting IKE packet capture to file \"%s\"", 389 pcaplog_file); 390 391 sf_hdr.magic = TCPDUMP_MAGIC; 392 sf_hdr.version_major = PCAP_VERSION_MAJOR; 393 sf_hdr.version_minor = PCAP_VERSION_MINOR; 394 sf_hdr.thiszone = 0; 395 sf_hdr.snaplen = SNAPLEN; 396 sf_hdr.sigfigs = 0; 397 sf_hdr.linktype = DLT_NULL; 398 399 fwrite ((char *)&sf_hdr, sizeof sf_hdr, 1, packet_log); 400 fflush (packet_log); 401 402 /* prep dummy header prepended to each packet */ 403 hdr = (struct packhdr *)pack; 404 hdr->null.null_family = AF_INET; 405 hdr->ip.ip_v = 0x4; 406 hdr->ip.ip_hl = 0x5; 407 hdr->ip.ip_p = IPPROTO_UDP; 408 hdr->udp.uh_sport = htons (500); 409 hdr->udp.uh_dport = htons (500); 410} 411 412void 413log_packet_restart (char *newname) 414{ 415 struct stat st; 416 417 if (packet_log) 418 { 419 log_print ("log_packet_restart: capture already active on file \"%s\"", 420 pcaplog_file); 421 return; 422 } 423 424 if (newname) 425 { 426 if (stat (newname, &st) == 0) 427 log_print ("log_packet_restart: won't overwrite existing \"%s\"", 428 newname); 429 else 430 log_packet_init (newname); 431 } 432 else if (!pcaplog_file) 433 log_packet_init (PCAP_FILE_DEFAULT); 434 else if (stat (pcaplog_file, &st) != 0) 435 log_packet_init (pcaplog_file); 436 else 437 { 438 /* Re-activate capture on current file. */ 439 packet_log = fopen (pcaplog_file, "a"); 440 if (!packet_log) 441 log_error ("log_packet_restart: fopen (\"%s\", \"a\") failed", 442 pcaplog_file); 443 else 444 log_print ("log_packet_restart: capture restarted on file \"%s\"", 445 pcaplog_file); 446 } 447} 448 449void 450log_packet_stop (void) 451{ 452 /* Stop capture. */ 453 if (packet_log) 454 { 455 fclose (packet_log); 456 log_print ("log_packet_stop: stopped capture"); 457 } 458 packet_log = 0; 459} 460 461void 462log_packet_iov (struct sockaddr *src, struct sockaddr *dst, struct iovec *iov, 463 int iovcnt) 464{ 465 struct isakmp_hdr *isakmphdr; 466 int off, len, i; 467 468 len = 0; 469 for (i = 0; i < iovcnt; i++) 470 len += iov[i].iov_len; 471 472 if (!packet_log || len > SNAPLEN) 473 return; 474 475 /* copy packet into buffer */ 476 off = sizeof *hdr; 477 for (i = 0; i < iovcnt; i++) 478 { 479 memcpy (pack + off, iov[i].iov_base, iov[i].iov_len); 480 off += iov[i].iov_len; 481 } 482 483 /* isakmp - turn off the encryption bit in the isakmp hdr */ 484 isakmphdr = (struct isakmp_hdr *)(pack + sizeof *hdr); 485 isakmphdr->flags &= ~(ISAKMP_FLAGS_ENC); 486 487 /* udp */ 488 len += sizeof hdr->udp; 489 hdr->udp.uh_ulen = htons (len); 490 491 /* ip */ 492 len += sizeof hdr->ip; 493 hdr->ip.ip_len = htons (len); 494 495 switch (src->sa_family) 496 { 497 case AF_INET: 498 hdr->ip.ip_src.s_addr = ((struct sockaddr_in *)src)->sin_addr.s_addr; 499 hdr->ip.ip_dst.s_addr = ((struct sockaddr_in *)dst)->sin_addr.s_addr; 500 break; 501 case AF_INET6: 502 /* XXX TBD */ 503 default: 504 hdr->ip.ip_src.s_addr = 0x02020202; 505 hdr->ip.ip_dst.s_addr = 0x01010101; 506 } 507 508 /* Let's use the IP ID as a "packet counter". */ 509 i = ntohs (hdr->ip.ip_id) + 1; 510 hdr->ip.ip_id = htons (i); 511 512 /* Calculate UDP checksum. */ 513 hdr->udp.uh_sum = 0; 514 hdr->udp.uh_sum = udp_cksum (&hdr->ip, &hdr->udp, len); 515 516 /* Calculate IP header checksum. */ 517 hdr->ip.ip_sum = 0; 518 hdr->ip.ip_sum = in_cksum (&hdr->ip, hdr->ip.ip_hl << 2); 519 520 /* null header */ 521 len += sizeof hdr->null; 522 523 /* pcap file packet header */ 524 gettimeofday (&hdr->pcap.ts, 0); 525 hdr->pcap.caplen = len; 526 hdr->pcap.len = len; 527 len += sizeof hdr->pcap; 528 529 fwrite (pack, len, 1, packet_log); 530 fflush (packet_log); 531 return; 532} 533 534/* Copied from tcpdump/print-udp.c */ 535static int 536udp_cksum (const struct ip *ip, const struct udphdr *up, int len) 537{ 538 int i, tlen; 539 union phu { 540 struct phdr { 541 u_int32_t src; 542 u_int32_t dst; 543 u_char mbz; 544 u_char proto; 545 u_int16_t len; 546 } ph; 547 u_int16_t pa[6]; 548 } phu; 549 const u_int16_t *sp; 550 u_int32_t sum; 551 tlen = ntohs (ip->ip_len) - ((const char *)up-(const char*)ip); 552 553 /* pseudo-header.. */ 554 phu.ph.len = htons (tlen); 555 phu.ph.mbz = 0; 556 phu.ph.proto = ip->ip_p; 557 memcpy (&phu.ph.src, &ip->ip_src.s_addr, sizeof (u_int32_t)); 558 memcpy (&phu.ph.dst, &ip->ip_dst.s_addr, sizeof (u_int32_t)); 559 560 sp = &phu.pa[0]; 561 sum = sp[0] + sp[1] + sp[2] + sp[3] + sp[4] + sp[5]; 562 563 sp = (const u_int16_t *)up; 564 565 for (i = 0; i < (tlen&~1); i += 2) 566 sum += *sp++; 567 568 if (tlen & 1) { 569 sum += htons ((*(const char *)sp) << 8); 570 } 571 572 while (sum > 0xffff) 573 sum = (sum & 0xffff) + (sum >> 16); 574 sum = ~sum & 0xffff; 575 576 return sum; 577} 578 579/* Copied from tcpdump/print-ip.c, modified. */ 580static u_int16_t 581in_cksum (const struct ip *ip, int len) 582{ 583 int nleft = len; 584 const u_short *w = (const u_short *)ip; 585 u_short answer; 586 int sum = 0; 587 588 while (nleft > 1) { 589 sum += *w++; 590 nleft -= 2; 591 } 592 if (nleft == 1) 593 sum += htons (*(u_char *)w<<8); 594 595 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 596 sum += (sum >> 16); /* add carry */ 597 answer = ~sum; /* truncate to 16 bits */ 598 return answer; 599} 600 601 602#endif /* USE_DEBUG */ 603