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