1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * lib/kadm/logger.c 8 * 9 * Copyright 1995 by the Massachusetts Institute of Technology. 10 * All Rights Reserved. 11 * 12 * Export of this software from the United States of America may 13 * require a specific license from the United States Government. 14 * It is the responsibility of any person or organization contemplating 15 * export to obtain such a license before exporting. 16 * 17 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 18 * distribute this software and its documentation for any purpose and 19 * without fee is hereby granted, provided that the above copyright 20 * notice appear in all copies and that both that copyright notice and 21 * this permission notice appear in supporting documentation, and that 22 * the name of M.I.T. not be used in advertising or publicity pertaining 23 * to distribution of the software without specific, written prior 24 * permission. Furthermore if you modify this software you must label 25 * your software as modified software and not distribute it in such a 26 * fashion that it might be confused with the original M.I.T. software. 27 * M.I.T. makes no representations about the suitability of 28 * this software for any purpose. It is provided "as is" without express 29 * or implied warranty. 30 * 31 */ 32 33 /* KADM5 wants non-syslog log files to contain syslog-like entries */ 34 #define VERBOSE_LOGS 35 36 /* 37 * logger.c - Handle logging functions for those who want it. 38 */ 39 #include "k5-int.h" 40 #include "adm_proto.h" 41 #include "com_err.h" 42 #include <stdio.h> 43 #include <ctype.h> 44 #include <ctype.h> 45 #ifdef HAVE_SYSLOG_H 46 #include <syslog.h> 47 #endif /* HAVE_SYSLOG_H */ 48 #ifdef HAVE_STDARG_H 49 #include <stdarg.h> 50 #else /* HAVE_STDARG_H */ 51 #include <varargs.h> 52 #endif /* HAVE_STDARG_H */ 53 #include <libintl.h> 54 #include <sys/types.h> 55 #include <sys/stat.h> 56 57 #define KRB5_KLOG_MAX_ERRMSG_SIZE 2048 58 #ifndef MAXHOSTNAMELEN 59 #define MAXHOSTNAMELEN 256 60 #endif /* MAXHOSTNAMELEN */ 61 62 #define LSPEC_PARSE_ERR_1 1 63 #define LSPEC_PARSE_ERR_2 2 64 #define LOG_FILE_ERR 3 65 #define LOG_DEVICE_ERR 4 66 #define LOG_UFO_STRING 5 67 #define LOG_EMERG_STRING 6 68 #define LOG_ALERT_STRING 7 69 #define LOG_CRIT_STRING 8 70 #define LOG_ERR_STRING 9 71 #define LOG_WARNING_STRING 10 72 #define LOG_NOTICE_STRING 11 73 #define LOG_INFO_STRING 12 74 #define LOG_DEBUG_STRING 13 75 /* This is to assure that we have at least one match in the syslog stuff */ 76 /* 77 static const char LSPEC_PARSE_ERR_1[] = "%s: cannot parse <%s>\n"; 78 static const char LSPEC_PARSE_ERR_2[] = "%s: warning - logging entry syntax error\n"; 79 static const char LOG_FILE_ERR[] = "%s: error writing to %s\n"; 80 static const char LOG_DEVICE_ERR[] = "%s: error writing to %s device\n"; 81 static const char LOG_UFO_STRING[] = "???"; 82 static const char LOG_EMERG_STRING[] = "EMERGENCY"; 83 static const char LOG_ALERT_STRING[] = "ALERT"; 84 static const char LOG_CRIT_STRING[] = "CRITICAL"; 85 static const char LOG_ERR_STRING[] = "Error"; 86 static const char LOG_WARNING_STRING[] = "Warning"; 87 static const char LOG_NOTICE_STRING[] = "Notice"; 88 static const char LOG_INFO_STRING[] = "info"; 89 static const char LOG_DEBUG_STRING[] = "debug"; 90 */ 91 92 93 const char * 94 krb5_log_error_table(long errorno) { 95 switch (errorno) { 96 case LSPEC_PARSE_ERR_1: 97 return(gettext("%s: cannot parse <%s>\n")); 98 case LSPEC_PARSE_ERR_2: 99 return(gettext("%s: warning - logging entry syntax error\n")); 100 case LOG_FILE_ERR: 101 return(gettext("%s: error writing to %s\n")); 102 case LOG_DEVICE_ERR: 103 return(gettext("%s: error writing to %s device\n")); 104 case LOG_UFO_STRING: 105 return(gettext("???")); 106 case LOG_EMERG_STRING: 107 return(gettext("EMERGENCY")); 108 case LOG_ALERT_STRING: 109 return(gettext("ALERT")); 110 case LOG_CRIT_STRING: 111 return(gettext("CRITICAL")); 112 case LOG_ERR_STRING: 113 return(gettext("Error")); 114 case LOG_WARNING_STRING: 115 return(gettext("Warning")); 116 case LOG_NOTICE_STRING: 117 return(gettext("Notice")); 118 case LOG_INFO_STRING: 119 case LOG_DEBUG_STRING: 120 default: 121 return(gettext("info")); 122 } 123 } 124 125 /* 126 * Output logging. 127 * 128 * Output logging is now controlled by the configuration file. We can specify 129 * the following syntaxes under the [logging]->entity specification. 130 * FILE<opentype><pathname> 131 * SYSLOG[=<severity>[:<facility>]] 132 * STDERR 133 * CONSOLE 134 * DEVICE=<device-spec> 135 * 136 * Where: 137 * <opentype> is ":" for open/append, "=" for open/create. 138 * <pathname> is a valid path name. 139 * <severity> is one of: (default = ERR) 140 * EMERG 141 * ALERT 142 * CRIT 143 * ERR 144 * WARNING 145 * NOTICE 146 * INFO 147 * DEBUG 148 * <facility> is one of: (default = AUTH) 149 * KERN 150 * USER 151 * MAIL 152 * DAEMON 153 * AUTH 154 * LPR 155 * NEWS 156 * UUCP 157 * AUDIT 158 * CRON 159 * LOCAL0..LOCAL7 160 * <device-spec> is a valid device specification. 161 */ 162 struct log_entry { 163 enum log_type { K_LOG_FILE, 164 K_LOG_SYSLOG, 165 K_LOG_STDERR, 166 K_LOG_CONSOLE, 167 K_LOG_DEVICE, 168 K_LOG_NONE } log_type; 169 krb5_pointer log_2free; 170 union log_union { 171 struct log_file { 172 FILE *lf_filep; 173 char *lf_fname; 174 char *lf_fopen_mode; /* "a+" or "w" */ 175 #define K_LOG_DEF_FILE_ROTATE_PERIOD -1 /* never */ 176 #define K_LOG_DEF_FILE_ROTATE_VERSIONS 0 /* no versions */ 177 time_t lf_rotate_period; 178 time_t lf_last_rotated; 179 int lf_rotate_versions; 180 } log_file; 181 struct log_syslog { 182 int ls_facility; 183 int ls_severity; 184 } log_syslog; 185 struct log_device { 186 FILE *ld_filep; 187 char *ld_devname; 188 } log_device; 189 } log_union; 190 }; 191 #define lfu_filep log_union.log_file.lf_filep 192 #define lfu_fname log_union.log_file.lf_fname 193 #define lfu_fopen_mode log_union.log_file.lf_fopen_mode 194 #define lfu_rotate_period log_union.log_file.lf_rotate_period 195 #define lfu_last_rotated log_union.log_file.lf_last_rotated 196 #define lfu_rotate_versions log_union.log_file.lf_rotate_versions 197 #define lsu_facility log_union.log_syslog.ls_facility 198 #define lsu_severity log_union.log_syslog.ls_severity 199 #define ldu_filep log_union.log_device.ld_filep 200 #define ldu_devname log_union.log_device.ld_devname 201 202 struct log_control { 203 struct log_entry *log_entries; 204 int log_nentries; 205 char *log_whoami; 206 char *log_hostname; 207 krb5_boolean log_opened; 208 }; 209 210 static struct log_control log_control = { 211 (struct log_entry *) NULL, 212 0, 213 (char *) NULL, 214 (char *) NULL, 215 0 216 }; 217 static struct log_entry def_log_entry; 218 219 /* 220 * These macros define any special processing that needs to happen for 221 * devices. For unix, of course, this is hardly anything. 222 */ 223 #define DEVICE_OPEN(d, m) fopen(d, m) 224 #define CONSOLE_OPEN(m) fopen("/dev/console", m) 225 #define DEVICE_PRINT(f, m) ((fprintf(f, "%s\r\n", m) >= 0) ? \ 226 (fflush(f), 0) : \ 227 -1) 228 #define DEVICE_CLOSE(d) fclose(d) 229 230 231 /* 232 * klog_rotate() - roate a log file if we have specified rotation 233 * parameters in krb5.conf. 234 */ 235 static void 236 klog_rotate(struct log_entry *le) 237 { 238 time_t t; 239 int i; 240 char *name_buf1; 241 char *name_buf2; 242 char *old_name; 243 char *new_name; 244 char *tmp; 245 FILE *fp; 246 int num_vers; 247 mode_t old_umask; 248 249 250 /* 251 * By default we don't rotate. 252 */ 253 if (le->lfu_rotate_period == K_LOG_DEF_FILE_ROTATE_PERIOD) 254 return; 255 256 t = time(0); 257 258 if (t >= le->lfu_last_rotated + le->lfu_rotate_period) { 259 /* 260 * The N log file versions will be renamed X.N-1 X.N-2, ... X.0. 261 * So the allocate file name buffers that can the version 262 * number extensions. 263 * 32 extra bytes is plenty. 264 */ 265 name_buf1 = malloc(strlen(le->lfu_fname) + 32); 266 267 if (name_buf1 == NULL) 268 return; 269 270 name_buf2 = malloc(strlen(le->lfu_fname) + 32); 271 272 if (name_buf2 == NULL) { 273 free(name_buf1); 274 return; 275 } 276 277 old_name = name_buf1; 278 new_name = name_buf2; 279 280 /* 281 * If there N versions, then the first one has file extension 282 * of N-1. 283 */ 284 (void) sprintf(new_name, "%s.%d", le->lfu_fname, 285 le->lfu_rotate_versions - 1); 286 287 /* 288 * Rename file.N-2 to file.N-1, file.N-3 to file.N-2, ... 289 * file.0 to file.1 290 */ 291 for (i = le->lfu_rotate_versions - 1; i > 0; i--) { 292 (void) sprintf(old_name, "%s.%d", le->lfu_fname, i - 1); 293 (void) rename(old_name, new_name); 294 295 /* 296 * swap old name and new name. This way, 297 * on the next iteration, new_name.X 298 * becomes new_name.X-1. 299 */ 300 tmp = old_name; 301 old_name = new_name; 302 new_name = tmp; 303 } 304 old_name = le->lfu_fname; 305 306 (void) rename(old_name, new_name); 307 308 /* 309 * Even though we don't know yet if the fopen() 310 * of the log file will succeed, we mark the log 311 * as rotated. This is so we don't repeatably 312 * rotate file.N-2 to file.N-1 ... etc without 313 * waiting for the rotate period to elapse. 314 */ 315 le->lfu_last_rotated = t; 316 317 /* 318 * Default log file creation mode should be read-only 319 * by owner(root), but the admin can override with 320 * chmod(1) if desired. 321 */ 322 323 old_umask = umask(077); 324 fp = fopen(old_name, le->lfu_fopen_mode); 325 326 umask(old_umask); 327 328 if (fp != NULL) { 329 330 (void) fclose(le->lfu_filep); 331 le->lfu_filep = fp; 332 333 /* 334 * If the version parameter in krb5.conf was 335 * 0, then we take this to mean that rotating the 336 * log file will cause us to dispose of the 337 * old one, and created a new one. We have just 338 * renamed the old one to file.-1, so remove it. 339 */ 340 if (le->lfu_rotate_versions <= 0) 341 (void) unlink(new_name); 342 343 } else { 344 fprintf(stderr, 345 gettext("During rotate, couldn't open log file %s: %s\n"), 346 old_name, error_message(errno)); 347 /* 348 * Put it back. 349 */ 350 (void) rename(new_name, old_name); 351 } 352 free(name_buf1); 353 free(name_buf2); 354 } 355 } 356 357 /* 358 * klog_com_err_proc() - Handle com_err(3) messages as specified by the 359 * profile. 360 */ 361 static krb5_context err_context; 362 static void 363 klog_com_err_proc(const char *whoami, long code, const char *format, va_list ap) 364 { 365 char outbuf[KRB5_KLOG_MAX_ERRMSG_SIZE]; 366 int lindex; 367 const char *actual_format; 368 #ifdef HAVE_SYSLOG 369 int log_pri = -1; 370 #endif /* HAVE_SYSLOG */ 371 char *cp; 372 char *syslogp; 373 374 /* Make the header */ 375 sprintf(outbuf, "%s: ", whoami); 376 /* 377 * Squirrel away address after header for syslog since syslog makes 378 * a header 379 */ 380 syslogp = &outbuf[strlen(outbuf)]; 381 382 /* If reporting an error message, separate it. */ 383 if (code) { 384 const char *emsg; 385 outbuf[sizeof(outbuf) - 1] = '\0'; 386 387 emsg = krb5_get_error_message (err_context, code); 388 strncat(outbuf, emsg, sizeof(outbuf) - 1 - strlen(outbuf)); 389 strncat(outbuf, " - ", sizeof(outbuf) - 1 - strlen(outbuf)); 390 krb5_free_error_message(err_context, emsg); 391 } 392 cp = &outbuf[strlen(outbuf)]; 393 394 actual_format = format; 395 #ifdef HAVE_SYSLOG 396 /* 397 * This is an unpleasant hack. If the first character is less than 398 * 8, then we assume that it is a priority. 399 * 400 * Since it is not guaranteed that there is a direct mapping between 401 * syslog priorities (e.g. Ultrix and old BSD), we resort to this 402 * intermediate representation. 403 */ 404 if ((((unsigned char) *format) > 0) && (((unsigned char) *format) <= 8)) { 405 actual_format = (format + 1); 406 switch ((unsigned char) *format) { 407 #ifdef LOG_EMERG 408 case 1: 409 log_pri = LOG_EMERG; 410 break; 411 #endif /* LOG_EMERG */ 412 #ifdef LOG_ALERT 413 case 2: 414 log_pri = LOG_ALERT; 415 break; 416 #endif /* LOG_ALERT */ 417 #ifdef LOG_CRIT 418 case 3: 419 log_pri = LOG_CRIT; 420 break; 421 #endif /* LOG_CRIT */ 422 default: 423 case 4: 424 log_pri = LOG_ERR; 425 break; 426 #ifdef LOG_WARNING 427 case 5: 428 log_pri = LOG_WARNING; 429 break; 430 #endif /* LOG_WARNING */ 431 #ifdef LOG_NOTICE 432 case 6: 433 log_pri = LOG_NOTICE; 434 break; 435 #endif /* LOG_NOTICE */ 436 #ifdef LOG_INFO 437 case 7: 438 log_pri = LOG_INFO; 439 break; 440 #endif /* LOG_INFO */ 441 #ifdef LOG_DEBUG 442 case 8: 443 log_pri = LOG_DEBUG; 444 break; 445 #endif /* LOG_DEBUG */ 446 } 447 } 448 #endif /* HAVE_SYSLOG */ 449 450 /* Now format the actual message */ 451 #if HAVE_VSNPRINTF 452 vsnprintf(cp, sizeof(outbuf) - (cp - outbuf), actual_format, ap); 453 #elif HAVE_VSPRINTF 454 vsprintf(cp, actual_format, ap); 455 #else /* HAVE_VSPRINTF */ 456 sprintf(cp, actual_format, ((int *) ap)[0], ((int *) ap)[1], 457 ((int *) ap)[2], ((int *) ap)[3], 458 ((int *) ap)[4], ((int *) ap)[5]); 459 #endif /* HAVE_VSPRINTF */ 460 461 /* 462 * Now that we have the message formatted, perform the output to each 463 * logging specification. 464 */ 465 for (lindex = 0; lindex < log_control.log_nentries; lindex++) { 466 switch (log_control.log_entries[lindex].log_type) { 467 case K_LOG_FILE: 468 469 klog_rotate(&log_control.log_entries[lindex]); 470 /*FALLTHRU*/ 471 case K_LOG_STDERR: 472 /* 473 * Files/standard error. 474 */ 475 if (fprintf(log_control.log_entries[lindex].lfu_filep, "%s\n", 476 outbuf) < 0) { 477 /* Attempt to report error */ 478 fprintf(stderr, krb5_log_error_table(LOG_FILE_ERR), whoami, 479 log_control.log_entries[lindex].lfu_fname); 480 } 481 else { 482 fflush(log_control.log_entries[lindex].lfu_filep); 483 } 484 break; 485 case K_LOG_CONSOLE: 486 case K_LOG_DEVICE: 487 /* 488 * Devices (may need special handling) 489 */ 490 if (DEVICE_PRINT(log_control.log_entries[lindex].ldu_filep, 491 outbuf) < 0) { 492 /* Attempt to report error */ 493 fprintf(stderr, krb5_log_error_table(LOG_DEVICE_ERR), whoami, 494 log_control.log_entries[lindex].ldu_devname); 495 } 496 break; 497 #ifdef HAVE_SYSLOG 498 case K_LOG_SYSLOG: 499 /* 500 * System log. 501 */ 502 /* 503 * If we have specified a priority through our hackery, then 504 * use it, otherwise use the default. 505 */ 506 if (log_pri >= 0) 507 log_pri |= log_control.log_entries[lindex].lsu_facility; 508 else 509 log_pri = log_control.log_entries[lindex].lsu_facility | 510 log_control.log_entries[lindex].lsu_severity; 511 512 /* Log the message with our header trimmed off */ 513 syslog(log_pri, syslogp); 514 break; 515 #endif /* HAVE_SYSLOG */ 516 default: 517 break; 518 } 519 } 520 } 521 522 /* 523 * krb5_klog_init() - Initialize logging. 524 * 525 * This routine parses the syntax described above to specify destinations for 526 * com_err(3) or krb5_klog_syslog() messages generated by the caller. 527 * 528 * Parameters: 529 * kcontext - Kerberos context. 530 * ename - Entity name as it is to appear in the profile. 531 * whoami - Entity name as it is to appear in error output. 532 * do_com_err - Take over com_err(3) processing. 533 * 534 * Implicit inputs: 535 * stderr - This is where STDERR output goes. 536 * 537 * Implicit outputs: 538 * log_nentries - Number of log entries, both valid and invalid. 539 * log_control - List of entries (log_nentries long) which contains 540 * data for klog_com_err_proc() to use to determine 541 * where/how to send output. 542 */ 543 krb5_error_code 544 krb5_klog_init(krb5_context kcontext, char *ename, char *whoami, krb5_boolean do_com_err) 545 { 546 const char *logging_profent[3]; 547 const char *logging_defent[3]; 548 char **logging_specs; 549 int i, ngood; 550 char *cp, *cp2; 551 char savec = '\0'; 552 int error; 553 int do_openlog, log_facility; 554 FILE *f; 555 mode_t old_umask; 556 557 /* Initialize */ 558 do_openlog = 0; 559 log_facility = 0; 560 561 err_context = kcontext; 562 563 /* 564 * Look up [logging]-><ename> in the profile. If that doesn't 565 * succeed, then look for [logging]->default. 566 */ 567 logging_profent[0] = "logging"; 568 logging_profent[1] = ename; 569 logging_profent[2] = (char *) NULL; 570 logging_defent[0] = "logging"; 571 logging_defent[1] = "default"; 572 logging_defent[2] = (char *) NULL; 573 logging_specs = (char **) NULL; 574 ngood = 0; 575 log_control.log_nentries = 0; 576 if (!profile_get_values(kcontext->profile, 577 logging_profent, 578 &logging_specs) || 579 !profile_get_values(kcontext->profile, 580 logging_defent, 581 &logging_specs)) { 582 /* 583 * We have a match, so we first count the number of elements 584 */ 585 for (log_control.log_nentries = 0; 586 logging_specs[log_control.log_nentries]; 587 log_control.log_nentries++); 588 589 /* 590 * Now allocate our structure. 591 */ 592 log_control.log_entries = (struct log_entry *) 593 malloc(log_control.log_nentries * sizeof(struct log_entry)); 594 if (log_control.log_entries) { 595 /* 596 * Scan through the list. 597 */ 598 for (i=0; i<log_control.log_nentries; i++) { 599 log_control.log_entries[i].log_type = K_LOG_NONE; 600 log_control.log_entries[i].log_2free = logging_specs[i]; 601 /* 602 * The format is: 603 * <whitespace><data><whitespace> 604 * so, trim off the leading and trailing whitespace here. 605 */ 606 for (cp = logging_specs[i]; isspace((int) *cp); cp++); 607 for (cp2 = &logging_specs[i][strlen(logging_specs[i])-1]; 608 isspace((int) *cp2); cp2--); 609 cp2++; 610 *cp2 = '\0'; 611 /* 612 * Is this a file? 613 */ 614 if (!strncasecmp(cp, "FILE", 4)) { 615 /* 616 * Check for append/overwrite, then open the file. 617 */ 618 if (cp[4] == ':' || cp[4] == '=') { 619 log_control.log_entries[i].lfu_fopen_mode = 620 (cp[4] == ':') ? "a+F" : "wF"; 621 old_umask = umask(077); 622 f = fopen(&cp[5], 623 log_control.log_entries[i].lfu_fopen_mode); 624 umask(old_umask); 625 if (f) { 626 char rotate_kw[128]; 627 628 log_control.log_entries[i].lfu_filep = f; 629 log_control.log_entries[i].log_type = K_LOG_FILE; 630 log_control.log_entries[i].lfu_fname = &cp[5]; 631 log_control.log_entries[i].lfu_rotate_period = 632 K_LOG_DEF_FILE_ROTATE_PERIOD; 633 log_control.log_entries[i].lfu_rotate_versions = 634 K_LOG_DEF_FILE_ROTATE_VERSIONS; 635 log_control.log_entries[i].lfu_last_rotated = 636 time(0); 637 638 /* 639 * Now parse for ename_"rotate" = { 640 * period = XXX 641 * versions = 10 642 * } 643 */ 644 if (strlen(ename) + strlen("_rotate") < 645 sizeof (rotate_kw)) { 646 647 char *time; 648 krb5_deltat dt; 649 int vers; 650 651 strcpy(rotate_kw, ename); 652 strcat(rotate_kw, "_rotate"); 653 654 if (!profile_get_string(kcontext->profile, 655 "logging", rotate_kw, "period", 656 NULL, &time)) { 657 658 if (time != NULL) { 659 if (!krb5_string_to_deltat(time, 660 &dt)) { 661 log_control.log_entries[i].lfu_rotate_period = 662 (time_t) dt; 663 } 664 free(time); 665 } 666 } 667 668 if (!profile_get_integer( 669 kcontext->profile, "logging", 670 rotate_kw, "versions", 671 K_LOG_DEF_FILE_ROTATE_VERSIONS, 672 &vers)) { 673 log_control.log_entries[i].lfu_rotate_versions = vers; 674 } 675 676 } 677 } else { 678 fprintf(stderr, gettext("Couldn't open log file %s: %s\n"), 679 &cp[5], error_message(errno)); 680 continue; 681 } 682 } 683 } 684 #ifdef HAVE_SYSLOG 685 /* 686 * Is this a syslog? 687 */ 688 else if (!strncasecmp(cp, "SYSLOG", 6)) { 689 error = 0; 690 log_control.log_entries[i].lsu_facility = LOG_AUTH; 691 log_control.log_entries[i].lsu_severity = LOG_ERR; 692 /* 693 * Is there a severify specified? 694 */ 695 if (cp[6] == ':') { 696 /* 697 * Find the end of the severity. 698 */ 699 cp2 = strchr(&cp[7], ':'); 700 if (cp2) { 701 savec = *cp2; 702 *cp2 = '\0'; 703 cp2++; 704 } 705 706 /* 707 * Match a severity. 708 */ 709 if (!strcasecmp(&cp[7], "ERR")) { 710 log_control.log_entries[i].lsu_severity = LOG_ERR; 711 } 712 #ifdef LOG_EMERG 713 else if (!strcasecmp(&cp[7], "EMERG")) { 714 log_control.log_entries[i].lsu_severity = 715 LOG_EMERG; 716 } 717 #endif /* LOG_EMERG */ 718 #ifdef LOG_ALERT 719 else if (!strcasecmp(&cp[7], "ALERT")) { 720 log_control.log_entries[i].lsu_severity = 721 LOG_ALERT; 722 } 723 #endif /* LOG_ALERT */ 724 #ifdef LOG_CRIT 725 else if (!strcasecmp(&cp[7], "CRIT")) { 726 log_control.log_entries[i].lsu_severity = LOG_CRIT; 727 } 728 #endif /* LOG_CRIT */ 729 #ifdef LOG_WARNING 730 else if (!strcasecmp(&cp[7], "WARNING")) { 731 log_control.log_entries[i].lsu_severity = 732 LOG_WARNING; 733 } 734 #endif /* LOG_WARNING */ 735 #ifdef LOG_NOTICE 736 else if (!strcasecmp(&cp[7], "NOTICE")) { 737 log_control.log_entries[i].lsu_severity = 738 LOG_NOTICE; 739 } 740 #endif /* LOG_NOTICE */ 741 #ifdef LOG_INFO 742 else if (!strcasecmp(&cp[7], "INFO")) { 743 log_control.log_entries[i].lsu_severity = LOG_INFO; 744 } 745 #endif /* LOG_INFO */ 746 #ifdef LOG_DEBUG 747 else if (!strcasecmp(&cp[7], "DEBUG")) { 748 log_control.log_entries[i].lsu_severity = 749 LOG_DEBUG; 750 } 751 #endif /* LOG_DEBUG */ 752 else 753 error = 1; 754 755 /* 756 * If there is a facility present, then parse that. 757 */ 758 if (cp2) { 759 if (!strcasecmp(cp2, "AUTH")) { 760 log_control.log_entries[i].lsu_facility = LOG_AUTH; 761 } 762 else if (!strcasecmp(cp2, "KERN")) { 763 log_control.log_entries[i].lsu_facility = LOG_KERN; 764 } 765 else if (!strcasecmp(cp2, "USER")) { 766 log_control.log_entries[i].lsu_facility = LOG_USER; 767 } 768 else if (!strcasecmp(cp2, "MAIL")) { 769 log_control.log_entries[i].lsu_facility = LOG_MAIL; 770 } 771 else if (!strcasecmp(cp2, "DAEMON")) { 772 log_control.log_entries[i].lsu_facility = LOG_DAEMON; 773 } 774 else if (!strcasecmp(cp2, "LPR")) { 775 log_control.log_entries[i].lsu_facility = LOG_LPR; 776 } 777 else if (!strcasecmp(cp2, "NEWS")) { 778 log_control.log_entries[i].lsu_facility = LOG_NEWS; 779 } 780 else if (!strcasecmp(cp2, "UUCP")) { 781 log_control.log_entries[i].lsu_facility = LOG_UUCP; 782 } 783 else if (!strcasecmp(cp2, "CRON")) { 784 log_control.log_entries[i].lsu_facility = LOG_CRON; 785 } 786 else if (!strcasecmp(cp2, "AUDIT")) { 787 log_control.log_entries[i].lsu_facility = LOG_AUDIT; 788 } 789 else if (!strcasecmp(cp2, "LOCAL0")) { 790 log_control.log_entries[i].lsu_facility = LOG_LOCAL0; 791 } 792 else if (!strcasecmp(cp2, "LOCAL1")) { 793 log_control.log_entries[i].lsu_facility = LOG_LOCAL1; 794 } 795 else if (!strcasecmp(cp2, "LOCAL2")) { 796 log_control.log_entries[i].lsu_facility = LOG_LOCAL2; 797 } 798 else if (!strcasecmp(cp2, "LOCAL3")) { 799 log_control.log_entries[i].lsu_facility = LOG_LOCAL3; 800 } 801 else if (!strcasecmp(cp2, "LOCAL4")) { 802 log_control.log_entries[i].lsu_facility = LOG_LOCAL4; 803 } 804 else if (!strcasecmp(cp2, "LOCAL5")) { 805 log_control.log_entries[i].lsu_facility = LOG_LOCAL5; 806 } 807 else if (!strcasecmp(cp2, "LOCAL6")) { 808 log_control.log_entries[i].lsu_facility = LOG_LOCAL6; 809 } 810 else if (!strcasecmp(cp2, "LOCAL7")) { 811 log_control.log_entries[i].lsu_facility = LOG_LOCAL7; 812 } 813 cp2--; 814 *cp2 = savec; 815 } 816 } 817 if (!error) { 818 log_control.log_entries[i].log_type = K_LOG_SYSLOG; 819 do_openlog = 1; 820 log_facility = log_control.log_entries[i].lsu_facility; 821 } 822 } 823 #endif /* HAVE_SYSLOG */ 824 /* 825 * Is this a standard error specification? 826 */ 827 else if (!strcasecmp(cp, "STDERR")) { 828 log_control.log_entries[i].lfu_filep = 829 fdopen(fileno(stderr), "a+F"); 830 if (log_control.log_entries[i].lfu_filep) { 831 log_control.log_entries[i].log_type = K_LOG_STDERR; 832 log_control.log_entries[i].lfu_fname = 833 "standard error"; 834 } 835 } 836 /* 837 * Is this a specification of the console? 838 */ 839 else if (!strcasecmp(cp, "CONSOLE")) { 840 log_control.log_entries[i].ldu_filep = 841 CONSOLE_OPEN("a+F"); 842 if (log_control.log_entries[i].ldu_filep) { 843 log_control.log_entries[i].log_type = K_LOG_CONSOLE; 844 log_control.log_entries[i].ldu_devname = "console"; 845 } 846 } 847 /* 848 * Is this a specification of a device? 849 */ 850 else if (!strncasecmp(cp, "DEVICE", 6)) { 851 /* 852 * We handle devices very similarly to files. 853 */ 854 if (cp[6] == '=') { 855 log_control.log_entries[i].ldu_filep = 856 DEVICE_OPEN(&cp[7], "wF"); 857 if (log_control.log_entries[i].ldu_filep) { 858 log_control.log_entries[i].log_type = K_LOG_DEVICE; 859 log_control.log_entries[i].ldu_devname = &cp[7]; 860 } 861 } 862 } 863 /* 864 * See if we successfully parsed this specification. 865 */ 866 if (log_control.log_entries[i].log_type == K_LOG_NONE) { 867 fprintf(stderr, krb5_log_error_table(LSPEC_PARSE_ERR_1), whoami, cp); 868 fprintf(stderr, krb5_log_error_table(LSPEC_PARSE_ERR_2), whoami); 869 } 870 else 871 ngood++; 872 } 873 } 874 /* 875 * If we didn't find anything, then free our lists. 876 */ 877 if (ngood == 0) { 878 for (i=0; i<log_control.log_nentries; i++) 879 free(logging_specs[i]); 880 } 881 free(logging_specs); 882 } 883 /* 884 * If we didn't find anything, go for the default which is to log to 885 * the system log. 886 */ 887 if (ngood == 0) { 888 if (log_control.log_entries) 889 free(log_control.log_entries); 890 log_control.log_entries = &def_log_entry; 891 log_control.log_entries->log_type = K_LOG_SYSLOG; 892 log_control.log_entries->log_2free = (krb5_pointer) NULL; 893 log_facility = log_control.log_entries->lsu_facility = LOG_AUTH; 894 log_control.log_entries->lsu_severity = LOG_ERR; 895 do_openlog = 1; 896 log_control.log_nentries = 1; 897 } 898 if (log_control.log_nentries) { 899 log_control.log_whoami = (char *) malloc(strlen(whoami)+1); 900 if (log_control.log_whoami) 901 strcpy(log_control.log_whoami, whoami); 902 903 log_control.log_hostname = (char *) malloc(MAXHOSTNAMELEN + 1); 904 if (log_control.log_hostname) { 905 gethostname(log_control.log_hostname, MAXHOSTNAMELEN); 906 log_control.log_hostname[MAXHOSTNAMELEN] = '\0'; 907 } 908 #ifdef HAVE_OPENLOG 909 if (do_openlog) { 910 openlog(whoami, LOG_NDELAY|LOG_PID, log_facility); 911 log_control.log_opened = 1; 912 } 913 #endif /* HAVE_OPENLOG */ 914 if (do_com_err) 915 (void) set_com_err_hook(klog_com_err_proc); 916 } 917 return((log_control.log_nentries) ? 0 : ENOENT); 918 } 919 920 /* 921 * krb5_klog_close() - Close the logging context and free all data. 922 */ 923 void 924 krb5_klog_close(krb5_context kcontext) 925 { 926 int lindex; 927 (void) reset_com_err_hook(); 928 for (lindex = 0; lindex < log_control.log_nentries; lindex++) { 929 switch (log_control.log_entries[lindex].log_type) { 930 case K_LOG_FILE: 931 case K_LOG_STDERR: 932 /* 933 * Files/standard error. 934 */ 935 fclose(log_control.log_entries[lindex].lfu_filep); 936 break; 937 case K_LOG_CONSOLE: 938 case K_LOG_DEVICE: 939 /* 940 * Devices (may need special handling) 941 */ 942 DEVICE_CLOSE(log_control.log_entries[lindex].ldu_filep); 943 break; 944 #ifdef HAVE_SYSLOG 945 case K_LOG_SYSLOG: 946 /* 947 * System log. 948 */ 949 break; 950 #endif /* HAVE_SYSLOG */ 951 default: 952 break; 953 } 954 if (log_control.log_entries[lindex].log_2free) 955 free(log_control.log_entries[lindex].log_2free); 956 } 957 if (log_control.log_entries != &def_log_entry) 958 free(log_control.log_entries); 959 log_control.log_entries = (struct log_entry *) NULL; 960 log_control.log_nentries = 0; 961 if (log_control.log_whoami) 962 free(log_control.log_whoami); 963 log_control.log_whoami = (char *) NULL; 964 if (log_control.log_hostname) 965 free(log_control.log_hostname); 966 log_control.log_hostname = (char *) NULL; 967 #ifdef HAVE_CLOSELOG 968 if (log_control.log_opened) 969 closelog(); 970 #endif /* HAVE_CLOSELOG */ 971 } 972 973 /* 974 * severity2string() - Convert a severity to a string. 975 */ 976 static const char * 977 severity2string(int severity) 978 { 979 int s; 980 const char *ss; 981 982 s = severity & LOG_PRIMASK; 983 ss = krb5_log_error_table(LOG_UFO_STRING); 984 switch (s) { 985 #ifdef LOG_EMERG 986 case LOG_EMERG: 987 ss = krb5_log_error_table(LOG_EMERG_STRING); 988 break; 989 #endif /* LOG_EMERG */ 990 #ifdef LOG_ALERT 991 case LOG_ALERT: 992 ss = krb5_log_error_table(LOG_ALERT_STRING); 993 break; 994 #endif /* LOG_ALERT */ 995 #ifdef LOG_CRIT 996 case LOG_CRIT: 997 ss = krb5_log_error_table(LOG_CRIT_STRING); 998 break; 999 #endif /* LOG_CRIT */ 1000 case LOG_ERR: 1001 ss = krb5_log_error_table(LOG_ERR_STRING); 1002 break; 1003 #ifdef LOG_WARNING 1004 case LOG_WARNING: 1005 ss = krb5_log_error_table(LOG_WARNING_STRING); 1006 break; 1007 #endif /* LOG_WARNING */ 1008 #ifdef LOG_NOTICE 1009 case LOG_NOTICE: 1010 ss = krb5_log_error_table(LOG_NOTICE_STRING); 1011 break; 1012 #endif /* LOG_NOTICE */ 1013 #ifdef LOG_INFO 1014 case LOG_INFO: 1015 ss = krb5_log_error_table(LOG_INFO_STRING); 1016 break; 1017 #endif /* LOG_INFO */ 1018 #ifdef LOG_DEBUG 1019 case LOG_DEBUG: 1020 ss = krb5_log_error_table(LOG_DEBUG_STRING); 1021 break; 1022 #endif /* LOG_DEBUG */ 1023 } 1024 return((char *) ss); 1025 } 1026 1027 /* 1028 * krb5_klog_syslog() - Simulate the calling sequence of syslog(3), while 1029 * also performing the logging redirection as specified 1030 * by krb5_klog_init(). 1031 */ 1032 static int 1033 klog_vsyslog(int priority, const char *format, va_list arglist) 1034 { 1035 char outbuf[KRB5_KLOG_MAX_ERRMSG_SIZE]; 1036 int lindex; 1037 char *syslogp; 1038 char *cp; 1039 time_t now; 1040 #ifdef HAVE_STRFTIME 1041 size_t soff; 1042 #endif /* HAVE_STRFTIME */ 1043 1044 /* 1045 * Format a syslog-esque message of the format: 1046 * 1047 * (verbose form) 1048 * <date> <hostname> <id>[<pid>](<priority>): <message> 1049 * 1050 * (short form) 1051 * <date> <message> 1052 */ 1053 cp = outbuf; 1054 (void) time(&now); 1055 #ifdef HAVE_STRFTIME 1056 /* 1057 * Format the date: mon dd hh:mm:ss 1058 */ 1059 soff = strftime(outbuf, sizeof(outbuf), "%b %d %H:%M:%S", localtime(&now)); 1060 if (soff > 0) 1061 cp += soff; 1062 else 1063 return(-1); 1064 #else /* HAVE_STRFTIME */ 1065 /* 1066 * Format the date: 1067 * We ASSUME here that the output of ctime is of the format: 1068 * dow mon dd hh:mm:ss tzs yyyy\n 1069 * 012345678901234567890123456789 1070 */ 1071 strncpy(outbuf, ctime(&now) + 4, 15); 1072 cp += 15; 1073 #endif /* HAVE_STRFTIME */ 1074 #ifdef VERBOSE_LOGS 1075 sprintf(cp, " %s %s[%ld](%s): ", 1076 log_control.log_hostname, log_control.log_whoami, (long) getpid(), 1077 severity2string(priority)); 1078 #else 1079 sprintf(cp, " "); 1080 #endif 1081 syslogp = &outbuf[strlen(outbuf)]; 1082 1083 /* Now format the actual message */ 1084 #ifdef HAVE_VSNPRINTF 1085 vsnprintf(syslogp, sizeof(outbuf) - (syslogp - outbuf), format, arglist); 1086 #elif HAVE_VSPRINTF 1087 vsprintf(syslogp, format, arglist); 1088 #else /* HAVE_VSPRINTF */ 1089 sprintf(syslogp, format, ((int *) arglist)[0], ((int *) arglist)[1], 1090 ((int *) arglist)[2], ((int *) arglist)[3], 1091 ((int *) arglist)[4], ((int *) arglist)[5]); 1092 #endif /* HAVE_VSPRINTF */ 1093 1094 /* 1095 * If the user did not use krb5_klog_init() instead of dropping 1096 * the request on the floor, syslog it - if it exists 1097 */ 1098 #ifdef HAVE_SYSLOG 1099 if (log_control.log_nentries == 0) { 1100 /* Log the message with our header trimmed off */ 1101 syslog(priority, "%s", syslogp); 1102 } 1103 #endif 1104 1105 /* 1106 * Now that we have the message formatted, perform the output to each 1107 * logging specification. 1108 */ 1109 for (lindex = 0; lindex < log_control.log_nentries; lindex++) { 1110 switch (log_control.log_entries[lindex].log_type) { 1111 case K_LOG_FILE: 1112 1113 klog_rotate(&log_control.log_entries[lindex]); 1114 /*FALLTHRU*/ 1115 case K_LOG_STDERR: 1116 /* 1117 * Files/standard error. 1118 */ 1119 if (fprintf(log_control.log_entries[lindex].lfu_filep, "%s\n", 1120 outbuf) < 0) { 1121 /* Attempt to report error */ 1122 fprintf(stderr, krb5_log_error_table(LOG_FILE_ERR), 1123 log_control.log_whoami, 1124 log_control.log_entries[lindex].lfu_fname); 1125 } 1126 else { 1127 fflush(log_control.log_entries[lindex].lfu_filep); 1128 } 1129 break; 1130 case K_LOG_CONSOLE: 1131 case K_LOG_DEVICE: 1132 /* 1133 * Devices (may need special handling) 1134 */ 1135 if (DEVICE_PRINT(log_control.log_entries[lindex].ldu_filep, 1136 outbuf) < 0) { 1137 /* Attempt to report error */ 1138 fprintf(stderr, krb5_log_error_table(LOG_DEVICE_ERR), 1139 log_control.log_whoami, 1140 log_control.log_entries[lindex].ldu_devname); 1141 } 1142 break; 1143 #ifdef HAVE_SYSLOG 1144 case K_LOG_SYSLOG: 1145 /* 1146 * System log. 1147 */ 1148 1149 /* Log the message with our header trimmed off */ 1150 syslog(priority, "%s", syslogp); 1151 break; 1152 #endif /* HAVE_SYSLOG */ 1153 default: 1154 break; 1155 } 1156 } 1157 return(0); 1158 } 1159 1160 int 1161 krb5_klog_syslog(int priority, const char *format, ...) 1162 { 1163 int retval; 1164 va_list pvar; 1165 1166 va_start(pvar, format); 1167 retval = klog_vsyslog(priority, format, pvar); 1168 va_end(pvar); 1169 return(retval); 1170 } 1171