Home | History | Annotate | Line # | Download | only in krb5
      1 /*	$NetBSD: log.c,v 1.3 2019/12/15 22:50:50 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997-2006 Kungliga Tekniska Hgskolan
      5  * (Royal Institute of Technology, Stockholm, Sweden).
      6  * All rights reserved.
      7  *
      8  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  *
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  *
     17  * 2. Redistributions in binary form must reproduce the above copyright
     18  *    notice, this list of conditions and the following disclaimer in the
     19  *    documentation and/or other materials provided with the distribution.
     20  *
     21  * 3. Neither the name of the Institute nor the names of its contributors
     22  *    may be used to endorse or promote products derived from this software
     23  *    without specific prior written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     35  * SUCH DAMAGE.
     36  */
     37 
     38 #include "krb5_locl.h"
     39 #include <vis.h>
     40 
     41 struct facility {
     42     int min;
     43     int max;
     44     krb5_log_log_func_t log_func;
     45     krb5_log_close_func_t close_func;
     46     void *data;
     47 };
     48 
     49 static struct facility*
     50 log_realloc(krb5_log_facility *f)
     51 {
     52     struct facility *fp;
     53     fp = realloc(f->val, (f->len + 1) * sizeof(*f->val));
     54     if(fp == NULL)
     55 	return NULL;
     56     f->len++;
     57     f->val = fp;
     58     fp += f->len - 1;
     59     return fp;
     60 }
     61 
     62 struct s2i {
     63     const char *s;
     64     int val;
     65 };
     66 
     67 #define L(X) { #X, LOG_ ## X }
     68 
     69 static struct s2i syslogvals[] = {
     70     L(EMERG),
     71     L(ALERT),
     72     L(CRIT),
     73     L(ERR),
     74     L(WARNING),
     75     L(NOTICE),
     76     L(INFO),
     77     L(DEBUG),
     78 
     79     L(AUTH),
     80 #ifdef LOG_AUTHPRIV
     81     L(AUTHPRIV),
     82 #endif
     83 #ifdef LOG_CRON
     84     L(CRON),
     85 #endif
     86     L(DAEMON),
     87 #ifdef LOG_FTP
     88     L(FTP),
     89 #endif
     90     L(KERN),
     91     L(LPR),
     92     L(MAIL),
     93 #ifdef LOG_NEWS
     94     L(NEWS),
     95 #endif
     96     L(SYSLOG),
     97     L(USER),
     98 #ifdef LOG_UUCP
     99     L(UUCP),
    100 #endif
    101     L(LOCAL0),
    102     L(LOCAL1),
    103     L(LOCAL2),
    104     L(LOCAL3),
    105     L(LOCAL4),
    106     L(LOCAL5),
    107     L(LOCAL6),
    108     L(LOCAL7),
    109     { NULL, -1 }
    110 };
    111 
    112 static int
    113 find_value(const char *s, struct s2i *table)
    114 {
    115     while(table->s && strcasecmp(table->s, s))
    116 	table++;
    117     return table->val;
    118 }
    119 
    120 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    121 krb5_initlog(krb5_context context,
    122 	     const char *program,
    123 	     krb5_log_facility **fac)
    124 {
    125     krb5_log_facility *f = calloc(1, sizeof(*f));
    126     if (f == NULL)
    127 	return krb5_enomem(context);
    128     f->program = strdup(program);
    129     if(f->program == NULL){
    130 	free(f);
    131 	return krb5_enomem(context);
    132     }
    133     *fac = f;
    134     return 0;
    135 }
    136 
    137 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    138 krb5_addlog_func(krb5_context context,
    139 		 krb5_log_facility *fac,
    140 		 int min,
    141 		 int max,
    142 		 krb5_log_log_func_t log_func,
    143 		 krb5_log_close_func_t close_func,
    144 		 void *data)
    145 {
    146     struct facility *fp = log_realloc(fac);
    147     if (fp == NULL)
    148 	return krb5_enomem(context);
    149     fp->min = min;
    150     fp->max = max;
    151     fp->log_func = log_func;
    152     fp->close_func = close_func;
    153     fp->data = data;
    154     return 0;
    155 }
    156 
    157 
    158 struct _heimdal_syslog_data{
    159     int priority;
    160 };
    161 
    162 static void KRB5_CALLCONV
    163 log_syslog(const char *timestr,
    164 	   const char *msg,
    165 	   void *data)
    166 
    167 {
    168     struct _heimdal_syslog_data *s = data;
    169     syslog(s->priority, "%s", msg);
    170 }
    171 
    172 static void KRB5_CALLCONV
    173 close_syslog(void *data)
    174 {
    175     free(data);
    176     closelog();
    177 }
    178 
    179 static krb5_error_code
    180 open_syslog(krb5_context context,
    181 	    krb5_log_facility *facility, int min, int max,
    182 	    const char *sev, const char *fac)
    183 {
    184     struct _heimdal_syslog_data *sd = malloc(sizeof(*sd));
    185     int i;
    186 
    187     if (sd == NULL)
    188 	return krb5_enomem(context);
    189     i = find_value(sev, syslogvals);
    190     if(i == -1)
    191 	i = LOG_ERR;
    192     sd->priority = i;
    193     i = find_value(fac, syslogvals);
    194     if(i == -1)
    195 	i = LOG_AUTH;
    196     sd->priority |= i;
    197     roken_openlog(facility->program, LOG_PID | LOG_NDELAY, i);
    198     return krb5_addlog_func(context, facility, min, max,
    199 			    log_syslog, close_syslog, sd);
    200 }
    201 
    202 struct file_data{
    203     const char *filename;
    204     const char *mode;
    205     FILE *fd;
    206     int keep_open;
    207     int freefilename;
    208 };
    209 
    210 static void KRB5_CALLCONV
    211 log_file(const char *timestr,
    212 	 const char *msg,
    213 	 void *data)
    214 {
    215     struct file_data *f = data;
    216     char *msgclean;
    217     size_t len = strlen(msg);
    218     if(f->keep_open == 0)
    219 	f->fd = fopen(f->filename, f->mode);
    220     if(f->fd == NULL)
    221 	return;
    222     /* make sure the log doesn't contain special chars */
    223     msgclean = malloc((len + 1) * 4);
    224     if (msgclean == NULL)
    225 	goto out;
    226     strvisx(msgclean, rk_UNCONST(msg), len, VIS_OCTAL);
    227     fprintf(f->fd, "%s %s\n", timestr, msgclean);
    228     free(msgclean);
    229  out:
    230     if(f->keep_open == 0) {
    231 	fclose(f->fd);
    232 	f->fd = NULL;
    233     }
    234 }
    235 
    236 static void KRB5_CALLCONV
    237 close_file(void *data)
    238 {
    239     struct file_data *f = data;
    240     if(f->keep_open && f->filename)
    241 	fclose(f->fd);
    242     if (f->filename && f->freefilename)
    243 	free((char *)f->filename);
    244     free(data);
    245 }
    246 
    247 static krb5_error_code
    248 open_file(krb5_context context, krb5_log_facility *fac, int min, int max,
    249 	  const char *filename, const char *mode, FILE *f, int keep_open,
    250 	  int freefilename)
    251 {
    252     struct file_data *fd = malloc(sizeof(*fd));
    253     if (fd == NULL) {
    254 	if (freefilename && filename)
    255 	    free((char *)filename);
    256 	return krb5_enomem(context);
    257     }
    258     fd->filename = filename;
    259     fd->mode = mode;
    260     fd->fd = f;
    261     fd->keep_open = keep_open;
    262     fd->freefilename = freefilename;
    263 
    264     return krb5_addlog_func(context, fac, min, max, log_file, close_file, fd);
    265 }
    266 
    267 
    268 
    269 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    270 krb5_addlog_dest(krb5_context context, krb5_log_facility *f, const char *orig)
    271 {
    272     krb5_error_code ret = 0;
    273     int min = 0, max = -1, n;
    274     char c;
    275     const char *p = orig;
    276 #ifdef _WIN32
    277     const char *q;
    278 #endif
    279 
    280     n = sscanf(p, "%d%c%d/", &min, &c, &max);
    281     if(n == 2){
    282 	if(ISPATHSEP(c)) {
    283 	    if(min < 0){
    284 		max = -min;
    285 		min = 0;
    286 	    }else{
    287 		max = min;
    288 	    }
    289 	}
    290     }
    291     if(n){
    292 #ifdef _WIN32
    293 	q = strrchr(p, '\\');
    294 	if (q != NULL)
    295 	    p = q;
    296 	else
    297 #endif
    298 	p = strchr(p, '/');
    299 	if(p == NULL) {
    300 	    krb5_set_error_message(context, HEIM_ERR_LOG_PARSE,
    301 				   N_("failed to parse \"%s\"", ""), orig);
    302 	    return HEIM_ERR_LOG_PARSE;
    303 	}
    304 	p++;
    305     }
    306     if(strcmp(p, "STDERR") == 0){
    307 	ret = open_file(context, f, min, max, NULL, NULL, stderr, 1, 0);
    308     }else if(strcmp(p, "CONSOLE") == 0){
    309 	ret = open_file(context, f, min, max, "/dev/console", "w", NULL, 0, 0);
    310     }else if(strncmp(p, "FILE", 4) == 0 && (p[4] == ':' || p[4] == '=')){
    311 	char *fn;
    312 	FILE *file = NULL;
    313 	int keep_open = 0;
    314 	fn = strdup(p + 5);
    315 	if (fn == NULL)
    316 	    return krb5_enomem(context);
    317 	if(p[4] == '='){
    318 	    int i = open(fn, O_WRONLY | O_CREAT |
    319 			 O_TRUNC | O_APPEND, 0666);
    320 	    if(i < 0) {
    321 		ret = errno;
    322 		krb5_set_error_message(context, ret,
    323 				       N_("open(%s) logfile: %s", ""), fn,
    324 				       strerror(ret));
    325 		free(fn);
    326 		return ret;
    327 	    }
    328 	    rk_cloexec(i);
    329 	    file = fdopen(i, "a");
    330 	    if(file == NULL){
    331 		ret = errno;
    332 		close(i);
    333 		krb5_set_error_message(context, ret,
    334 				       N_("fdopen(%s) logfile: %s", ""),
    335 				       fn, strerror(ret));
    336 		free(fn);
    337 		return ret;
    338 	    }
    339 	    keep_open = 1;
    340 	}
    341 	ret = open_file(context, f, min, max, fn, "a", file, keep_open, 1);
    342     }else if(strncmp(p, "DEVICE", 6) == 0 && (p[6] == ':' || p[6] == '=')){
    343 	ret = open_file(context, f, min, max, strdup(p + 7), "w", NULL, 0, 1);
    344     }else if(strncmp(p, "SYSLOG", 6) == 0 && (p[6] == '\0' || p[6] == ':')){
    345 	char severity[128] = "";
    346 	char facility[128] = "";
    347 	p += 6;
    348 	if(*p != '\0')
    349 	    p++;
    350 	if(strsep_copy(&p, ":", severity, sizeof(severity)) != -1)
    351 	    strsep_copy(&p, ":", facility, sizeof(facility));
    352 	if(*severity == '\0')
    353 	    strlcpy(severity, "ERR", sizeof(severity));
    354  	if(*facility == '\0')
    355 	    strlcpy(facility, "AUTH", sizeof(facility));
    356 	ret = open_syslog(context, f, min, max, severity, facility);
    357     }else{
    358 	ret = HEIM_ERR_LOG_PARSE; /* XXX */
    359 	krb5_set_error_message (context, ret,
    360 				N_("unknown log type: %s", ""), p);
    361     }
    362     return ret;
    363 }
    364 
    365 
    366 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    367 krb5_openlog(krb5_context context,
    368 	     const char *program,
    369 	     krb5_log_facility **fac)
    370 {
    371     krb5_error_code ret;
    372     char **p, **q;
    373 
    374     ret = krb5_initlog(context, program, fac);
    375     if(ret)
    376 	return ret;
    377 
    378     p = krb5_config_get_strings(context, NULL, "logging", program, NULL);
    379     if(p == NULL)
    380 	p = krb5_config_get_strings(context, NULL, "logging", "default", NULL);
    381     if(p){
    382 	for(q = p; *q && ret == 0; q++)
    383 	    ret = krb5_addlog_dest(context, *fac, *q);
    384 	krb5_config_free_strings(p);
    385     }else
    386 	ret = krb5_addlog_dest(context, *fac, "SYSLOG");
    387     return ret;
    388 }
    389 
    390 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    391 krb5_closelog(krb5_context context,
    392 	      krb5_log_facility *fac)
    393 {
    394     int i;
    395     for(i = 0; i < fac->len; i++)
    396 	(*fac->val[i].close_func)(fac->val[i].data);
    397     free(fac->val);
    398     free(fac->program);
    399     fac->val = NULL;
    400     fac->len = 0;
    401     fac->program = NULL;
    402     free(fac);
    403     return 0;
    404 }
    405 
    406 #undef __attribute__
    407 #define __attribute__(X)
    408 
    409 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    410 krb5_vlog_msg(krb5_context context,
    411 	      krb5_log_facility *fac,
    412 	      char **reply,
    413 	      int level,
    414 	      const char *fmt,
    415 	      va_list ap)
    416      __attribute__ ((__format__ (__printf__, 5, 0)))
    417 {
    418 
    419     char *msg = NULL;
    420     const char *actual = NULL;
    421     char buf[64];
    422     time_t t = 0;
    423     int i;
    424 
    425     for(i = 0; fac && i < fac->len; i++)
    426 	if(fac->val[i].min <= level &&
    427 	   (fac->val[i].max < 0 || fac->val[i].max >= level)) {
    428 	    if(t == 0) {
    429 		t = time(NULL);
    430 		krb5_format_time(context, t, buf, sizeof(buf), TRUE);
    431 	    }
    432 	    if(actual == NULL) {
    433 		int ret = vasprintf(&msg, fmt, ap);
    434 		if(ret < 0 || msg == NULL)
    435 		    actual = fmt;
    436 		else
    437 		    actual = msg;
    438 	    }
    439 	    (*fac->val[i].log_func)(buf, actual, fac->val[i].data);
    440 	}
    441     if(reply == NULL)
    442 	free(msg);
    443     else
    444 	*reply = msg;
    445     return 0;
    446 }
    447 
    448 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    449 krb5_vlog(krb5_context context,
    450 	  krb5_log_facility *fac,
    451 	  int level,
    452 	  const char *fmt,
    453 	  va_list ap)
    454      __attribute__ ((__format__ (__printf__, 4, 0)))
    455 {
    456     return krb5_vlog_msg(context, fac, NULL, level, fmt, ap);
    457 }
    458 
    459 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    460 krb5_log_msg(krb5_context context,
    461 	     krb5_log_facility *fac,
    462 	     int level,
    463 	     char **reply,
    464 	     const char *fmt,
    465 	     ...)
    466      __attribute__ ((__format__ (__printf__, 5, 6)))
    467 {
    468     va_list ap;
    469     krb5_error_code ret;
    470 
    471     va_start(ap, fmt);
    472     ret = krb5_vlog_msg(context, fac, reply, level, fmt, ap);
    473     va_end(ap);
    474     return ret;
    475 }
    476 
    477 
    478 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    479 krb5_log(krb5_context context,
    480 	 krb5_log_facility *fac,
    481 	 int level,
    482 	 const char *fmt,
    483 	 ...)
    484      __attribute__ ((__format__ (__printf__, 4, 5)))
    485 {
    486     va_list ap;
    487     krb5_error_code ret;
    488 
    489     va_start(ap, fmt);
    490     ret = krb5_vlog(context, fac, level, fmt, ap);
    491     va_end(ap);
    492     return ret;
    493 }
    494 
    495 void KRB5_LIB_FUNCTION
    496 _krb5_debug(krb5_context context,
    497 	    int level,
    498 	    const char *fmt,
    499 	    ...)
    500     __attribute__ ((__format__ (__printf__, 3, 4)))
    501 {
    502     va_list ap;
    503 
    504     if (context == NULL || context->debug_dest == NULL)
    505 	return;
    506 
    507     va_start(ap, fmt);
    508     krb5_vlog(context, context->debug_dest, level, fmt, ap);
    509     va_end(ap);
    510 }
    511 
    512 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
    513 _krb5_have_debug(krb5_context context, int level)
    514 {
    515     if (context == NULL || context->debug_dest == NULL)
    516 	return 0 ;
    517     return 1;
    518 }
    519 
    520 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    521 krb5_set_debug_dest(krb5_context context, const char *program,
    522                     const char *log_spec)
    523 {
    524     krb5_error_code ret;
    525 
    526     if (context->debug_dest == NULL) {
    527         ret = krb5_initlog(context, program, &context->debug_dest);
    528         if (ret)
    529             return ret;
    530     }
    531 
    532     ret = krb5_addlog_dest(context, context->debug_dest, log_spec);
    533     if (ret)
    534         return ret;
    535     return 0;
    536 }
    537