1 /*	$NetBSD: config_file.c,v 1.1.1.2 2014/04/24 12:45:49 pettai 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_config_section *
_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     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 EINVAL;	/* XXX */
376 	} else if(*p != '\0') {
377 	    if (s == NULL) {
378 		*err_message = "binding before section";
379 		return EINVAL;
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
is_plist_file(const char * fname)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
krb5_config_parse_file_multi(krb5_context context,const char * fname,krb5_config_section ** res)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 (fname[0] == '~' && fname[1] == '/') {
431 #ifndef KRB5_USE_PATH_TOKENS
432 	const char *home = NULL;
433 
434 	if (!_krb5_homedir_access(context)) {
435 	    krb5_set_error_message(context, EPERM,
436 				   "Access to home directory not allowed");
437 	    return EPERM;
438 	}
439 
440 	if(!issuid())
441 	    home = getenv("HOME");
442 
443 	if (home == NULL) {
444 	    struct passwd *pw = getpwuid(getuid());
445 	    if(pw != NULL)
446 		home = pw->pw_dir;
447 	}
448 	if (home) {
449 	    asprintf(&newfname, "%s%s", home, &fname[1]);
450 	    if (newfname == NULL) {
451 		krb5_set_error_message(context, ENOMEM,
452 				       N_("malloc: out of memory", ""));
453 		return ENOMEM;
454 	    }
455 	    fname = newfname;
456 	}
457 #else  /* KRB5_USE_PATH_TOKENS */
458 	if (asprintf(&newfname, "%%{USERCONFIG}%s", &fname[1]) < 0 ||
459 	    newfname == NULL)
460 	{
461 	    krb5_set_error_message(context, ENOMEM,
462 				   N_("malloc: out of memory", ""));
463 	    return ENOMEM;
464 	}
465 	fname = newfname;
466 #endif
467     }
468 
469     if (is_plist_file(fname)) {
470 #ifdef __APPLE__
471 	ret = parse_plist_config(context, fname, res);
472 	if (ret) {
473 	    krb5_set_error_message(context, ret,
474 				   "Failed to parse plist %s", fname);
475 	    if (newfname)
476 		free(newfname);
477 	    return ret;
478 	}
479 #else
480 	krb5_set_error_message(context, ENOENT,
481 			       "no support for plist configuration files");
482 	return ENOENT;
483 #endif
484     } else {
485 #ifdef KRB5_USE_PATH_TOKENS
486 	char * exp_fname = NULL;
487 
488 	ret = _krb5_expand_path_tokens(context, fname, &exp_fname);
489 	if (ret) {
490 	    if (newfname)
491 		free(newfname);
492 	    return ret;
493 	}
494 
495 	if (newfname)
496 	    free(newfname);
497 	fname = newfname = exp_fname;
498 #endif
499 
500 	f.f = fopen(fname, "r");
501 	f.s = NULL;
502 	if(f.f == NULL) {
503 	    ret = errno;
504 	    krb5_set_error_message (context, ret, "open %s: %s",
505 				    fname, strerror(ret));
506 	    if (newfname)
507 		free(newfname);
508 	    return ret;
509 	}
510 
511 	ret = krb5_config_parse_debug (&f, res, &lineno, &str);
512 	fclose(f.f);
513 	if (ret) {
514 	    krb5_set_error_message (context, ret, "%s:%u: %s",
515 				    fname, lineno, str);
516 	    if (newfname)
517 		free(newfname);
518 	    return ret;
519 	}
520     }
521     return 0;
522 }
523 
524 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_config_parse_file(krb5_context context,const char * fname,krb5_config_section ** res)525 krb5_config_parse_file (krb5_context context,
526 			const char *fname,
527 			krb5_config_section **res)
528 {
529     *res = NULL;
530     return krb5_config_parse_file_multi(context, fname, res);
531 }
532 
533 static void
free_binding(krb5_context context,krb5_config_binding * b)534 free_binding (krb5_context context, krb5_config_binding *b)
535 {
536     krb5_config_binding *next_b;
537 
538     while (b) {
539 	free (b->name);
540 	if (b->type == krb5_config_string)
541 	    free (b->u.string);
542 	else if (b->type == krb5_config_list)
543 	    free_binding (context, b->u.list);
544 	else
545 	    krb5_abortx(context, "unknown binding type (%d) in free_binding",
546 			b->type);
547 	next_b = b->next;
548 	free (b);
549 	b = next_b;
550     }
551 }
552 
553 /**
554  * Free configuration file section, the result of
555  * krb5_config_parse_file() and krb5_config_parse_file_multi().
556  *
557  * @param context A Kerberos 5 context
558  * @param s the configuration section to free
559  *
560  * @return returns 0 on successes, otherwise an error code, see
561  *          krb5_get_error_message()
562  *
563  * @ingroup krb5_support
564  */
565 
566 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_config_file_free(krb5_context context,krb5_config_section * s)567 krb5_config_file_free (krb5_context context, krb5_config_section *s)
568 {
569     free_binding (context, s);
570     return 0;
571 }
572 
573 #ifndef HEIMDAL_SMALLER
574 
575 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_config_copy(krb5_context context,krb5_config_section * c,krb5_config_section ** head)576 _krb5_config_copy(krb5_context context,
577 		  krb5_config_section *c,
578 		  krb5_config_section **head)
579 {
580     krb5_config_binding *d, *previous = NULL;
581 
582     *head = NULL;
583 
584     while (c) {
585 	d = calloc(1, sizeof(*d));
586 
587 	if (*head == NULL)
588 	    *head = d;
589 
590 	d->name = strdup(c->name);
591 	d->type = c->type;
592 	if (d->type == krb5_config_string)
593 	    d->u.string = strdup(c->u.string);
594 	else if (d->type == krb5_config_list)
595 	    _krb5_config_copy (context, c->u.list, &d->u.list);
596 	else
597 	    krb5_abortx(context,
598 			"unknown binding type (%d) in krb5_config_copy",
599 			d->type);
600 	if (previous)
601 	    previous->next = d;
602 
603 	previous = d;
604 	c = c->next;
605     }
606     return 0;
607 }
608 
609 #endif /* HEIMDAL_SMALLER */
610 
611 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,...)612 _krb5_config_get_next (krb5_context context,
613 		       const krb5_config_section *c,
614 		       const krb5_config_binding **pointer,
615 		       int type,
616 		       ...)
617 {
618     const char *ret;
619     va_list args;
620 
621     va_start(args, type);
622     ret = _krb5_config_vget_next (context, c, pointer, type, args);
623     va_end(args);
624     return ret;
625 }
626 
627 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)628 vget_next(krb5_context context,
629 	  const krb5_config_binding *b,
630 	  const krb5_config_binding **pointer,
631 	  int type,
632 	  const char *name,
633 	  va_list args)
634 {
635     const char *p = va_arg(args, const char *);
636     while(b != NULL) {
637 	if(strcmp(b->name, name) == 0) {
638 	    if(b->type == (unsigned)type && p == NULL) {
639 		*pointer = b;
640 		return b->u.generic;
641 	    } else if(b->type == krb5_config_list && p != NULL) {
642 		return vget_next(context, b->u.list, pointer, type, p, args);
643 	    }
644 	}
645 	b = b->next;
646     }
647     return NULL;
648 }
649 
650 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)651 _krb5_config_vget_next (krb5_context context,
652 			const krb5_config_section *c,
653 			const krb5_config_binding **pointer,
654 			int type,
655 			va_list args)
656 {
657     const krb5_config_binding *b;
658     const char *p;
659 
660     if(c == NULL)
661 	c = context->cf;
662 
663     if (c == NULL)
664 	return NULL;
665 
666     if (*pointer == NULL) {
667 	/* first time here, walk down the tree looking for the right
668            section */
669 	p = va_arg(args, const char *);
670 	if (p == NULL)
671 	    return NULL;
672 	return vget_next(context, c, pointer, type, p, args);
673     }
674 
675     /* we were called again, so just look for more entries with the
676        same name and type */
677     for (b = (*pointer)->next; b != NULL; b = b->next) {
678 	if(strcmp(b->name, (*pointer)->name) == 0 && b->type == (unsigned)type) {
679 	    *pointer = b;
680 	    return b->u.generic;
681 	}
682     }
683     return NULL;
684 }
685 
686 KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
_krb5_config_get(krb5_context context,const krb5_config_section * c,int type,...)687 _krb5_config_get (krb5_context context,
688 		  const krb5_config_section *c,
689 		  int type,
690 		  ...)
691 {
692     const void *ret;
693     va_list args;
694 
695     va_start(args, type);
696     ret = _krb5_config_vget (context, c, type, args);
697     va_end(args);
698     return ret;
699 }
700 
701 
702 const void *
_krb5_config_vget(krb5_context context,const krb5_config_section * c,int type,va_list args)703 _krb5_config_vget (krb5_context context,
704 		   const krb5_config_section *c,
705 		   int type,
706 		   va_list args)
707 {
708     const krb5_config_binding *foo = NULL;
709 
710     return _krb5_config_vget_next (context, c, &foo, type, args);
711 }
712 
713 /**
714  * Get a list of configuration binding list for more processing
715  *
716  * @param context A Kerberos 5 context.
717  * @param c a configuration section, or NULL to use the section from context
718  * @param ... a list of names, terminated with NULL.
719  *
720  * @return NULL if configuration list is not found, a list otherwise
721  *
722  * @ingroup krb5_support
723  */
724 
725 KRB5_LIB_FUNCTION const krb5_config_binding * KRB5_LIB_CALL
krb5_config_get_list(krb5_context context,const krb5_config_section * c,...)726 krb5_config_get_list (krb5_context context,
727 		      const krb5_config_section *c,
728 		      ...)
729 {
730     const krb5_config_binding *ret;
731     va_list args;
732 
733     va_start(args, c);
734     ret = krb5_config_vget_list (context, c, args);
735     va_end(args);
736     return ret;
737 }
738 
739 /**
740  * Get a list of configuration binding list for more processing
741  *
742  * @param context A Kerberos 5 context.
743  * @param c a configuration section, or NULL to use the section from context
744  * @param args a va_list of arguments
745  *
746  * @return NULL if configuration list is not found, a list otherwise
747  *
748  * @ingroup krb5_support
749  */
750 
751 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)752 krb5_config_vget_list (krb5_context context,
753 		       const krb5_config_section *c,
754 		       va_list args)
755 {
756     return _krb5_config_vget (context, c, krb5_config_list, args);
757 }
758 
759 /**
760  * Returns a "const char *" to a string in the configuration database.
761  * The string may not be valid after a reload of the configuration
762  * database so a caller should make a local copy if it needs to keep
763  * the string.
764  *
765  * @param context A Kerberos 5 context.
766  * @param c a configuration section, or NULL to use the section from context
767  * @param ... a list of names, terminated with NULL.
768  *
769  * @return NULL if configuration string not found, a string otherwise
770  *
771  * @ingroup krb5_support
772  */
773 
774 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
krb5_config_get_string(krb5_context context,const krb5_config_section * c,...)775 krb5_config_get_string (krb5_context context,
776 			const krb5_config_section *c,
777 			...)
778 {
779     const char *ret;
780     va_list args;
781 
782     va_start(args, c);
783     ret = krb5_config_vget_string (context, c, args);
784     va_end(args);
785     return ret;
786 }
787 
788 /**
789  * Like krb5_config_get_string(), but uses a va_list instead of ...
790  *
791  * @param context A Kerberos 5 context.
792  * @param c a configuration section, or NULL to use the section from context
793  * @param args a va_list of arguments
794  *
795  * @return NULL if configuration string not found, a string otherwise
796  *
797  * @ingroup krb5_support
798  */
799 
800 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
krb5_config_vget_string(krb5_context context,const krb5_config_section * c,va_list args)801 krb5_config_vget_string (krb5_context context,
802 			 const krb5_config_section *c,
803 			 va_list args)
804 {
805     return _krb5_config_vget (context, c, krb5_config_string, args);
806 }
807 
808 /**
809  * Like krb5_config_vget_string(), but instead of returning NULL,
810  * instead return a default value.
811  *
812  * @param context A Kerberos 5 context.
813  * @param c a configuration section, or NULL to use the section from context
814  * @param def_value the default value to return if no configuration
815  *        found in the database.
816  * @param args a va_list of arguments
817  *
818  * @return a configuration string
819  *
820  * @ingroup krb5_support
821  */
822 
823 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)824 krb5_config_vget_string_default (krb5_context context,
825 				 const krb5_config_section *c,
826 				 const char *def_value,
827 				 va_list args)
828 {
829     const char *ret;
830 
831     ret = krb5_config_vget_string (context, c, args);
832     if (ret == NULL)
833 	ret = def_value;
834     return ret;
835 }
836 
837 /**
838  * Like krb5_config_get_string(), but instead of returning NULL,
839  * instead return a default value.
840  *
841  * @param context A Kerberos 5 context.
842  * @param c a configuration section, or NULL to use the section from context
843  * @param def_value the default value to return if no configuration
844  *        found in the database.
845  * @param ... a list of names, terminated with NULL.
846  *
847  * @return a configuration string
848  *
849  * @ingroup krb5_support
850  */
851 
852 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,...)853 krb5_config_get_string_default (krb5_context context,
854 				const krb5_config_section *c,
855 				const char *def_value,
856 				...)
857 {
858     const char *ret;
859     va_list args;
860 
861     va_start(args, def_value);
862     ret = krb5_config_vget_string_default (context, c, def_value, args);
863     va_end(args);
864     return ret;
865 }
866 
867 static char *
next_component_string(char * begin,const char * delims,char ** state)868 next_component_string(char * begin, const char * delims, char **state)
869 {
870     char * end;
871 
872     if (begin == NULL)
873         begin = *state;
874 
875     if (*begin == '\0')
876         return NULL;
877 
878     end = begin;
879     while (*end == '"') {
880         char * t = strchr(end + 1, '"');
881 
882         if (t)
883             end = ++t;
884         else
885             end += strlen(end);
886     }
887 
888     if (*end != '\0') {
889         size_t pos;
890 
891         pos = strcspn(end, delims);
892         end = end + pos;
893     }
894 
895     if (*end != '\0') {
896         *end = '\0';
897         *state = end + 1;
898         if (*begin == '"' && *(end - 1) == '"' && begin + 1 < end) {
899             begin++; *(end - 1) = '\0';
900         }
901         return begin;
902     }
903 
904     *state = end;
905     if (*begin == '"' && *(end - 1) == '"' && begin + 1 < end) {
906         begin++; *(end - 1) = '\0';
907     }
908     return begin;
909 }
910 
911 /**
912  * Get a list of configuration strings, free the result with
913  * krb5_config_free_strings().
914  *
915  * @param context A Kerberos 5 context.
916  * @param c a configuration section, or NULL to use the section from context
917  * @param args a va_list of arguments
918  *
919  * @return TRUE or FALSE
920  *
921  * @ingroup krb5_support
922  */
923 
924 KRB5_LIB_FUNCTION char ** KRB5_LIB_CALL
krb5_config_vget_strings(krb5_context context,const krb5_config_section * c,va_list args)925 krb5_config_vget_strings(krb5_context context,
926 			 const krb5_config_section *c,
927 			 va_list args)
928 {
929     char **strings = NULL;
930     int nstr = 0;
931     const krb5_config_binding *b = NULL;
932     const char *p;
933 
934     while((p = _krb5_config_vget_next(context, c, &b,
935 				      krb5_config_string, args))) {
936 	char *tmp = strdup(p);
937 	char *pos = NULL;
938 	char *s;
939 	if(tmp == NULL)
940 	    goto cleanup;
941 	s = next_component_string(tmp, " \t", &pos);
942 	while(s){
943 	    char **tmp2 = realloc(strings, (nstr + 1) * sizeof(*strings));
944 	    if(tmp2 == NULL)
945 		goto cleanup;
946 	    strings = tmp2;
947 	    strings[nstr] = strdup(s);
948 	    nstr++;
949 	    if(strings[nstr-1] == NULL)
950 		goto cleanup;
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
krb5_config_get_strings(krb5_context context,const krb5_config_section * c,...)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
krb5_config_free_strings(char ** strings)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
krb5_config_vget_bool_default(krb5_context context,const krb5_config_section * c,krb5_boolean def_value,va_list args)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
krb5_config_vget_bool(krb5_context context,const krb5_config_section * c,va_list args)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
krb5_config_get_bool_default(krb5_context context,const krb5_config_section * c,krb5_boolean def_value,...)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
krb5_config_get_bool(krb5_context context,const krb5_config_section * c,...)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
krb5_config_vget_time_default(krb5_context context,const krb5_config_section * c,int def_value,va_list args)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
krb5_config_vget_time(krb5_context context,const krb5_config_section * c,va_list args)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
krb5_config_get_time_default(krb5_context context,const krb5_config_section * c,int def_value,...)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
krb5_config_get_time(krb5_context context,const krb5_config_section * c,...)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
krb5_config_vget_int_default(krb5_context context,const krb5_config_section * c,int def_value,va_list args)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
krb5_config_vget_int(krb5_context context,const krb5_config_section * c,va_list args)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
krb5_config_get_int_default(krb5_context context,const krb5_config_section * c,int def_value,...)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
krb5_config_get_int(krb5_context context,const krb5_config_section * c,...)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
krb5_config_parse_string_multi(krb5_context context,const char * string,krb5_config_section ** res)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