1/* $NetBSD: config_file.c,v 1.5 2023/06/19 21:41:44 christos Exp $ */ 2 3/* 4 * Copyright (c) 1997 - 2004 Kungliga Tekniska H��gskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * 3. Neither the name of the Institute nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38#include "krb5_locl.h" 39 40#ifdef __APPLE__ 41#include <CoreFoundation/CoreFoundation.h> 42#endif 43 44/* Gaah! I want a portable funopen */ 45struct fileptr { 46 const char *s; 47 FILE *f; 48}; 49 50static char * 51config_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 76static krb5_error_code parse_section(char *p, krb5_config_section **s, 77 krb5_config_section **res, 78 const char **err_message); 79static 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); 83static krb5_error_code parse_list(struct fileptr *f, unsigned *lineno, 84 krb5_config_binding **parent, 85 const char **err_message); 86 87KRB5_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 125static krb5_error_code 126parse_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 153static krb5_error_code 154parse_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 191static krb5_error_code 192parse_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 248static char * 249cfstring2cstring(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 271static void 272convert_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 296static krb5_error_code 297parse_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 347static krb5_error_code 348krb5_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 392static int 393is_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 417KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 418krb5_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 523KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 524krb5_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 532static void 533free_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 565KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 566krb5_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 574KRB5_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 610KRB5_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 626static const void * 627vget_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 649KRB5_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 685KRB5_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 701KRB5_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 724KRB5_LIB_FUNCTION const krb5_config_binding * KRB5_LIB_CALL 725krb5_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 750KRB5_LIB_FUNCTION const krb5_config_binding * KRB5_LIB_CALL 751krb5_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 773KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL 774krb5_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 799KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL 800krb5_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 822KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL 823krb5_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 851KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL 852krb5_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 866static char * 867next_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 923KRB5_LIB_FUNCTION char ** KRB5_LIB_CALL 924krb5_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; 966cleanup: 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 987KRB5_LIB_FUNCTION char** KRB5_LIB_CALL 988krb5_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 1009KRB5_LIB_FUNCTION void KRB5_LIB_CALL 1010krb5_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 1038KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 1039krb5_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 1068KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 1069krb5_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 1092KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 1093krb5_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 1122KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 1123krb5_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 1152KRB5_LIB_FUNCTION int KRB5_LIB_CALL 1153krb5_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 1181KRB5_LIB_FUNCTION int KRB5_LIB_CALL 1182krb5_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 1203KRB5_LIB_FUNCTION int KRB5_LIB_CALL 1204krb5_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 1229KRB5_LIB_FUNCTION int KRB5_LIB_CALL 1230krb5_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 1243KRB5_LIB_FUNCTION int KRB5_LIB_CALL 1244krb5_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 1264KRB5_LIB_FUNCTION int KRB5_LIB_CALL 1265krb5_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 1272KRB5_LIB_FUNCTION int KRB5_LIB_CALL 1273krb5_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 1286KRB5_LIB_FUNCTION int KRB5_LIB_CALL 1287krb5_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 1308KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1309krb5_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