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