xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/krb5/config_file.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /*	$NetBSD: config_file.c,v 1.4 2018/06/16 18:51:36 kamil 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 	struct passwd pw, *pwd = NULL;
434 	char pwbuf[2048];
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 	    if (rk_getpwuid_r(getuid(), &pw, pwbuf, sizeof(pwbuf), &pwd) == 0)
447 		home = pwd->pw_dir;
448 	}
449 	if (home) {
450 	    int aret;
451 
452 	    aret = asprintf(&newfname, "%s%s", home, &fname[1]);
453 	    if (aret == -1 || newfname == NULL)
454 		return krb5_enomem(context);
455 	    fname = newfname;
456 	}
457 #else  /* KRB5_USE_PATH_TOKENS */
458 	if (asprintf(&newfname, "%%{USERCONFIG}%s", &fname[1]) < 0 ||
459 	    newfname == NULL)
460 	    return krb5_enomem(context);
461 	fname = newfname;
462 #endif
463     }
464 
465     if (is_plist_file(fname)) {
466 #ifdef __APPLE__
467 	ret = parse_plist_config(context, fname, res);
468 	if (ret) {
469 	    krb5_set_error_message(context, ret,
470 				   "Failed to parse plist %s", fname);
471 	    if (newfname)
472 		free(newfname);
473 	    return ret;
474 	}
475 #else
476 	krb5_set_error_message(context, ENOENT,
477 			       "no support for plist configuration files");
478 	return ENOENT;
479 #endif
480     } else {
481 #ifdef KRB5_USE_PATH_TOKENS
482 	char * exp_fname = NULL;
483 
484 	ret = _krb5_expand_path_tokens(context, fname, 1, &exp_fname);
485 	if (ret) {
486 	    if (newfname)
487 		free(newfname);
488 	    return ret;
489 	}
490 
491 	if (newfname)
492 	    free(newfname);
493 	fname = newfname = exp_fname;
494 #endif
495 
496 	f.f = fopen(fname, "r");
497 	f.s = NULL;
498 	if(f.f == NULL) {
499 	    ret = errno;
500 	    krb5_set_error_message (context, ret, "open %s: %s",
501 				    fname, strerror(ret));
502 	    if (newfname)
503 		free(newfname);
504 	    return ret;
505 	}
506 
507 	ret = krb5_config_parse_debug (&f, res, &lineno, &str);
508 	fclose(f.f);
509 	if (ret) {
510 	    krb5_set_error_message (context, ret, "%s:%u: %s",
511 				    fname, lineno, str);
512 	    if (newfname)
513 		free(newfname);
514 	    return ret;
515 	}
516     }
517     return 0;
518 }
519 
520 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
521 krb5_config_parse_file (krb5_context context,
522 			const char *fname,
523 			krb5_config_section **res)
524 {
525     *res = NULL;
526     return krb5_config_parse_file_multi(context, fname, res);
527 }
528 
529 static void
530 free_binding (krb5_context context, krb5_config_binding *b)
531 {
532     krb5_config_binding *next_b;
533 
534     while (b) {
535 	free (b->name);
536 	if (b->type == krb5_config_string)
537 	    free (b->u.string);
538 	else if (b->type == krb5_config_list)
539 	    free_binding (context, b->u.list);
540 	else
541 	    krb5_abortx(context, "unknown binding type (%d) in free_binding",
542 			b->type);
543 	next_b = b->next;
544 	free (b);
545 	b = next_b;
546     }
547 }
548 
549 /**
550  * Free configuration file section, the result of
551  * krb5_config_parse_file() and krb5_config_parse_file_multi().
552  *
553  * @param context A Kerberos 5 context
554  * @param s the configuration section to free
555  *
556  * @return returns 0 on successes, otherwise an error code, see
557  *          krb5_get_error_message()
558  *
559  * @ingroup krb5_support
560  */
561 
562 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
563 krb5_config_file_free (krb5_context context, krb5_config_section *s)
564 {
565     free_binding (context, s);
566     return 0;
567 }
568 
569 #ifndef HEIMDAL_SMALLER
570 
571 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
572 _krb5_config_copy(krb5_context context,
573 		  krb5_config_section *c,
574 		  krb5_config_section **head)
575 {
576     krb5_config_binding *d, *previous = NULL;
577 
578     *head = NULL;
579 
580     while (c) {
581 	d = calloc(1, sizeof(*d));
582 
583 	if (*head == NULL)
584 	    *head = d;
585 
586 	d->name = strdup(c->name);
587 	d->type = c->type;
588 	if (d->type == krb5_config_string)
589 	    d->u.string = strdup(c->u.string);
590 	else if (d->type == krb5_config_list)
591 	    _krb5_config_copy (context, c->u.list, &d->u.list);
592 	else
593 	    krb5_abortx(context,
594 			"unknown binding type (%d) in krb5_config_copy",
595 			d->type);
596 	if (previous)
597 	    previous->next = d;
598 
599 	previous = d;
600 	c = c->next;
601     }
602     return 0;
603 }
604 
605 #endif /* HEIMDAL_SMALLER */
606 
607 KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
608 _krb5_config_get_next (krb5_context context,
609 		       const krb5_config_section *c,
610 		       const krb5_config_binding **pointer,
611 		       int type,
612 		       ...)
613 {
614     const char *ret;
615     va_list args;
616 
617     va_start(args, type);
618     ret = _krb5_config_vget_next (context, c, pointer, type, args);
619     va_end(args);
620     return ret;
621 }
622 
623 static const void *
624 vget_next(krb5_context context,
625 	  const krb5_config_binding *b,
626 	  const krb5_config_binding **pointer,
627 	  int type,
628 	  const char *name,
629 	  va_list args)
630 {
631     const char *p = va_arg(args, const char *);
632     while(b != NULL) {
633 	if(strcmp(b->name, name) == 0) {
634 	    if(b->type == (unsigned)type && p == NULL) {
635 		*pointer = b;
636 		return b->u.generic;
637 	    } else if(b->type == krb5_config_list && p != NULL) {
638 		return vget_next(context, b->u.list, pointer, type, p, args);
639 	    }
640 	}
641 	b = b->next;
642     }
643     return NULL;
644 }
645 
646 KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
647 _krb5_config_vget_next (krb5_context context,
648 			const krb5_config_section *c,
649 			const krb5_config_binding **pointer,
650 			int type,
651 			va_list args)
652 {
653     const krb5_config_binding *b;
654     const char *p;
655 
656     if(c == NULL)
657 	c = context->cf;
658 
659     if (c == NULL)
660 	return NULL;
661 
662     if (*pointer == NULL) {
663 	/* first time here, walk down the tree looking for the right
664            section */
665 	p = va_arg(args, const char *);
666 	if (p == NULL)
667 	    return NULL;
668 	return vget_next(context, c, pointer, type, p, args);
669     }
670 
671     /* we were called again, so just look for more entries with the
672        same name and type */
673     for (b = (*pointer)->next; b != NULL; b = b->next) {
674 	if(strcmp(b->name, (*pointer)->name) == 0 && b->type == (unsigned)type) {
675 	    *pointer = b;
676 	    return b->u.generic;
677 	}
678     }
679     return NULL;
680 }
681 
682 KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
683 _krb5_config_get (krb5_context context,
684 		  const krb5_config_section *c,
685 		  int type,
686 		  ...)
687 {
688     const void *ret;
689     va_list args;
690 
691     va_start(args, type);
692     ret = _krb5_config_vget (context, c, type, args);
693     va_end(args);
694     return ret;
695 }
696 
697 
698 KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
699 _krb5_config_vget (krb5_context context,
700 		   const krb5_config_section *c,
701 		   int type,
702 		   va_list args)
703 {
704     const krb5_config_binding *foo = NULL;
705 
706     return _krb5_config_vget_next (context, c, &foo, type, args);
707 }
708 
709 /**
710  * Get a list of configuration binding list for more processing
711  *
712  * @param context A Kerberos 5 context.
713  * @param c a configuration section, or NULL to use the section from context
714  * @param ... a list of names, terminated with NULL.
715  *
716  * @return NULL if configuration list is not found, a list otherwise
717  *
718  * @ingroup krb5_support
719  */
720 
721 KRB5_LIB_FUNCTION const krb5_config_binding * KRB5_LIB_CALL
722 krb5_config_get_list (krb5_context context,
723 		      const krb5_config_section *c,
724 		      ...)
725 {
726     const krb5_config_binding *ret;
727     va_list args;
728 
729     va_start(args, c);
730     ret = krb5_config_vget_list (context, c, args);
731     va_end(args);
732     return ret;
733 }
734 
735 /**
736  * Get a list of configuration binding list for more processing
737  *
738  * @param context A Kerberos 5 context.
739  * @param c a configuration section, or NULL to use the section from context
740  * @param args a va_list of arguments
741  *
742  * @return NULL if configuration list is not found, a list otherwise
743  *
744  * @ingroup krb5_support
745  */
746 
747 KRB5_LIB_FUNCTION const krb5_config_binding * KRB5_LIB_CALL
748 krb5_config_vget_list (krb5_context context,
749 		       const krb5_config_section *c,
750 		       va_list args)
751 {
752     return _krb5_config_vget (context, c, krb5_config_list, args);
753 }
754 
755 /**
756  * Returns a "const char *" to a string in the configuration database.
757  * The string may not be valid after a reload of the configuration
758  * database so a caller should make a local copy if it needs to keep
759  * the string.
760  *
761  * @param context A Kerberos 5 context.
762  * @param c a configuration section, or NULL to use the section from context
763  * @param ... a list of names, terminated with NULL.
764  *
765  * @return NULL if configuration string not found, a string otherwise
766  *
767  * @ingroup krb5_support
768  */
769 
770 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
771 krb5_config_get_string (krb5_context context,
772 			const krb5_config_section *c,
773 			...)
774 {
775     const char *ret;
776     va_list args;
777 
778     va_start(args, c);
779     ret = krb5_config_vget_string (context, c, args);
780     va_end(args);
781     return ret;
782 }
783 
784 /**
785  * Like krb5_config_get_string(), but uses a va_list instead of ...
786  *
787  * @param context A Kerberos 5 context.
788  * @param c a configuration section, or NULL to use the section from context
789  * @param args a va_list of arguments
790  *
791  * @return NULL if configuration string not found, a string otherwise
792  *
793  * @ingroup krb5_support
794  */
795 
796 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
797 krb5_config_vget_string (krb5_context context,
798 			 const krb5_config_section *c,
799 			 va_list args)
800 {
801     return _krb5_config_vget (context, c, krb5_config_string, args);
802 }
803 
804 /**
805  * Like krb5_config_vget_string(), but instead of returning NULL,
806  * instead return a default value.
807  *
808  * @param context A Kerberos 5 context.
809  * @param c a configuration section, or NULL to use the section from context
810  * @param def_value the default value to return if no configuration
811  *        found in the database.
812  * @param args a va_list of arguments
813  *
814  * @return a configuration string
815  *
816  * @ingroup krb5_support
817  */
818 
819 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
820 krb5_config_vget_string_default (krb5_context context,
821 				 const krb5_config_section *c,
822 				 const char *def_value,
823 				 va_list args)
824 {
825     const char *ret;
826 
827     ret = krb5_config_vget_string (context, c, args);
828     if (ret == NULL)
829 	ret = def_value;
830     return ret;
831 }
832 
833 /**
834  * Like krb5_config_get_string(), but instead of returning NULL,
835  * instead return a default value.
836  *
837  * @param context A Kerberos 5 context.
838  * @param c a configuration section, or NULL to use the section from context
839  * @param def_value the default value to return if no configuration
840  *        found in the database.
841  * @param ... a list of names, terminated with NULL.
842  *
843  * @return a configuration string
844  *
845  * @ingroup krb5_support
846  */
847 
848 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
849 krb5_config_get_string_default (krb5_context context,
850 				const krb5_config_section *c,
851 				const char *def_value,
852 				...)
853 {
854     const char *ret;
855     va_list args;
856 
857     va_start(args, def_value);
858     ret = krb5_config_vget_string_default (context, c, def_value, args);
859     va_end(args);
860     return ret;
861 }
862 
863 static char *
864 next_component_string(char * begin, const char * delims, char **state)
865 {
866     char * end;
867 
868     if (begin == NULL)
869         begin = *state;
870 
871     if (*begin == '\0')
872         return NULL;
873 
874     end = begin;
875     while (*end == '"') {
876         char * t = strchr(end + 1, '"');
877 
878         if (t)
879             end = ++t;
880         else
881             end += strlen(end);
882     }
883 
884     if (*end != '\0') {
885         size_t pos;
886 
887         pos = strcspn(end, delims);
888         end = end + pos;
889     }
890 
891     if (*end != '\0') {
892         *end = '\0';
893         *state = end + 1;
894         if (*begin == '"' && *(end - 1) == '"' && begin + 1 < end) {
895             begin++; *(end - 1) = '\0';
896         }
897         return begin;
898     }
899 
900     *state = end;
901     if (*begin == '"' && *(end - 1) == '"' && begin + 1 < end) {
902         begin++; *(end - 1) = '\0';
903     }
904     return begin;
905 }
906 
907 /**
908  * Get a list of configuration strings, free the result with
909  * krb5_config_free_strings().
910  *
911  * @param context A Kerberos 5 context.
912  * @param c a configuration section, or NULL to use the section from context
913  * @param args a va_list of arguments
914  *
915  * @return TRUE or FALSE
916  *
917  * @ingroup krb5_support
918  */
919 
920 KRB5_LIB_FUNCTION char ** KRB5_LIB_CALL
921 krb5_config_vget_strings(krb5_context context,
922 			 const krb5_config_section *c,
923 			 va_list args)
924 {
925     char **strings = NULL;
926     int nstr = 0;
927     const krb5_config_binding *b = NULL;
928     const char *p;
929 
930     while((p = _krb5_config_vget_next(context, c, &b,
931 				      krb5_config_string, args))) {
932 	char *tmp = strdup(p);
933 	char *pos = NULL;
934 	char *s;
935 	if(tmp == NULL)
936 	    goto cleanup;
937 	s = next_component_string(tmp, " \t", &pos);
938 	while(s){
939 	    char **tmp2 = realloc(strings, (nstr + 1) * sizeof(*strings));
940 	    if(tmp2 == NULL) {
941 		free(tmp);
942 		goto cleanup;
943 	    }
944 	    strings = tmp2;
945 	    strings[nstr] = strdup(s);
946 	    nstr++;
947 	    if(strings[nstr-1] == NULL)	{
948 		free(tmp);
949 		goto cleanup;
950 	    }
951 	    s = next_component_string(NULL, " \t", &pos);
952 	}
953 	free(tmp);
954     }
955     if(nstr){
956 	char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings));
957 	if(tmp == NULL)
958 	    goto cleanup;
959 	strings = tmp;
960 	strings[nstr] = NULL;
961     }
962     return strings;
963 cleanup:
964     while(nstr--)
965 	free(strings[nstr]);
966     free(strings);
967     return NULL;
968 
969 }
970 
971 /**
972  * Get a list of configuration strings, free the result with
973  * krb5_config_free_strings().
974  *
975  * @param context A Kerberos 5 context.
976  * @param c a configuration section, or NULL to use the section from context
977  * @param ... a list of names, terminated with NULL.
978  *
979  * @return TRUE or FALSE
980  *
981  * @ingroup krb5_support
982  */
983 
984 KRB5_LIB_FUNCTION char** KRB5_LIB_CALL
985 krb5_config_get_strings(krb5_context context,
986 			const krb5_config_section *c,
987 			...)
988 {
989     va_list ap;
990     char **ret;
991     va_start(ap, c);
992     ret = krb5_config_vget_strings(context, c, ap);
993     va_end(ap);
994     return ret;
995 }
996 
997 /**
998  * Free the resulting strings from krb5_config-get_strings() and
999  * krb5_config_vget_strings().
1000  *
1001  * @param strings strings to free
1002  *
1003  * @ingroup krb5_support
1004  */
1005 
1006 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1007 krb5_config_free_strings(char **strings)
1008 {
1009     char **s = strings;
1010     while(s && *s){
1011 	free(*s);
1012 	s++;
1013     }
1014     free(strings);
1015 }
1016 
1017 /**
1018  * Like krb5_config_get_bool_default() but with a va_list list of
1019  * configuration selection.
1020  *
1021  * Configuration value to a boolean value, where yes/true and any
1022  * non-zero number means TRUE and other value is FALSE.
1023  *
1024  * @param context A Kerberos 5 context.
1025  * @param c a configuration section, or NULL to use the section from context
1026  * @param def_value the default value to return if no configuration
1027  *        found in the database.
1028  * @param args a va_list of arguments
1029  *
1030  * @return TRUE or FALSE
1031  *
1032  * @ingroup krb5_support
1033  */
1034 
1035 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1036 krb5_config_vget_bool_default (krb5_context context,
1037 			       const krb5_config_section *c,
1038 			       krb5_boolean def_value,
1039 			       va_list args)
1040 {
1041     const char *str;
1042     str = krb5_config_vget_string (context, c, args);
1043     if(str == NULL)
1044 	return def_value;
1045     if(strcasecmp(str, "yes") == 0 ||
1046        strcasecmp(str, "true") == 0 ||
1047        atoi(str)) return TRUE;
1048     return FALSE;
1049 }
1050 
1051 /**
1052  * krb5_config_get_bool() will convert the configuration
1053  * option value to a boolean value, where yes/true and any non-zero
1054  * number means TRUE and other value is FALSE.
1055  *
1056  * @param context A Kerberos 5 context.
1057  * @param c a configuration section, or NULL to use the section from context
1058  * @param args a va_list of arguments
1059  *
1060  * @return TRUE or FALSE
1061  *
1062  * @ingroup krb5_support
1063  */
1064 
1065 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1066 krb5_config_vget_bool  (krb5_context context,
1067 			const krb5_config_section *c,
1068 			va_list args)
1069 {
1070     return krb5_config_vget_bool_default (context, c, FALSE, args);
1071 }
1072 
1073 /**
1074  * krb5_config_get_bool_default() will convert the configuration
1075  * option value to a boolean value, where yes/true and any non-zero
1076  * number means TRUE and other value is FALSE.
1077  *
1078  * @param context A Kerberos 5 context.
1079  * @param c a configuration section, or NULL to use the section from context
1080  * @param def_value the default value to return if no configuration
1081  *        found in the database.
1082  * @param ... a list of names, terminated with NULL.
1083  *
1084  * @return TRUE or FALSE
1085  *
1086  * @ingroup krb5_support
1087  */
1088 
1089 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1090 krb5_config_get_bool_default (krb5_context context,
1091 			      const krb5_config_section *c,
1092 			      krb5_boolean def_value,
1093 			      ...)
1094 {
1095     va_list ap;
1096     krb5_boolean ret;
1097     va_start(ap, def_value);
1098     ret = krb5_config_vget_bool_default(context, c, def_value, ap);
1099     va_end(ap);
1100     return ret;
1101 }
1102 
1103 /**
1104  * Like krb5_config_get_bool() but with a va_list list of
1105  * configuration selection.
1106  *
1107  * Configuration value to a boolean value, where yes/true and any
1108  * non-zero number means TRUE and other value is FALSE.
1109  *
1110  * @param context A Kerberos 5 context.
1111  * @param c a configuration section, or NULL to use the section from context
1112  * @param ... a list of names, terminated with NULL.
1113  *
1114  * @return TRUE or FALSE
1115  *
1116  * @ingroup krb5_support
1117  */
1118 
1119 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1120 krb5_config_get_bool (krb5_context context,
1121 		      const krb5_config_section *c,
1122 		      ...)
1123 {
1124     va_list ap;
1125     krb5_boolean ret;
1126     va_start(ap, c);
1127     ret = krb5_config_vget_bool (context, c, ap);
1128     va_end(ap);
1129     return ret;
1130 }
1131 
1132 /**
1133  * Get the time from the configuration file using a relative time.
1134  *
1135  * Like krb5_config_get_time_default() but with a va_list list of
1136  * configuration selection.
1137  *
1138  * @param context A Kerberos 5 context.
1139  * @param c a configuration section, or NULL to use the section from context
1140  * @param def_value the default value to return if no configuration
1141  *        found in the database.
1142  * @param args a va_list of arguments
1143  *
1144  * @return parsed the time (or def_value on parse error)
1145  *
1146  * @ingroup krb5_support
1147  */
1148 
1149 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1150 krb5_config_vget_time_default (krb5_context context,
1151 			       const krb5_config_section *c,
1152 			       int def_value,
1153 			       va_list args)
1154 {
1155     const char *str;
1156     krb5_deltat t;
1157 
1158     str = krb5_config_vget_string (context, c, args);
1159     if(str == NULL)
1160 	return def_value;
1161     if (krb5_string_to_deltat(str, &t))
1162 	return def_value;
1163     return t;
1164 }
1165 
1166 /**
1167  * Get the time from the configuration file using a relative time, for example: 1h30s
1168  *
1169  * @param context A Kerberos 5 context.
1170  * @param c a configuration section, or NULL to use the section from context
1171  * @param args a va_list of arguments
1172  *
1173  * @return parsed the time or -1 on error
1174  *
1175  * @ingroup krb5_support
1176  */
1177 
1178 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1179 krb5_config_vget_time  (krb5_context context,
1180 			const krb5_config_section *c,
1181 			va_list args)
1182 {
1183     return krb5_config_vget_time_default (context, c, -1, args);
1184 }
1185 
1186 /**
1187  * Get the time from the configuration file using a relative time, for example: 1h30s
1188  *
1189  * @param context A Kerberos 5 context.
1190  * @param c a configuration section, or NULL to use the section from context
1191  * @param def_value the default value to return if no configuration
1192  *        found in the database.
1193  * @param ... a list of names, terminated with NULL.
1194  *
1195  * @return parsed the time (or def_value on parse error)
1196  *
1197  * @ingroup krb5_support
1198  */
1199 
1200 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1201 krb5_config_get_time_default (krb5_context context,
1202 			      const krb5_config_section *c,
1203 			      int def_value,
1204 			      ...)
1205 {
1206     va_list ap;
1207     int ret;
1208     va_start(ap, def_value);
1209     ret = krb5_config_vget_time_default(context, c, def_value, ap);
1210     va_end(ap);
1211     return ret;
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 ... a list of names, terminated with NULL.
1220  *
1221  * @return parsed the time or -1 on error
1222  *
1223  * @ingroup krb5_support
1224  */
1225 
1226 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1227 krb5_config_get_time (krb5_context context,
1228 		      const krb5_config_section *c,
1229 		      ...)
1230 {
1231     va_list ap;
1232     int ret;
1233     va_start(ap, c);
1234     ret = krb5_config_vget_time (context, c, ap);
1235     va_end(ap);
1236     return ret;
1237 }
1238 
1239 
1240 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1241 krb5_config_vget_int_default (krb5_context context,
1242 			      const krb5_config_section *c,
1243 			      int def_value,
1244 			      va_list args)
1245 {
1246     const char *str;
1247     str = krb5_config_vget_string (context, c, args);
1248     if(str == NULL)
1249 	return def_value;
1250     else {
1251 	char *endptr;
1252 	long l;
1253 	l = strtol(str, &endptr, 0);
1254 	if (endptr == str)
1255 	    return def_value;
1256 	else
1257 	    return l;
1258     }
1259 }
1260 
1261 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1262 krb5_config_vget_int  (krb5_context context,
1263 		       const krb5_config_section *c,
1264 		       va_list args)
1265 {
1266     return krb5_config_vget_int_default (context, c, -1, args);
1267 }
1268 
1269 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1270 krb5_config_get_int_default (krb5_context context,
1271 			     const krb5_config_section *c,
1272 			     int def_value,
1273 			     ...)
1274 {
1275     va_list ap;
1276     int ret;
1277     va_start(ap, def_value);
1278     ret = krb5_config_vget_int_default(context, c, def_value, ap);
1279     va_end(ap);
1280     return ret;
1281 }
1282 
1283 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1284 krb5_config_get_int (krb5_context context,
1285 		     const krb5_config_section *c,
1286 		     ...)
1287 {
1288     va_list ap;
1289     int ret;
1290     va_start(ap, c);
1291     ret = krb5_config_vget_int (context, c, ap);
1292     va_end(ap);
1293     return ret;
1294 }
1295 
1296 
1297 #ifndef HEIMDAL_SMALLER
1298 
1299 /**
1300  * Deprecated: configuration files are not strings
1301  *
1302  * @ingroup krb5_deprecated
1303  */
1304 
1305 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1306 krb5_config_parse_string_multi(krb5_context context,
1307 			       const char *string,
1308 			       krb5_config_section **res)
1309     KRB5_DEPRECATED_FUNCTION("Use X instead")
1310 {
1311     const char *str;
1312     unsigned lineno = 0;
1313     krb5_error_code ret;
1314     struct fileptr f;
1315     f.f = NULL;
1316     f.s = string;
1317 
1318     ret = krb5_config_parse_debug (&f, res, &lineno, &str);
1319     if (ret) {
1320 	krb5_set_error_message (context, ret, "%s:%u: %s",
1321 				"<constant>", lineno, str);
1322 	return ret;
1323     }
1324     return 0;
1325 }
1326 
1327 #endif
1328