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 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
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 /* 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
krb5_klog_init(krb5_context kcontext,char * ename,char * whoami,krb5_boolean do_com_err)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
krb5_klog_close(krb5_context kcontext)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 *
severity2string(int severity)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
klog_vsyslog(int priority,const char * format,va_list arglist)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
krb5_klog_syslog(int priority,const char * format,...)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
krb5_klog_reopen(krb5_context kcontext)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 */
krb5_klog_set_context(krb5_context context)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 */
facility2string(int facility)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 */
krb5_klog_list_logs(const char * whoami)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 */
krb5_klog_add_stderr()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 */
krb5_klog_remove_stderr()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 */
krb5_klog_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