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