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, (int)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
269struct array_ctx {
270    krb5_config_section *parent;
271    char *key;
272};
273
274static void
275convert_array(const void *value, void *context)
276{
277    struct array_ctx *ctx = context;
278    krb5_config_section *tmp;
279
280    if (CFGetTypeID(value) == CFStringGetTypeID()) {
281	tmp = _krb5_config_get_entry(&ctx->parent, ctx->key, krb5_config_string);
282	tmp->u.string = cfstring2cstring(value);
283    }
284}
285
286
287static void
288convert_content(const void *key, const void *value, void *context)
289{
290    krb5_config_section *tmp, **parent = context;
291    char *k;
292
293    if (CFGetTypeID(key) != CFStringGetTypeID())
294	return;
295
296    k = cfstring2cstring(key);
297    if (k == NULL)
298	return;
299
300    if (CFGetTypeID(value) == CFStringGetTypeID()) {
301	tmp = _krb5_config_get_entry(parent, k, krb5_config_string);
302	tmp->u.string = cfstring2cstring(value);
303    } else if (CFGetTypeID(value) == CFDictionaryGetTypeID()) {
304	tmp = _krb5_config_get_entry(parent, k, krb5_config_list);
305	CFDictionaryApplyFunction(value, convert_content, &tmp->u.list);
306    } else if (CFGetTypeID(value) == CFArrayGetTypeID()) {
307	struct array_ctx ctx;
308	ctx.parent = *parent;
309	ctx.key = k;
310	CFArrayApplyFunction(value, CFRangeMake(0, CFArrayGetCount(value)),
311			     convert_array, &ctx);
312    } else {
313	/* log */
314    }
315    free(k);
316}
317
318static krb5_error_code
319parse_plist_config(krb5_context context, const char *path, krb5_config_section **parent)
320{
321    CFReadStreamRef s;
322    CFDictionaryRef d;
323    CFURLRef url;
324
325    url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8 *)path, strlen(path), FALSE);
326    if (url == NULL) {
327	krb5_clear_error_message(context);
328	return ENOMEM;
329    }
330
331    s = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
332    CFRelease(url);
333    if (s == NULL) {
334	krb5_clear_error_message(context);
335	return ENOMEM;
336    }
337
338    if (!CFReadStreamOpen(s)) {
339	CFRelease(s);
340	krb5_clear_error_message(context);
341	return ENOENT;
342    }
343
344#ifdef HAVE_CFPROPERTYLISTCREATEWITHSTREAM
345    d = (CFDictionaryRef)CFPropertyListCreateWithStream(NULL, s, 0, kCFPropertyListImmutable, NULL, NULL);
346#else
347    d = (CFDictionaryRef)CFPropertyListCreateFromStream(NULL, s, 0, kCFPropertyListImmutable, NULL, NULL);
348#endif
349    CFRelease(s);
350    if (d == NULL) {
351	krb5_clear_error_message(context);
352	return ENOENT;
353    }
354
355    CFDictionaryApplyFunction(d, convert_content, parent);
356    CFRelease(d);
357
358    return 0;
359}
360
361#endif
362
363
364/*
365 * Parse the config file `fname', generating the structures into `res'
366 * returning error messages in `err_message'
367 */
368
369static krb5_error_code
370krb5_config_parse_debug (struct fileptr *f,
371			 krb5_config_section **res,
372			 unsigned *lineno,
373			 const char **err_message)
374{
375    krb5_config_section *s = NULL;
376    krb5_config_binding *b = NULL;
377    char buf[KRB5_BUFSIZ];
378    krb5_error_code ret;
379
380    while (config_fgets(buf, sizeof(buf), f) != NULL) {
381	char *p;
382
383	++*lineno;
384	buf[strcspn(buf, "\r\n")] = '\0';
385	p = buf;
386	while(isspace((unsigned char)*p))
387	    ++p;
388	if (*p == '#' || *p == ';')
389	    continue;
390	if (*p == '[') {
391	    ret = parse_section(p, &s, res, err_message);
392	    if (ret)
393		return ret;
394	    b = NULL;
395	} else if (*p == '}') {
396	    *err_message = "unmatched }";
397	    return KRB5_CONFIG_BADFORMAT;
398	} else if(*p != '\0') {
399	    if (s == NULL) {
400		*err_message = "binding before section";
401		return KRB5_CONFIG_BADFORMAT;
402	    }
403	    ret = parse_binding(f, lineno, p, &b, &s->u.list, err_message);
404	    if (ret)
405		return ret;
406	}
407    }
408    return 0;
409}
410
411static int
412is_plist_file(const char *fname)
413{
414    size_t len = strlen(fname);
415    char suffix[] = ".plist";
416    if (len < sizeof(suffix))
417	return 0;
418    if (strcasecmp(&fname[len - (sizeof(suffix) - 1)], suffix) != 0)
419	return 0;
420    return 1;
421}
422
423/**
424 * Parse a configuration file and add the result into res. This
425 * interface can be used to parse several configuration files into one
426 * resulting krb5_config_section by calling it repeatably.
427 *
428 * @param context a Kerberos 5 context.
429 * @param fname a file name to a Kerberos configuration file
430 * @param res the returned result, must be free with krb5_free_config_files().
431 * @return Return an error code or 0, see krb5_get_error_message().
432 *
433 * @ingroup krb5_support
434 */
435
436KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
437krb5_config_parse_file_multi (krb5_context context,
438			      const char *fname,
439			      krb5_config_section **res)
440{
441    const char *str;
442    char *newfname = NULL;
443#ifdef KRB5_USE_PATH_TOKENS
444    char *exp_fname = NULL;
445#endif
446    unsigned lineno = 0;
447    krb5_error_code ret;
448    struct fileptr f;
449
450    /**
451     * If the fname starts with "~/" parse configuration file in the
452     * current users home directory. The behavior can be disabled and
453     * enabled by calling krb5_set_home_dir_access().
454     */
455    if (fname[0] == '~' && fname[1] == '/') {
456#ifndef _WIN32
457	const char *home = NULL;
458#endif
459	if (!krb5_homedir_access(context)) {
460	    ret = EPERM;
461	    krb5_set_error_message(context, ret,
462				   "Access to home directory not allowed");
463	    goto out;
464	}
465
466#ifndef _WIN32
467	if(!issuid())
468	    home = getenv("HOME");
469
470	if (home == NULL) {
471	    struct passwd *pw = getpwuid(getuid());
472	    if(pw != NULL)
473		home = pw->pw_dir;
474	}
475	if (home) {
476	    asprintf(&newfname, "%s%s", home, &fname[1]);
477	    if (newfname == NULL) {
478		ret = ENOMEM;
479		krb5_set_error_message(context, ret,
480				       N_("malloc: out of memory", ""));
481		goto out;
482	    }
483	    fname = newfname;
484	}
485#else  /* _WIN32 */
486	if (asprintf(&newfname, "%%{USERCONFIG}%s", &fname[1]) < 0 ||
487	    newfname == NULL)
488	{
489	    krb5_set_error_message(context, ENOMEM,
490				   N_("malloc: out of memory", ""));
491	    goto out;
492	}
493	fname = newfname;
494#endif
495    }
496
497#ifdef KRB5_USE_PATH_TOKENS
498    ret = _krb5_expand_path_tokens(context, fname, &exp_fname);
499    if (ret)
500	goto out;
501
502    if (ret)
503	goto out;
504
505    fname = exp_fname;
506#endif
507
508    if (is_plist_file(fname)) {
509#ifdef __APPLE__
510	ret = parse_plist_config(context, fname, res);
511	if (ret) {
512	    krb5_set_error_message(context, ret,
513				   "Failed to parse plist %s", fname);
514	    goto out;
515	}
516#else
517	ret = ENOENT;
518	krb5_set_error_message(context, ret,
519			       "no support for plist configuration files");
520	goto out;
521#endif
522    } else {
523	f.f = fopen(fname, "r");
524	f.s = NULL;
525	if(f.f == NULL) {
526	    char buf[128];
527	    ret = errno;
528	    rk_strerror_r(ret, buf, sizeof(buf));
529	    krb5_set_error_message (context, ret, "open %s: %s",
530				    fname, buf);
531	    goto out;
532	}
533
534	ret = krb5_config_parse_debug (&f, res, &lineno, &str);
535	fclose(f.f);
536	if (ret) {
537	    krb5_set_error_message (context, ret, "%s:%u: %s",
538				    fname, lineno, str);
539	    goto out;
540	}
541    }
542 out:
543    if (newfname)
544	free(newfname);
545#ifdef KRB5_USE_PATH_TOKENS
546    if (exp_fname)
547	free(exp_fname);
548#endif
549    return ret;
550}
551
552KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
553krb5_config_parse_file (krb5_context context,
554			const char *fname,
555			krb5_config_section **res)
556{
557    *res = NULL;
558    return krb5_config_parse_file_multi(context, fname, res);
559}
560
561static void
562free_binding (krb5_context context, krb5_config_binding *b)
563{
564    krb5_config_binding *next_b;
565
566    while (b) {
567	free (b->name);
568	if (b->type == krb5_config_string)
569	    free (b->u.string);
570	else if (b->type == krb5_config_list)
571	    free_binding (context, b->u.list);
572	else
573	    krb5_abortx(context, "unknown binding type (%d) in free_binding",
574			b->type);
575	next_b = b->next;
576	free (b);
577	b = next_b;
578    }
579}
580
581/**
582 * Free configuration file section, the result of
583 * krb5_config_parse_file() and krb5_config_parse_file_multi().
584 *
585 * @param context A Kerberos 5 context
586 * @param s the configuration section to free
587 *
588 * @return returns 0 on successes, otherwise an error code, see
589 *          krb5_get_error_message()
590 *
591 * @ingroup krb5_support
592 */
593
594KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
595krb5_config_file_free (krb5_context context, krb5_config_section *s)
596{
597    free_binding (context, s);
598    return 0;
599}
600
601#ifndef HEIMDAL_SMALLER
602
603KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
604_krb5_config_copy(krb5_context context,
605		  krb5_config_section *c,
606		  krb5_config_section **head)
607{
608    krb5_config_binding *d, *previous = NULL;
609
610    *head = NULL;
611
612    while (c) {
613	d = calloc(1, sizeof(*d));
614
615	if (*head == NULL)
616	    *head = d;
617
618	d->name = strdup(c->name);
619	d->type = c->type;
620	if (d->type == krb5_config_string)
621	    d->u.string = strdup(c->u.string);
622	else if (d->type == krb5_config_list)
623	    _krb5_config_copy (context, c->u.list, &d->u.list);
624	else
625	    krb5_abortx(context,
626			"unknown binding type (%d) in krb5_config_copy",
627			d->type);
628	if (previous)
629	    previous->next = d;
630
631	previous = d;
632	c = c->next;
633    }
634    return 0;
635}
636
637#endif /* HEIMDAL_SMALLER */
638
639KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
640_krb5_config_get_next (krb5_context context,
641		       const krb5_config_section *c,
642		       const krb5_config_binding **pointer,
643		       int type,
644		       ...)
645{
646    const char *ret;
647    va_list args;
648
649    va_start(args, type);
650    ret = _krb5_config_vget_next (context, c, pointer, type, args);
651    va_end(args);
652    return ret;
653}
654
655static const void *
656vget_next(krb5_context context,
657	  const krb5_config_binding *b,
658	  const krb5_config_binding **pointer,
659	  int type,
660	  const char *name,
661	  va_list args)
662{
663    const char *p = va_arg(args, const char *);
664    while(b != NULL) {
665	if(strcmp(b->name, name) == 0) {
666	    if(b->type == (unsigned)type && p == NULL) {
667		*pointer = b;
668		return b->u.generic;
669	    } else if(b->type == krb5_config_list && p != NULL) {
670		return vget_next(context, b->u.list, pointer, type, p, args);
671	    }
672	}
673	b = b->next;
674    }
675    return NULL;
676}
677
678KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
679_krb5_config_vget_next (krb5_context context,
680			const krb5_config_section *c,
681			const krb5_config_binding **pointer,
682			int type,
683			va_list args)
684{
685    const krb5_config_binding *b;
686    const char *p;
687
688    if(c == NULL)
689	c = context->cf;
690
691    if (c == NULL)
692	return NULL;
693
694    if (*pointer == NULL) {
695	/* first time here, walk down the tree looking for the right
696           section */
697	p = va_arg(args, const char *);
698	if (p == NULL)
699	    return NULL;
700	return vget_next(context, c, pointer, type, p, args);
701    }
702
703    /* we were called again, so just look for more entries with the
704       same name and type */
705    for (b = (*pointer)->next; b != NULL; b = b->next) {
706	if(strcmp(b->name, (*pointer)->name) == 0 && b->type == (unsigned)type) {
707	    *pointer = b;
708	    return b->u.generic;
709	}
710    }
711    return NULL;
712}
713
714KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
715_krb5_config_get (krb5_context context,
716		  const krb5_config_section *c,
717		  int type,
718		  ...)
719{
720    const void *ret;
721    va_list args;
722
723    va_start(args, type);
724    ret = _krb5_config_vget (context, c, type, args);
725    va_end(args);
726    return ret;
727}
728
729
730const void *
731_krb5_config_vget (krb5_context context,
732		   const krb5_config_section *c,
733		   int type,
734		   va_list args)
735{
736    const krb5_config_binding *foo = NULL;
737
738    return _krb5_config_vget_next (context, c, &foo, type, args);
739}
740
741/**
742 * Get a list of configuration binding list for more processing
743 *
744 * @param context A Kerberos 5 context.
745 * @param c a configuration section, or NULL to use the section from context
746 * @param ... a list of names, terminated with NULL.
747 *
748 * @return NULL if configuration list is not found, a list otherwise
749 *
750 * @ingroup krb5_support
751 */
752
753KRB5_LIB_FUNCTION const krb5_config_binding * KRB5_LIB_CALL
754krb5_config_get_list (krb5_context context,
755		      const krb5_config_section *c,
756		      ...)
757{
758    const krb5_config_binding *ret;
759    va_list args;
760
761    va_start(args, c);
762    ret = krb5_config_vget_list (context, c, args);
763    va_end(args);
764    return ret;
765}
766
767/**
768 * Get a list of configuration binding list for more processing
769 *
770 * @param context A Kerberos 5 context.
771 * @param c a configuration section, or NULL to use the section from context
772 * @param args a va_list of arguments
773 *
774 * @return NULL if configuration list is not found, a list otherwise
775 *
776 * @ingroup krb5_support
777 */
778
779KRB5_LIB_FUNCTION const krb5_config_binding * KRB5_LIB_CALL
780krb5_config_vget_list (krb5_context context,
781		       const krb5_config_section *c,
782		       va_list args)
783{
784    return _krb5_config_vget (context, c, krb5_config_list, args);
785}
786
787/**
788 * Returns a "const char *" to a string in the configuration database.
789 * The string may not be valid after a reload of the configuration
790 * database so a caller should make a local copy if it needs to keep
791 * the string.
792 *
793 * @param context A Kerberos 5 context.
794 * @param c a configuration section, or NULL to use the section from context
795 * @param ... a list of names, terminated with NULL.
796 *
797 * @return NULL if configuration string not found, a string otherwise
798 *
799 * @ingroup krb5_support
800 */
801
802KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
803krb5_config_get_string (krb5_context context,
804			const krb5_config_section *c,
805			...)
806{
807    const char *ret;
808    va_list args;
809
810    va_start(args, c);
811    ret = krb5_config_vget_string (context, c, args);
812    va_end(args);
813    return ret;
814}
815
816/**
817 * Like krb5_config_get_string(), but uses a va_list instead of ...
818 *
819 * @param context A Kerberos 5 context.
820 * @param c a configuration section, or NULL to use the section from context
821 * @param args a va_list of arguments
822 *
823 * @return NULL if configuration string not found, a string otherwise
824 *
825 * @ingroup krb5_support
826 */
827
828KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
829krb5_config_vget_string (krb5_context context,
830			 const krb5_config_section *c,
831			 va_list args)
832{
833    return _krb5_config_vget (context, c, krb5_config_string, args);
834}
835
836/**
837 * Like krb5_config_vget_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 args a va_list of arguments
845 *
846 * @return a configuration string
847 *
848 * @ingroup krb5_support
849 */
850
851KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
852krb5_config_vget_string_default (krb5_context context,
853				 const krb5_config_section *c,
854				 const char *def_value,
855				 va_list args)
856{
857    const char *ret;
858
859    ret = krb5_config_vget_string (context, c, args);
860    if (ret == NULL)
861	ret = def_value;
862    return ret;
863}
864
865/**
866 * Like krb5_config_get_string(), but instead of returning NULL,
867 * instead return a default value.
868 *
869 * @param context A Kerberos 5 context.
870 * @param c a configuration section, or NULL to use the section from context
871 * @param def_value the default value to return if no configuration
872 *        found in the database.
873 * @param ... a list of names, terminated with NULL.
874 *
875 * @return a configuration string
876 *
877 * @ingroup krb5_support
878 */
879
880KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
881krb5_config_get_string_default (krb5_context context,
882				const krb5_config_section *c,
883				const char *def_value,
884				...)
885{
886    const char *ret;
887    va_list args;
888
889    va_start(args, def_value);
890    ret = krb5_config_vget_string_default (context, c, def_value, args);
891    va_end(args);
892    return ret;
893}
894
895static char *
896next_component_string(char * begin, const char * delims, char **state)
897{
898    char * end;
899
900    if (begin == NULL)
901        begin = *state;
902
903    if (*begin == '\0')
904        return NULL;
905
906    end = begin;
907    while (*end == '"') {
908        char * t = strchr(end + 1, '"');
909
910        if (t)
911            end = ++t;
912        else
913            end += strlen(end);
914    }
915
916    if (*end != '\0') {
917        size_t pos;
918
919        pos = strcspn(end, delims);
920        end = end + pos;
921    }
922
923    if (*end != '\0') {
924        *end = '\0';
925        *state = end + 1;
926        if (*begin == '"' && *(end - 1) == '"' && begin + 1 < end) {
927            begin++; *(end - 1) = '\0';
928        }
929        return begin;
930    }
931
932    *state = end;
933    if (*begin == '"' && *(end - 1) == '"' && begin + 1 < end) {
934        begin++; *(end - 1) = '\0';
935    }
936    return begin;
937}
938
939/**
940 * Get a list of configuration strings, free the result with
941 * krb5_config_free_strings().
942 *
943 * @param context A Kerberos 5 context.
944 * @param c a configuration section, or NULL to use the section from context
945 * @param args a va_list of arguments
946 *
947 * @return TRUE or FALSE
948 *
949 * @ingroup krb5_support
950 */
951
952KRB5_LIB_FUNCTION char ** KRB5_LIB_CALL
953krb5_config_vget_strings(krb5_context context,
954			 const krb5_config_section *c,
955			 va_list args)
956{
957    char **strings = NULL;
958    int nstr = 0;
959    const krb5_config_binding *b = NULL;
960    const char *p;
961
962    while((p = _krb5_config_vget_next(context, c, &b,
963				      krb5_config_string, args))) {
964	char *tmp = strdup(p);
965	char *pos = NULL;
966	char *s;
967	if(tmp == NULL)
968	    goto cleanup;
969	s = next_component_string(tmp, " \t", &pos);
970	while(s){
971	    char **tmp2 = realloc(strings, (nstr + 1) * sizeof(*strings));
972	    if(tmp2 == NULL)
973		goto cleanup;
974	    strings = tmp2;
975	    strings[nstr] = strdup(s);
976	    nstr++;
977	    if(strings[nstr-1] == NULL)
978		goto cleanup;
979	    s = next_component_string(NULL, " \t", &pos);
980	}
981	free(tmp);
982    }
983    if(nstr){
984	char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings));
985	if(tmp == NULL)
986	    goto cleanup;
987	strings = tmp;
988	strings[nstr] = NULL;
989    }
990    return strings;
991cleanup:
992    while(nstr--)
993	free(strings[nstr]);
994    free(strings);
995    return NULL;
996
997}
998
999/**
1000 * Get a list of configuration strings, free the result with
1001 * krb5_config_free_strings().
1002 *
1003 * @param context A Kerberos 5 context.
1004 * @param c a configuration section, or NULL to use the section from context
1005 * @param ... a list of names, terminated with NULL.
1006 *
1007 * @return TRUE or FALSE
1008 *
1009 * @ingroup krb5_support
1010 */
1011
1012KRB5_LIB_FUNCTION char** KRB5_LIB_CALL
1013krb5_config_get_strings(krb5_context context,
1014			const krb5_config_section *c,
1015			...)
1016{
1017    va_list ap;
1018    char **ret;
1019    va_start(ap, c);
1020    ret = krb5_config_vget_strings(context, c, ap);
1021    va_end(ap);
1022    return ret;
1023}
1024
1025/**
1026 * Free the resulting strings from krb5_config-get_strings() and
1027 * krb5_config_vget_strings().
1028 *
1029 * @param strings strings to free
1030 *
1031 * @ingroup krb5_support
1032 */
1033
1034KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1035krb5_config_free_strings(char **strings)
1036{
1037    char **s = strings;
1038    while(s && *s){
1039	free(*s);
1040	s++;
1041    }
1042    free(strings);
1043}
1044
1045/**
1046 * Like krb5_config_get_bool_default() but with a va_list list of
1047 * configuration selection.
1048 *
1049 * Configuration value to a boolean value, where yes/true and any
1050 * non-zero number means TRUE and other value is FALSE.
1051 *
1052 * @param context A Kerberos 5 context.
1053 * @param c a configuration section, or NULL to use the section from context
1054 * @param def_value the default value to return if no configuration
1055 *        found in the database.
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_default (krb5_context context,
1065			       const krb5_config_section *c,
1066			       krb5_boolean def_value,
1067			       va_list args)
1068{
1069    const char *str;
1070    str = krb5_config_vget_string (context, c, args);
1071    if(str == NULL)
1072	return def_value;
1073    if(strcasecmp(str, "yes") == 0 ||
1074       strcasecmp(str, "true") == 0 ||
1075       atoi(str)) return TRUE;
1076    return FALSE;
1077}
1078
1079/**
1080 * krb5_config_get_bool() will convert the configuration
1081 * option value to a boolean value, where yes/true and any non-zero
1082 * number means TRUE and other value is FALSE.
1083 *
1084 * @param context A Kerberos 5 context.
1085 * @param c a configuration section, or NULL to use the section from context
1086 * @param args a va_list of arguments
1087 *
1088 * @return TRUE or FALSE
1089 *
1090 * @ingroup krb5_support
1091 */
1092
1093KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1094krb5_config_vget_bool  (krb5_context context,
1095			const krb5_config_section *c,
1096			va_list args)
1097{
1098    return krb5_config_vget_bool_default (context, c, FALSE, args);
1099}
1100
1101/**
1102 * krb5_config_get_bool_default() will convert the configuration
1103 * option value to a boolean value, where yes/true and any non-zero
1104 * number means TRUE and other value is FALSE.
1105 *
1106 * @param context A Kerberos 5 context.
1107 * @param c a configuration section, or NULL to use the section from context
1108 * @param def_value the default value to return if no configuration
1109 *        found in the database.
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_default (krb5_context context,
1119			      const krb5_config_section *c,
1120			      krb5_boolean def_value,
1121			      ...)
1122{
1123    va_list ap;
1124    krb5_boolean ret;
1125    va_start(ap, def_value);
1126    ret = krb5_config_vget_bool_default(context, c, def_value, ap);
1127    va_end(ap);
1128    return ret;
1129}
1130
1131/**
1132 * Like krb5_config_get_bool() but with a va_list list of
1133 * configuration selection.
1134 *
1135 * Configuration value to a boolean value, where yes/true and any
1136 * non-zero number means TRUE and other value is FALSE.
1137 *
1138 * @param context A Kerberos 5 context.
1139 * @param c a configuration section, or NULL to use the section from context
1140 * @param ... a list of names, terminated with NULL.
1141 *
1142 * @return TRUE or FALSE
1143 *
1144 * @ingroup krb5_support
1145 */
1146
1147KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1148krb5_config_get_bool (krb5_context context,
1149		      const krb5_config_section *c,
1150		      ...)
1151{
1152    va_list ap;
1153    krb5_boolean ret;
1154    va_start(ap, c);
1155    ret = krb5_config_vget_bool (context, c, ap);
1156    va_end(ap);
1157    return ret;
1158}
1159
1160/**
1161 * Get the time from the configuration file using a relative time.
1162 *
1163 * Like krb5_config_get_time_default() but with a va_list list of
1164 * configuration selection.
1165 *
1166 * @param context A Kerberos 5 context.
1167 * @param c a configuration section, or NULL to use the section from context
1168 * @param def_value the default value to return if no configuration
1169 *        found in the database.
1170 * @param args a va_list of arguments
1171 *
1172 * @return parsed the time (or def_value on parse error)
1173 *
1174 * @ingroup krb5_support
1175 */
1176
1177KRB5_LIB_FUNCTION krb5_deltat KRB5_LIB_CALL
1178krb5_config_vget_time_default (krb5_context context,
1179			       const krb5_config_section *c,
1180			       krb5_deltat def_value,
1181			       va_list args)
1182{
1183    const char *str;
1184    krb5_deltat t;
1185
1186    str = krb5_config_vget_string (context, c, args);
1187    if(str == NULL)
1188	return def_value;
1189    if (krb5_string_to_deltat(str, &t))
1190	return def_value;
1191    return (int)t;
1192}
1193
1194/**
1195 * Get the time from the configuration file using a relative time, for example: 1h30s
1196 *
1197 * @param context A Kerberos 5 context.
1198 * @param c a configuration section, or NULL to use the section from context
1199 * @param args a va_list of arguments
1200 *
1201 * @return parsed the time or -1 on error
1202 *
1203 * @ingroup krb5_support
1204 */
1205
1206KRB5_LIB_FUNCTION krb5_deltat KRB5_LIB_CALL
1207krb5_config_vget_time  (krb5_context context,
1208			const krb5_config_section *c,
1209			va_list args)
1210{
1211    return krb5_config_vget_time_default (context, c, -1, args);
1212}
1213
1214/**
1215 * Get the time from the configuration file using a relative time, for example: 1h30s
1216 *
1217 * @param context A Kerberos 5 context.
1218 * @param c a configuration section, or NULL to use the section from context
1219 * @param def_value the default value to return if no configuration
1220 *        found in the database.
1221 * @param ... a list of names, terminated with NULL.
1222 *
1223 * @return parsed the time (or def_value on parse error)
1224 *
1225 * @ingroup krb5_support
1226 */
1227
1228KRB5_LIB_FUNCTION krb5_deltat KRB5_LIB_CALL
1229krb5_config_get_time_default (krb5_context context,
1230			      const krb5_config_section *c,
1231			      krb5_deltat def_value,
1232			      ...)
1233{
1234    va_list ap;
1235    krb5_deltat ret;
1236    va_start(ap, def_value);
1237    ret = krb5_config_vget_time_default(context, c, def_value, ap);
1238    va_end(ap);
1239    return ret;
1240}
1241
1242/**
1243 * Get the time from the configuration file using a relative time, for example: 1h30s
1244 *
1245 * @param context A Kerberos 5 context.
1246 * @param c a configuration section, or NULL to use the section from context
1247 * @param ... a list of names, terminated with NULL.
1248 *
1249 * @return parsed the time or -1 on error
1250 *
1251 * @ingroup krb5_support
1252 */
1253
1254KRB5_LIB_FUNCTION krb5_deltat KRB5_LIB_CALL
1255krb5_config_get_time (krb5_context context,
1256		      const krb5_config_section *c,
1257		      ...)
1258{
1259    va_list ap;
1260    krb5_deltat ret;
1261    va_start(ap, c);
1262    ret = krb5_config_vget_time (context, c, ap);
1263    va_end(ap);
1264    return ret;
1265}
1266
1267
1268KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1269krb5_config_vget_int_default (krb5_context context,
1270			      const krb5_config_section *c,
1271			      int def_value,
1272			      va_list args)
1273{
1274    const char *str;
1275    str = krb5_config_vget_string (context, c, args);
1276    if(str == NULL)
1277	return def_value;
1278    else {
1279	char *endptr;
1280	long l;
1281	l = strtol(str, &endptr, 0);
1282	if (endptr == str)
1283	    return def_value;
1284	else
1285	    return (int)l;
1286    }
1287}
1288
1289KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1290krb5_config_vget_int  (krb5_context context,
1291		       const krb5_config_section *c,
1292		       va_list args)
1293{
1294    return krb5_config_vget_int_default (context, c, -1, args);
1295}
1296
1297KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1298krb5_config_get_int_default (krb5_context context,
1299			     const krb5_config_section *c,
1300			     int def_value,
1301			     ...)
1302{
1303    va_list ap;
1304    int ret;
1305    va_start(ap, def_value);
1306    ret = krb5_config_vget_int_default(context, c, def_value, ap);
1307    va_end(ap);
1308    return ret;
1309}
1310
1311KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1312krb5_config_get_int (krb5_context context,
1313		     const krb5_config_section *c,
1314		     ...)
1315{
1316    va_list ap;
1317    int ret;
1318    va_start(ap, c);
1319    ret = krb5_config_vget_int (context, c, ap);
1320    va_end(ap);
1321    return ret;
1322}
1323
1324
1325#ifndef HEIMDAL_SMALLER
1326
1327/**
1328 * Deprecated: configuration files are not strings
1329 *
1330 * @ingroup krb5_deprecated
1331 */
1332
1333KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1334krb5_config_parse_string_multi(krb5_context context,
1335			       const char *string,
1336			       krb5_config_section **res)
1337    KRB5_DEPRECATED_FUNCTION("Use X instead")
1338{
1339    const char *str;
1340    unsigned lineno = 0;
1341    krb5_error_code ret;
1342    struct fileptr f;
1343    f.f = NULL;
1344    f.s = string;
1345
1346    ret = krb5_config_parse_debug (&f, res, &lineno, &str);
1347    if (ret) {
1348	krb5_set_error_message (context, ret, "%s:%u: %s",
1349				"<constant>", lineno, str);
1350	return ret;
1351    }
1352    return 0;
1353}
1354
1355#endif
1356