1 /* 2 * Copyright 2007 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 2048 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 384 strncat(outbuf, error_message(code), 385 sizeof(outbuf) - 1 - strlen(outbuf)); 386 strncat(outbuf, " - ", sizeof(outbuf) - 1 - strlen(outbuf)); 387 } 388 cp = &outbuf[strlen(outbuf)]; 389 390 actual_format = (char *) format; 391 /* 392 * This is an unpleasant hack. If the first character is less than 393 * 8, then we assume that it is a priority. 394 * 395 * Since it is not guaranteed that there is a direct mapping between 396 * syslog priorities (e.g. Ultrix and old BSD), we resort to this 397 * intermediate representation. 398 */ 399 if ((((unsigned char) *format) > 0) && (((unsigned char) *format) <= 8)) { 400 actual_format = (char *) (format + 1); 401 switch ((unsigned char) *format) { 402 case 1: 403 log_pri = LOG_EMERG; 404 break; 405 case 2: 406 log_pri = LOG_ALERT; 407 break; 408 case 3: 409 log_pri = LOG_CRIT; 410 break; 411 default: 412 case 4: 413 log_pri = LOG_ERR; 414 break; 415 case 5: 416 log_pri = LOG_WARNING; 417 break; 418 case 6: 419 log_pri = LOG_NOTICE; 420 break; 421 case 7: 422 log_pri = LOG_INFO; 423 break; 424 case 8: 425 log_pri = LOG_DEBUG; 426 break; 427 } 428 } 429 430 /* Now format the actual message */ 431 vsnprintf(cp, sizeof (outbuf) - (cp - outbuf), actual_format, ap); 432 433 /* 434 * Now that we have the message formatted, perform the output to each 435 * logging specification. 436 */ 437 for (lindex = 0; lindex < log_control.log_nentries; lindex++) { 438 switch (log_control.log_entries[lindex].log_type) { 439 case K_LOG_FILE: 440 441 klog_rotate(&log_control.log_entries[lindex]); 442 /*FALLTHRU*/ 443 case K_LOG_STDERR: 444 /* 445 * Files/standard error. 446 */ 447 if (fprintf(log_control.log_entries[lindex].lfu_filep, "%s\n", 448 outbuf) < 0) { 449 /* Attempt to report error */ 450 fprintf(stderr, krb5_log_error_table(LOG_FILE_ERR), whoami, 451 log_control.log_entries[lindex].lfu_fname); 452 } 453 else { 454 fflush(log_control.log_entries[lindex].lfu_filep); 455 } 456 break; 457 case K_LOG_CONSOLE: 458 case K_LOG_DEVICE: 459 /* 460 * Devices (may need special handling) 461 */ 462 if (DEVICE_PRINT(log_control.log_entries[lindex].ldu_filep, 463 outbuf) < 0) { 464 /* Attempt to report error */ 465 fprintf(stderr, krb5_log_error_table(LOG_DEVICE_ERR), whoami, 466 log_control.log_entries[lindex].ldu_devname); 467 } 468 break; 469 case K_LOG_SYSLOG: 470 /* 471 * System log. 472 */ 473 /* 474 * If we have specified a priority through our hackery, then 475 * use it, otherwise use the default. 476 */ 477 if (log_pri >= 0) 478 log_pri |= log_control.log_entries[lindex].lsu_facility; 479 else 480 log_pri = log_control.log_entries[lindex].lsu_facility | 481 log_control.log_entries[lindex].lsu_severity; 482 483 /* Log the message with our header trimmed off */ 484 syslog(log_pri, "%s", syslogp); 485 break; 486 default: 487 break; 488 } 489 } 490 } 491 492 /* 493 * krb5_klog_init() - Initialize logging. 494 * 495 * This routine parses the syntax described above to specify destinations for 496 * com_err(3) or krb5_klog_syslog() messages generated by the caller. 497 * 498 * Parameters: 499 * kcontext - Kerberos context. 500 * ename - Entity name as it is to appear in the profile. 501 * whoami - Entity name as it is to appear in error output. 502 * do_com_err - Take over com_err(3) processing. 503 * 504 * Implicit inputs: 505 * stderr - This is where STDERR output goes. 506 * 507 * Implicit outputs: 508 * log_nentries - Number of log entries, both valid and invalid. 509 * log_control - List of entries (log_nentries long) which contains 510 * data for klog_com_err_proc() to use to determine 511 * where/how to send output. 512 */ 513 krb5_error_code 514 krb5_klog_init(kcontext, ename, whoami, do_com_err) 515 krb5_context kcontext; 516 char *ename; 517 char *whoami; 518 krb5_boolean do_com_err; 519 { 520 const char *logging_profent[3]; 521 const char *logging_defent[3]; 522 char **logging_specs; 523 int i, ngood; 524 char *cp, *cp2; 525 char savec; 526 int error; 527 int do_openlog, log_facility; 528 FILE *f; 529 mode_t old_umask; 530 531 /* Initialize */ 532 do_openlog = 0; 533 log_facility = 0; 534 535 /* 536 * Look up [logging]-><ename> in the profile. If that doesn't 537 * succeed, then look for [logging]->default. 538 */ 539 logging_profent[0] = "logging"; 540 logging_profent[1] = ename; 541 logging_profent[2] = (char *) NULL; 542 logging_defent[0] = "logging"; 543 logging_defent[1] = "default"; 544 logging_defent[2] = (char *) NULL; 545 logging_specs = (char **) NULL; 546 ngood = 0; 547 log_control.log_nentries = 0; 548 if (!profile_get_values(kcontext->profile, 549 logging_profent, 550 &logging_specs) || 551 !profile_get_values(kcontext->profile, 552 logging_defent, 553 &logging_specs)) { 554 /* 555 * We have a match, so we first count the number of elements 556 */ 557 for (log_control.log_nentries = 0; 558 logging_specs[log_control.log_nentries]; 559 log_control.log_nentries++); 560 561 /* 562 * Now allocate our structure. 563 */ 564 log_control.log_entries = (struct log_entry *) 565 malloc(log_control.log_nentries * sizeof(struct log_entry)); 566 if (log_control.log_entries) { 567 /* 568 * Scan through the list. 569 */ 570 for (i=0; i<log_control.log_nentries; i++) { 571 log_control.log_entries[i].log_type = K_LOG_NONE; 572 log_control.log_entries[i].log_2free = logging_specs[i]; 573 /* 574 * The format is: 575 * <whitespace><data><whitespace> 576 * so, trim off the leading and trailing whitespace here. 577 */ 578 for (cp = logging_specs[i]; isspace(*cp); cp++); 579 for (cp2 = &logging_specs[i][strlen(logging_specs[i])-1]; 580 isspace(*cp2); cp2--); 581 cp2++; 582 *cp2 = '\0'; 583 /* 584 * Is this a file? 585 */ 586 if (!strncasecmp(cp, "FILE", 4)) { 587 /* 588 * Check for append/overwrite, then open the file. 589 */ 590 if (cp[4] == ':' || cp[4] == '=') { 591 log_control.log_entries[i].lfu_fopen_mode = 592 (cp[4] == ':') ? "a+F" : "wF"; 593 old_umask = umask(077); 594 f = fopen(&cp[5], 595 log_control.log_entries[i].lfu_fopen_mode); 596 umask(old_umask); 597 if (f) { 598 char rotate_kw[128]; 599 600 log_control.log_entries[i].lfu_filep = f; 601 log_control.log_entries[i].log_type = K_LOG_FILE; 602 log_control.log_entries[i].lfu_fname = &cp[5]; 603 log_control.log_entries[i].lfu_rotate_period = 604 K_LOG_DEF_FILE_ROTATE_PERIOD; 605 log_control.log_entries[i].lfu_rotate_versions = 606 K_LOG_DEF_FILE_ROTATE_VERSIONS; 607 log_control.log_entries[i].lfu_last_rotated = 608 time(0); 609 610 /* 611 * Now parse for ename_"rotate" = { 612 * period = XXX 613 * versions = 10 614 * } 615 */ 616 if (strlen(ename) + strlen("_rotate") < 617 sizeof (rotate_kw)) { 618 619 char *time; 620 krb5_deltat dt; 621 int vers; 622 623 strcpy(rotate_kw, ename); 624 strcat(rotate_kw, "_rotate"); 625 626 if (!profile_get_string(kcontext->profile, 627 "logging", rotate_kw, "period", 628 NULL, &time)) { 629 630 if (time != NULL) { 631 if (!krb5_string_to_deltat(time, 632 &dt)) { 633 log_control.log_entries[i].lfu_rotate_period = 634 (time_t) dt; 635 } 636 free(time); 637 } 638 } 639 640 if (!profile_get_integer( 641 kcontext->profile, "logging", 642 rotate_kw, "versions", 643 K_LOG_DEF_FILE_ROTATE_VERSIONS, 644 &vers)) { 645 log_control.log_entries[i].lfu_rotate_versions = vers; 646 } 647 648 } 649 } else { 650 fprintf(stderr,gettext("Couldn't open log file %s: %s\n"), 651 &cp[5], error_message(errno)); 652 continue; 653 } 654 } 655 } 656 /* 657 * Is this a syslog? 658 */ 659 else if (!strncasecmp(cp, "SYSLOG", 6)) { 660 error = 0; 661 log_control.log_entries[i].lsu_facility = LOG_AUTH; 662 log_control.log_entries[i].lsu_severity = LOG_ERR; 663 /* 664 * Is there a severify specified? 665 */ 666 if (cp[6] == ':') { 667 /* 668 * Find the end of the severity. 669 */ 670 if (cp2 = strchr(&cp[7], ':')) { 671 savec = *cp2; 672 *cp2 = '\0'; 673 cp2++; 674 } 675 676 /* 677 * Match a severity. 678 */ 679 if (!strcasecmp(&cp[7], "ERR")) { 680 log_control.log_entries[i].lsu_severity = LOG_ERR; 681 } 682 else if (!strcasecmp(&cp[7], "EMERG")) { 683 log_control.log_entries[i].lsu_severity = 684 LOG_EMERG; 685 } 686 else if (!strcasecmp(&cp[7], "ALERT")) { 687 log_control.log_entries[i].lsu_severity = 688 LOG_ALERT; 689 } 690 else if (!strcasecmp(&cp[7], "CRIT")) { 691 log_control.log_entries[i].lsu_severity = LOG_CRIT; 692 } 693 else if (!strcasecmp(&cp[7], "WARNING")) { 694 log_control.log_entries[i].lsu_severity = 695 LOG_WARNING; 696 } 697 else if (!strcasecmp(&cp[7], "NOTICE")) { 698 log_control.log_entries[i].lsu_severity = 699 LOG_NOTICE; 700 } 701 else if (!strcasecmp(&cp[7], "INFO")) { 702 log_control.log_entries[i].lsu_severity = LOG_INFO; 703 } 704 else if (!strcasecmp(&cp[7], "DEBUG")) { 705 log_control.log_entries[i].lsu_severity = 706 LOG_DEBUG; 707 } 708 else 709 error = 1; 710 711 /* 712 * If there is a facility present, then parse that. 713 */ 714 if (cp2) { 715 if (!strcasecmp(cp2, "AUTH")) { 716 log_control.log_entries[i].lsu_facility = LOG_AUTH; 717 } 718 else if (!strcasecmp(cp2, "KERN")) { 719 log_control.log_entries[i].lsu_facility = LOG_KERN; 720 } 721 else if (!strcasecmp(cp2, "USER")) { 722 log_control.log_entries[i].lsu_facility = LOG_USER; 723 } 724 else if (!strcasecmp(cp2, "MAIL")) { 725 log_control.log_entries[i].lsu_facility = LOG_MAIL; 726 } 727 else if (!strcasecmp(cp2, "DAEMON")) { 728 log_control.log_entries[i].lsu_facility = LOG_DAEMON; 729 } 730 else if (!strcasecmp(cp2, "LPR")) { 731 log_control.log_entries[i].lsu_facility = LOG_LPR; 732 } 733 else if (!strcasecmp(cp2, "NEWS")) { 734 log_control.log_entries[i].lsu_facility = LOG_NEWS; 735 } 736 else if (!strcasecmp(cp2, "UUCP")) { 737 log_control.log_entries[i].lsu_facility = LOG_UUCP; 738 } 739 else if (!strcasecmp(cp2, "CRON")) { 740 log_control.log_entries[i].lsu_facility = LOG_CRON; 741 } 742 else if (!strcasecmp(cp2, "LOCAL0")) { 743 log_control.log_entries[i].lsu_facility = LOG_LOCAL0; 744 } 745 else if (!strcasecmp(cp2, "LOCAL1")) { 746 log_control.log_entries[i].lsu_facility = LOG_LOCAL1; 747 } 748 else if (!strcasecmp(cp2, "LOCAL2")) { 749 log_control.log_entries[i].lsu_facility = LOG_LOCAL2; 750 } 751 else if (!strcasecmp(cp2, "LOCAL3")) { 752 log_control.log_entries[i].lsu_facility = LOG_LOCAL3; 753 } 754 else if (!strcasecmp(cp2, "LOCAL4")) { 755 log_control.log_entries[i].lsu_facility = LOG_LOCAL4; 756 } 757 else if (!strcasecmp(cp2, "LOCAL5")) { 758 log_control.log_entries[i].lsu_facility = LOG_LOCAL5; 759 } 760 else if (!strcasecmp(cp2, "LOCAL6")) { 761 log_control.log_entries[i].lsu_facility = LOG_LOCAL6; 762 } 763 else if (!strcasecmp(cp2, "LOCAL7")) { 764 log_control.log_entries[i].lsu_facility = LOG_LOCAL7; 765 } 766 cp2--; 767 *cp2 = savec; 768 } 769 } 770 if (!error) { 771 log_control.log_entries[i].log_type = K_LOG_SYSLOG; 772 do_openlog = 1; 773 log_facility = log_control.log_entries[i].lsu_facility; 774 } 775 } 776 /* 777 * Is this a standard error specification? 778 */ 779 else if (!strcasecmp(cp, "STDERR")) { 780 if (log_control.log_entries[i].lfu_filep = 781 fdopen(fileno(stderr), "a+F")) { 782 log_control.log_entries[i].log_type = K_LOG_STDERR; 783 log_control.log_entries[i].lfu_fname = 784 "standard error"; 785 } 786 } 787 /* 788 * Is this a specification of the console? 789 */ 790 else if (!strcasecmp(cp, "CONSOLE")) { 791 if (log_control.log_entries[i].ldu_filep = 792 CONSOLE_OPEN("a+F")) { 793 log_control.log_entries[i].log_type = K_LOG_CONSOLE; 794 log_control.log_entries[i].ldu_devname = "console"; 795 } 796 } 797 /* 798 * Is this a specification of a device? 799 */ 800 else if (!strncasecmp(cp, "DEVICE", 6)) { 801 /* 802 * We handle devices very similarly to files. 803 */ 804 if (cp[6] == '=') { 805 if (log_control.log_entries[i].ldu_filep = 806 DEVICE_OPEN(&cp[7], "wF")) { 807 log_control.log_entries[i].log_type = K_LOG_DEVICE; 808 log_control.log_entries[i].ldu_devname = &cp[7]; 809 } 810 } 811 } 812 /* 813 * See if we successfully parsed this specification. 814 */ 815 if (log_control.log_entries[i].log_type == K_LOG_NONE) { 816 fprintf(stderr, krb5_log_error_table(LSPEC_PARSE_ERR_1), whoami, cp); 817 fprintf(stderr, krb5_log_error_table(LSPEC_PARSE_ERR_2), whoami); 818 } 819 else 820 ngood++; 821 } 822 } 823 /* 824 * If we didn't find anything, then free our lists. 825 */ 826 if (ngood == 0) { 827 for (i=0; i<log_control.log_nentries; i++) 828 free(logging_specs[i]); 829 } 830 free(logging_specs); 831 } 832 /* 833 * If we didn't find anything, go for the default which is to log to 834 * the system log. 835 */ 836 if (ngood == 0) { 837 if (log_control.log_entries) 838 free(log_control.log_entries); 839 log_control.log_entries = &def_log_entry; 840 log_control.log_entries->log_type = K_LOG_SYSLOG; 841 log_control.log_entries->log_2free = (krb5_pointer) NULL; 842 log_facility = log_control.log_entries->lsu_facility = LOG_AUTH; 843 log_control.log_entries->lsu_severity = LOG_ERR; 844 do_openlog = 1; 845 log_control.log_nentries = 1; 846 } 847 if (log_control.log_nentries) { 848 if (log_control.log_whoami = (char *) malloc(strlen(whoami)+1)) 849 strcpy(log_control.log_whoami, whoami); 850 if (log_control.log_hostname = (char *) malloc(MAXHOSTNAMELEN)) 851 gethostname(log_control.log_hostname, MAXHOSTNAMELEN); 852 if (do_openlog) { 853 openlog(whoami, LOG_NDELAY|LOG_PID, log_facility); 854 log_control.log_opened = 1; 855 } 856 if (do_com_err) 857 (void) set_com_err_hook(klog_com_err_proc); 858 } 859 return((log_control.log_nentries) ? 0 : ENOENT); 860 } 861 862 /* 863 * krb5_klog_close() - Close the logging context and free all data. 864 */ 865 void 866 krb5_klog_close(kcontext) 867 krb5_context kcontext; 868 { 869 int lindex; 870 (void) reset_com_err_hook(); 871 for (lindex = 0; lindex < log_control.log_nentries; lindex++) { 872 switch (log_control.log_entries[lindex].log_type) { 873 case K_LOG_FILE: 874 case K_LOG_STDERR: 875 /* 876 * Files/standard error. 877 */ 878 fclose(log_control.log_entries[lindex].lfu_filep); 879 break; 880 case K_LOG_CONSOLE: 881 case K_LOG_DEVICE: 882 /* 883 * Devices (may need special handling) 884 */ 885 DEVICE_CLOSE(log_control.log_entries[lindex].ldu_filep); 886 break; 887 case K_LOG_SYSLOG: 888 /* 889 * System log. 890 */ 891 break; 892 default: 893 break; 894 } 895 if (log_control.log_entries[lindex].log_2free) 896 free(log_control.log_entries[lindex].log_2free); 897 } 898 if (log_control.log_entries != &def_log_entry) 899 free(log_control.log_entries); 900 log_control.log_entries = (struct log_entry *) NULL; 901 log_control.log_nentries = 0; 902 if (log_control.log_whoami) 903 free(log_control.log_whoami); 904 log_control.log_whoami = (char *) NULL; 905 if (log_control.log_hostname) 906 free(log_control.log_hostname); 907 log_control.log_hostname = (char *) NULL; 908 if (log_control.log_opened) 909 closelog(); 910 } 911 912 /* 913 * severity2string() - Convert a severity to a string. 914 */ 915 static char * 916 severity2string(severity) 917 int severity; 918 { 919 int s; 920 const char *ss; 921 922 s = severity & LOG_PRIMASK; 923 ss = krb5_log_error_table(LOG_UFO_STRING); 924 switch (s) { 925 case LOG_EMERG: 926 ss = krb5_log_error_table(LOG_EMERG_STRING); 927 break; 928 case LOG_ALERT: 929 ss = krb5_log_error_table(LOG_ALERT_STRING); 930 break; 931 case LOG_CRIT: 932 ss = krb5_log_error_table(LOG_CRIT_STRING); 933 break; 934 case LOG_ERR: 935 ss = krb5_log_error_table(LOG_ERR_STRING); 936 break; 937 case LOG_WARNING: 938 ss = krb5_log_error_table(LOG_WARNING_STRING); 939 break; 940 case LOG_NOTICE: 941 ss = krb5_log_error_table(LOG_NOTICE_STRING); 942 break; 943 case LOG_INFO: 944 ss = krb5_log_error_table(LOG_INFO_STRING); 945 break; 946 case LOG_DEBUG: 947 ss = krb5_log_error_table(LOG_DEBUG_STRING); 948 break; 949 } 950 return((char *) ss); 951 } 952 953 /* 954 * krb5_klog_syslog() - Simulate the calling sequence of syslog(3), while 955 * also performing the logging redirection as specified 956 * by krb5_klog_init(). 957 */ 958 static int 959 klog_vsyslog(priority, format, arglist) 960 int priority; 961 const char *format; 962 va_list arglist; 963 { 964 char outbuf[KRB5_KLOG_MAX_ERRMSG_SIZE]; 965 int lindex; 966 char *syslogp; 967 char *cp; 968 time_t now; 969 size_t soff; 970 971 /* 972 * Format a syslog-esque message of the format: 973 * 974 * (verbose form) 975 * <date> <hostname> <id>[<pid>](<priority>): <message> 976 * 977 * (short form) 978 * <date> <message> 979 */ 980 cp = outbuf; 981 (void) time(&now); 982 /* 983 * Format the date: mon dd hh:mm:ss 984 */ 985 soff = strftime(outbuf, sizeof(outbuf), "%b %d %H:%M:%S", localtime(&now)); 986 if (soff > 0) 987 cp += soff; 988 else 989 return(-1); 990 #ifdef VERBOSE_LOGS 991 sprintf(cp, " %s %s[%ld](%s): ", 992 log_control.log_hostname, log_control.log_whoami, (long) getpid(), 993 severity2string(priority)); 994 #else 995 sprintf(cp, " "); 996 #endif 997 syslogp = &outbuf[strlen(outbuf)]; 998 999 /* Now format the actual message */ 1000 vsnprintf(syslogp, sizeof (outbuf) - (syslogp - outbuf), format, arglist); 1001 1002 /* 1003 * Now that we have the message formatted, perform the output to each 1004 * logging specification. 1005 */ 1006 for (lindex = 0; lindex < log_control.log_nentries; lindex++) { 1007 switch (log_control.log_entries[lindex].log_type) { 1008 case K_LOG_FILE: 1009 1010 klog_rotate(&log_control.log_entries[lindex]); 1011 /*FALLTHRU*/ 1012 case K_LOG_STDERR: 1013 /* 1014 * Files/standard error. 1015 */ 1016 if (fprintf(log_control.log_entries[lindex].lfu_filep, "%s\n", 1017 outbuf) < 0) { 1018 /* Attempt to report error */ 1019 fprintf(stderr, krb5_log_error_table(LOG_FILE_ERR), 1020 log_control.log_whoami, 1021 log_control.log_entries[lindex].lfu_fname); 1022 } 1023 else { 1024 fflush(log_control.log_entries[lindex].lfu_filep); 1025 } 1026 break; 1027 case K_LOG_CONSOLE: 1028 case K_LOG_DEVICE: 1029 /* 1030 * Devices (may need special handling) 1031 */ 1032 if (DEVICE_PRINT(log_control.log_entries[lindex].ldu_filep, 1033 outbuf) < 0) { 1034 /* Attempt to report error */ 1035 fprintf(stderr, krb5_log_error_table(LOG_DEVICE_ERR), 1036 log_control.log_whoami, 1037 log_control.log_entries[lindex].ldu_devname); 1038 } 1039 break; 1040 case K_LOG_SYSLOG: 1041 /* 1042 * System log. 1043 */ 1044 1045 /* Log the message with our header trimmed off */ 1046 syslog(priority, "%s", syslogp); 1047 break; 1048 default: 1049 break; 1050 } 1051 } 1052 return(0); 1053 } 1054 1055 int 1056 krb5_klog_syslog(int priority, const char *format, ...) 1057 { 1058 int retval; 1059 va_list pvar; 1060 1061 va_start(pvar, format); 1062 retval = klog_vsyslog(priority, format, pvar); 1063 va_end(pvar); 1064 return(retval); 1065 } 1066 1067 /* 1068 * krb5_klog_reopen() - Close and reopen any open (non-syslog) log files. 1069 * This function is called when a SIGHUP is received 1070 * so that external log-archival utilities may 1071 * alert the Kerberos daemons that they should get 1072 * a new file descriptor for the give filename. 1073 */ 1074 void 1075 krb5_klog_reopen(kcontext) 1076 krb5_context kcontext; 1077 { 1078 int lindex; 1079 FILE *f; 1080 1081 /* 1082 * Only logs which are actually files need to be closed 1083 * and reopened in response to a SIGHUP 1084 */ 1085 for (lindex = 0; lindex < log_control.log_nentries; lindex++) { 1086 if (log_control.log_entries[lindex].log_type == K_LOG_FILE) { 1087 fclose(log_control.log_entries[lindex].lfu_filep); 1088 /* 1089 * In case the old logfile did not get moved out of the 1090 * way, open for append to prevent squashing the old logs. 1091 */ 1092 f = fopen(log_control.log_entries[lindex].lfu_fname, "a+F"); 1093 if (f) { 1094 log_control.log_entries[lindex].lfu_filep = f; 1095 } else { 1096 fprintf(stderr, "Couldn't open log file %s: %s\n", 1097 log_control.log_entries[lindex].lfu_fname, 1098 error_message(errno)); 1099 } 1100 } 1101 } 1102 } 1103