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