Home | History | Annotate | Line # | Download | only in krb5
      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 Hgskolan
      5  * (Royal Institute of Technology, Stockholm, Sweden).
      6  * All rights reserved.
      7  *
      8  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  *
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  *
     17  * 2. Redistributions in binary form must reproduce the above copyright
     18  *    notice, this list of conditions and the following disclaimer in the
     19  *    documentation and/or other materials provided with the distribution.
     20  *
     21  * 3. Neither the name of the Institute nor the names of its contributors
     22  *    may be used to endorse or promote products derived from this software
     23  *    without specific prior written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     35  * SUCH DAMAGE.
     36  */
     37 
     38 #include "krb5_locl.h"
     39 
     40 #ifdef __APPLE__
     41 #include <CoreFoundation/CoreFoundation.h>
     42 #endif
     43 
     44 /* Gaah! I want a portable funopen */
     45 struct fileptr {
     46     const char *s;
     47     FILE *f;
     48 };
     49 
     50 static char *
     51 config_fgets(char *str, size_t len, struct fileptr *ptr)
     52 {
     53     /* XXX this is not correct, in that they don't do the same if the
     54        line is longer than len */
     55     if(ptr->f != NULL)
     56 	return fgets(str, len, ptr->f);
     57     else {
     58 	/* this is almost strsep_copy */
     59 	const char *p;
     60 	ssize_t l;
     61 	if(*ptr->s == '\0')
     62 	    return NULL;
     63 	p = ptr->s + strcspn(ptr->s, "\n");
     64 	if(*p == '\n')
     65 	    p++;
     66 	l = min(len, (size_t)(p - ptr->s));
     67 	if(len > 0) {
     68 	    memcpy(str, ptr->s, l);
     69 	    str[l] = '\0';
     70 	}
     71 	ptr->s = p;
     72 	return str;
     73     }
     74 }
     75 
     76 static krb5_error_code parse_section(char *p, krb5_config_section **s,
     77 				     krb5_config_section **res,
     78 				     const char **err_message);
     79 static krb5_error_code parse_binding(struct fileptr *f, unsigned *lineno, char *p,
     80 				     krb5_config_binding **b,
     81 				     krb5_config_binding **parent,
     82 				     const char **err_message);
     83 static krb5_error_code parse_list(struct fileptr *f, unsigned *lineno,
     84 				  krb5_config_binding **parent,
     85 				  const char **err_message);
     86 
     87 KRB5_LIB_FUNCTION krb5_config_section * KRB5_LIB_CALL
     88 _krb5_config_get_entry(krb5_config_section **parent, const char *name, int type)
     89 {
     90     krb5_config_section **q;
     91 
     92     for(q = parent; *q != NULL; q = &(*q)->next)
     93 	if(type == krb5_config_list &&
     94 	   (unsigned)type == (*q)->type &&
     95 	   strcmp(name, (*q)->name) == 0)
     96 	    return *q;
     97     *q = calloc(1, sizeof(**q));
     98     if(*q == NULL)
     99 	return NULL;
    100     (*q)->name = strdup(name);
    101     (*q)->type = type;
    102     if((*q)->name == NULL) {
    103 	free(*q);
    104 	*q = NULL;
    105 	return NULL;
    106     }
    107     return *q;
    108 }
    109 
    110 /*
    111  * Parse a section:
    112  *
    113  * [section]
    114  *	foo = bar
    115  *	b = {
    116  *		a
    117  *	    }
    118  * ...
    119  *
    120  * starting at the line in `p', storing the resulting structure in
    121  * `s' and hooking it into `parent'.
    122  * Store the error message in `err_message'.
    123  */
    124 
    125 static krb5_error_code
    126 parse_section(char *p, krb5_config_section **s, krb5_config_section **parent,
    127 	      const char **err_message)
    128 {
    129     char *p1;
    130     krb5_config_section *tmp;
    131 
    132     p1 = strchr (p + 1, ']');
    133     if (p1 == NULL) {
    134 	*err_message = "missing ]";
    135 	return KRB5_CONFIG_BADFORMAT;
    136     }
    137     *p1 = '\0';
    138     tmp = _krb5_config_get_entry(parent, p + 1, krb5_config_list);
    139     if(tmp == NULL) {
    140 	*err_message = "out of memory";
    141 	return KRB5_CONFIG_BADFORMAT;
    142     }
    143     *s = tmp;
    144     return 0;
    145 }
    146 
    147 /*
    148  * Parse a brace-enclosed list from `f', hooking in the structure at
    149  * `parent'.
    150  * Store the error message in `err_message'.
    151  */
    152 
    153 static krb5_error_code
    154 parse_list(struct fileptr *f, unsigned *lineno, krb5_config_binding **parent,
    155 	   const char **err_message)
    156 {
    157     char buf[KRB5_BUFSIZ];
    158     krb5_error_code ret;
    159     krb5_config_binding *b = NULL;
    160     unsigned beg_lineno = *lineno;
    161 
    162     while(config_fgets(buf, sizeof(buf), f) != NULL) {
    163 	char *p;
    164 
    165 	++*lineno;
    166 	buf[strcspn(buf, "\r\n")] = '\0';
    167 	p = buf;
    168 	while(isspace((unsigned char)*p))
    169 	    ++p;
    170 	if (*p == '#' || *p == ';' || *p == '\0')
    171 	    continue;
    172 	while(isspace((unsigned char)*p))
    173 	    ++p;
    174 	if (*p == '}')
    175 	    return 0;
    176 	if (*p == '\0')
    177 	    continue;
    178 	ret = parse_binding (f, lineno, p, &b, parent, err_message);
    179 	if (ret)
    180 	    return ret;
    181     }
    182     *lineno = beg_lineno;
    183     *err_message = "unclosed {";
    184     return KRB5_CONFIG_BADFORMAT;
    185 }
    186 
    187 /*
    188  *
    189  */
    190 
    191 static krb5_error_code
    192 parse_binding(struct fileptr *f, unsigned *lineno, char *p,
    193 	      krb5_config_binding **b, krb5_config_binding **parent,
    194 	      const char **err_message)
    195 {
    196     krb5_config_binding *tmp;
    197     char *p1, *p2;
    198     krb5_error_code ret = 0;
    199 
    200     p1 = p;
    201     while (*p && *p != '=' && !isspace((unsigned char)*p))
    202 	++p;
    203     if (*p == '\0') {
    204 	*err_message = "missing =";
    205 	return KRB5_CONFIG_BADFORMAT;
    206     }
    207     p2 = p;
    208     while (isspace((unsigned char)*p))
    209 	++p;
    210     if (*p != '=') {
    211 	*err_message = "missing =";
    212 	return KRB5_CONFIG_BADFORMAT;
    213     }
    214     ++p;
    215     while(isspace((unsigned char)*p))
    216 	++p;
    217     *p2 = '\0';
    218     if (*p == '{') {
    219 	tmp = _krb5_config_get_entry(parent, p1, krb5_config_list);
    220 	if (tmp == NULL) {
    221 	    *err_message = "out of memory";
    222 	    return KRB5_CONFIG_BADFORMAT;
    223 	}
    224 	ret = parse_list (f, lineno, &tmp->u.list, err_message);
    225     } else {
    226 	tmp = _krb5_config_get_entry(parent, p1, krb5_config_string);
    227 	if (tmp == NULL) {
    228 	    *err_message = "out of memory";
    229 	    return KRB5_CONFIG_BADFORMAT;
    230 	}
    231 	p1 = p;
    232 	p = p1 + strlen(p1);
    233 	while(p > p1 && isspace((unsigned char)*(p-1)))
    234 	    --p;
    235 	*p = '\0';
    236 	tmp->u.string = strdup(p1);
    237     }
    238     *b = tmp;
    239     return ret;
    240 }
    241 
    242 #if defined(__APPLE__)
    243 
    244 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
    245 #define HAVE_CFPROPERTYLISTCREATEWITHSTREAM 1
    246 #endif
    247 
    248 static char *
    249 cfstring2cstring(CFStringRef string)
    250 {
    251     CFIndex len;
    252     char *str;
    253 
    254     str = (char *) CFStringGetCStringPtr(string, kCFStringEncodingUTF8);
    255     if (str)
    256 	return strdup(str);
    257 
    258     len = CFStringGetLength(string);
    259     len = 1 + CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8);
    260     str = malloc(len);
    261     if (str == NULL)
    262 	return NULL;
    263 
    264     if (!CFStringGetCString (string, str, len, kCFStringEncodingUTF8)) {
    265 	free (str);
    266 	return NULL;
    267     }
    268     return str;
    269 }
    270 
    271 static void
    272 convert_content(const void *key, const void *value, void *context)
    273 {
    274     krb5_config_section *tmp, **parent = context;
    275     char *k;
    276 
    277     if (CFGetTypeID(key) != CFStringGetTypeID())
    278 	return;
    279 
    280     k = cfstring2cstring(key);
    281     if (k == NULL)
    282 	return;
    283 
    284     if (CFGetTypeID(value) == CFStringGetTypeID()) {
    285 	tmp = _krb5_config_get_entry(parent, k, krb5_config_string);
    286 	tmp->u.string = cfstring2cstring(value);
    287     } else if (CFGetTypeID(value) == CFDictionaryGetTypeID()) {
    288 	tmp = _krb5_config_get_entry(parent, k, krb5_config_list);
    289 	CFDictionaryApplyFunction(value, convert_content, &tmp->u.list);
    290     } else {
    291 	/* log */
    292     }
    293     free(k);
    294 }
    295 
    296 static krb5_error_code
    297 parse_plist_config(krb5_context context, const char *path, krb5_config_section **parent)
    298 {
    299     CFReadStreamRef s;
    300     CFDictionaryRef d;
    301     CFURLRef url;
    302 
    303     url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8 *)path, strlen(path), FALSE);
    304     if (url == NULL) {
    305 	krb5_clear_error_message(context);
    306 	return ENOMEM;
    307     }
    308 
    309     s = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
    310     CFRelease(url);
    311     if (s == NULL) {
    312 	krb5_clear_error_message(context);
    313 	return ENOMEM;
    314     }
    315 
    316     if (!CFReadStreamOpen(s)) {
    317 	CFRelease(s);
    318 	krb5_clear_error_message(context);
    319 	return ENOENT;
    320     }
    321 
    322 #ifdef HAVE_CFPROPERTYLISTCREATEWITHSTREAM
    323     d = (CFDictionaryRef)CFPropertyListCreateWithStream(NULL, s, 0, kCFPropertyListImmutable, NULL, NULL);
    324 #else
    325     d = (CFDictionaryRef)CFPropertyListCreateFromStream(NULL, s, 0, kCFPropertyListImmutable, NULL, NULL);
    326 #endif
    327     CFRelease(s);
    328     if (d == NULL) {
    329 	krb5_clear_error_message(context);
    330 	return ENOENT;
    331     }
    332 
    333     CFDictionaryApplyFunction(d, convert_content, parent);
    334     CFRelease(d);
    335 
    336     return 0;
    337 }
    338 
    339 #endif
    340 
    341 
    342 /*
    343  * Parse the config file `fname', generating the structures into `res'
    344  * returning error messages in `err_message'
    345  */
    346 
    347 static krb5_error_code
    348 krb5_config_parse_debug (struct fileptr *f,
    349 			 krb5_config_section **res,
    350 			 unsigned *lineno,
    351 			 const char **err_message)
    352 {
    353     krb5_config_section *s = NULL;
    354     krb5_config_binding *b = NULL;
    355     char buf[KRB5_BUFSIZ];
    356     krb5_error_code ret;
    357 
    358     *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
    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
    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
    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
    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
    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
    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
    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 *
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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 *
    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
    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
    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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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