1/* 2 * Copyright (c) 1997 - 2004 Kungliga Tekniska H��gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#include "krb5_locl.h" 35 36/* Gaah! I want a portable funopen */ 37struct fileptr { 38 const char *s; 39 FILE *f; 40}; 41 42static char * 43config_fgets(char *str, size_t len, struct fileptr *ptr) 44{ 45 /* XXX this is not correct, in that they don't do the same if the 46 line is longer than len */ 47 if(ptr->f != NULL) 48 return fgets(str, len, ptr->f); 49 else { 50 /* this is almost strsep_copy */ 51 const char *p; 52 ssize_t l; 53 if(*ptr->s == '\0') 54 return NULL; 55 p = ptr->s + strcspn(ptr->s, "\n"); 56 if(*p == '\n') 57 p++; 58 l = min(len, p - ptr->s); 59 if(len > 0) { 60 memcpy(str, ptr->s, l); 61 str[l] = '\0'; 62 } 63 ptr->s = p; 64 return str; 65 } 66} 67 68static krb5_error_code parse_section(char *p, krb5_config_section **s, 69 krb5_config_section **res, 70 const char **error_message); 71static krb5_error_code parse_binding(struct fileptr *f, unsigned *lineno, char *p, 72 krb5_config_binding **b, 73 krb5_config_binding **parent, 74 const char **error_message); 75static krb5_error_code parse_list(struct fileptr *f, unsigned *lineno, 76 krb5_config_binding **parent, 77 const char **error_message); 78 79static krb5_config_section * 80get_entry(krb5_config_section **parent, const char *name, int type) 81{ 82 krb5_config_section **q; 83 84 for(q = parent; *q != NULL; q = &(*q)->next) 85 if(type == krb5_config_list && 86 type == (*q)->type && 87 strcmp(name, (*q)->name) == 0) 88 return *q; 89 *q = calloc(1, sizeof(**q)); 90 if(*q == NULL) 91 return NULL; 92 (*q)->name = strdup(name); 93 (*q)->type = type; 94 if((*q)->name == NULL) { 95 free(*q); 96 *q = NULL; 97 return NULL; 98 } 99 return *q; 100} 101 102/* 103 * Parse a section: 104 * 105 * [section] 106 * foo = bar 107 * b = { 108 * a 109 * } 110 * ... 111 * 112 * starting at the line in `p', storing the resulting structure in 113 * `s' and hooking it into `parent'. 114 * Store the error message in `error_message'. 115 */ 116 117static krb5_error_code 118parse_section(char *p, krb5_config_section **s, krb5_config_section **parent, 119 const char **error_message) 120{ 121 char *p1; 122 krb5_config_section *tmp; 123 124 p1 = strchr (p + 1, ']'); 125 if (p1 == NULL) { 126 *error_message = "missing ]"; 127 return KRB5_CONFIG_BADFORMAT; 128 } 129 *p1 = '\0'; 130 tmp = get_entry(parent, p + 1, krb5_config_list); 131 if(tmp == NULL) { 132 *error_message = "out of memory"; 133 return KRB5_CONFIG_BADFORMAT; 134 } 135 *s = tmp; 136 return 0; 137} 138 139/* 140 * Parse a brace-enclosed list from `f', hooking in the structure at 141 * `parent'. 142 * Store the error message in `error_message'. 143 */ 144 145static krb5_error_code 146parse_list(struct fileptr *f, unsigned *lineno, krb5_config_binding **parent, 147 const char **error_message) 148{ 149 char buf[BUFSIZ]; 150 krb5_error_code ret; 151 krb5_config_binding *b = NULL; 152 unsigned beg_lineno = *lineno; 153 154 while(config_fgets(buf, sizeof(buf), f) != NULL) { 155 char *p; 156 157 ++*lineno; 158 buf[strcspn(buf, "\r\n")] = '\0'; 159 p = buf; 160 while(isspace((unsigned char)*p)) 161 ++p; 162 if (*p == '#' || *p == ';' || *p == '\0') 163 continue; 164 while(isspace((unsigned char)*p)) 165 ++p; 166 if (*p == '}') 167 return 0; 168 if (*p == '\0') 169 continue; 170 ret = parse_binding (f, lineno, p, &b, parent, error_message); 171 if (ret) 172 return ret; 173 } 174 *lineno = beg_lineno; 175 *error_message = "unclosed {"; 176 return KRB5_CONFIG_BADFORMAT; 177} 178 179/* 180 * 181 */ 182 183static krb5_error_code 184parse_binding(struct fileptr *f, unsigned *lineno, char *p, 185 krb5_config_binding **b, krb5_config_binding **parent, 186 const char **error_message) 187{ 188 krb5_config_binding *tmp; 189 char *p1, *p2; 190 krb5_error_code ret = 0; 191 192 p1 = p; 193 while (*p && *p != '=' && !isspace((unsigned char)*p)) 194 ++p; 195 if (*p == '\0') { 196 *error_message = "missing ="; 197 return KRB5_CONFIG_BADFORMAT; 198 } 199 p2 = p; 200 while (isspace((unsigned char)*p)) 201 ++p; 202 if (*p != '=') { 203 *error_message = "missing ="; 204 return KRB5_CONFIG_BADFORMAT; 205 } 206 ++p; 207 while(isspace((unsigned char)*p)) 208 ++p; 209 *p2 = '\0'; 210 if (*p == '{') { 211 tmp = get_entry(parent, p1, krb5_config_list); 212 if (tmp == NULL) { 213 *error_message = "out of memory"; 214 return KRB5_CONFIG_BADFORMAT; 215 } 216 ret = parse_list (f, lineno, &tmp->u.list, error_message); 217 } else { 218 tmp = get_entry(parent, p1, krb5_config_string); 219 if (tmp == NULL) { 220 *error_message = "out of memory"; 221 return KRB5_CONFIG_BADFORMAT; 222 } 223 p1 = p; 224 p = p1 + strlen(p1); 225 while(p > p1 && isspace((unsigned char)*(p-1))) 226 --p; 227 *p = '\0'; 228 tmp->u.string = strdup(p1); 229 } 230 *b = tmp; 231 return ret; 232} 233 234/* 235 * Parse the config file `fname', generating the structures into `res' 236 * returning error messages in `error_message' 237 */ 238 239static krb5_error_code 240krb5_config_parse_debug (struct fileptr *f, 241 krb5_config_section **res, 242 unsigned *lineno, 243 const char **error_message) 244{ 245 krb5_config_section *s = NULL; 246 krb5_config_binding *b = NULL; 247 char buf[BUFSIZ]; 248 krb5_error_code ret; 249 250 while (config_fgets(buf, sizeof(buf), f) != NULL) { 251 char *p; 252 253 ++*lineno; 254 buf[strcspn(buf, "\r\n")] = '\0'; 255 p = buf; 256 while(isspace((unsigned char)*p)) 257 ++p; 258 if (*p == '#' || *p == ';') 259 continue; 260 if (*p == '[') { 261 ret = parse_section(p, &s, res, error_message); 262 if (ret) 263 return ret; 264 b = NULL; 265 } else if (*p == '}') { 266 *error_message = "unmatched }"; 267 return EINVAL; /* XXX */ 268 } else if(*p != '\0') { 269 if (s == NULL) { 270 *error_message = "binding before section"; 271 return EINVAL; 272 } 273 ret = parse_binding(f, lineno, p, &b, &s->u.list, error_message); 274 if (ret) 275 return ret; 276 } 277 } 278 return 0; 279} 280 281krb5_error_code KRB5_LIB_FUNCTION 282krb5_config_parse_string_multi(krb5_context context, 283 const char *string, 284 krb5_config_section **res) 285{ 286 const char *str; 287 unsigned lineno = 0; 288 krb5_error_code ret; 289 struct fileptr f; 290 f.f = NULL; 291 f.s = string; 292 293 ret = krb5_config_parse_debug (&f, res, &lineno, &str); 294 if (ret) { 295 krb5_set_error_message (context, ret, "%s:%u: %s", 296 "<constant>", lineno, str); 297 return ret; 298 } 299 return 0; 300} 301 302/** 303 * Parse a configuration file and add the result into res. This 304 * interface can be used to parse several configuration files into one 305 * resulting krb5_config_section by calling it repeatably. 306 * 307 * @param context a Kerberos 5 context. 308 * @param fname a file name to a Kerberos configuration file 309 * @param res the returned result, must be free with krb5_free_config_files(). 310 * @return Return an error code or 0, see krb5_get_error_message(). 311 * 312 * @ingroup krb5_support 313 */ 314 315krb5_error_code KRB5_LIB_FUNCTION 316krb5_config_parse_file_multi (krb5_context context, 317 const char *fname, 318 krb5_config_section **res) 319{ 320 const char *str; 321 char *newfname = NULL; 322 unsigned lineno = 0; 323 krb5_error_code ret; 324 struct fileptr f; 325 326 /** 327 * If the fname starts with "~/" parse configuration file in the 328 * current users home directory. The behavior can be disabled and 329 * enabled by calling krb5_set_home_dir_access(). 330 */ 331 if (_krb5_homedir_access(context) && fname[0] == '~' && fname[1] == '/') { 332 const char *home = NULL; 333 334 if(!issuid()) 335 home = getenv("HOME"); 336 337 if (home == NULL) { 338 struct passwd *pw = getpwuid(getuid()); 339 if(pw != NULL) 340 home = pw->pw_dir; 341 } 342 if (home) { 343 asprintf(&newfname, "%s%s", home, &fname[1]); 344 if (newfname == NULL) { 345 krb5_set_error_message(context, ENOMEM, 346 N_("malloc: out of memory", "")); 347 return ENOMEM; 348 } 349 fname = newfname; 350 } 351 } 352 353 f.f = fopen(fname, "r"); 354 f.s = NULL; 355 if(f.f == NULL) { 356 ret = errno; 357 krb5_set_error_message (context, ret, "open %s: %s", 358 fname, strerror(ret)); 359 if (newfname) 360 free(newfname); 361 return ret; 362 } 363 364 ret = krb5_config_parse_debug (&f, res, &lineno, &str); 365 fclose(f.f); 366 if (ret) { 367 krb5_set_error_message (context, ret, "%s:%u: %s", fname, lineno, str); 368 if (newfname) 369 free(newfname); 370 return ret; 371 } 372 if (newfname) 373 free(newfname); 374 return 0; 375} 376 377krb5_error_code KRB5_LIB_FUNCTION 378krb5_config_parse_file (krb5_context context, 379 const char *fname, 380 krb5_config_section **res) 381{ 382 *res = NULL; 383 return krb5_config_parse_file_multi(context, fname, res); 384} 385 386static void 387free_binding (krb5_context context, krb5_config_binding *b) 388{ 389 krb5_config_binding *next_b; 390 391 while (b) { 392 free (b->name); 393 if (b->type == krb5_config_string) 394 free (b->u.string); 395 else if (b->type == krb5_config_list) 396 free_binding (context, b->u.list); 397 else 398 krb5_abortx(context, "unknown binding type (%d) in free_binding", 399 b->type); 400 next_b = b->next; 401 free (b); 402 b = next_b; 403 } 404} 405 406krb5_error_code KRB5_LIB_FUNCTION 407krb5_config_file_free (krb5_context context, krb5_config_section *s) 408{ 409 free_binding (context, s); 410 return 0; 411} 412 413krb5_error_code 414_krb5_config_copy(krb5_context context, 415 krb5_config_section *c, 416 krb5_config_section **head) 417{ 418 krb5_config_binding *d, *previous = NULL; 419 420 *head = NULL; 421 422 while (c) { 423 d = calloc(1, sizeof(*d)); 424 425 if (*head == NULL) 426 *head = d; 427 428 d->name = strdup(c->name); 429 d->type = c->type; 430 if (d->type == krb5_config_string) 431 d->u.string = strdup(c->u.string); 432 else if (d->type == krb5_config_list) 433 _krb5_config_copy (context, c->u.list, &d->u.list); 434 else 435 krb5_abortx(context, 436 "unknown binding type (%d) in krb5_config_copy", 437 d->type); 438 if (previous) 439 previous->next = d; 440 441 previous = d; 442 c = c->next; 443 } 444 return 0; 445} 446 447 448 449const void * 450krb5_config_get_next (krb5_context context, 451 const krb5_config_section *c, 452 const krb5_config_binding **pointer, 453 int type, 454 ...) 455{ 456 const char *ret; 457 va_list args; 458 459 va_start(args, type); 460 ret = krb5_config_vget_next (context, c, pointer, type, args); 461 va_end(args); 462 return ret; 463} 464 465static const void * 466vget_next(krb5_context context, 467 const krb5_config_binding *b, 468 const krb5_config_binding **pointer, 469 int type, 470 const char *name, 471 va_list args) 472{ 473 const char *p = va_arg(args, const char *); 474 while(b != NULL) { 475 if(strcmp(b->name, name) == 0) { 476 if(b->type == type && p == NULL) { 477 *pointer = b; 478 return b->u.generic; 479 } else if(b->type == krb5_config_list && p != NULL) { 480 return vget_next(context, b->u.list, pointer, type, p, args); 481 } 482 } 483 b = b->next; 484 } 485 return NULL; 486} 487 488const void * 489krb5_config_vget_next (krb5_context context, 490 const krb5_config_section *c, 491 const krb5_config_binding **pointer, 492 int type, 493 va_list args) 494{ 495 const krb5_config_binding *b; 496 const char *p; 497 498 if(c == NULL) 499 c = context->cf; 500 501 if (c == NULL) 502 return NULL; 503 504 if (*pointer == NULL) { 505 /* first time here, walk down the tree looking for the right 506 section */ 507 p = va_arg(args, const char *); 508 if (p == NULL) 509 return NULL; 510 return vget_next(context, c, pointer, type, p, args); 511 } 512 513 /* we were called again, so just look for more entries with the 514 same name and type */ 515 for (b = (*pointer)->next; b != NULL; b = b->next) { 516 if(strcmp(b->name, (*pointer)->name) == 0 && b->type == type) { 517 *pointer = b; 518 return b->u.generic; 519 } 520 } 521 return NULL; 522} 523 524const void * 525krb5_config_get (krb5_context context, 526 const krb5_config_section *c, 527 int type, 528 ...) 529{ 530 const void *ret; 531 va_list args; 532 533 va_start(args, type); 534 ret = krb5_config_vget (context, c, type, args); 535 va_end(args); 536 return ret; 537} 538 539const void * 540krb5_config_vget (krb5_context context, 541 const krb5_config_section *c, 542 int type, 543 va_list args) 544{ 545 const krb5_config_binding *foo = NULL; 546 547 return krb5_config_vget_next (context, c, &foo, type, args); 548} 549 550const krb5_config_binding * 551krb5_config_get_list (krb5_context context, 552 const krb5_config_section *c, 553 ...) 554{ 555 const krb5_config_binding *ret; 556 va_list args; 557 558 va_start(args, c); 559 ret = krb5_config_vget_list (context, c, args); 560 va_end(args); 561 return ret; 562} 563 564const krb5_config_binding * 565krb5_config_vget_list (krb5_context context, 566 const krb5_config_section *c, 567 va_list args) 568{ 569 return krb5_config_vget (context, c, krb5_config_list, args); 570} 571 572const char* KRB5_LIB_FUNCTION 573krb5_config_get_string (krb5_context context, 574 const krb5_config_section *c, 575 ...) 576{ 577 const char *ret; 578 va_list args; 579 580 va_start(args, c); 581 ret = krb5_config_vget_string (context, c, args); 582 va_end(args); 583 return ret; 584} 585 586const char* KRB5_LIB_FUNCTION 587krb5_config_vget_string (krb5_context context, 588 const krb5_config_section *c, 589 va_list args) 590{ 591 return krb5_config_vget (context, c, krb5_config_string, args); 592} 593 594const char* KRB5_LIB_FUNCTION 595krb5_config_vget_string_default (krb5_context context, 596 const krb5_config_section *c, 597 const char *def_value, 598 va_list args) 599{ 600 const char *ret; 601 602 ret = krb5_config_vget_string (context, c, args); 603 if (ret == NULL) 604 ret = def_value; 605 return ret; 606} 607 608const char* KRB5_LIB_FUNCTION 609krb5_config_get_string_default (krb5_context context, 610 const krb5_config_section *c, 611 const char *def_value, 612 ...) 613{ 614 const char *ret; 615 va_list args; 616 617 va_start(args, def_value); 618 ret = krb5_config_vget_string_default (context, c, def_value, args); 619 va_end(args); 620 return ret; 621} 622 623char ** KRB5_LIB_FUNCTION 624krb5_config_vget_strings(krb5_context context, 625 const krb5_config_section *c, 626 va_list args) 627{ 628 char **strings = NULL; 629 int nstr = 0; 630 const krb5_config_binding *b = NULL; 631 const char *p; 632 633 while((p = krb5_config_vget_next(context, c, &b, 634 krb5_config_string, args))) { 635 char *tmp = strdup(p); 636 char *pos = NULL; 637 char *s; 638 if(tmp == NULL) 639 goto cleanup; 640 s = strtok_r(tmp, " \t", &pos); 641 while(s){ 642 char **tmp2 = realloc(strings, (nstr + 1) * sizeof(*strings)); 643 if(tmp2 == NULL) 644 goto cleanup; 645 strings = tmp2; 646 strings[nstr] = strdup(s); 647 nstr++; 648 if(strings[nstr-1] == NULL) 649 goto cleanup; 650 s = strtok_r(NULL, " \t", &pos); 651 } 652 free(tmp); 653 } 654 if(nstr){ 655 char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings)); 656 if(tmp == NULL) 657 goto cleanup; 658 strings = tmp; 659 strings[nstr] = NULL; 660 } 661 return strings; 662cleanup: 663 while(nstr--) 664 free(strings[nstr]); 665 free(strings); 666 return NULL; 667 668} 669 670char** 671krb5_config_get_strings(krb5_context context, 672 const krb5_config_section *c, 673 ...) 674{ 675 va_list ap; 676 char **ret; 677 va_start(ap, c); 678 ret = krb5_config_vget_strings(context, c, ap); 679 va_end(ap); 680 return ret; 681} 682 683void KRB5_LIB_FUNCTION 684krb5_config_free_strings(char **strings) 685{ 686 char **s = strings; 687 while(s && *s){ 688 free(*s); 689 s++; 690 } 691 free(strings); 692} 693 694krb5_boolean KRB5_LIB_FUNCTION 695krb5_config_vget_bool_default (krb5_context context, 696 const krb5_config_section *c, 697 krb5_boolean def_value, 698 va_list args) 699{ 700 const char *str; 701 str = krb5_config_vget_string (context, c, args); 702 if(str == NULL) 703 return def_value; 704 if(strcasecmp(str, "yes") == 0 || 705 strcasecmp(str, "true") == 0 || 706 atoi(str)) return TRUE; 707 return FALSE; 708} 709 710krb5_boolean KRB5_LIB_FUNCTION 711krb5_config_vget_bool (krb5_context context, 712 const krb5_config_section *c, 713 va_list args) 714{ 715 return krb5_config_vget_bool_default (context, c, FALSE, args); 716} 717 718krb5_boolean KRB5_LIB_FUNCTION 719krb5_config_get_bool_default (krb5_context context, 720 const krb5_config_section *c, 721 krb5_boolean def_value, 722 ...) 723{ 724 va_list ap; 725 krb5_boolean ret; 726 va_start(ap, def_value); 727 ret = krb5_config_vget_bool_default(context, c, def_value, ap); 728 va_end(ap); 729 return ret; 730} 731 732krb5_boolean KRB5_LIB_FUNCTION 733krb5_config_get_bool (krb5_context context, 734 const krb5_config_section *c, 735 ...) 736{ 737 va_list ap; 738 krb5_boolean ret; 739 va_start(ap, c); 740 ret = krb5_config_vget_bool (context, c, ap); 741 va_end(ap); 742 return ret; 743} 744 745int KRB5_LIB_FUNCTION 746krb5_config_vget_time_default (krb5_context context, 747 const krb5_config_section *c, 748 int def_value, 749 va_list args) 750{ 751 const char *str; 752 krb5_deltat t; 753 754 str = krb5_config_vget_string (context, c, args); 755 if(str == NULL) 756 return def_value; 757 if (krb5_string_to_deltat(str, &t)) 758 return def_value; 759 return t; 760} 761 762int KRB5_LIB_FUNCTION 763krb5_config_vget_time (krb5_context context, 764 const krb5_config_section *c, 765 va_list args) 766{ 767 return krb5_config_vget_time_default (context, c, -1, args); 768} 769 770int KRB5_LIB_FUNCTION 771krb5_config_get_time_default (krb5_context context, 772 const krb5_config_section *c, 773 int def_value, 774 ...) 775{ 776 va_list ap; 777 int ret; 778 va_start(ap, def_value); 779 ret = krb5_config_vget_time_default(context, c, def_value, ap); 780 va_end(ap); 781 return ret; 782} 783 784int KRB5_LIB_FUNCTION 785krb5_config_get_time (krb5_context context, 786 const krb5_config_section *c, 787 ...) 788{ 789 va_list ap; 790 int ret; 791 va_start(ap, c); 792 ret = krb5_config_vget_time (context, c, ap); 793 va_end(ap); 794 return ret; 795} 796 797 798int KRB5_LIB_FUNCTION 799krb5_config_vget_int_default (krb5_context context, 800 const krb5_config_section *c, 801 int def_value, 802 va_list args) 803{ 804 const char *str; 805 str = krb5_config_vget_string (context, c, args); 806 if(str == NULL) 807 return def_value; 808 else { 809 char *endptr; 810 long l; 811 l = strtol(str, &endptr, 0); 812 if (endptr == str) 813 return def_value; 814 else 815 return l; 816 } 817} 818 819int KRB5_LIB_FUNCTION 820krb5_config_vget_int (krb5_context context, 821 const krb5_config_section *c, 822 va_list args) 823{ 824 return krb5_config_vget_int_default (context, c, -1, args); 825} 826 827int KRB5_LIB_FUNCTION 828krb5_config_get_int_default (krb5_context context, 829 const krb5_config_section *c, 830 int def_value, 831 ...) 832{ 833 va_list ap; 834 int ret; 835 va_start(ap, def_value); 836 ret = krb5_config_vget_int_default(context, c, def_value, ap); 837 va_end(ap); 838 return ret; 839} 840 841int KRB5_LIB_FUNCTION 842krb5_config_get_int (krb5_context context, 843 const krb5_config_section *c, 844 ...) 845{ 846 va_list ap; 847 int ret; 848 va_start(ap, c); 849 ret = krb5_config_vget_int (context, c, ap); 850 va_end(ap); 851 return ret; 852} 853