/* error logging functions * Copyright (C) 1997 Angelos D. Keromytis. * Copyright (C) 1998, 1999 D. Hugh Redelmeier. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * RCSID $Id: log.c,v 1.33 2000/06/21 18:24:34 dhr Exp $ */ #include #include #include #include #include #include #include #include #include #include "constants.h" #include "defs.h" #include "log.h" #include "state.h" #include "id.h" #include "connections.h" /* needs id.h */ #include "whack.h" /* needs connections.h */ bool log_to_stderr = TRUE, /* should log go to stderr? */ log_to_syslog = TRUE; /* should log go to syslog? */ /* Context for logging. * * Global variables: must be carefully adjusted at transaction boundaries! * If the context provides a whack file descriptor, messages * should be copied to it -- see whack_log() */ int whack_log_fd = NULL_FD; /* only set during whack_handle() */ struct state *cur_state = NULL; /* current state, for diagnostics */ struct connection *cur_connection = NULL; /* current connection, for diagnostics */ const struct sockaddr_in *cur_from = NULL; /* source of current current message */ void init_log(void) { if (log_to_stderr) setbuf(stderr, NULL); if (log_to_syslog) openlog("Pluto", LOG_CONS | LOG_NDELAY | LOG_PID, LOG_AUTHPRIV); } void close_log(void) { if (log_to_syslog) closelog(); } /* Sanitize character string in situ: turns dangerous characters into \OOO. * With a bit of work, we could use simpler reps for \\, \r, etc., * but this is only to protect against something that shouldn't be used. * Truncate resulting string to what fits in buffer. */ static size_t sanitize(char *buf, size_t size) { # define UGLY_WIDTH 4 /* width for ugly character: \OOO */ size_t len; size_t added = 0; char *p; passert(size >= UGLY_WIDTH); /* need room to swing cat */ /* find right side of string to be sanitized and count * number of columns to be added. Stop on end of string * or lack of room for more result. */ for (p = buf; *p != '\0' && &p[added] < &buf[size - UGLY_WIDTH]; ) { unsigned char c = *p++; if (c == '\\' || !isprint(c)) added += UGLY_WIDTH - 1; } /* at this point, p points after last original character to be * included. added is how many characters are added to sanitize. * so p[added] will point after last sanitized character. */ p[added] = '\0'; len = &p[added] - buf; /* scan backwards, copying characters to their new home * and inserting the expansions for ugly characters. * It is finished when no more shifting is required. * This is a predecrement loop. */ while (added != 0) { char fmtd[UGLY_WIDTH + 1]; unsigned char c; while ((c = *--p) != '\\' && isprint(c)) p[added] = c; added -= UGLY_WIDTH - 1; snprintf(fmtd, sizeof(fmtd), "\\%03o", c); memcpy(p + added, fmtd, UGLY_WIDTH); } return len; # undef UGLY_WIDTH } /* format a string for the log, with suitable prefixes. * A format starting with ~ indicates that this is a reprocessing * of the message, so prefixing and quoting is suppressed. */ static void fmt_log(char *buf, size_t buf_len, const char *fmt, va_list ap) { bool reproc = *fmt == '~'; size_t ps; buf[0] = '\0'; if (reproc) fmt++; /* ~ at start of format suppresses this prefix */ else if (cur_state != NULL) snprintf(buf, buf_len, "\"%s\" #%lu: " , cur_state->st_connection->name, cur_state->st_serialno); else if (cur_connection != NULL) snprintf(buf, buf_len, "\"%s\": ", cur_connection->name); else if (cur_from != NULL) snprintf(buf, buf_len, "packet from %s: ", show_sa_in(cur_from)); ps = strlen(buf); vsnprintf(buf + ps, buf_len - ps, fmt, ap); if (!reproc) (void)sanitize(buf, buf_len); } void log(const char *message, ...) { va_list args; char m[1024]; va_start(args, message); fmt_log(m, sizeof(m), message, args); va_end(args); if (log_to_stderr) fprintf(stderr, "%s\n", m); if (log_to_syslog) syslog(LOG_WARNING, "%s", m); whack_log(RC_LOG, "~%s", m); } void loglog(int mess_no, const char *message, ...) { va_list args; char m[1024]; va_start(args, message); fmt_log(m, sizeof(m), message, args); va_end(args); if (log_to_stderr) fprintf(stderr, "%s\n", m); if (log_to_syslog) syslog(LOG_WARNING, "%s", m); whack_log(mess_no, "~%s", m); } void log_errno_routine(int e, const char *message, ...) { va_list args; char m[1024]; va_start(args, message); fmt_log(m, sizeof(m), message, args); va_end(args); if (log_to_stderr) fprintf(stderr, "ERROR: %s. Errno %d: %s\n", m, e, strerror(e)); if (log_to_syslog) syslog(LOG_ERR, "ERROR: %s. Errno %d: %s", m, e, strerror(e)); whack_log(RC_LOG_SERIOUS , "~ERROR: %s. Errno %d: %s", m, e, strerror(e)); } void exit_log(const char *message, ...) { va_list args; char m[1024]; va_start(args, message); fmt_log(m, sizeof(m), message, args); va_end(args); if (log_to_stderr) fprintf(stderr, "FATAL ERROR: %s\n", m); if (log_to_syslog) syslog(LOG_ERR, "FATAL ERROR: %s", m); whack_log(RC_LOG_SERIOUS, "~FATAL ERROR: %s", m); exit_pluto(1); } void exit_log_errno_routine(int e, const char *message, ...) { va_list args; char m[1024]; va_start(args, message); fmt_log(m, sizeof(m), message, args); va_end(args); if (log_to_stderr) fprintf(stderr, "FATAL ERROR: %s. Errno %d: %s\n", m, e, strerror(e)); if (log_to_syslog) syslog(LOG_ERR, "FATAL ERROR: %s. Errno %d: %s", m, e, strerror(e)); whack_log(RC_LOG_SERIOUS , "~FATAL ERROR: %s. Errno %d: %s", m, e, strerror(e)); exit_pluto(1); } /* emit message to whack. * form is "ddd statename text" where * - ddd is a decimal status code (RC_*) as described in whack.h * - text is a human-readable annotation */ void whack_log(int mess_no, const char *message, ...) { int wfd = whack_log_fd != NULL_FD ? whack_log_fd : cur_state != NULL ? cur_state->st_whack_sock : NULL_FD; if (wfd != NULL_FD) { va_list args; char m[1024]; int prelen = snprintf(m, sizeof(m), "%03d ", mess_no); size_t len; passert(prelen >= 0); va_start(args, message); fmt_log(m+prelen, sizeof(m)-prelen, message, args); va_end(args); len = strlen(m); m[len] = '\n'; /* don't need NUL, do need NL */ { void (*old_sigpipe)(int) = signal(SIGPIPE, SIG_IGN); write(wfd, m, len + 1); signal(SIGPIPE, old_sigpipe); } } } /* * This routine returns a user readable form of an address contained in a * sockaddr structure. The return value CAN be a statically allocated * object (as is the case with inet_ntoa()). */ #include #include #include char * get_address_in(struct sockaddr_in sin) { static char mess[SOCKADDR_STRING_SIZE]; switch (sin.sin_family) { case AF_INET: return inet_ntoa(sin.sin_addr); default: snprintf(mess, sizeof(mess), "(UNEXPECTED address family %d)", sin.sin_family); return mess; } } char * get_address(struct sockaddr sa) { static char mess[SOCKADDR_STRING_SIZE]; struct sockaddr_in sin; switch (sa.sa_family) { case AF_INET: memcpy(&sin, &sa, sizeof(sa)); return get_address_in(sin); default: snprintf(mess, sizeof(mess), "(unknown address family %d)", sa.sa_family); return mess; } } /* * Return a port (if applicable), from a struct sockaddr. If not applicable, * return -1. */ int get_port_in(struct sockaddr_in sin) { switch (sin.sin_family) { case AF_INET: return ntohs(sin.sin_port); default: return -1; } } int get_port(struct sockaddr sa) { struct sockaddr_in sin; switch (sa.sa_family) { case AF_INET: memcpy(&sin, &sa, sizeof(sa)); return get_port_in(sin); default: return -1; } } /* format address and port together in a static buffer */ const char * show_sa(const struct sockaddr *sa) { static char buf[SOCKADDR_STRING_SIZE]; switch (sa->sa_family) { case AF_INET: snprintf(buf, sizeof(buf), "%s, port %d", get_address(*sa), get_port(*sa)); break; default: snprintf(buf, sizeof(buf), "(unexpected address family %d)", sa->sa_family); break; } return buf; } const char * show_sa_in(const struct sockaddr_in *sin) { return show_sa((const struct sockaddr *)sin); } /* Debugging message support */ #ifdef DEBUG unsigned int base_debugging = DBG_NONE, /* default to reporting nothing */ cur_debugging = DBG_NONE; void extra_debugging(const struct connection *c) { if (c->extra_debugging != 0) { log("enabling for connection: %s" , bitnamesof(debug_bit_names, c->extra_debugging & ~cur_debugging)); cur_debugging |= c->extra_debugging; } } /* log a debugging message (prefixed by "| ") */ void DBG_log(const char *message, ...) { va_list args; char m[1024]; va_start(args, message); vsnprintf(m, sizeof(m), message, args); va_end(args); (void)sanitize(m, sizeof(m)); if (log_to_stderr) fprintf(stderr, "| %s\n", m); if (log_to_syslog) syslog(LOG_DEBUG, "| %s", m); } /* dump raw bytes in hex to stderr (for lack of any better destination) */ void DBG_dump(const char *label, const void *p, size_t len) { char buf[100]; char *bp; const unsigned char *cp = p; bp = buf; if (label != NULL && label[0] != '\0') { size_t llen = strlen(label); passert(llen + 1 <= sizeof(buf)); strcpy(buf, label); if (buf[llen-1] == '\n') { buf[llen-1] = '\0'; /* get rid of newline */ DBG_log("%s", buf); } else { passert(llen + 4 * (1 + 4 * 3) + 1 <= sizeof(buf)); bp = buf + llen; } } do { int i, j; for (i = 0; len!=0 && i!=4; i++) { *bp++ = ' '; for (j = 0; len!=0 && j!=4; len--, j++) { static const char hexdig[] = "0123456789abcdef"; *bp++ = ' '; *bp++ = hexdig[(*cp >> 4) & 0xF]; *bp++ = hexdig[*cp & 0xF]; cp++; } } *bp = '\0'; DBG_log("%s", buf); bp = buf; } while (len != 0); } #endif /* DEBUG */