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