xref: /freebsd/crypto/krb5/src/lib/kadm5/logger.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/kadm5/logger.c */
3 /*
4  * Copyright 1995, 2007 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 
27 /* KADM5 wants non-syslog log files to contain syslog-like entries */
28 #define VERBOSE_LOGS
29 
30 /*
31  * logger.c     - Handle logging functions for those who want it.
32  */
33 #include "k5-int.h"
34 #include "adm_proto.h"
35 #include "com_err.h"
36 #include <stdio.h>
37 #include <ctype.h>
38 #include <syslog.h>
39 #include <stdarg.h>
40 
41 #define KRB5_KLOG_MAX_ERRMSG_SIZE       2048
42 #ifndef MAXHOSTNAMELEN
43 #define MAXHOSTNAMELEN  256
44 #endif  /* MAXHOSTNAMELEN */
45 
46 /* This is to assure that we have at least one match in the syslog stuff */
47 #ifndef LOG_AUTH
48 #define LOG_AUTH        0
49 #endif  /* LOG_AUTH */
50 #ifndef LOG_ERR
51 #define LOG_ERR         0
52 #endif  /* LOG_ERR */
53 
54 #define lspec_parse_err_1       _("%s: cannot parse <%s>\n")
55 #define lspec_parse_err_2       _("%s: warning - logging entry syntax error\n")
56 #define log_file_err            _("%s: error writing to %s\n")
57 #define log_device_err          _("%s: error writing to %s device\n")
58 #define log_ufo_string          "?\?\?" /* nb: avoid trigraphs */
59 #define log_emerg_string        _("EMERGENCY")
60 #define log_alert_string        _("ALERT")
61 #define log_crit_string         _("CRITICAL")
62 #define log_err_string          _("Error")
63 #define log_warning_string      _("Warning")
64 #define log_notice_string       _("Notice")
65 #define log_info_string         _("info")
66 #define log_debug_string        _("debug")
67 
68 /*
69  * Output logging.
70  *
71  * Output logging is now controlled by the configuration file.  We can specify
72  * the following syntaxes under the [logging]->entity specification.
73  *      FILE<opentype><pathname>
74  *      SYSLOG[=<severity>[:<facility>]]
75  *      STDERR
76  *      CONSOLE
77  *      DEVICE=<device-spec>
78  *
79  * Where:
80  *      <opentype> is ":" for open/append, "=" for open/create.
81  *      <pathname> is a valid path name.
82  *      <severity> is one of: (default = ERR)
83  *              EMERG
84  *              ALERT
85  *              CRIT
86  *              ERR
87  *              WARNING
88  *              NOTICE
89  *              INFO
90  *              DEBUG
91  *      <facility> is one of: (default = AUTH)
92  *              KERN
93  *              USER
94  *              MAIL
95  *              DAEMON
96  *              AUTH
97  *              LPR
98  *              NEWS
99  *              UUCP
100  *              CRON
101  *              LOCAL0..LOCAL7
102  *      <device-spec> is a valid device specification.
103  */
104 struct log_entry {
105     enum log_type { K_LOG_FILE,
106                     K_LOG_SYSLOG,
107                     K_LOG_STDERR,
108                     K_LOG_CONSOLE,
109                     K_LOG_DEVICE,
110                     K_LOG_NONE } log_type;
111     krb5_pointer log_2free;
112     union log_union {
113         struct log_file {
114             FILE        *lf_filep;
115             char        *lf_fname;
116         } log_file;
117         struct log_syslog {
118             int         ls_facility;
119         } log_syslog;
120         struct log_device {
121             FILE        *ld_filep;
122             char        *ld_devname;
123         } log_device;
124     } log_union;
125 };
126 #define lfu_filep       log_union.log_file.lf_filep
127 #define lfu_fname       log_union.log_file.lf_fname
128 #define lsu_facility    log_union.log_syslog.ls_facility
129 #define ldu_filep       log_union.log_device.ld_filep
130 #define ldu_devname     log_union.log_device.ld_devname
131 
132 struct log_control {
133     struct log_entry    *log_entries;
134     int                 log_nentries;
135     char                *log_whoami;
136     char                *log_hostname;
137     krb5_boolean        log_opened;
138     krb5_boolean        log_debug;
139 };
140 
141 static struct log_control log_control = {
142     (struct log_entry *) NULL,
143     0,
144     (char *) NULL,
145     (char *) NULL,
146     0
147 };
148 static struct log_entry def_log_entry;
149 
150 /*
151  * These macros define any special processing that needs to happen for
152  * devices.  For unix, of course, this is hardly anything.
153  */
154 #define DEVICE_OPEN(d, m)       fopen(d, m)
155 #define CONSOLE_OPEN(m)         fopen("/dev/console", m)
156 #define DEVICE_PRINT(f, m)      ((fprintf(f, "%s\r\n", m) >= 0) ?       \
157                                  (fflush(f), 0) :                       \
158                                  -1)
159 #define DEVICE_CLOSE(d)         fclose(d)
160 
161 /*
162  * klog_com_err_proc()  - Handle com_err(3) messages as specified by the
163  *                        profile.
164  */
165 static krb5_context err_context;
166 
167 static void
168 klog_com_err_proc(const char *whoami, long int code, const char *format, va_list ap)
169 #if !defined(__cplusplus) && (__GNUC__ > 2)
170     __attribute__((__format__(__printf__, 3, 0)))
171 #endif
172     ;
173 
174 /*
175  * Write com_err() messages to the configured logging devices.  Ignore whoami,
176  * as krb5_klog_init() already received a whoami value.  If code is nonzero,
177  * log its error message (retrieved using err_context) and the formatted
178  * message at error severity.  If code is zero, log the formatted message at
179  * informational severity.
180  */
181 static void
klog_com_err_proc(const char * whoami,long int code,const char * format,va_list ap)182 klog_com_err_proc(const char *whoami, long int code, const char *format, va_list ap)
183 {
184     struct k5buf buf;
185     const char *emsg, *msg;
186 
187     if (format == NULL)
188         return;
189 
190     k5_buf_init_dynamic(&buf);
191 
192     if (code) {
193         /* Start with the error message and a separator. */
194         emsg = krb5_get_error_message(err_context, code);
195         k5_buf_add(&buf, emsg);
196         krb5_free_error_message(err_context, emsg);
197         k5_buf_add(&buf, " - ");
198     }
199 
200     /* Add the formatted message. */
201     k5_buf_add_vfmt(&buf, format, ap);
202 
203     msg = k5_buf_cstring(&buf);
204     if (msg != NULL)
205         krb5_klog_syslog(code ? LOG_ERR : LOG_INFO, "%s", msg);
206 
207     k5_buf_free(&buf);
208 }
209 
210 /*
211  * krb5_klog_init()     - Initialize logging.
212  *
213  * This routine parses the syntax described above to specify destinations for
214  * com_err(3) or krb5_klog_syslog() messages generated by the caller.
215  *
216  * Parameters:
217  *      kcontext        - Kerberos context.
218  *      ename           - Entity name as it is to appear in the profile.
219  *      whoami          - Entity name as it is to appear in error output.
220  *      do_com_err      - Take over com_err(3) processing.
221  *
222  * Implicit inputs:
223  *      stderr          - This is where STDERR output goes.
224  *
225  * Implicit outputs:
226  *      log_nentries    - Number of log entries, both valid and invalid.
227  *      log_control     - List of entries (log_nentries long) which contains
228  *                        data for klog_com_err_proc() to use to determine
229  *                        where/how to send output.
230  */
231 krb5_error_code
krb5_klog_init(krb5_context kcontext,char * ename,char * whoami,krb5_boolean do_com_err)232 krb5_klog_init(krb5_context kcontext, char *ename, char *whoami, krb5_boolean do_com_err)
233 {
234     const char  *logging_profent[3];
235     const char  *logging_defent[3];
236     char        **logging_specs;
237     int         i, ngood, fd, append;
238     char        *cp, *cp2;
239     char        savec = '\0';
240     int         error, debug;
241     int         do_openlog, log_facility;
242     FILE        *f = NULL;
243 
244     /* Initialize */
245     do_openlog = 0;
246     log_facility = 0;
247 
248     err_context = kcontext;
249 
250     /* Look up [logging]->debug in the profile to see if we should include
251      * debug messages for types other than syslog.  Default to false. */
252     if (!profile_get_boolean(kcontext->profile, KRB5_CONF_LOGGING,
253                              KRB5_CONF_DEBUG, NULL, 0, &debug))
254         log_control.log_debug = debug;
255 
256     /*
257      * Look up [logging]-><ename> in the profile.  If that doesn't
258      * succeed, then look for [logging]->default.
259      */
260     logging_profent[0] = KRB5_CONF_LOGGING;
261     logging_profent[1] = ename;
262     logging_profent[2] = (char *) NULL;
263     logging_defent[0] = KRB5_CONF_LOGGING;
264     logging_defent[1] = KRB5_CONF_DEFAULT;
265     logging_defent[2] = (char *) NULL;
266     logging_specs = (char **) NULL;
267     ngood = 0;
268     log_control.log_nentries = 0;
269     if (!profile_get_values(kcontext->profile,
270                             logging_profent,
271                             &logging_specs) ||
272         !profile_get_values(kcontext->profile,
273                             logging_defent,
274                             &logging_specs)) {
275         /*
276          * We have a match, so we first count the number of elements
277          */
278         for (log_control.log_nentries = 0;
279              logging_specs[log_control.log_nentries];
280              log_control.log_nentries++);
281 
282         /*
283          * Now allocate our structure.
284          */
285         log_control.log_entries = (struct log_entry *)
286             malloc(log_control.log_nentries * sizeof(struct log_entry));
287         if (log_control.log_entries) {
288             /*
289              * Scan through the list.
290              */
291             for (i=0; i<log_control.log_nentries; i++) {
292                 log_control.log_entries[i].log_type = K_LOG_NONE;
293                 log_control.log_entries[i].log_2free = logging_specs[i];
294                 /*
295                  * The format is:
296                  *      <whitespace><data><whitespace>
297                  * so, trim off the leading and trailing whitespace here.
298                  */
299                 for (cp = logging_specs[i]; isspace((int) *cp); cp++);
300                 for (cp2 = &logging_specs[i][strlen(logging_specs[i])-1];
301                      isspace((int) *cp2); cp2--);
302                 cp2++;
303                 *cp2 = '\0';
304                 /*
305                  * Is this a file?
306                  */
307                 if (!strncasecmp(cp, "FILE", 4)) {
308                     /*
309                      * Check for append/overwrite, then open the file.
310                      */
311                     append = (cp[4] == ':') ? O_APPEND : 0;
312                     if (append || cp[4] == '=') {
313                         fd = open(&cp[5], O_CREAT | O_WRONLY | append,
314                                   S_IRUSR | S_IWUSR | S_IRGRP);
315                         if (fd != -1)
316                             f = fdopen(fd, append ? "a" : "w");
317                         if (fd == -1 || f == NULL) {
318                             fprintf(stderr,"Couldn't open log file %s: %s\n",
319                                     &cp[5], error_message(errno));
320                             continue;
321                         }
322                         set_cloexec_file(f);
323                         log_control.log_entries[i].lfu_filep = f;
324                         log_control.log_entries[i].log_type = K_LOG_FILE;
325                         log_control.log_entries[i].lfu_fname = &cp[5];
326                     }
327                 }
328                 /*
329                  * Is this a syslog?
330                  */
331                 else if (!strncasecmp(cp, "SYSLOG", 6)) {
332                     error = 0;
333                     log_control.log_entries[i].lsu_facility = LOG_AUTH;
334                     /*
335                      * Is there a severify (which is now ignored) specified?
336                      */
337                     if (cp[6] == ':') {
338                         /*
339                          * Find the end of the severity.
340                          */
341                         cp2 = strchr(&cp[7], ':');
342                         if (cp2) {
343                             savec = *cp2;
344                             *cp2 = '\0';
345                             cp2++;
346                         }
347 
348                         /*
349                          * If there is a facility present, then parse that.
350                          */
351                         if (cp2) {
352                             static const struct {
353                                 const char *name;
354                                 int value;
355                             } facilities[] = {
356                                 { "AUTH",       LOG_AUTH        },
357 #ifdef  LOG_AUTHPRIV
358                                 { "AUTHPRIV",   LOG_AUTHPRIV    },
359 #endif  /* LOG_AUTHPRIV */
360 #ifdef  LOG_KERN
361                                 { "KERN",       LOG_KERN        },
362 #endif  /* LOG_KERN */
363 #ifdef  LOG_USER
364                                 { "USER",       LOG_USER        },
365 #endif  /* LOG_USER */
366 #ifdef  LOG_MAIL
367                                 { "MAIL",       LOG_MAIL        },
368 #endif  /* LOG_MAIL */
369 #ifdef  LOG_DAEMON
370                                 { "DAEMON",     LOG_DAEMON      },
371 #endif  /* LOG_DAEMON */
372 #ifdef  LOG_FTP
373                                 { "FTP",        LOG_FTP         },
374 #endif  /* LOG_FTP */
375 #ifdef  LOG_LPR
376                                 { "LPR",        LOG_LPR         },
377 #endif  /* LOG_LPR */
378 #ifdef  LOG_NEWS
379                                 { "NEWS",       LOG_NEWS        },
380 #endif  /* LOG_NEWS */
381 #ifdef  LOG_UUCP
382                                 { "UUCP",       LOG_UUCP        },
383 #endif  /* LOG_UUCP */
384 #ifdef  LOG_CRON
385                                 { "CRON",       LOG_CRON        },
386 #endif  /* LOG_CRON */
387 #ifdef  LOG_LOCAL0
388                                 { "LOCAL0",     LOG_LOCAL0      },
389 #endif  /* LOG_LOCAL0 */
390 #ifdef  LOG_LOCAL1
391                                 { "LOCAL1",     LOG_LOCAL1      },
392 #endif  /* LOG_LOCAL1 */
393 #ifdef  LOG_LOCAL2
394                                 { "LOCAL2",     LOG_LOCAL2      },
395 #endif  /* LOG_LOCAL2 */
396 #ifdef  LOG_LOCAL3
397                                 { "LOCAL3",     LOG_LOCAL3      },
398 #endif  /* LOG_LOCAL3 */
399 #ifdef  LOG_LOCAL4
400                                 { "LOCAL4",     LOG_LOCAL4      },
401 #endif  /* LOG_LOCAL4 */
402 #ifdef  LOG_LOCAL5
403                                 { "LOCAL5",     LOG_LOCAL5      },
404 #endif  /* LOG_LOCAL5 */
405 #ifdef  LOG_LOCAL6
406                                 { "LOCAL6",     LOG_LOCAL6      },
407 #endif  /* LOG_LOCAL6 */
408 #ifdef  LOG_LOCAL7
409                                 { "LOCAL7",     LOG_LOCAL7      },
410 #endif  /* LOG_LOCAL7 */
411                             };
412                             unsigned int j;
413 
414                             for (j = 0; j < sizeof(facilities)/sizeof(facilities[0]); j++)
415                                 if (!strcasecmp(cp2, facilities[j].name)) {
416                                     log_control.log_entries[i].lsu_facility = facilities[j].value;
417                                     break;
418                                 }
419                             cp2--;
420                             *cp2 = savec;
421                         }
422                     }
423                     if (!error) {
424                         log_control.log_entries[i].log_type = K_LOG_SYSLOG;
425                         do_openlog = 1;
426                         log_facility = log_control.log_entries[i].lsu_facility;
427                     }
428                 }
429                 /*
430                  * Is this a standard error specification?
431                  */
432                 else if (!strcasecmp(cp, "STDERR")) {
433                     log_control.log_entries[i].lfu_filep =
434                         fdopen(fileno(stderr), "w");
435                     if (log_control.log_entries[i].lfu_filep) {
436                         log_control.log_entries[i].log_type = K_LOG_STDERR;
437                         log_control.log_entries[i].lfu_fname =
438                             "standard error";
439                     }
440                 }
441                 /*
442                  * Is this a specification of the console?
443                  */
444                 else if (!strcasecmp(cp, "CONSOLE")) {
445                     log_control.log_entries[i].ldu_filep =
446                         CONSOLE_OPEN("a+");
447                     if (log_control.log_entries[i].ldu_filep) {
448                         set_cloexec_file(log_control.log_entries[i].ldu_filep);
449                         log_control.log_entries[i].log_type = K_LOG_CONSOLE;
450                         log_control.log_entries[i].ldu_devname = "console";
451                     }
452                 }
453                 /*
454                  * Is this a specification of a device?
455                  */
456                 else if (!strncasecmp(cp, "DEVICE", 6)) {
457                     /*
458                      * We handle devices very similarly to files.
459                      */
460                     if (cp[6] == '=') {
461                         log_control.log_entries[i].ldu_filep =
462                             DEVICE_OPEN(&cp[7], "w");
463                         if (log_control.log_entries[i].ldu_filep) {
464                             set_cloexec_file(log_control.log_entries[i].ldu_filep);
465                             log_control.log_entries[i].log_type = K_LOG_DEVICE;
466                             log_control.log_entries[i].ldu_devname = &cp[7];
467                         }
468                     }
469                 }
470                 /*
471                  * See if we successfully parsed this specification.
472                  */
473                 if (log_control.log_entries[i].log_type == K_LOG_NONE) {
474                     fprintf(stderr, lspec_parse_err_1, whoami, cp);
475                     fprintf(stderr, lspec_parse_err_2, whoami);
476                 }
477                 else
478                     ngood++;
479             }
480         }
481         /*
482          * If we didn't find anything, then free our lists.
483          */
484         if (ngood == 0) {
485             for (i=0; i<log_control.log_nentries; i++)
486                 free(logging_specs[i]);
487         }
488         free(logging_specs);
489     }
490     /*
491      * If we didn't find anything, go for the default which is to log to
492      * the system log.
493      */
494     if (ngood == 0) {
495         if (log_control.log_entries)
496             free(log_control.log_entries);
497         log_control.log_entries = &def_log_entry;
498         log_control.log_entries->log_type = K_LOG_SYSLOG;
499         log_control.log_entries->log_2free = (krb5_pointer) NULL;
500         log_facility = log_control.log_entries->lsu_facility = LOG_AUTH;
501         do_openlog = 1;
502         log_control.log_nentries = 1;
503     }
504     if (log_control.log_nentries) {
505         log_control.log_whoami = strdup(whoami);
506         log_control.log_hostname = (char *) malloc(MAXHOSTNAMELEN + 1);
507         if (log_control.log_hostname) {
508             if (gethostname(log_control.log_hostname, MAXHOSTNAMELEN) == -1) {
509                 free(log_control.log_hostname);
510                 log_control.log_hostname = NULL;
511             } else
512                 log_control.log_hostname[MAXHOSTNAMELEN] = '\0';
513         }
514         if (do_openlog) {
515             openlog(whoami, LOG_NDELAY|LOG_PID, log_facility);
516             log_control.log_opened = 1;
517         }
518         if (do_com_err)
519             (void) set_com_err_hook(klog_com_err_proc);
520     }
521     return((log_control.log_nentries) ? 0 : ENOENT);
522 }
523 
524 /* Reset the context used by the com_err hook to retrieve error messages. */
525 void
krb5_klog_set_context(krb5_context kcontext)526 krb5_klog_set_context(krb5_context kcontext)
527 {
528     err_context = kcontext;
529 }
530 
531 /*
532  * krb5_klog_close()    - Close the logging context and free all data.
533  */
534 void
krb5_klog_close(krb5_context kcontext)535 krb5_klog_close(krb5_context kcontext)
536 {
537     int lindex;
538     (void) reset_com_err_hook();
539     for (lindex = 0; lindex < log_control.log_nentries; lindex++) {
540         switch (log_control.log_entries[lindex].log_type) {
541         case K_LOG_FILE:
542         case K_LOG_STDERR:
543             /*
544              * Files/standard error.
545              */
546             fclose(log_control.log_entries[lindex].lfu_filep);
547             break;
548         case K_LOG_CONSOLE:
549         case K_LOG_DEVICE:
550             /*
551              * Devices (may need special handling)
552              */
553             DEVICE_CLOSE(log_control.log_entries[lindex].ldu_filep);
554             break;
555         case K_LOG_SYSLOG:
556             /*
557              * System log.
558              */
559             break;
560         default:
561             break;
562         }
563         if (log_control.log_entries[lindex].log_2free)
564             free(log_control.log_entries[lindex].log_2free);
565     }
566     if (log_control.log_entries != &def_log_entry)
567         free(log_control.log_entries);
568     log_control.log_entries = (struct log_entry *) NULL;
569     log_control.log_nentries = 0;
570     if (log_control.log_whoami)
571         free(log_control.log_whoami);
572     log_control.log_whoami = (char *) NULL;
573     if (log_control.log_hostname)
574         free(log_control.log_hostname);
575     log_control.log_hostname = (char *) NULL;
576     if (log_control.log_opened)
577         closelog();
578 }
579 
580 /*
581  * severity2string()    - Convert a severity to a string.
582  */
583 static const char *
severity2string(int severity)584 severity2string(int severity)
585 {
586     int s;
587     const char *ss;
588 
589     s = severity & LOG_PRIMASK;
590     ss = log_ufo_string;
591     switch (s) {
592     case LOG_EMERG:
593         ss = log_emerg_string;
594         break;
595     case LOG_ALERT:
596         ss = log_alert_string;
597         break;
598     case LOG_CRIT:
599         ss = log_crit_string;
600         break;
601     case LOG_ERR:
602         ss = log_err_string;
603         break;
604     case LOG_WARNING:
605         ss = log_warning_string;
606         break;
607     case LOG_NOTICE:
608         ss = log_notice_string;
609         break;
610     case LOG_INFO:
611         ss = log_info_string;
612         break;
613     case LOG_DEBUG:
614         ss = log_debug_string;
615         break;
616     }
617     return(ss);
618 }
619 
620 /*
621  * krb5_klog_syslog()   - Simulate the calling sequence of syslog(3), while
622  *                        also performing the logging redirection as specified
623  *                        by krb5_klog_init().
624  */
625 static int
626 klog_vsyslog(int priority, const char *format, va_list arglist)
627 #if !defined(__cplusplus) && (__GNUC__ > 2)
628     __attribute__((__format__(__printf__, 2, 0)))
629 #endif
630     ;
631 
632 static int
klog_vsyslog(int priority,const char * format,va_list arglist)633 klog_vsyslog(int priority, const char *format, va_list arglist)
634 {
635     char        outbuf[KRB5_KLOG_MAX_ERRMSG_SIZE];
636     int         lindex;
637     char        *syslogp;
638     char        *cp;
639     time_t      now;
640     size_t      soff;
641     struct tm  *tm;
642 
643     /*
644      * Format a syslog-esque message of the format:
645      *
646      * (verbose form)
647      *          <date> <hostname> <id>[<pid>](<priority>): <message>
648      *
649      * (short form)
650      *          <date> <message>
651      */
652     cp = outbuf;
653     (void) time(&now);
654 
655     /*
656      * Format the date: mon dd hh:mm:ss
657      */
658     tm = localtime(&now);
659     if (tm == NULL)
660         return(-1);
661     soff = strftime(outbuf, sizeof(outbuf), "%b %d %H:%M:%S", tm);
662     if (soff > 0)
663         cp += soff;
664     else
665         return(-1);
666 
667 #ifdef VERBOSE_LOGS
668     snprintf(cp, sizeof(outbuf) - (cp-outbuf), " %s %s[%ld](%s): ",
669              log_control.log_hostname ? log_control.log_hostname : "",
670              log_control.log_whoami ? log_control.log_whoami : "",
671              (long) getpid(),
672              severity2string(priority));
673 #else
674     snprintf(cp, sizeof(outbuf) - (cp-outbuf), " ");
675 #endif
676     syslogp = &outbuf[strlen(outbuf)];
677 
678     /* Now format the actual message */
679     vsnprintf(syslogp, sizeof(outbuf) - (syslogp - outbuf), format, arglist);
680 
681     /*
682      * If the user did not use krb5_klog_init() instead of dropping
683      * the request on the floor, syslog it - if it exists
684      */
685     if (log_control.log_nentries == 0) {
686         /* Log the message with our header trimmed off */
687         syslog(priority, "%s", syslogp);
688     }
689 
690     /*
691      * Now that we have the message formatted, perform the output to each
692      * logging specification.
693      */
694     for (lindex = 0; lindex < log_control.log_nentries; lindex++) {
695         /* Omit LOG_DEBUG messages for non-syslog outputs unless we are
696          * configured to include them. */
697         if (priority == LOG_DEBUG && !log_control.log_debug &&
698             log_control.log_entries[lindex].log_type != K_LOG_SYSLOG)
699             continue;
700 
701         switch (log_control.log_entries[lindex].log_type) {
702         case K_LOG_FILE:
703         case K_LOG_STDERR:
704             /*
705              * Files/standard error.
706              */
707             if (fprintf(log_control.log_entries[lindex].lfu_filep, "%s\n",
708                         outbuf) < 0) {
709                 /* Attempt to report error */
710                 fprintf(stderr, log_file_err, log_control.log_whoami,
711                         log_control.log_entries[lindex].lfu_fname);
712             }
713             else {
714                 fflush(log_control.log_entries[lindex].lfu_filep);
715             }
716             break;
717         case K_LOG_CONSOLE:
718         case K_LOG_DEVICE:
719             /*
720              * Devices (may need special handling)
721              */
722             if (DEVICE_PRINT(log_control.log_entries[lindex].ldu_filep,
723                              outbuf) < 0) {
724                 /* Attempt to report error */
725                 fprintf(stderr, log_device_err, log_control.log_whoami,
726                         log_control.log_entries[lindex].ldu_devname);
727             }
728             break;
729         case K_LOG_SYSLOG:
730             /*
731              * System log.
732              */
733 
734             /* Log the message with our header trimmed off */
735             syslog(priority, "%s", syslogp);
736             break;
737         default:
738             break;
739         }
740     }
741     return(0);
742 }
743 
744 int
krb5_klog_syslog(int priority,const char * format,...)745 krb5_klog_syslog(int priority, const char *format, ...)
746 {
747     int         retval;
748     va_list     pvar;
749 
750     va_start(pvar, format);
751     retval = klog_vsyslog(priority, format, pvar);
752     va_end(pvar);
753     return(retval);
754 }
755 
756 /*
757  * krb5_klog_reopen() - Close and reopen any open (non-syslog) log files.
758  *                      This function is called when a SIGHUP is received
759  *                      so that external log-archival utilities may
760  *                      alert the Kerberos daemons that they should get
761  *                      a new file descriptor for the give filename.
762  */
763 void
krb5_klog_reopen(krb5_context kcontext)764 krb5_klog_reopen(krb5_context kcontext)
765 {
766     int lindex;
767     FILE *f;
768 
769     /*
770      * Only logs which are actually files need to be closed
771      * and reopened in response to a SIGHUP
772      */
773     for (lindex = 0; lindex < log_control.log_nentries; lindex++) {
774         if (log_control.log_entries[lindex].log_type == K_LOG_FILE) {
775             fclose(log_control.log_entries[lindex].lfu_filep);
776             /*
777              * In case the old logfile did not get moved out of the
778              * way, open for append to prevent squashing the old logs.
779              */
780             f = fopen(log_control.log_entries[lindex].lfu_fname, "a+");
781             if (f) {
782                 set_cloexec_file(f);
783                 log_control.log_entries[lindex].lfu_filep = f;
784             } else {
785                 fprintf(stderr, _("Couldn't open log file %s: %s\n"),
786                         log_control.log_entries[lindex].lfu_fname,
787                         error_message(errno));
788             }
789         }
790     }
791 }
792