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