1/* $NetBSD: log.c,v 1.27 2023/12/20 17:15:20 christos Exp $ */ 2/* $OpenBSD: log.c,v 1.61 2023/12/06 21:06:48 djm Exp $ */ 3 4/* 5 * Author: Tatu Ylonen <ylo@cs.hut.fi> 6 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 7 * All rights reserved 8 * 9 * As far as I am concerned, the code I have written for this software 10 * can be used freely for any purpose. Any derived versions of this 11 * software must be clearly marked as such, and if the derived work is 12 * incompatible with the protocol description in the RFC file, it must be 13 * called by a name other than "ssh" or "Secure Shell". 14 */ 15/* 16 * Copyright (c) 2000 Markus Friedl. All rights reserved. 17 * 18 * Redistribution and use in source and binary forms, with or without 19 * modification, are permitted provided that the following conditions 20 * are met: 21 * 1. Redistributions of source code must retain the above copyright 22 * notice, this list of conditions and the following disclaimer. 23 * 2. Redistributions in binary form must reproduce the above copyright 24 * notice, this list of conditions and the following disclaimer in the 25 * documentation and/or other materials provided with the distribution. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 28 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 29 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 30 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 32 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 34 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 36 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39#include "includes.h" 40__RCSID("$NetBSD: log.c,v 1.27 2023/12/20 17:15:20 christos Exp $"); 41#include <sys/types.h> 42#include <sys/uio.h> 43 44#include <fcntl.h> 45#include <stdarg.h> 46#include <stdio.h> 47#include <stdlib.h> 48#include <string.h> 49#include <syslog.h> 50#include <unistd.h> 51#include <errno.h> 52#include <vis.h> 53 54#include "log.h" 55#include "match.h" 56 57static LogLevel log_level = SYSLOG_LEVEL_INFO; 58static int log_on_stderr = 1; 59static int log_stderr_fd = STDERR_FILENO; 60static int log_facility = LOG_AUTH; 61static const char *argv0; 62static log_handler_fn *log_handler; 63static void *log_handler_ctx; 64static char **log_verbose; 65static size_t nlog_verbose; 66extern char *__progname; 67 68/* textual representation of log-facilities/levels */ 69 70static struct { 71 const char *name; 72 SyslogFacility val; 73} log_facilities[] = { 74 { "DAEMON", SYSLOG_FACILITY_DAEMON }, 75 { "USER", SYSLOG_FACILITY_USER }, 76 { "AUTH", SYSLOG_FACILITY_AUTH }, 77 { "LOCAL0", SYSLOG_FACILITY_LOCAL0 }, 78 { "LOCAL1", SYSLOG_FACILITY_LOCAL1 }, 79 { "LOCAL2", SYSLOG_FACILITY_LOCAL2 }, 80 { "LOCAL3", SYSLOG_FACILITY_LOCAL3 }, 81 { "LOCAL4", SYSLOG_FACILITY_LOCAL4 }, 82 { "LOCAL5", SYSLOG_FACILITY_LOCAL5 }, 83 { "LOCAL6", SYSLOG_FACILITY_LOCAL6 }, 84 { "LOCAL7", SYSLOG_FACILITY_LOCAL7 }, 85 { NULL, SYSLOG_FACILITY_NOT_SET } 86}; 87 88static struct { 89 const char *name; 90 LogLevel val; 91} log_levels[] = 92{ 93 { "QUIET", SYSLOG_LEVEL_QUIET }, 94 { "FATAL", SYSLOG_LEVEL_FATAL }, 95 { "ERROR", SYSLOG_LEVEL_ERROR }, 96 { "INFO", SYSLOG_LEVEL_INFO }, 97 { "VERBOSE", SYSLOG_LEVEL_VERBOSE }, 98 { "DEBUG", SYSLOG_LEVEL_DEBUG1 }, 99 { "DEBUG1", SYSLOG_LEVEL_DEBUG1 }, 100 { "DEBUG2", SYSLOG_LEVEL_DEBUG2 }, 101 { "DEBUG3", SYSLOG_LEVEL_DEBUG3 }, 102 { NULL, SYSLOG_LEVEL_NOT_SET } 103}; 104 105LogLevel 106log_level_get(void) 107{ 108 return log_level; 109} 110 111SyslogFacility 112log_facility_number(char *name) 113{ 114 int i; 115 116 if (name != NULL) 117 for (i = 0; log_facilities[i].name; i++) 118 if (strcasecmp(log_facilities[i].name, name) == 0) 119 return log_facilities[i].val; 120 return SYSLOG_FACILITY_NOT_SET; 121} 122 123const char * 124log_facility_name(SyslogFacility facility) 125{ 126 u_int i; 127 128 for (i = 0; log_facilities[i].name; i++) 129 if (log_facilities[i].val == facility) 130 return log_facilities[i].name; 131 return NULL; 132} 133 134LogLevel 135log_level_number(char *name) 136{ 137 int i; 138 139 if (name != NULL) 140 for (i = 0; log_levels[i].name; i++) 141 if (strcasecmp(log_levels[i].name, name) == 0) 142 return log_levels[i].val; 143 return SYSLOG_LEVEL_NOT_SET; 144} 145 146const char * 147log_level_name(LogLevel level) 148{ 149 u_int i; 150 151 for (i = 0; log_levels[i].name != NULL; i++) 152 if (log_levels[i].val == level) 153 return log_levels[i].name; 154 return NULL; 155} 156 157void 158log_verbose_add(const char *s) 159{ 160 char **tmp; 161 162 /* Ignore failures here */ 163 if ((tmp = recallocarray(log_verbose, nlog_verbose, nlog_verbose + 1, 164 sizeof(*log_verbose))) != NULL) { 165 log_verbose = tmp; 166 if ((log_verbose[nlog_verbose] = strdup(s)) != NULL) 167 nlog_verbose++; 168 } 169} 170 171void 172log_verbose_reset(void) 173{ 174 size_t i; 175 176 for (i = 0; i < nlog_verbose; i++) 177 free(log_verbose[i]); 178 free(log_verbose); 179 log_verbose = NULL; 180 nlog_verbose = 0; 181} 182 183/* 184 * Initialize the log. 185 */ 186 187void 188log_init(const char *av0, LogLevel level, SyslogFacility facility, 189 int on_stderr) 190{ 191 argv0 = av0; 192 193 if (log_change_level(level) != 0) { 194 fprintf(stderr, "Unrecognized internal syslog level code %d\n", 195 (int) level); 196 exit(1); 197 } 198 199 log_handler = NULL; 200 log_handler_ctx = NULL; 201 202 log_on_stderr = on_stderr; 203 if (on_stderr) 204 return; 205 206 switch (facility) { 207 case SYSLOG_FACILITY_DAEMON: 208 log_facility = LOG_DAEMON; 209 break; 210 case SYSLOG_FACILITY_USER: 211 log_facility = LOG_USER; 212 break; 213 case SYSLOG_FACILITY_AUTH: 214 log_facility = LOG_AUTH; 215 break; 216 case SYSLOG_FACILITY_LOCAL0: 217 log_facility = LOG_LOCAL0; 218 break; 219 case SYSLOG_FACILITY_LOCAL1: 220 log_facility = LOG_LOCAL1; 221 break; 222 case SYSLOG_FACILITY_LOCAL2: 223 log_facility = LOG_LOCAL2; 224 break; 225 case SYSLOG_FACILITY_LOCAL3: 226 log_facility = LOG_LOCAL3; 227 break; 228 case SYSLOG_FACILITY_LOCAL4: 229 log_facility = LOG_LOCAL4; 230 break; 231 case SYSLOG_FACILITY_LOCAL5: 232 log_facility = LOG_LOCAL5; 233 break; 234 case SYSLOG_FACILITY_LOCAL6: 235 log_facility = LOG_LOCAL6; 236 break; 237 case SYSLOG_FACILITY_LOCAL7: 238 log_facility = LOG_LOCAL7; 239 break; 240 default: 241 fprintf(stderr, 242 "Unrecognized internal syslog facility code %d\n", 243 (int) facility); 244 exit(1); 245 } 246} 247 248int 249log_change_level(LogLevel new_log_level) 250{ 251 /* no-op if log_init has not been called */ 252 if (argv0 == NULL) 253 return 0; 254 255 switch (new_log_level) { 256 case SYSLOG_LEVEL_QUIET: 257 case SYSLOG_LEVEL_FATAL: 258 case SYSLOG_LEVEL_ERROR: 259 case SYSLOG_LEVEL_INFO: 260 case SYSLOG_LEVEL_VERBOSE: 261 case SYSLOG_LEVEL_DEBUG1: 262 case SYSLOG_LEVEL_DEBUG2: 263 case SYSLOG_LEVEL_DEBUG3: 264 log_level = new_log_level; 265 return 0; 266 default: 267 return -1; 268 } 269} 270 271int 272log_is_on_stderr(void) 273{ 274 return log_on_stderr && log_stderr_fd == STDERR_FILENO; 275} 276 277/* redirect what would usually get written to stderr to specified file */ 278void 279log_redirect_stderr_to(const char *logfile) 280{ 281 int fd; 282 283 if (logfile == NULL) { 284 if (log_stderr_fd != STDERR_FILENO) { 285 close(log_stderr_fd); 286 log_stderr_fd = STDERR_FILENO; 287 } 288 return; 289 } 290 291 if ((fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0600)) == -1) { 292 fprintf(stderr, "Couldn't open logfile %s: %s\n", logfile, 293 strerror(errno)); 294 exit(1); 295 } 296 log_stderr_fd = fd; 297} 298 299#define MSGBUFSIZ 1024 300 301void 302set_log_handler(log_handler_fn *handler, void *ctx) 303{ 304 log_handler = handler; 305 log_handler_ctx = ctx; 306} 307 308static void 309do_log(LogLevel level, int force, const char *suffix, const char *fmt, 310 va_list args) 311{ 312#ifdef SYSLOG_DATA_INIT 313 struct syslog_data sdata = SYSLOG_DATA_INIT; 314#endif 315 char msgbuf[MSGBUFSIZ], *msgbufp; 316 char visbuf[MSGBUFSIZ * 4 + 1]; 317 size_t len, len2; 318 const char *txt = NULL; 319 int pri = LOG_INFO; 320 int saved_errno = errno; 321 log_handler_fn *tmp_handler; 322 const char *progname = argv0 != NULL ? argv0 : __progname; 323 324 if (!force && level > log_level) 325 return; 326 327 switch (level) { 328 case SYSLOG_LEVEL_FATAL: 329 if (!log_on_stderr) 330 txt = "fatal"; 331 pri = LOG_CRIT; 332 break; 333 case SYSLOG_LEVEL_ERROR: 334 if (!log_on_stderr) 335 txt = "error"; 336 pri = LOG_ERR; 337 break; 338 case SYSLOG_LEVEL_INFO: 339 pri = LOG_INFO; 340 break; 341 case SYSLOG_LEVEL_VERBOSE: 342 pri = LOG_INFO; 343 break; 344 case SYSLOG_LEVEL_DEBUG1: 345 txt = "debug1"; 346 pri = LOG_DEBUG; 347 break; 348 case SYSLOG_LEVEL_DEBUG2: 349 txt = "debug2"; 350 pri = LOG_DEBUG; 351 break; 352 case SYSLOG_LEVEL_DEBUG3: 353 txt = "debug3"; 354 pri = LOG_DEBUG; 355 break; 356 default: 357 txt = "internal error"; 358 pri = LOG_ERR; 359 break; 360 } 361 len = sizeof(msgbuf); 362 msgbufp = msgbuf; 363 if (txt != NULL && log_handler == NULL) { 364 len2 = strlen(txt); 365 if (len2 > len - 2) 366 len2 = len - 2; 367 memcpy(msgbufp, txt, len2); 368 msgbufp += len2; 369 *msgbufp++ = ':'; 370 *msgbufp++ = ' '; 371 len -= len2 + 2; 372 } 373 vsnprintf(msgbufp, len, fmt, args); 374 if (suffix != NULL) { 375 snprintf(visbuf, sizeof(visbuf), "%s: %s", msgbuf, suffix); 376 strlcpy(msgbuf, visbuf, sizeof(msgbuf)); 377 } 378 strnvis(visbuf, sizeof(visbuf), msgbuf, VIS_SAFE|VIS_OCTAL); 379 if (log_handler != NULL) { 380 /* Avoid recursion */ 381 tmp_handler = log_handler; 382 log_handler = NULL; 383 tmp_handler(level, force, visbuf, log_handler_ctx); 384 log_handler = tmp_handler; 385 } else if (log_on_stderr) { 386 snprintf(msgbuf, sizeof msgbuf, "%s%s%.*s\r\n", 387 (log_on_stderr > 1) ? progname : "", 388 (log_on_stderr > 1) ? ": " : "", 389 (int)sizeof msgbuf - 10, visbuf); 390 (void)write(log_stderr_fd, msgbuf, strlen(msgbuf)); 391 } else { 392#ifdef SYSLOG_DATA_INIT 393 openlog_r(progname, LOG_PID, log_facility, &sdata); 394 syslog_r(pri, &sdata, "%.500s", visbuf); 395 closelog_r(&sdata); 396#else 397 openlog(progname, LOG_PID, log_facility); 398 syslog(pri, "%.500s", visbuf); 399 closelog(); 400#endif 401 } 402 errno = saved_errno; 403} 404 405void 406sshlog(const char *file, const char *func, int line, int showfunc, 407 LogLevel level, const char *suffix, const char *fmt, ...) 408{ 409 va_list args; 410 411 va_start(args, fmt); 412 sshlogv(file, func, line, showfunc, level, suffix, fmt, args); 413 va_end(args); 414} 415 416void 417sshlogdie(const char *file, const char *func, int line, int showfunc, 418 LogLevel level, const char *suffix, const char *fmt, ...) 419{ 420 va_list args; 421 422 va_start(args, fmt); 423 sshlogv(file, func, line, showfunc, SYSLOG_LEVEL_INFO, 424 suffix, fmt, args); 425 va_end(args); 426 cleanup_exit(254); 427} 428 429void 430sshsigdie(const char *file, const char *func, int line, int showfunc, 431 LogLevel level, const char *suffix, const char *fmt, ...) 432{ 433 va_list args; 434 435 va_start(args, fmt); 436 sshlogv(file, func, line, showfunc, SYSLOG_LEVEL_FATAL, 437 suffix, fmt, args); 438 va_end(args); 439 _exit(1); 440} 441 442void 443sshlogv(const char *file, const char *func, int line, int showfunc, 444 LogLevel level, const char *suffix, const char *fmt, va_list args) 445{ 446 char tag[128], fmt2[MSGBUFSIZ + 128]; 447 int forced = 0; 448 const char *cp; 449 size_t i; 450 451 /* short circuit processing early if we're not going to log anything */ 452 if (nlog_verbose == 0 && level > log_level) 453 return; 454 455 snprintf(tag, sizeof(tag), "%.48s:%.48s():%d (pid=%ld)", 456 (cp = strrchr(file, '/')) == NULL ? file : cp + 1, func, line, 457 (long)getpid()); 458 for (i = 0; i < nlog_verbose; i++) { 459 if (match_pattern_list(tag, log_verbose[i], 0) == 1) { 460 forced = 1; 461 break; 462 } 463 } 464 465 if (forced) 466 snprintf(fmt2, sizeof(fmt2), "%s: %s", tag, fmt); 467 else if (showfunc) 468 snprintf(fmt2, sizeof(fmt2), "%s: %s", func, fmt); 469 else 470 strlcpy(fmt2, fmt, sizeof(fmt2)); 471 472 do_log(level, forced, suffix, fmt2, args); 473} 474 475void 476sshlogdirect(LogLevel level, int forced, const char *fmt, ...) 477{ 478 va_list args; 479 480 va_start(args, fmt); 481 do_log(level, forced, NULL, fmt, args); 482 va_end(args); 483} 484