/* assuan-logging.c - Default logging function. * Copyright (C) 2002, 2003, 2004, 2007, 2009, * 2010 Free Software Foundation, Inc. * * This file is part of Assuan. * * Assuan is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * Assuan 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #ifdef HAVE_W32_SYSTEM # ifdef HAVE_WINSOCK2_H # include # endif # include #endif /*HAVE_W32_SYSTEM*/ #include #include #include "assuan-defs.h" /* The default log handler is useful for global logging, but it should only be used by one user of libassuan at a time. Libraries that use libassuan can register their own log handler. */ /* A common prefix for all log messages. */ static char prefix_buffer[80]; /* A global flag read from the environment to check if to enable full logging of buffer data. This is also used by custom log handlers. */ static int full_logging; /* A bitfield that specifies the categories to log. */ static int log_cats; #define TEST_LOG_CAT(x) (!! (log_cats & (1 << (x - 1)))) static FILE *_assuan_log; void _assuan_init_log_envvars (void) { char *flagstr; full_logging = !!getenv ("ASSUAN_FULL_LOGGING"); flagstr = getenv ("ASSUAN_DEBUG"); if (flagstr) log_cats = atoi (flagstr); else /* Default to log the control channel. */ log_cats = (1 << (ASSUAN_LOG_CONTROL - 1)); _assuan_sysutils_blurb (); /* Make sure this code gets linked in. */ } void assuan_set_assuan_log_stream (FILE *fp) { _assuan_log = fp; _assuan_init_log_envvars (); } /* Set the per context log stream. Also enable the default log stream if it has not been set. */ void assuan_set_log_stream (assuan_context_t ctx, FILE *fp) { if (ctx) { if (ctx->log_fp) fflush (ctx->log_fp); ctx->log_fp = fp; if (! _assuan_log) assuan_set_assuan_log_stream (fp); } } /* Set the prefix to be used for logging to TEXT or resets it to the default if TEXT is NULL. */ void assuan_set_assuan_log_prefix (const char *text) { if (text) { strncpy (prefix_buffer, text, sizeof (prefix_buffer)-1); prefix_buffer[sizeof (prefix_buffer)-1] = 0; } else *prefix_buffer = 0; } /* Get the prefix to be used for logging. */ const char * assuan_get_assuan_log_prefix (void) { return prefix_buffer; } /* Default log handler. */ int _assuan_log_handler (assuan_context_t ctx, void *hook, unsigned int cat, const char *msg) { FILE *fp; const char *prf; int saved_errno = errno; /* For now. */ if (msg == NULL) return TEST_LOG_CAT (cat); if (! TEST_LOG_CAT (cat)) return 0; fp = ctx->log_fp ? ctx->log_fp : _assuan_log; if (!fp) return 0; prf = assuan_get_assuan_log_prefix (); if (*prf) fprintf (fp, "%s[%u]: ", prf, (unsigned int)getpid ()); fprintf (fp, "%s", msg); /* If the log stream is a file, the output would be buffered. This is bad for debugging, thus we flush the stream if FORMAT ends with a LF. */ if (msg && *msg && msg[strlen (msg) - 1] == '\n') fflush (fp); gpg_err_set_errno (saved_errno); return 0; } /* Log a control channel message. This is either a STRING with a diagnostic or actual data in (BUFFER1,LENGTH1) and (BUFFER2,LENGTH2). If OUTBOUND is true the data is intended for the peer. */ void _assuan_log_control_channel (assuan_context_t ctx, int outbound, const char *string, const void *buffer1, size_t length1, const void *buffer2, size_t length2) { int res; char *outbuf; int saved_errno; /* Check whether logging is enabled and do a quick check to see whether the callback supports our category. */ if (!ctx || !ctx->log_cb || ctx->flags.no_logging || !(*ctx->log_cb) (ctx, ctx->log_cb_data, ASSUAN_LOG_CONTROL, NULL)) return; saved_errno = errno; /* Note that we use the inbound channel fd as the printed channel number for both directions. */ #ifdef HAVE_W32_SYSTEM # define CHANNEL_FMT "%p" #else # define CHANNEL_FMT "%d" #endif #define TOHEX(val) (((val) < 10) ? ((val) + '0') : ((val) - 10 + 'a')) if (!buffer1 && buffer2) { buffer1 = buffer2; length1 = length2; buffer2 = NULL; length2 = 0; } if (ctx->flags.confidential && !string && buffer1) string = "[Confidential data not shown]"; if (string) { /* Print the diagnostic. */ res = gpgrt_asprintf (&outbuf, "chan_" CHANNEL_FMT " %s [%s]\n", ctx->inbound.fd, outbound? "->":"<-", string); } else if (buffer1) { /* Print the control channel data. */ const unsigned char *s; unsigned int n, x; for (n = length1, s = buffer1; n; n--, s++) if ((!isascii (*s) || iscntrl (*s) || !isprint (*s) || !*s) && !(*s >= 0x80)) break; if (!n && buffer2) { for (n = length2, s = buffer2; n; n--, s++) if ((!isascii (*s) || iscntrl (*s) || !isprint (*s) || !*s) && !(*s >= 0x80)) break; } if (!buffer2) length2 = 0; if (!n && (length1 && *(const char*)buffer1 != '[')) { /* No control characters and not starting with our error message indicator. Log it verbatim. */ res = gpgrt_asprintf (&outbuf, "chan_" CHANNEL_FMT " %s %.*s%.*s\n", ctx->inbound.fd, outbound? "->":"<-", (int)length1, (const char*)buffer1, (int)length2, buffer2? (const char*)buffer2:""); } else { /* The buffer contains control characters - do a hex dump. Even in full logging mode we limit the line length - however this is no real limit because the provided buffers will never be larger than the maximum assuan line length. */ char *hp; unsigned int nbytes; unsigned int maxbytes = full_logging? (2*LINELENGTH) : 16; nbytes = length1 + length2; if (nbytes > maxbytes) nbytes = maxbytes; if (!(outbuf = gpgrt_malloc (50 + 3*nbytes + 60 + 3 + 1))) res = -1; else { res = 0; hp = outbuf; snprintf (hp, 50, "chan_" CHANNEL_FMT " %s [", ctx->inbound.fd, outbound? "->":"<-"); hp += strlen (hp); n = 0; for (s = buffer1, x = 0; x < length1 && n < nbytes; x++, n++) { *hp++ = ' '; *hp++ = TOHEX (*s >> 4); *hp++ = TOHEX (*s & 0x0f); s++; } for (s = buffer2, x = 0; x < length2 && n < nbytes; x++, n++) { *hp++ = ' '; *hp++ = TOHEX (*s >> 4); *hp++ = TOHEX (*s & 0x0f); s++; } if (nbytes < length1 + length2) { snprintf (hp, 60, " ...(%u byte(s) skipped)", (unsigned int)((length1+length2) - nbytes)); hp += strlen (hp); } strcpy (hp, " ]\n"); } } } else { res = 0; outbuf = NULL; } if (res < 0) ctx->log_cb (ctx, ctx->log_cb_data, ASSUAN_LOG_CONTROL, "[libassuan failed to format the log message]"); else if (outbuf) { ctx->log_cb (ctx, ctx->log_cb_data, ASSUAN_LOG_CONTROL, outbuf); gpgrt_free (outbuf); } #undef TOHEX #undef CHANNEL_FMT gpg_err_set_errno (saved_errno); }