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