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