#define VERBOSE_LOGS
#include "k5-int.h"
#include "adm_proto.h"
#include "com_err.h"
#include <stdio.h>
#include <ctype.h>
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#else
#include <varargs.h>
#endif
#define KRB5_KLOG_MAX_ERRMSG_SIZE 2048
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 256
#endif
#ifndef LOG_AUTH
#define LOG_AUTH 0
#endif
#ifndef LOG_ERR
#define LOG_ERR 0
#endif
#define lspec_parse_err_1 "%s: cannot parse <%s>\n"
#define lspec_parse_err_2 "%s: warning - logging entry syntax error\n"
#define log_file_err "%s: error writing to %s\n"
#define log_device_err "%s: error writing to %s device\n"
#define log_ufo_string "?\?\?"
#define log_emerg_string "EMERGENCY"
#define log_alert_string "ALERT"
#define log_crit_string "CRITICAL"
#define log_err_string "Error"
#define log_warning_string "Warning"
#define log_notice_string "Notice"
#define log_info_string "info"
#define log_debug_string "debug"
struct log_entry {
enum log_type { K_LOG_FILE,
K_LOG_SYSLOG,
K_LOG_STDERR,
K_LOG_CONSOLE,
K_LOG_DEVICE,
K_LOG_NONE } log_type;
krb5_pointer log_2free;
union log_union {
struct log_file {
FILE *lf_filep;
char *lf_fname;
} log_file;
struct log_syslog {
int ls_facility;
int ls_severity;
} log_syslog;
struct log_device {
FILE *ld_filep;
char *ld_devname;
} log_device;
} log_union;
};
#define lfu_filep log_union.log_file.lf_filep
#define lfu_fname log_union.log_file.lf_fname
#define lsu_facility log_union.log_syslog.ls_facility
#define lsu_severity log_union.log_syslog.ls_severity
#define ldu_filep log_union.log_device.ld_filep
#define ldu_devname log_union.log_device.ld_devname
struct log_control {
struct log_entry *log_entries;
int log_nentries;
char *log_whoami;
char *log_hostname;
krb5_boolean log_opened;
};
static struct log_control log_control = {
(struct log_entry *) NULL,
0,
(char *) NULL,
(char *) NULL,
0
};
static struct log_entry def_log_entry;
#define DEVICE_OPEN(d, m) fopen(d, m)
#define CONSOLE_OPEN(m) fopen("/dev/console", m)
#define DEVICE_PRINT(f, m) ((fprintf(f, "%s\r\n", m) >= 0) ? \
(fflush(f), 0) : \
-1)
#define DEVICE_CLOSE(d) fclose(d)
static krb5_context err_context;
static void
klog_com_err_proc(const char *whoami, long int code, const char *format, va_list ap)
{
char outbuf[KRB5_KLOG_MAX_ERRMSG_SIZE];
int lindex;
const char *actual_format;
#ifdef HAVE_SYSLOG
int log_pri = -1;
#endif
char *cp;
char *syslogp;
sprintf(outbuf, "%s: ", whoami);
syslogp = &outbuf[strlen(outbuf)];
if (code) {
char *emsg;
outbuf[sizeof(outbuf) - 1] = '\0';
emsg = krb5_get_error_message (err_context, code);
strncat(outbuf, emsg, sizeof(outbuf) - 1 - strlen(outbuf));
strncat(outbuf, " - ", sizeof(outbuf) - 1 - strlen(outbuf));
krb5_free_error_message(err_context, emsg);
}
cp = &outbuf[strlen(outbuf)];
actual_format = format;
#ifdef HAVE_SYSLOG
if ((((unsigned char) *format) > 0) && (((unsigned char) *format) <= 8)) {
actual_format = (format + 1);
switch ((unsigned char) *format) {
#ifdef LOG_EMERG
case 1:
log_pri = LOG_EMERG;
break;
#endif
#ifdef LOG_ALERT
case 2:
log_pri = LOG_ALERT;
break;
#endif
#ifdef LOG_CRIT
case 3:
log_pri = LOG_CRIT;
break;
#endif
default:
case 4:
log_pri = LOG_ERR;
break;
#ifdef LOG_WARNING
case 5:
log_pri = LOG_WARNING;
break;
#endif
#ifdef LOG_NOTICE
case 6:
log_pri = LOG_NOTICE;
break;
#endif
#ifdef LOG_INFO
case 7:
log_pri = LOG_INFO;
break;
#endif
#ifdef LOG_DEBUG
case 8:
log_pri = LOG_DEBUG;
break;
#endif
}
}
#endif
#if HAVE_VSNPRINTF
vsnprintf(cp, sizeof(outbuf) - (cp - outbuf), actual_format, ap);
#elif HAVE_VSPRINTF
vsprintf(cp, actual_format, ap);
#else
sprintf(cp, actual_format, ((int *) ap)[0], ((int *) ap)[1],
((int *) ap)[2], ((int *) ap)[3],
((int *) ap)[4], ((int *) ap)[5]);
#endif
for (lindex = 0; lindex < log_control.log_nentries; lindex++) {
switch (log_control.log_entries[lindex].log_type) {
case K_LOG_FILE:
case K_LOG_STDERR:
if (fprintf(log_control.log_entries[lindex].lfu_filep, "%s\n",
outbuf) < 0) {
fprintf(stderr, log_file_err, whoami,
log_control.log_entries[lindex].lfu_fname);
}
else {
fflush(log_control.log_entries[lindex].lfu_filep);
}
break;
case K_LOG_CONSOLE:
case K_LOG_DEVICE:
if (DEVICE_PRINT(log_control.log_entries[lindex].ldu_filep,
outbuf) < 0) {
fprintf(stderr, log_device_err, whoami,
log_control.log_entries[lindex].ldu_devname);
}
break;
#ifdef HAVE_SYSLOG
case K_LOG_SYSLOG:
if (log_pri >= 0)
log_pri |= log_control.log_entries[lindex].lsu_facility;
else
log_pri = log_control.log_entries[lindex].lsu_facility |
log_control.log_entries[lindex].lsu_severity;
syslog(log_pri, "%s", syslogp);
break;
#endif
default:
break;
}
}
}
krb5_error_code
krb5_klog_init(krb5_context kcontext, char *ename, char *whoami, krb5_boolean do_com_err)
{
const char *logging_profent[3];
const char *logging_defent[3];
char **logging_specs;
int i, ngood;
char *cp, *cp2;
char savec = '\0';
int error;
int do_openlog, log_facility;
FILE *f;
do_openlog = 0;
log_facility = 0;
err_context = kcontext;
logging_profent[0] = "logging";
logging_profent[1] = ename;
logging_profent[2] = (char *) NULL;
logging_defent[0] = "logging";
logging_defent[1] = "default";
logging_defent[2] = (char *) NULL;
logging_specs = (char **) NULL;
ngood = 0;
log_control.log_nentries = 0;
if (!profile_get_values(kcontext->profile,
logging_profent,
&logging_specs) ||
!profile_get_values(kcontext->profile,
logging_defent,
&logging_specs)) {
for (log_control.log_nentries = 0;
logging_specs[log_control.log_nentries];
log_control.log_nentries++);
log_control.log_entries = (struct log_entry *)
malloc(log_control.log_nentries * sizeof(struct log_entry));
if (log_control.log_entries) {
for (i=0; i<log_control.log_nentries; i++) {
log_control.log_entries[i].log_type = K_LOG_NONE;
log_control.log_entries[i].log_2free = logging_specs[i];
for (cp = logging_specs[i]; isspace((int) *cp); cp++);
for (cp2 = &logging_specs[i][strlen(logging_specs[i])-1];
isspace((int) *cp2); cp2--);
cp2++;
*cp2 = '\0';
if (!strncasecmp(cp, "FILE", 4)) {
if (cp[4] == ':' || cp[4] == '=') {
f = fopen(&cp[5], (cp[4] == ':') ? "a+" : "w");
if (f) {
log_control.log_entries[i].lfu_filep = f;
log_control.log_entries[i].log_type = K_LOG_FILE;
log_control.log_entries[i].lfu_fname = &cp[5];
} else {
fprintf(stderr,"Couldn't open log file %s: %s\n",
&cp[5], error_message(errno));
continue;
}
}
}
#ifdef HAVE_SYSLOG
else if (!strncasecmp(cp, "SYSLOG", 6)) {
error = 0;
log_control.log_entries[i].lsu_facility = LOG_AUTH;
log_control.log_entries[i].lsu_severity = LOG_ERR;
if (cp[6] == ':') {
cp2 = strchr(&cp[7], ':');
if (cp2) {
savec = *cp2;
*cp2 = '\0';
cp2++;
}
if (!strcasecmp(&cp[7], "ERR")) {
log_control.log_entries[i].lsu_severity = LOG_ERR;
}
#ifdef LOG_EMERG
else if (!strcasecmp(&cp[7], "EMERG")) {
log_control.log_entries[i].lsu_severity =
LOG_EMERG;
}
#endif
#ifdef LOG_ALERT
else if (!strcasecmp(&cp[7], "ALERT")) {
log_control.log_entries[i].lsu_severity =
LOG_ALERT;
}
#endif
#ifdef LOG_CRIT
else if (!strcasecmp(&cp[7], "CRIT")) {
log_control.log_entries[i].lsu_severity = LOG_CRIT;
}
#endif
#ifdef LOG_WARNING
else if (!strcasecmp(&cp[7], "WARNING")) {
log_control.log_entries[i].lsu_severity =
LOG_WARNING;
}
#endif
#ifdef LOG_NOTICE
else if (!strcasecmp(&cp[7], "NOTICE")) {
log_control.log_entries[i].lsu_severity =
LOG_NOTICE;
}
#endif
#ifdef LOG_INFO
else if (!strcasecmp(&cp[7], "INFO")) {
log_control.log_entries[i].lsu_severity = LOG_INFO;
}
#endif
#ifdef LOG_DEBUG
else if (!strcasecmp(&cp[7], "DEBUG")) {
log_control.log_entries[i].lsu_severity =
LOG_DEBUG;
}
#endif
else
error = 1;
if (cp2) {
static const struct {
const char *name;
int value;
} facilities[] = {
{ "AUTH", LOG_AUTH },
#ifdef LOG_AUTHPRIV
{ "AUTHPRIV", LOG_AUTHPRIV },
#endif
#ifdef LOG_KERN
{ "KERN", LOG_KERN },
#endif
#ifdef LOG_USER
{ "USER", LOG_USER },
#endif
#ifdef LOG_MAIL
{ "MAIL", LOG_MAIL },
#endif
#ifdef LOG_DAEMON
{ "DAEMON", LOG_DAEMON },
#endif
#ifdef LOG_FTP
{ "FTP", LOG_FTP },
#endif
#ifdef LOG_LPR
{ "LPR", LOG_LPR },
#endif
#ifdef LOG_NEWS
{ "NEWS", LOG_NEWS },
#endif
#ifdef LOG_UUCP
{ "UUCP", LOG_UUCP },
#endif
#ifdef LOG_CRON
{ "CRON", LOG_CRON },
#endif
#ifdef LOG_LOCAL0
{ "LOCAL0", LOG_LOCAL0 },
#endif
#ifdef LOG_LOCAL1
{ "LOCAL1", LOG_LOCAL1 },
#endif
#ifdef LOG_LOCAL2
{ "LOCAL2", LOG_LOCAL2 },
#endif
#ifdef LOG_LOCAL3
{ "LOCAL3", LOG_LOCAL3 },
#endif
#ifdef LOG_LOCAL4
{ "LOCAL4", LOG_LOCAL4 },
#endif
#ifdef LOG_LOCAL5
{ "LOCAL5", LOG_LOCAL5 },
#endif
#ifdef LOG_LOCAL6
{ "LOCAL6", LOG_LOCAL6 },
#endif
#ifdef LOG_LOCAL7
{ "LOCAL7", LOG_LOCAL7 },
#endif
};
int j;
for (j = 0; j < sizeof(facilities)/sizeof(facilities[0]); j++)
if (!strcasecmp(cp2, facilities[j].name)) {
log_control.log_entries[i].lsu_facility = facilities[j].value;
break;
}
cp2--;
*cp2 = savec;
}
}
if (!error) {
log_control.log_entries[i].log_type = K_LOG_SYSLOG;
do_openlog = 1;
log_facility = log_control.log_entries[i].lsu_facility;
}
}
#endif
else if (!strcasecmp(cp, "STDERR")) {
log_control.log_entries[i].lfu_filep =
fdopen(fileno(stderr), "a+");
if (log_control.log_entries[i].lfu_filep) {
log_control.log_entries[i].log_type = K_LOG_STDERR;
log_control.log_entries[i].lfu_fname =
"standard error";
}
}
else if (!strcasecmp(cp, "CONSOLE")) {
log_control.log_entries[i].ldu_filep =
CONSOLE_OPEN("a+");
if (log_control.log_entries[i].ldu_filep) {
log_control.log_entries[i].log_type = K_LOG_CONSOLE;
log_control.log_entries[i].ldu_devname = "console";
}
}
else if (!strncasecmp(cp, "DEVICE", 6)) {
if (cp[6] == '=') {
log_control.log_entries[i].ldu_filep =
DEVICE_OPEN(&cp[7], "w");
if (log_control.log_entries[i].ldu_filep) {
log_control.log_entries[i].log_type = K_LOG_DEVICE;
log_control.log_entries[i].ldu_devname = &cp[7];
}
}
}
if (log_control.log_entries[i].log_type == K_LOG_NONE) {
fprintf(stderr, lspec_parse_err_1, whoami, cp);
fprintf(stderr, lspec_parse_err_2, whoami);
}
else
ngood++;
}
}
if (ngood == 0) {
for (i=0; i<log_control.log_nentries; i++)
free(logging_specs[i]);
}
free(logging_specs);
}
if (ngood == 0) {
if (log_control.log_entries)
free(log_control.log_entries);
log_control.log_entries = &def_log_entry;
log_control.log_entries->log_type = K_LOG_SYSLOG;
log_control.log_entries->log_2free = (krb5_pointer) NULL;
log_facility = log_control.log_entries->lsu_facility = LOG_AUTH;
log_control.log_entries->lsu_severity = LOG_ERR;
do_openlog = 1;
log_control.log_nentries = 1;
}
if (log_control.log_nentries) {
log_control.log_whoami = (char *) malloc(strlen(whoami)+1);
if (log_control.log_whoami)
strcpy(log_control.log_whoami, whoami);
log_control.log_hostname = (char *) malloc(MAXHOSTNAMELEN + 1);
if (log_control.log_hostname) {
gethostname(log_control.log_hostname, MAXHOSTNAMELEN);
log_control.log_hostname[MAXHOSTNAMELEN] = '\0';
}
#ifdef HAVE_OPENLOG
if (do_openlog) {
openlog(whoami, LOG_NDELAY|LOG_PID, log_facility);
log_control.log_opened = 1;
}
#endif
if (do_com_err)
(void) set_com_err_hook(klog_com_err_proc);
}
return((log_control.log_nentries) ? 0 : ENOENT);
}
void
krb5_klog_close(krb5_context kcontext)
{
int lindex;
(void) reset_com_err_hook();
for (lindex = 0; lindex < log_control.log_nentries; lindex++) {
switch (log_control.log_entries[lindex].log_type) {
case K_LOG_FILE:
case K_LOG_STDERR:
fclose(log_control.log_entries[lindex].lfu_filep);
break;
case K_LOG_CONSOLE:
case K_LOG_DEVICE:
DEVICE_CLOSE(log_control.log_entries[lindex].ldu_filep);
break;
#ifdef HAVE_SYSLOG
case K_LOG_SYSLOG:
break;
#endif
default:
break;
}
if (log_control.log_entries[lindex].log_2free)
free(log_control.log_entries[lindex].log_2free);
}
if (log_control.log_entries != &def_log_entry)
free(log_control.log_entries);
log_control.log_entries = (struct log_entry *) NULL;
log_control.log_nentries = 0;
if (log_control.log_whoami)
free(log_control.log_whoami);
log_control.log_whoami = (char *) NULL;
if (log_control.log_hostname)
free(log_control.log_hostname);
log_control.log_hostname = (char *) NULL;
#ifdef HAVE_CLOSELOG
if (log_control.log_opened)
closelog();
#endif
}
static const char *
severity2string(int severity)
{
int s;
const char *ss;
s = severity & LOG_PRIMASK;
ss = log_ufo_string;
switch (s) {
#ifdef LOG_EMERG
case LOG_EMERG:
ss = log_emerg_string;
break;
#endif
#ifdef LOG_ALERT
case LOG_ALERT:
ss = log_alert_string;
break;
#endif
#ifdef LOG_CRIT
case LOG_CRIT:
ss = log_crit_string;
break;
#endif
case LOG_ERR:
ss = log_err_string;
break;
#ifdef LOG_WARNING
case LOG_WARNING:
ss = log_warning_string;
break;
#endif
#ifdef LOG_NOTICE
case LOG_NOTICE:
ss = log_notice_string;
break;
#endif
#ifdef LOG_INFO
case LOG_INFO:
ss = log_info_string;
break;
#endif
#ifdef LOG_DEBUG
case LOG_DEBUG:
ss = log_debug_string;
break;
#endif
}
return(ss);
}
static int
klog_vsyslog(int priority, const char *format, va_list arglist)
{
char outbuf[KRB5_KLOG_MAX_ERRMSG_SIZE];
int lindex;
char *syslogp;
char *cp;
time_t now;
#ifdef HAVE_STRFTIME
size_t soff;
#endif
cp = outbuf;
(void) time(&now);
#ifdef HAVE_STRFTIME
soff = strftime(outbuf, sizeof(outbuf), "%b %d %H:%M:%S", localtime(&now));
if (soff > 0)
cp += soff;
else
return(-1);
#else
strncpy(outbuf, ctime(&now) + 4, 15);
cp += 15;
#endif
#ifdef VERBOSE_LOGS
sprintf(cp, " %s %s[%ld](%s): ",
log_control.log_hostname ? log_control.log_hostname : "",
log_control.log_whoami ? log_control.log_whoami : "",
(long) getpid(),
severity2string(priority));
#else
sprintf(cp, " ");
#endif
syslogp = &outbuf[strlen(outbuf)];
#ifdef HAVE_VSNPRINTF
vsnprintf(syslogp, sizeof(outbuf) - (syslogp - outbuf), format, arglist);
#elif HAVE_VSPRINTF
vsprintf(syslogp, format, arglist);
#else
sprintf(syslogp, format, ((int *) arglist)[0], ((int *) arglist)[1],
((int *) arglist)[2], ((int *) arglist)[3],
((int *) arglist)[4], ((int *) arglist)[5]);
#endif
#ifdef HAVE_SYSLOG
if (log_control.log_nentries == 0) {
syslog(priority, "%s", syslogp);
}
#endif
for (lindex = 0; lindex < log_control.log_nentries; lindex++) {
switch (log_control.log_entries[lindex].log_type) {
case K_LOG_FILE:
case K_LOG_STDERR:
if (fprintf(log_control.log_entries[lindex].lfu_filep, "%s\n",
outbuf) < 0) {
fprintf(stderr, log_file_err, log_control.log_whoami,
log_control.log_entries[lindex].lfu_fname);
}
else {
fflush(log_control.log_entries[lindex].lfu_filep);
}
break;
case K_LOG_CONSOLE:
case K_LOG_DEVICE:
if (DEVICE_PRINT(log_control.log_entries[lindex].ldu_filep,
outbuf) < 0) {
fprintf(stderr, log_device_err, log_control.log_whoami,
log_control.log_entries[lindex].ldu_devname);
}
break;
#ifdef HAVE_SYSLOG
case K_LOG_SYSLOG:
syslog(priority, "%s", syslogp);
break;
#endif
default:
break;
}
}
return(0);
}
int
krb5_klog_syslog(int priority, const char *format, ...)
{
int retval;
va_list pvar;
va_start(pvar, format);
retval = klog_vsyslog(priority, format, pvar);
va_end(pvar);
return(retval);
}
void
krb5_klog_reopen(krb5_context kcontext)
{
int lindex;
FILE *f;
for (lindex = 0; lindex < log_control.log_nentries; lindex++) {
if (log_control.log_entries[lindex].log_type == K_LOG_FILE) {
fclose(log_control.log_entries[lindex].lfu_filep);
f = fopen(log_control.log_entries[lindex].lfu_fname, "a+");
if (f) {
log_control.log_entries[lindex].lfu_filep = f;
} else {
fprintf(stderr, "Couldn't open log file %s: %s\n",
log_control.log_entries[lindex].lfu_fname,
error_message(errno));
}
}
}
}