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