1/*	$NetBSD: config_file.c,v 1.5 2023/06/19 21:41:44 christos Exp $	*/
2
3/*
4 * Copyright (c) 1997 - 2004 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
40#ifdef __APPLE__
41#include <CoreFoundation/CoreFoundation.h>
42#endif
43
44/* Gaah! I want a portable funopen */
45struct fileptr {
46    const char *s;
47    FILE *f;
48};
49
50static char *
51config_fgets(char *str, size_t len, struct fileptr *ptr)
52{
53    /* XXX this is not correct, in that they don't do the same if the
54       line is longer than len */
55    if(ptr->f != NULL)
56	return fgets(str, len, ptr->f);
57    else {
58	/* this is almost strsep_copy */
59	const char *p;
60	ssize_t l;
61	if(*ptr->s == '\0')
62	    return NULL;
63	p = ptr->s + strcspn(ptr->s, "\n");
64	if(*p == '\n')
65	    p++;
66	l = min(len, (size_t)(p - ptr->s));
67	if(len > 0) {
68	    memcpy(str, ptr->s, l);
69	    str[l] = '\0';
70	}
71	ptr->s = p;
72	return str;
73    }
74}
75
76static krb5_error_code parse_section(char *p, krb5_config_section **s,
77				     krb5_config_section **res,
78				     const char **err_message);
79static krb5_error_code parse_binding(struct fileptr *f, unsigned *lineno, char *p,
80				     krb5_config_binding **b,
81				     krb5_config_binding **parent,
82				     const char **err_message);
83static krb5_error_code parse_list(struct fileptr *f, unsigned *lineno,
84				  krb5_config_binding **parent,
85				  const char **err_message);
86
87KRB5_LIB_FUNCTION krb5_config_section * KRB5_LIB_CALL
88_krb5_config_get_entry(krb5_config_section **parent, const char *name, int type)
89{
90    krb5_config_section **q;
91
92    for(q = parent; *q != NULL; q = &(*q)->next)
93	if(type == krb5_config_list &&
94	   (unsigned)type == (*q)->type &&
95	   strcmp(name, (*q)->name) == 0)
96	    return *q;
97    *q = calloc(1, sizeof(**q));
98    if(*q == NULL)
99	return NULL;
100    (*q)->name = strdup(name);
101    (*q)->type = type;
102    if((*q)->name == NULL) {
103	free(*q);
104	*q = NULL;
105	return NULL;
106    }
107    return *q;
108}
109
110/*
111 * Parse a section:
112 *
113 * [section]
114 *	foo = bar
115 *	b = {
116 *		a
117 *	    }
118 * ...
119 *
120 * starting at the line in `p', storing the resulting structure in
121 * `s' and hooking it into `parent'.
122 * Store the error message in `err_message'.
123 */
124
125static krb5_error_code
126parse_section(char *p, krb5_config_section **s, krb5_config_section **parent,
127	      const char **err_message)
128{
129    char *p1;
130    krb5_config_section *tmp;
131
132    p1 = strchr (p + 1, ']');
133    if (p1 == NULL) {
134	*err_message = "missing ]";
135	return KRB5_CONFIG_BADFORMAT;
136    }
137    *p1 = '\0';
138    tmp = _krb5_config_get_entry(parent, p + 1, krb5_config_list);
139    if(tmp == NULL) {
140	*err_message = "out of memory";
141	return KRB5_CONFIG_BADFORMAT;
142    }
143    *s = tmp;
144    return 0;
145}
146
147/*
148 * Parse a brace-enclosed list from `f', hooking in the structure at
149 * `parent'.
150 * Store the error message in `err_message'.
151 */
152
153static krb5_error_code
154parse_list(struct fileptr *f, unsigned *lineno, krb5_config_binding **parent,
155	   const char **err_message)
156{
157    char buf[KRB5_BUFSIZ];
158    krb5_error_code ret;
159    krb5_config_binding *b = NULL;
160    unsigned beg_lineno = *lineno;
161
162    while(config_fgets(buf, sizeof(buf), f) != NULL) {
163	char *p;
164
165	++*lineno;
166	buf[strcspn(buf, "\r\n")] = '\0';
167	p = buf;
168	while(isspace((unsigned char)*p))
169	    ++p;
170	if (*p == '#' || *p == ';' || *p == '\0')
171	    continue;
172	while(isspace((unsigned char)*p))
173	    ++p;
174	if (*p == '}')
175	    return 0;
176	if (*p == '\0')
177	    continue;
178	ret = parse_binding (f, lineno, p, &b, parent, err_message);
179	if (ret)
180	    return ret;
181    }
182    *lineno = beg_lineno;
183    *err_message = "unclosed {";
184    return KRB5_CONFIG_BADFORMAT;
185}
186
187/*
188 *
189 */
190
191static krb5_error_code
192parse_binding(struct fileptr *f, unsigned *lineno, char *p,
193	      krb5_config_binding **b, krb5_config_binding **parent,
194	      const char **err_message)
195{
196    krb5_config_binding *tmp;
197    char *p1, *p2;
198    krb5_error_code ret = 0;
199
200    p1 = p;
201    while (*p && *p != '=' && !isspace((unsigned char)*p))
202	++p;
203    if (*p == '\0') {
204	*err_message = "missing =";
205	return KRB5_CONFIG_BADFORMAT;
206    }
207    p2 = p;
208    while (isspace((unsigned char)*p))
209	++p;
210    if (*p != '=') {
211	*err_message = "missing =";
212	return KRB5_CONFIG_BADFORMAT;
213    }
214    ++p;
215    while(isspace((unsigned char)*p))
216	++p;
217    *p2 = '\0';
218    if (*p == '{') {
219	tmp = _krb5_config_get_entry(parent, p1, krb5_config_list);
220	if (tmp == NULL) {
221	    *err_message = "out of memory";
222	    return KRB5_CONFIG_BADFORMAT;
223	}
224	ret = parse_list (f, lineno, &tmp->u.list, err_message);
225    } else {
226	tmp = _krb5_config_get_entry(parent, p1, krb5_config_string);
227	if (tmp == NULL) {
228	    *err_message = "out of memory";
229	    return KRB5_CONFIG_BADFORMAT;
230	}
231	p1 = p;
232	p = p1 + strlen(p1);
233	while(p > p1 && isspace((unsigned char)*(p-1)))
234	    --p;
235	*p = '\0';
236	tmp->u.string = strdup(p1);
237    }
238    *b = tmp;
239    return ret;
240}
241
242#if defined(__APPLE__)
243
244#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
245#define HAVE_CFPROPERTYLISTCREATEWITHSTREAM 1
246#endif
247
248static char *
249cfstring2cstring(CFStringRef string)
250{
251    CFIndex len;
252    char *str;
253
254    str = (char *) CFStringGetCStringPtr(string, kCFStringEncodingUTF8);
255    if (str)
256	return strdup(str);
257
258    len = CFStringGetLength(string);
259    len = 1 + CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8);
260    str = malloc(len);
261    if (str == NULL)
262	return NULL;
263
264    if (!CFStringGetCString (string, str, len, kCFStringEncodingUTF8)) {
265	free (str);
266	return NULL;
267    }
268    return str;
269}
270
271static void
272convert_content(const void *key, const void *value, void *context)
273{
274    krb5_config_section *tmp, **parent = context;
275    char *k;
276
277    if (CFGetTypeID(key) != CFStringGetTypeID())
278	return;
279
280    k = cfstring2cstring(key);
281    if (k == NULL)
282	return;
283
284    if (CFGetTypeID(value) == CFStringGetTypeID()) {
285	tmp = _krb5_config_get_entry(parent, k, krb5_config_string);
286	tmp->u.string = cfstring2cstring(value);
287    } else if (CFGetTypeID(value) == CFDictionaryGetTypeID()) {
288	tmp = _krb5_config_get_entry(parent, k, krb5_config_list);
289	CFDictionaryApplyFunction(value, convert_content, &tmp->u.list);
290    } else {
291	/* log */
292    }
293    free(k);
294}
295
296static krb5_error_code
297parse_plist_config(krb5_context context, const char *path, krb5_config_section **parent)
298{
299    CFReadStreamRef s;
300    CFDictionaryRef d;
301    CFURLRef url;
302
303    url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8 *)path, strlen(path), FALSE);
304    if (url == NULL) {
305	krb5_clear_error_message(context);
306	return ENOMEM;
307    }
308
309    s = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
310    CFRelease(url);
311    if (s == NULL) {
312	krb5_clear_error_message(context);
313	return ENOMEM;
314    }
315
316    if (!CFReadStreamOpen(s)) {
317	CFRelease(s);
318	krb5_clear_error_message(context);
319	return ENOENT;
320    }
321
322#ifdef HAVE_CFPROPERTYLISTCREATEWITHSTREAM
323    d = (CFDictionaryRef)CFPropertyListCreateWithStream(NULL, s, 0, kCFPropertyListImmutable, NULL, NULL);
324#else
325    d = (CFDictionaryRef)CFPropertyListCreateFromStream(NULL, s, 0, kCFPropertyListImmutable, NULL, NULL);
326#endif
327    CFRelease(s);
328    if (d == NULL) {
329	krb5_clear_error_message(context);
330	return ENOENT;
331    }
332
333    CFDictionaryApplyFunction(d, convert_content, parent);
334    CFRelease(d);
335
336    return 0;
337}
338
339#endif
340
341
342/*
343 * Parse the config file `fname', generating the structures into `res'
344 * returning error messages in `err_message'
345 */
346
347static krb5_error_code
348krb5_config_parse_debug (struct fileptr *f,
349			 krb5_config_section **res,
350			 unsigned *lineno,
351			 const char **err_message)
352{
353    krb5_config_section *s = NULL;
354    krb5_config_binding *b = NULL;
355    char buf[KRB5_BUFSIZ];
356    krb5_error_code ret;
357
358    *lineno = 0;
359    *err_message = "";
360
361    while (config_fgets(buf, sizeof(buf), f) != NULL) {
362	char *p;
363
364	++*lineno;
365	buf[strcspn(buf, "\r\n")] = '\0';
366	p = buf;
367	while(isspace((unsigned char)*p))
368	    ++p;
369	if (*p == '#' || *p == ';')
370	    continue;
371	if (*p == '[') {
372	    ret = parse_section(p, &s, res, err_message);
373	    if (ret)
374		return ret;
375	    b = NULL;
376	} else if (*p == '}') {
377	    *err_message = "unmatched }";
378	    return KRB5_CONFIG_BADFORMAT;
379	} else if(*p != '\0') {
380	    if (s == NULL) {
381		*err_message = "binding before section";
382		return KRB5_CONFIG_BADFORMAT;
383	    }
384	    ret = parse_binding(f, lineno, p, &b, &s->u.list, err_message);
385	    if (ret)
386		return ret;
387	}
388    }
389    return 0;
390}
391
392static int
393is_plist_file(const char *fname)
394{
395    size_t len = strlen(fname);
396    char suffix[] = ".plist";
397    if (len < sizeof(suffix))
398	return 0;
399    if (strcasecmp(&fname[len - (sizeof(suffix) - 1)], suffix) != 0)
400	return 0;
401    return 1;
402}
403
404/**
405 * Parse a configuration file and add the result into res. This
406 * interface can be used to parse several configuration files into one
407 * resulting krb5_config_section by calling it repeatably.
408 *
409 * @param context a Kerberos 5 context.
410 * @param fname a file name to a Kerberos configuration file
411 * @param res the returned result, must be free with krb5_free_config_files().
412 * @return Return an error code or 0, see krb5_get_error_message().
413 *
414 * @ingroup krb5_support
415 */
416
417KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
418krb5_config_parse_file_multi (krb5_context context,
419			      const char *fname,
420			      krb5_config_section **res)
421{
422    const char *str;
423    char *newfname = NULL;
424    unsigned lineno = 0;
425    krb5_error_code ret;
426    struct fileptr f;
427
428    /**
429     * If the fname starts with "~/" parse configuration file in the
430     * current users home directory. The behavior can be disabled and
431     * enabled by calling krb5_set_home_dir_access().
432     */
433    if (ISTILDE(fname[0]) && ISPATHSEP(fname[1])) {
434#ifndef KRB5_USE_PATH_TOKENS
435	const char *home = NULL;
436	struct passwd pw, *pwd = NULL;
437	char pwbuf[2048];
438
439	if (!_krb5_homedir_access(context)) {
440	    krb5_set_error_message(context, EPERM,
441				   "Access to home directory not allowed");
442	    return EPERM;
443	}
444
445	if(!issuid())
446	    home = getenv("HOME");
447
448	if (home == NULL) {
449	    if (rk_getpwuid_r(getuid(), &pw, pwbuf, sizeof(pwbuf), &pwd) == 0)
450		home = pwd->pw_dir;
451	}
452	if (home) {
453	    int aret;
454
455	    aret = asprintf(&newfname, "%s%s", home, &fname[1]);
456	    if (aret == -1 || newfname == NULL)
457		return krb5_enomem(context);
458	    fname = newfname;
459	}
460#else  /* KRB5_USE_PATH_TOKENS */
461	if (asprintf(&newfname, "%%{USERCONFIG}%s", &fname[1]) < 0 ||
462	    newfname == NULL)
463	    return krb5_enomem(context);
464	fname = newfname;
465#endif
466    }
467
468    if (is_plist_file(fname)) {
469#ifdef __APPLE__
470	ret = parse_plist_config(context, fname, res);
471	if (ret) {
472	    krb5_set_error_message(context, ret,
473				   "Failed to parse plist %s", fname);
474	    if (newfname)
475		free(newfname);
476	    return ret;
477	}
478#else
479	krb5_set_error_message(context, ENOENT,
480			       "no support for plist configuration files");
481	return ENOENT;
482#endif
483    } else {
484#ifdef KRB5_USE_PATH_TOKENS
485	char * exp_fname = NULL;
486
487	ret = _krb5_expand_path_tokens(context, fname, 1, &exp_fname);
488	if (ret) {
489	    if (newfname)
490		free(newfname);
491	    return ret;
492	}
493
494	if (newfname)
495	    free(newfname);
496	fname = newfname = exp_fname;
497#endif
498
499	f.f = fopen(fname, "r");
500	f.s = NULL;
501	if(f.f == NULL) {
502	    ret = errno;
503	    krb5_set_error_message (context, ret, "open %s: %s",
504				    fname, strerror(ret));
505	    if (newfname)
506		free(newfname);
507	    return ret;
508	}
509
510	ret = krb5_config_parse_debug (&f, res, &lineno, &str);
511	fclose(f.f);
512	if (ret) {
513	    krb5_set_error_message (context, ret, "%s:%u: %s",
514				    fname, lineno, str);
515	    if (newfname)
516		free(newfname);
517	    return ret;
518	}
519    }
520    return 0;
521}
522
523KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
524krb5_config_parse_file (krb5_context context,
525			const char *fname,
526			krb5_config_section **res)
527{
528    *res = NULL;
529    return krb5_config_parse_file_multi(context, fname, res);
530}
531
532static void
533free_binding (krb5_context context, krb5_config_binding *b)
534{
535    krb5_config_binding *next_b;
536
537    while (b) {
538	free (b->name);
539	if (b->type == krb5_config_string)
540	    free (b->u.string);
541	else if (b->type == krb5_config_list)
542	    free_binding (context, b->u.list);
543	else
544	    krb5_abortx(context, "unknown binding type (%d) in free_binding",
545			b->type);
546	next_b = b->next;
547	free (b);
548	b = next_b;
549    }
550}
551
552/**
553 * Free configuration file section, the result of
554 * krb5_config_parse_file() and krb5_config_parse_file_multi().
555 *
556 * @param context A Kerberos 5 context
557 * @param s the configuration section to free
558 *
559 * @return returns 0 on successes, otherwise an error code, see
560 *          krb5_get_error_message()
561 *
562 * @ingroup krb5_support
563 */
564
565KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
566krb5_config_file_free (krb5_context context, krb5_config_section *s)
567{
568    free_binding (context, s);
569    return 0;
570}
571
572#ifndef HEIMDAL_SMALLER
573
574KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
575_krb5_config_copy(krb5_context context,
576		  krb5_config_section *c,
577		  krb5_config_section **head)
578{
579    krb5_config_binding *d, *previous = NULL;
580
581    *head = NULL;
582
583    while (c) {
584	d = calloc(1, sizeof(*d));
585
586	if (*head == NULL)
587	    *head = d;
588
589	d->name = strdup(c->name);
590	d->type = c->type;
591	if (d->type == krb5_config_string)
592	    d->u.string = strdup(c->u.string);
593	else if (d->type == krb5_config_list)
594	    _krb5_config_copy (context, c->u.list, &d->u.list);
595	else
596	    krb5_abortx(context,
597			"unknown binding type (%d) in krb5_config_copy",
598			d->type);
599	if (previous)
600	    previous->next = d;
601
602	previous = d;
603	c = c->next;
604    }
605    return 0;
606}
607
608#endif /* HEIMDAL_SMALLER */
609
610KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
611_krb5_config_get_next (krb5_context context,
612		       const krb5_config_section *c,
613		       const krb5_config_binding **pointer,
614		       int type,
615		       ...)
616{
617    const char *ret;
618    va_list args;
619
620    va_start(args, type);
621    ret = _krb5_config_vget_next (context, c, pointer, type, args);
622    va_end(args);
623    return ret;
624}
625
626static const void *
627vget_next(krb5_context context,
628	  const krb5_config_binding *b,
629	  const krb5_config_binding **pointer,
630	  int type,
631	  const char *name,
632	  va_list args)
633{
634    const char *p = va_arg(args, const char *);
635    while(b != NULL) {
636	if(strcmp(b->name, name) == 0) {
637	    if(b->type == (unsigned)type && p == NULL) {
638		*pointer = b;
639		return b->u.generic;
640	    } else if(b->type == krb5_config_list && p != NULL) {
641		return vget_next(context, b->u.list, pointer, type, p, args);
642	    }
643	}
644	b = b->next;
645    }
646    return NULL;
647}
648
649KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
650_krb5_config_vget_next (krb5_context context,
651			const krb5_config_section *c,
652			const krb5_config_binding **pointer,
653			int type,
654			va_list args)
655{
656    const krb5_config_binding *b;
657    const char *p;
658
659    if(c == NULL)
660	c = context->cf;
661
662    if (c == NULL)
663	return NULL;
664
665    if (*pointer == NULL) {
666	/* first time here, walk down the tree looking for the right
667           section */
668	p = va_arg(args, const char *);
669	if (p == NULL)
670	    return NULL;
671	return vget_next(context, c, pointer, type, p, args);
672    }
673
674    /* we were called again, so just look for more entries with the
675       same name and type */
676    for (b = (*pointer)->next; b != NULL; b = b->next) {
677	if(strcmp(b->name, (*pointer)->name) == 0 && b->type == (unsigned)type) {
678	    *pointer = b;
679	    return b->u.generic;
680	}
681    }
682    return NULL;
683}
684
685KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
686_krb5_config_get (krb5_context context,
687		  const krb5_config_section *c,
688		  int type,
689		  ...)
690{
691    const void *ret;
692    va_list args;
693
694    va_start(args, type);
695    ret = _krb5_config_vget (context, c, type, args);
696    va_end(args);
697    return ret;
698}
699
700
701KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
702_krb5_config_vget (krb5_context context,
703		   const krb5_config_section *c,
704		   int type,
705		   va_list args)
706{
707    const krb5_config_binding *foo = NULL;
708
709    return _krb5_config_vget_next (context, c, &foo, type, args);
710}
711
712/**
713 * Get a list of configuration binding list for more processing
714 *
715 * @param context A Kerberos 5 context.
716 * @param c a configuration section, or NULL to use the section from context
717 * @param ... a list of names, terminated with NULL.
718 *
719 * @return NULL if configuration list is not found, a list otherwise
720 *
721 * @ingroup krb5_support
722 */
723
724KRB5_LIB_FUNCTION const krb5_config_binding * KRB5_LIB_CALL
725krb5_config_get_list (krb5_context context,
726		      const krb5_config_section *c,
727		      ...)
728{
729    const krb5_config_binding *ret;
730    va_list args;
731
732    va_start(args, c);
733    ret = krb5_config_vget_list (context, c, args);
734    va_end(args);
735    return ret;
736}
737
738/**
739 * Get a list of configuration binding list for more processing
740 *
741 * @param context A Kerberos 5 context.
742 * @param c a configuration section, or NULL to use the section from context
743 * @param args a va_list of arguments
744 *
745 * @return NULL if configuration list is not found, a list otherwise
746 *
747 * @ingroup krb5_support
748 */
749
750KRB5_LIB_FUNCTION const krb5_config_binding * KRB5_LIB_CALL
751krb5_config_vget_list (krb5_context context,
752		       const krb5_config_section *c,
753		       va_list args)
754{
755    return _krb5_config_vget (context, c, krb5_config_list, args);
756}
757
758/**
759 * Returns a "const char *" to a string in the configuration database.
760 * The string may not be valid after a reload of the configuration
761 * database so a caller should make a local copy if it needs to keep
762 * the string.
763 *
764 * @param context A Kerberos 5 context.
765 * @param c a configuration section, or NULL to use the section from context
766 * @param ... a list of names, terminated with NULL.
767 *
768 * @return NULL if configuration string not found, a string otherwise
769 *
770 * @ingroup krb5_support
771 */
772
773KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
774krb5_config_get_string (krb5_context context,
775			const krb5_config_section *c,
776			...)
777{
778    const char *ret;
779    va_list args;
780
781    va_start(args, c);
782    ret = krb5_config_vget_string (context, c, args);
783    va_end(args);
784    return ret;
785}
786
787/**
788 * Like krb5_config_get_string(), but uses a va_list instead of ...
789 *
790 * @param context A Kerberos 5 context.
791 * @param c a configuration section, or NULL to use the section from context
792 * @param args a va_list of arguments
793 *
794 * @return NULL if configuration string not found, a string otherwise
795 *
796 * @ingroup krb5_support
797 */
798
799KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
800krb5_config_vget_string (krb5_context context,
801			 const krb5_config_section *c,
802			 va_list args)
803{
804    return _krb5_config_vget (context, c, krb5_config_string, args);
805}
806
807/**
808 * Like krb5_config_vget_string(), but instead of returning NULL,
809 * instead return a default value.
810 *
811 * @param context A Kerberos 5 context.
812 * @param c a configuration section, or NULL to use the section from context
813 * @param def_value the default value to return if no configuration
814 *        found in the database.
815 * @param args a va_list of arguments
816 *
817 * @return a configuration string
818 *
819 * @ingroup krb5_support
820 */
821
822KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
823krb5_config_vget_string_default (krb5_context context,
824				 const krb5_config_section *c,
825				 const char *def_value,
826				 va_list args)
827{
828    const char *ret;
829
830    ret = krb5_config_vget_string (context, c, args);
831    if (ret == NULL)
832	ret = def_value;
833    return ret;
834}
835
836/**
837 * Like krb5_config_get_string(), but instead of returning NULL,
838 * instead return a default value.
839 *
840 * @param context A Kerberos 5 context.
841 * @param c a configuration section, or NULL to use the section from context
842 * @param def_value the default value to return if no configuration
843 *        found in the database.
844 * @param ... a list of names, terminated with NULL.
845 *
846 * @return a configuration string
847 *
848 * @ingroup krb5_support
849 */
850
851KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
852krb5_config_get_string_default (krb5_context context,
853				const krb5_config_section *c,
854				const char *def_value,
855				...)
856{
857    const char *ret;
858    va_list args;
859
860    va_start(args, def_value);
861    ret = krb5_config_vget_string_default (context, c, def_value, args);
862    va_end(args);
863    return ret;
864}
865
866static char *
867next_component_string(char * begin, const char * delims, char **state)
868{
869    char * end;
870
871    if (begin == NULL)
872        begin = *state;
873
874    if (*begin == '\0')
875        return NULL;
876
877    end = begin;
878    while (*end == '"') {
879        char * t = strchr(end + 1, '"');
880
881        if (t)
882            end = ++t;
883        else
884            end += strlen(end);
885    }
886
887    if (*end != '\0') {
888        size_t pos;
889
890        pos = strcspn(end, delims);
891        end = end + pos;
892    }
893
894    if (*end != '\0') {
895        *end = '\0';
896        *state = end + 1;
897        if (*begin == '"' && *(end - 1) == '"' && begin + 1 < end) {
898            begin++; *(end - 1) = '\0';
899        }
900        return begin;
901    }
902
903    *state = end;
904    if (*begin == '"' && *(end - 1) == '"' && begin + 1 < end) {
905        begin++; *(end - 1) = '\0';
906    }
907    return begin;
908}
909
910/**
911 * Get a list of configuration strings, free the result with
912 * krb5_config_free_strings().
913 *
914 * @param context A Kerberos 5 context.
915 * @param c a configuration section, or NULL to use the section from context
916 * @param args a va_list of arguments
917 *
918 * @return TRUE or FALSE
919 *
920 * @ingroup krb5_support
921 */
922
923KRB5_LIB_FUNCTION char ** KRB5_LIB_CALL
924krb5_config_vget_strings(krb5_context context,
925			 const krb5_config_section *c,
926			 va_list args)
927{
928    char **strings = NULL;
929    int nstr = 0;
930    const krb5_config_binding *b = NULL;
931    const char *p;
932
933    while((p = _krb5_config_vget_next(context, c, &b,
934				      krb5_config_string, args))) {
935	char *tmp = strdup(p);
936	char *pos = NULL;
937	char *s;
938	if(tmp == NULL)
939	    goto cleanup;
940	s = next_component_string(tmp, " \t", &pos);
941	while(s){
942	    char **tmp2 = realloc(strings, (nstr + 1) * sizeof(*strings));
943	    if(tmp2 == NULL) {
944		free(tmp);
945		goto cleanup;
946	    }
947	    strings = tmp2;
948	    strings[nstr] = strdup(s);
949	    nstr++;
950	    if(strings[nstr-1] == NULL)	{
951		free(tmp);
952		goto cleanup;
953	    }
954	    s = next_component_string(NULL, " \t", &pos);
955	}
956	free(tmp);
957    }
958    if(nstr){
959	char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings));
960	if(tmp == NULL)
961	    goto cleanup;
962	strings = tmp;
963	strings[nstr] = NULL;
964    }
965    return strings;
966cleanup:
967    while(nstr--)
968	free(strings[nstr]);
969    free(strings);
970    return NULL;
971
972}
973
974/**
975 * Get a list of configuration strings, free the result with
976 * krb5_config_free_strings().
977 *
978 * @param context A Kerberos 5 context.
979 * @param c a configuration section, or NULL to use the section from context
980 * @param ... a list of names, terminated with NULL.
981 *
982 * @return TRUE or FALSE
983 *
984 * @ingroup krb5_support
985 */
986
987KRB5_LIB_FUNCTION char** KRB5_LIB_CALL
988krb5_config_get_strings(krb5_context context,
989			const krb5_config_section *c,
990			...)
991{
992    va_list ap;
993    char **ret;
994    va_start(ap, c);
995    ret = krb5_config_vget_strings(context, c, ap);
996    va_end(ap);
997    return ret;
998}
999
1000/**
1001 * Free the resulting strings from krb5_config-get_strings() and
1002 * krb5_config_vget_strings().
1003 *
1004 * @param strings strings to free
1005 *
1006 * @ingroup krb5_support
1007 */
1008
1009KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1010krb5_config_free_strings(char **strings)
1011{
1012    char **s = strings;
1013    while(s && *s){
1014	free(*s);
1015	s++;
1016    }
1017    free(strings);
1018}
1019
1020/**
1021 * Like krb5_config_get_bool_default() but with a va_list list of
1022 * configuration selection.
1023 *
1024 * Configuration value to a boolean value, where yes/true and any
1025 * non-zero number means TRUE and other value is FALSE.
1026 *
1027 * @param context A Kerberos 5 context.
1028 * @param c a configuration section, or NULL to use the section from context
1029 * @param def_value the default value to return if no configuration
1030 *        found in the database.
1031 * @param args a va_list of arguments
1032 *
1033 * @return TRUE or FALSE
1034 *
1035 * @ingroup krb5_support
1036 */
1037
1038KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1039krb5_config_vget_bool_default (krb5_context context,
1040			       const krb5_config_section *c,
1041			       krb5_boolean def_value,
1042			       va_list args)
1043{
1044    const char *str;
1045    str = krb5_config_vget_string (context, c, args);
1046    if(str == NULL)
1047	return def_value;
1048    if(strcasecmp(str, "yes") == 0 ||
1049       strcasecmp(str, "true") == 0 ||
1050       atoi(str)) return TRUE;
1051    return FALSE;
1052}
1053
1054/**
1055 * krb5_config_get_bool() will convert the configuration
1056 * option value to a boolean value, where yes/true and any non-zero
1057 * number means TRUE and other value is FALSE.
1058 *
1059 * @param context A Kerberos 5 context.
1060 * @param c a configuration section, or NULL to use the section from context
1061 * @param args a va_list of arguments
1062 *
1063 * @return TRUE or FALSE
1064 *
1065 * @ingroup krb5_support
1066 */
1067
1068KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1069krb5_config_vget_bool  (krb5_context context,
1070			const krb5_config_section *c,
1071			va_list args)
1072{
1073    return krb5_config_vget_bool_default (context, c, FALSE, args);
1074}
1075
1076/**
1077 * krb5_config_get_bool_default() will convert the configuration
1078 * option value to a boolean value, where yes/true and any non-zero
1079 * number means TRUE and other value is FALSE.
1080 *
1081 * @param context A Kerberos 5 context.
1082 * @param c a configuration section, or NULL to use the section from context
1083 * @param def_value the default value to return if no configuration
1084 *        found in the database.
1085 * @param ... a list of names, terminated with NULL.
1086 *
1087 * @return TRUE or FALSE
1088 *
1089 * @ingroup krb5_support
1090 */
1091
1092KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1093krb5_config_get_bool_default (krb5_context context,
1094			      const krb5_config_section *c,
1095			      krb5_boolean def_value,
1096			      ...)
1097{
1098    va_list ap;
1099    krb5_boolean ret;
1100    va_start(ap, def_value);
1101    ret = krb5_config_vget_bool_default(context, c, def_value, ap);
1102    va_end(ap);
1103    return ret;
1104}
1105
1106/**
1107 * Like krb5_config_get_bool() but with a va_list list of
1108 * configuration selection.
1109 *
1110 * Configuration value to a boolean value, where yes/true and any
1111 * non-zero number means TRUE and other value is FALSE.
1112 *
1113 * @param context A Kerberos 5 context.
1114 * @param c a configuration section, or NULL to use the section from context
1115 * @param ... a list of names, terminated with NULL.
1116 *
1117 * @return TRUE or FALSE
1118 *
1119 * @ingroup krb5_support
1120 */
1121
1122KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1123krb5_config_get_bool (krb5_context context,
1124		      const krb5_config_section *c,
1125		      ...)
1126{
1127    va_list ap;
1128    krb5_boolean ret;
1129    va_start(ap, c);
1130    ret = krb5_config_vget_bool (context, c, ap);
1131    va_end(ap);
1132    return ret;
1133}
1134
1135/**
1136 * Get the time from the configuration file using a relative time.
1137 *
1138 * Like krb5_config_get_time_default() but with a va_list list of
1139 * configuration selection.
1140 *
1141 * @param context A Kerberos 5 context.
1142 * @param c a configuration section, or NULL to use the section from context
1143 * @param def_value the default value to return if no configuration
1144 *        found in the database.
1145 * @param args a va_list of arguments
1146 *
1147 * @return parsed the time (or def_value on parse error)
1148 *
1149 * @ingroup krb5_support
1150 */
1151
1152KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1153krb5_config_vget_time_default (krb5_context context,
1154			       const krb5_config_section *c,
1155			       int def_value,
1156			       va_list args)
1157{
1158    const char *str;
1159    krb5_deltat t;
1160
1161    str = krb5_config_vget_string (context, c, args);
1162    if(str == NULL)
1163	return def_value;
1164    if (krb5_string_to_deltat(str, &t))
1165	return def_value;
1166    return t;
1167}
1168
1169/**
1170 * Get the time from the configuration file using a relative time, for example: 1h30s
1171 *
1172 * @param context A Kerberos 5 context.
1173 * @param c a configuration section, or NULL to use the section from context
1174 * @param args a va_list of arguments
1175 *
1176 * @return parsed the time or -1 on error
1177 *
1178 * @ingroup krb5_support
1179 */
1180
1181KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1182krb5_config_vget_time  (krb5_context context,
1183			const krb5_config_section *c,
1184			va_list args)
1185{
1186    return krb5_config_vget_time_default (context, c, -1, args);
1187}
1188
1189/**
1190 * Get the time from the configuration file using a relative time, for example: 1h30s
1191 *
1192 * @param context A Kerberos 5 context.
1193 * @param c a configuration section, or NULL to use the section from context
1194 * @param def_value the default value to return if no configuration
1195 *        found in the database.
1196 * @param ... a list of names, terminated with NULL.
1197 *
1198 * @return parsed the time (or def_value on parse error)
1199 *
1200 * @ingroup krb5_support
1201 */
1202
1203KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1204krb5_config_get_time_default (krb5_context context,
1205			      const krb5_config_section *c,
1206			      int def_value,
1207			      ...)
1208{
1209    va_list ap;
1210    int ret;
1211    va_start(ap, def_value);
1212    ret = krb5_config_vget_time_default(context, c, def_value, ap);
1213    va_end(ap);
1214    return ret;
1215}
1216
1217/**
1218 * Get the time from the configuration file using a relative time, for example: 1h30s
1219 *
1220 * @param context A Kerberos 5 context.
1221 * @param c a configuration section, or NULL to use the section from context
1222 * @param ... a list of names, terminated with NULL.
1223 *
1224 * @return parsed the time or -1 on error
1225 *
1226 * @ingroup krb5_support
1227 */
1228
1229KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1230krb5_config_get_time (krb5_context context,
1231		      const krb5_config_section *c,
1232		      ...)
1233{
1234    va_list ap;
1235    int ret;
1236    va_start(ap, c);
1237    ret = krb5_config_vget_time (context, c, ap);
1238    va_end(ap);
1239    return ret;
1240}
1241
1242
1243KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1244krb5_config_vget_int_default (krb5_context context,
1245			      const krb5_config_section *c,
1246			      int def_value,
1247			      va_list args)
1248{
1249    const char *str;
1250    str = krb5_config_vget_string (context, c, args);
1251    if(str == NULL)
1252	return def_value;
1253    else {
1254	char *endptr;
1255	long l;
1256	l = strtol(str, &endptr, 0);
1257	if (endptr == str)
1258	    return def_value;
1259	else
1260	    return l;
1261    }
1262}
1263
1264KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1265krb5_config_vget_int  (krb5_context context,
1266		       const krb5_config_section *c,
1267		       va_list args)
1268{
1269    return krb5_config_vget_int_default (context, c, -1, args);
1270}
1271
1272KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1273krb5_config_get_int_default (krb5_context context,
1274			     const krb5_config_section *c,
1275			     int def_value,
1276			     ...)
1277{
1278    va_list ap;
1279    int ret;
1280    va_start(ap, def_value);
1281    ret = krb5_config_vget_int_default(context, c, def_value, ap);
1282    va_end(ap);
1283    return ret;
1284}
1285
1286KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1287krb5_config_get_int (krb5_context context,
1288		     const krb5_config_section *c,
1289		     ...)
1290{
1291    va_list ap;
1292    int ret;
1293    va_start(ap, c);
1294    ret = krb5_config_vget_int (context, c, ap);
1295    va_end(ap);
1296    return ret;
1297}
1298
1299
1300#ifndef HEIMDAL_SMALLER
1301
1302/**
1303 * Deprecated: configuration files are not strings
1304 *
1305 * @ingroup krb5_deprecated
1306 */
1307
1308KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1309krb5_config_parse_string_multi(krb5_context context,
1310			       const char *string,
1311			       krb5_config_section **res)
1312    KRB5_DEPRECATED_FUNCTION("Use X instead")
1313{
1314    const char *str;
1315    unsigned lineno = 0;
1316    krb5_error_code ret;
1317    struct fileptr f;
1318    f.f = NULL;
1319    f.s = string;
1320
1321    ret = krb5_config_parse_debug (&f, res, &lineno, &str);
1322    if (ret) {
1323	krb5_set_error_message (context, ret, "%s:%u: %s",
1324				"<constant>", lineno, str);
1325	return ret;
1326    }
1327    return 0;
1328}
1329
1330#endif
1331