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