1/*	$NetBSD$	*/
2
3/*
4 * Copyright (c) 1997-2006 Kungliga Tekniska H��gskolan
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
41struct 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
49static struct facility*
50log_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
62struct s2i {
63    const char *s;
64    int val;
65};
66
67#define L(X) { #X, LOG_ ## X }
68
69static 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
112static int
113find_value(const char *s, struct s2i *table)
114{
115    while(table->s && strcasecmp(table->s, s))
116	table++;
117    return table->val;
118}
119
120KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
121krb5_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	krb5_set_error_message(context, ENOMEM,
128			       N_("malloc: out of memory", ""));
129	return ENOMEM;
130    }
131    f->program = strdup(program);
132    if(f->program == NULL){
133	free(f);
134	krb5_set_error_message(context, ENOMEM,
135			       N_("malloc: out of memory", ""));
136	return ENOMEM;
137    }
138    *fac = f;
139    return 0;
140}
141
142KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
143krb5_addlog_func(krb5_context context,
144		 krb5_log_facility *fac,
145		 int min,
146		 int max,
147		 krb5_log_log_func_t log_func,
148		 krb5_log_close_func_t close_func,
149		 void *data)
150{
151    struct facility *fp = log_realloc(fac);
152    if(fp == NULL) {
153	krb5_set_error_message(context, ENOMEM,
154			       N_("malloc: out of memory", ""));
155	return ENOMEM;
156    }
157    fp->min = min;
158    fp->max = max;
159    fp->log_func = log_func;
160    fp->close_func = close_func;
161    fp->data = data;
162    return 0;
163}
164
165
166struct _heimdal_syslog_data{
167    int priority;
168};
169
170static void KRB5_CALLCONV
171log_syslog(const char *timestr,
172	   const char *msg,
173	   void *data)
174
175{
176    struct _heimdal_syslog_data *s = data;
177    syslog(s->priority, "%s", msg);
178}
179
180static void KRB5_CALLCONV
181close_syslog(void *data)
182{
183    free(data);
184    closelog();
185}
186
187static krb5_error_code
188open_syslog(krb5_context context,
189	    krb5_log_facility *facility, int min, int max,
190	    const char *sev, const char *fac)
191{
192    struct _heimdal_syslog_data *sd = malloc(sizeof(*sd));
193    int i;
194
195    if(sd == NULL) {
196	krb5_set_error_message(context, ENOMEM,
197			       N_("malloc: out of memory", ""));
198	return ENOMEM;
199    }
200    i = find_value(sev, syslogvals);
201    if(i == -1)
202	i = LOG_ERR;
203    sd->priority = i;
204    i = find_value(fac, syslogvals);
205    if(i == -1)
206	i = LOG_AUTH;
207    sd->priority |= i;
208    roken_openlog(facility->program, LOG_PID | LOG_NDELAY, i);
209    return krb5_addlog_func(context, facility, min, max,
210			    log_syslog, close_syslog, sd);
211}
212
213struct file_data{
214    const char *filename;
215    const char *mode;
216    FILE *fd;
217    int keep_open;
218};
219
220static void KRB5_CALLCONV
221log_file(const char *timestr,
222	 const char *msg,
223	 void *data)
224{
225    struct file_data *f = data;
226    char *msgclean;
227    size_t len = strlen(msg);
228    if(f->keep_open == 0)
229	f->fd = fopen(f->filename, f->mode);
230    if(f->fd == NULL)
231	return;
232    /* make sure the log doesn't contain special chars */
233    msgclean = malloc((len + 1) * 4);
234    if (msgclean == NULL)
235	goto out;
236    strvisx(msgclean, rk_UNCONST(msg), len, VIS_OCTAL);
237    fprintf(f->fd, "%s %s\n", timestr, msgclean);
238    free(msgclean);
239 out:
240    if(f->keep_open == 0) {
241	fclose(f->fd);
242	f->fd = NULL;
243    }
244}
245
246static void KRB5_CALLCONV
247close_file(void *data)
248{
249    struct file_data *f = data;
250    if(f->keep_open && f->filename)
251	fclose(f->fd);
252    free(data);
253}
254
255static krb5_error_code
256open_file(krb5_context context, krb5_log_facility *fac, int min, int max,
257	  const char *filename, const char *mode, FILE *f, int keep_open)
258{
259    struct file_data *fd = malloc(sizeof(*fd));
260    if(fd == NULL) {
261	krb5_set_error_message(context, ENOMEM,
262			       N_("malloc: out of memory", ""));
263	return ENOMEM;
264    }
265    fd->filename = filename;
266    fd->mode = mode;
267    fd->fd = f;
268    fd->keep_open = keep_open;
269
270    return krb5_addlog_func(context, fac, min, max, log_file, close_file, fd);
271}
272
273
274
275KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
276krb5_addlog_dest(krb5_context context, krb5_log_facility *f, const char *orig)
277{
278    krb5_error_code ret = 0;
279    int min = 0, max = -1, n;
280    char c;
281    const char *p = orig;
282
283    n = sscanf(p, "%d%c%d/", &min, &c, &max);
284    if(n == 2){
285	if(c == '/') {
286	    if(min < 0){
287		max = -min;
288		min = 0;
289	    }else{
290		max = min;
291	    }
292	}
293    }
294    if(n){
295	p = strchr(p, '/');
296	if(p == NULL) {
297	    krb5_set_error_message(context, HEIM_ERR_LOG_PARSE,
298				   N_("failed to parse \"%s\"", ""), orig);
299	    return HEIM_ERR_LOG_PARSE;
300	}
301	p++;
302    }
303    if(strcmp(p, "STDERR") == 0){
304	ret = open_file(context, f, min, max, NULL, NULL, stderr, 1);
305    }else if(strcmp(p, "CONSOLE") == 0){
306	ret = open_file(context, f, min, max, "/dev/console", "w", NULL, 0);
307    }else if(strncmp(p, "FILE", 4) == 0 && (p[4] == ':' || p[4] == '=')){
308	char *fn;
309	FILE *file = NULL;
310	int keep_open = 0;
311	fn = strdup(p + 5);
312	if(fn == NULL) {
313	    krb5_set_error_message(context, ENOMEM,
314				   N_("malloc: out of memory", ""));
315	    return ENOMEM;
316	}
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) logile: %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);
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);
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
366KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
367krb5_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
390KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
391krb5_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
409KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
410krb5_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
448KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
449krb5_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
459KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
460krb5_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
478KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
479krb5_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
495void 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
512krb5_boolean KRB5_LIB_FUNCTION
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