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