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