1 /* 2 * Copyright (c) 1997 - 2003 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 RCSID("$Id: config_file.c,v 1.46.4.2 2003/10/13 13:46:10 lha Exp $"); 36 37 #ifndef HAVE_NETINFO 38 39 static krb5_error_code parse_section(char *p, krb5_config_section **s, 40 krb5_config_section **res, 41 const char **error_message); 42 static krb5_error_code parse_binding(FILE *f, unsigned *lineno, char *p, 43 krb5_config_binding **b, 44 krb5_config_binding **parent, 45 const char **error_message); 46 static krb5_error_code parse_list(FILE *f, unsigned *lineno, 47 krb5_config_binding **parent, 48 const char **error_message); 49 50 static krb5_config_section * 51 get_entry(krb5_config_section **parent, const char *name, int type) 52 { 53 krb5_config_section **q; 54 55 for(q = parent; *q != NULL; q = &(*q)->next) 56 if(type == krb5_config_list && 57 type == (*q)->type && 58 strcmp(name, (*q)->name) == 0) 59 return *q; 60 *q = calloc(1, sizeof(**q)); 61 if(*q == NULL) 62 return NULL; 63 (*q)->name = strdup(name); 64 (*q)->type = type; 65 if((*q)->name == NULL) { 66 free(*q); 67 *q = NULL; 68 return NULL; 69 } 70 return *q; 71 } 72 73 /* 74 * Parse a section: 75 * 76 * [section] 77 * foo = bar 78 * b = { 79 * a 80 * } 81 * ... 82 * 83 * starting at the line in `p', storing the resulting structure in 84 * `s' and hooking it into `parent'. 85 * Store the error message in `error_message'. 86 */ 87 88 static krb5_error_code 89 parse_section(char *p, krb5_config_section **s, krb5_config_section **parent, 90 const char **error_message) 91 { 92 char *p1; 93 krb5_config_section *tmp; 94 95 p1 = strchr (p + 1, ']'); 96 if (p1 == NULL) { 97 *error_message = "missing ]"; 98 return KRB5_CONFIG_BADFORMAT; 99 } 100 *p1 = '\0'; 101 tmp = get_entry(parent, p + 1, krb5_config_list); 102 if(tmp == NULL) { 103 *error_message = "out of memory"; 104 return KRB5_CONFIG_BADFORMAT; 105 } 106 *s = tmp; 107 return 0; 108 } 109 110 /* 111 * Parse a brace-enclosed list from `f', hooking in the structure at 112 * `parent'. 113 * Store the error message in `error_message'. 114 */ 115 116 static krb5_error_code 117 parse_list(FILE *f, unsigned *lineno, krb5_config_binding **parent, 118 const char **error_message) 119 { 120 char buf[BUFSIZ]; 121 krb5_error_code ret; 122 krb5_config_binding *b = NULL; 123 unsigned beg_lineno = *lineno; 124 125 while(fgets(buf, sizeof(buf), f) != NULL) { 126 char *p; 127 128 ++*lineno; 129 if (buf[strlen(buf) - 1] == '\n') 130 buf[strlen(buf) - 1] = '\0'; 131 p = buf; 132 while(isspace((unsigned char)*p)) 133 ++p; 134 if (*p == '#' || *p == ';' || *p == '\0') 135 continue; 136 while(isspace((unsigned char)*p)) 137 ++p; 138 if (*p == '}') 139 return 0; 140 if (*p == '\0') 141 continue; 142 ret = parse_binding (f, lineno, p, &b, parent, error_message); 143 if (ret) 144 return ret; 145 } 146 *lineno = beg_lineno; 147 *error_message = "unclosed {"; 148 return KRB5_CONFIG_BADFORMAT; 149 } 150 151 /* 152 * 153 */ 154 155 static krb5_error_code 156 parse_binding(FILE *f, unsigned *lineno, char *p, 157 krb5_config_binding **b, krb5_config_binding **parent, 158 const char **error_message) 159 { 160 krb5_config_binding *tmp; 161 char *p1, *p2; 162 krb5_error_code ret = 0; 163 164 p1 = p; 165 while (*p && *p != '=' && !isspace((unsigned char)*p)) 166 ++p; 167 if (*p == '\0') { 168 *error_message = "missing ="; 169 return KRB5_CONFIG_BADFORMAT; 170 } 171 p2 = p; 172 while (isspace((unsigned char)*p)) 173 ++p; 174 if (*p != '=') { 175 *error_message = "missing ="; 176 return KRB5_CONFIG_BADFORMAT; 177 } 178 ++p; 179 while(isspace((unsigned char)*p)) 180 ++p; 181 *p2 = '\0'; 182 if (*p == '{') { 183 tmp = get_entry(parent, p1, krb5_config_list); 184 if (tmp == NULL) { 185 *error_message = "out of memory"; 186 return KRB5_CONFIG_BADFORMAT; 187 } 188 ret = parse_list (f, lineno, &tmp->u.list, error_message); 189 } else { 190 tmp = get_entry(parent, p1, krb5_config_string); 191 if (tmp == NULL) { 192 *error_message = "out of memory"; 193 return KRB5_CONFIG_BADFORMAT; 194 } 195 p1 = p; 196 p = p1 + strlen(p1); 197 while(p > p1 && isspace((unsigned char)*(p-1))) 198 --p; 199 *p = '\0'; 200 tmp->u.string = strdup(p1); 201 } 202 *b = tmp; 203 return ret; 204 } 205 206 /* 207 * Parse the config file `fname', generating the structures into `res' 208 * returning error messages in `error_message' 209 */ 210 211 static krb5_error_code 212 krb5_config_parse_file_debug (const char *fname, 213 krb5_config_section **res, 214 unsigned *lineno, 215 const char **error_message) 216 { 217 FILE *f; 218 krb5_config_section *s; 219 krb5_config_binding *b; 220 char buf[BUFSIZ]; 221 krb5_error_code ret = 0; 222 223 s = NULL; 224 b = NULL; 225 *lineno = 0; 226 f = fopen (fname, "r"); 227 if (f == NULL) { 228 *error_message = "cannot open file"; 229 return ENOENT; 230 } 231 while (fgets(buf, sizeof(buf), f) != NULL) { 232 char *p; 233 234 ++*lineno; 235 if(buf[strlen(buf) - 1] == '\n') 236 buf[strlen(buf) - 1] = '\0'; 237 p = buf; 238 while(isspace((unsigned char)*p)) 239 ++p; 240 if (*p == '#' || *p == ';') 241 continue; 242 if (*p == '[') { 243 ret = parse_section(p, &s, res, error_message); 244 if (ret) { 245 goto out; 246 } 247 b = NULL; 248 } else if (*p == '}') { 249 *error_message = "unmatched }"; 250 ret = EINVAL; /* XXX */ 251 goto out; 252 } else if(*p != '\0') { 253 if (s == NULL) { 254 *error_message = "binding before section"; 255 ret = EINVAL; 256 goto out; 257 } 258 ret = parse_binding(f, lineno, p, &b, &s->u.list, error_message); 259 if (ret) 260 goto out; 261 } 262 } 263 out: 264 fclose (f); 265 return ret; 266 } 267 268 krb5_error_code 269 krb5_config_parse_file_multi (krb5_context context, 270 const char *fname, 271 krb5_config_section **res) 272 { 273 const char *str; 274 unsigned lineno; 275 krb5_error_code ret; 276 277 ret = krb5_config_parse_file_debug (fname, res, &lineno, &str); 278 if (ret) { 279 krb5_set_error_string (context, "%s:%u: %s", fname, lineno, str); 280 return ret; 281 } 282 return 0; 283 } 284 285 krb5_error_code 286 krb5_config_parse_file (krb5_context context, 287 const char *fname, 288 krb5_config_section **res) 289 { 290 *res = NULL; 291 return krb5_config_parse_file_multi(context, fname, res); 292 } 293 294 #endif /* !HAVE_NETINFO */ 295 296 static void 297 free_binding (krb5_context context, krb5_config_binding *b) 298 { 299 krb5_config_binding *next_b; 300 301 while (b) { 302 free (b->name); 303 if (b->type == krb5_config_string) 304 free (b->u.string); 305 else if (b->type == krb5_config_list) 306 free_binding (context, b->u.list); 307 else 308 krb5_abortx(context, "unknown binding type (%d) in free_binding", 309 b->type); 310 next_b = b->next; 311 free (b); 312 b = next_b; 313 } 314 } 315 316 krb5_error_code 317 krb5_config_file_free (krb5_context context, krb5_config_section *s) 318 { 319 free_binding (context, s); 320 return 0; 321 } 322 323 const void * 324 krb5_config_get_next (krb5_context context, 325 const krb5_config_section *c, 326 const krb5_config_binding **pointer, 327 int type, 328 ...) 329 { 330 const char *ret; 331 va_list args; 332 333 va_start(args, type); 334 ret = krb5_config_vget_next (context, c, pointer, type, args); 335 va_end(args); 336 return ret; 337 } 338 339 static const void * 340 vget_next(krb5_context context, 341 const krb5_config_binding *b, 342 const krb5_config_binding **pointer, 343 int type, 344 const char *name, 345 va_list args) 346 { 347 const char *p = va_arg(args, const char *); 348 while(b != NULL) { 349 if(strcmp(b->name, name) == 0) { 350 if(b->type == type && p == NULL) { 351 *pointer = b; 352 return b->u.generic; 353 } else if(b->type == krb5_config_list && p != NULL) { 354 return vget_next(context, b->u.list, pointer, type, p, args); 355 } 356 } 357 b = b->next; 358 } 359 return NULL; 360 } 361 362 const void * 363 krb5_config_vget_next (krb5_context context, 364 const krb5_config_section *c, 365 const krb5_config_binding **pointer, 366 int type, 367 va_list args) 368 { 369 const krb5_config_binding *b; 370 const char *p; 371 372 if(c == NULL) 373 c = context->cf; 374 375 if (c == NULL) 376 return NULL; 377 378 if (*pointer == NULL) { 379 /* first time here, walk down the tree looking for the right 380 section */ 381 p = va_arg(args, const char *); 382 if (p == NULL) 383 return NULL; 384 return vget_next(context, c, pointer, type, p, args); 385 } 386 387 /* we were called again, so just look for more entries with the 388 same name and type */ 389 for (b = (*pointer)->next; b != NULL; b = b->next) { 390 if(strcmp(b->name, (*pointer)->name) == 0 && b->type == type) { 391 *pointer = b; 392 return b->u.generic; 393 } 394 } 395 return NULL; 396 } 397 398 const void * 399 krb5_config_get (krb5_context context, 400 const krb5_config_section *c, 401 int type, 402 ...) 403 { 404 const void *ret; 405 va_list args; 406 407 va_start(args, type); 408 ret = krb5_config_vget (context, c, type, args); 409 va_end(args); 410 return ret; 411 } 412 413 const void * 414 krb5_config_vget (krb5_context context, 415 const krb5_config_section *c, 416 int type, 417 va_list args) 418 { 419 const krb5_config_binding *foo = NULL; 420 421 return krb5_config_vget_next (context, c, &foo, type, args); 422 } 423 424 const krb5_config_binding * 425 krb5_config_get_list (krb5_context context, 426 const krb5_config_section *c, 427 ...) 428 { 429 const krb5_config_binding *ret; 430 va_list args; 431 432 va_start(args, c); 433 ret = krb5_config_vget_list (context, c, args); 434 va_end(args); 435 return ret; 436 } 437 438 const krb5_config_binding * 439 krb5_config_vget_list (krb5_context context, 440 const krb5_config_section *c, 441 va_list args) 442 { 443 return krb5_config_vget (context, c, krb5_config_list, args); 444 } 445 446 const char * 447 krb5_config_get_string (krb5_context context, 448 const krb5_config_section *c, 449 ...) 450 { 451 const char *ret; 452 va_list args; 453 454 va_start(args, c); 455 ret = krb5_config_vget_string (context, c, args); 456 va_end(args); 457 return ret; 458 } 459 460 const char * 461 krb5_config_vget_string (krb5_context context, 462 const krb5_config_section *c, 463 va_list args) 464 { 465 return krb5_config_vget (context, c, krb5_config_string, args); 466 } 467 468 const char * 469 krb5_config_vget_string_default (krb5_context context, 470 const krb5_config_section *c, 471 const char *def_value, 472 va_list args) 473 { 474 const char *ret; 475 476 ret = krb5_config_vget_string (context, c, args); 477 if (ret == NULL) 478 ret = def_value; 479 return ret; 480 } 481 482 const char * 483 krb5_config_get_string_default (krb5_context context, 484 const krb5_config_section *c, 485 const char *def_value, 486 ...) 487 { 488 const char *ret; 489 va_list args; 490 491 va_start(args, def_value); 492 ret = krb5_config_vget_string_default (context, c, def_value, args); 493 va_end(args); 494 return ret; 495 } 496 497 char ** 498 krb5_config_vget_strings(krb5_context context, 499 const krb5_config_section *c, 500 va_list args) 501 { 502 char **strings = NULL; 503 int nstr = 0; 504 const krb5_config_binding *b = NULL; 505 const char *p; 506 507 while((p = krb5_config_vget_next(context, c, &b, 508 krb5_config_string, args))) { 509 char *tmp = strdup(p); 510 char *pos = NULL; 511 char *s; 512 if(tmp == NULL) 513 goto cleanup; 514 s = strtok_r(tmp, " \t", &pos); 515 while(s){ 516 char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings)); 517 if(tmp == NULL) 518 goto cleanup; 519 strings = tmp; 520 strings[nstr] = strdup(s); 521 nstr++; 522 if(strings[nstr-1] == NULL) 523 goto cleanup; 524 s = strtok_r(NULL, " \t", &pos); 525 } 526 free(tmp); 527 } 528 if(nstr){ 529 char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings)); 530 if(strings == NULL) 531 goto cleanup; 532 strings = tmp; 533 strings[nstr] = NULL; 534 } 535 return strings; 536 cleanup: 537 while(nstr--) 538 free(strings[nstr]); 539 free(strings); 540 return NULL; 541 542 } 543 544 char** 545 krb5_config_get_strings(krb5_context context, 546 const krb5_config_section *c, 547 ...) 548 { 549 va_list ap; 550 char **ret; 551 va_start(ap, c); 552 ret = krb5_config_vget_strings(context, c, ap); 553 va_end(ap); 554 return ret; 555 } 556 557 void 558 krb5_config_free_strings(char **strings) 559 { 560 char **s = strings; 561 while(s && *s){ 562 free(*s); 563 s++; 564 } 565 free(strings); 566 } 567 568 krb5_boolean 569 krb5_config_vget_bool_default (krb5_context context, 570 const krb5_config_section *c, 571 krb5_boolean def_value, 572 va_list args) 573 { 574 const char *str; 575 str = krb5_config_vget_string (context, c, args); 576 if(str == NULL) 577 return def_value; 578 if(strcasecmp(str, "yes") == 0 || 579 strcasecmp(str, "true") == 0 || 580 atoi(str)) return TRUE; 581 return FALSE; 582 } 583 584 krb5_boolean 585 krb5_config_vget_bool (krb5_context context, 586 const krb5_config_section *c, 587 va_list args) 588 { 589 return krb5_config_vget_bool_default (context, c, FALSE, args); 590 } 591 592 krb5_boolean 593 krb5_config_get_bool_default (krb5_context context, 594 const krb5_config_section *c, 595 krb5_boolean def_value, 596 ...) 597 { 598 va_list ap; 599 krb5_boolean ret; 600 va_start(ap, def_value); 601 ret = krb5_config_vget_bool_default(context, c, def_value, ap); 602 va_end(ap); 603 return ret; 604 } 605 606 krb5_boolean 607 krb5_config_get_bool (krb5_context context, 608 const krb5_config_section *c, 609 ...) 610 { 611 va_list ap; 612 krb5_boolean ret; 613 va_start(ap, c); 614 ret = krb5_config_vget_bool (context, c, ap); 615 va_end(ap); 616 return ret; 617 } 618 619 int 620 krb5_config_vget_time_default (krb5_context context, 621 const krb5_config_section *c, 622 int def_value, 623 va_list args) 624 { 625 const char *str; 626 str = krb5_config_vget_string (context, c, args); 627 if(str == NULL) 628 return def_value; 629 return parse_time (str, NULL); 630 } 631 632 int 633 krb5_config_vget_time (krb5_context context, 634 const krb5_config_section *c, 635 va_list args) 636 { 637 return krb5_config_vget_time_default (context, c, -1, args); 638 } 639 640 int 641 krb5_config_get_time_default (krb5_context context, 642 const krb5_config_section *c, 643 int def_value, 644 ...) 645 { 646 va_list ap; 647 int ret; 648 va_start(ap, def_value); 649 ret = krb5_config_vget_time_default(context, c, def_value, ap); 650 va_end(ap); 651 return ret; 652 } 653 654 int 655 krb5_config_get_time (krb5_context context, 656 const krb5_config_section *c, 657 ...) 658 { 659 va_list ap; 660 int ret; 661 va_start(ap, c); 662 ret = krb5_config_vget_time (context, c, ap); 663 va_end(ap); 664 return ret; 665 } 666 667 668 int 669 krb5_config_vget_int_default (krb5_context context, 670 const krb5_config_section *c, 671 int def_value, 672 va_list args) 673 { 674 const char *str; 675 str = krb5_config_vget_string (context, c, args); 676 if(str == NULL) 677 return def_value; 678 else { 679 char *endptr; 680 long l; 681 l = strtol(str, &endptr, 0); 682 if (endptr == str) 683 return def_value; 684 else 685 return l; 686 } 687 } 688 689 int 690 krb5_config_vget_int (krb5_context context, 691 const krb5_config_section *c, 692 va_list args) 693 { 694 return krb5_config_vget_int_default (context, c, -1, args); 695 } 696 697 int 698 krb5_config_get_int_default (krb5_context context, 699 const krb5_config_section *c, 700 int def_value, 701 ...) 702 { 703 va_list ap; 704 int ret; 705 va_start(ap, def_value); 706 ret = krb5_config_vget_int_default(context, c, def_value, ap); 707 va_end(ap); 708 return ret; 709 } 710 711 int 712 krb5_config_get_int (krb5_context context, 713 const krb5_config_section *c, 714 ...) 715 { 716 va_list ap; 717 int ret; 718 va_start(ap, c); 719 ret = krb5_config_vget_int (context, c, ap); 720 va_end(ap); 721 return ret; 722 } 723