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