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