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