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