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