1 /* $OpenBSD: parse.y,v 1.33 2019/02/13 22:57:08 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> 5 * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org> 6 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 7 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> 8 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> 9 * Copyright (c) 2001 Markus Friedl. All rights reserved. 10 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 11 * Copyright (c) 2001 Theo de Raadt. All rights reserved. 12 * 13 * Permission to use, copy, modify, and distribute this software for any 14 * purpose with or without fee is hereby granted, provided that the above 15 * copyright notice and this permission notice appear in all copies. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 18 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 19 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 20 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 21 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 22 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 23 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 24 */ 25 26 %{ 27 #include <sys/types.h> 28 #include <sys/time.h> 29 #include <sys/queue.h> 30 #include <sys/tree.h> 31 #include <sys/socket.h> 32 #include <sys/stat.h> 33 34 #include <netinet/in.h> 35 #include <arpa/inet.h> 36 37 #include <ctype.h> 38 #include <err.h> 39 #include <errno.h> 40 #include <event.h> 41 #include <fcntl.h> 42 #include <limits.h> 43 #include <netdb.h> 44 #include <pwd.h> 45 #include <stdarg.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <syslog.h> 50 #include <tls.h> 51 #include <unistd.h> 52 53 #include "ypldap.h" 54 55 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 56 static struct file { 57 TAILQ_ENTRY(file) entry; 58 FILE *stream; 59 char *name; 60 size_t ungetpos; 61 size_t ungetsize; 62 u_char *ungetbuf; 63 int eof_reached; 64 int lineno; 65 int errors; 66 } *file, *topfile; 67 struct file *pushfile(const char *, int); 68 int popfile(void); 69 int check_file_secrecy(int, const char *); 70 int yyparse(void); 71 int yylex(void); 72 int yyerror(const char *, ...) 73 __attribute__((__format__ (printf, 1, 2))) 74 __attribute__((__nonnull__ (1))); 75 int kw_cmp(const void *, const void *); 76 int lookup(char *); 77 int igetc(void); 78 int lgetc(int); 79 void lungetc(int); 80 int findeol(void); 81 82 TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 83 struct sym { 84 TAILQ_ENTRY(sym) entry; 85 int used; 86 int persist; 87 char *nam; 88 char *val; 89 }; 90 int symset(const char *, const char *, int); 91 char *symget(const char *); 92 93 struct env *conf = NULL; 94 struct idm *idm = NULL; 95 static int errors = 0; 96 97 typedef struct { 98 union { 99 int64_t number; 100 char *string; 101 } v; 102 int lineno; 103 } YYSTYPE; 104 105 %} 106 107 %token SERVER FILTER ATTRIBUTE BASEDN BINDDN GROUPDN BINDCRED MAPS CHANGE DOMAIN PROVIDE 108 %token USER GROUP TO EXPIRE HOME SHELL GECOS UID GID INTERVAL 109 %token PASSWD NAME FIXED LIST GROUPNAME GROUPPASSWD GROUPGID MAP 110 %token INCLUDE DIRECTORY CLASS PORT ERROR GROUPMEMBERS LDAPS TLS CAFILE 111 %token <v.string> STRING 112 %token <v.number> NUMBER 113 %type <v.number> opcode attribute 114 %type <v.number> port 115 %type <v.number> ssl 116 117 %% 118 119 grammar : /* empty */ 120 | grammar '\n' 121 | grammar include '\n' 122 | grammar varset '\n' 123 | grammar directory '\n' 124 | grammar main '\n' 125 | grammar error '\n' { file->errors++; } 126 ; 127 128 nl : '\n' optnl 129 ; 130 131 optnl : '\n' optnl 132 | /* empty */ 133 ; 134 135 136 include : INCLUDE STRING { 137 struct file *nfile; 138 139 if ((nfile = pushfile($2, 1)) == NULL) { 140 yyerror("failed to include file %s", $2); 141 free($2); 142 YYERROR; 143 } 144 free($2); 145 146 file = nfile; 147 lungetc('\n'); 148 } 149 ; 150 151 varset : STRING '=' STRING { 152 char *s = $1; 153 while (*s++) { 154 if (isspace((unsigned char)*s)) { 155 yyerror("macro name cannot contain " 156 "whitespace"); 157 free($1); 158 free($3); 159 YYERROR; 160 } 161 } 162 if (symset($1, $3, 0) == -1) 163 fatal("cannot store variable"); 164 free($1); 165 free($3); 166 } 167 ; 168 169 port : PORT STRING { 170 struct servent *servent; 171 172 servent = getservbyname($2, "tcp"); 173 if (servent == NULL) { 174 yyerror("port %s is invalid", $2); 175 free($2); 176 YYERROR; 177 } 178 $$ = ntohs(servent->s_port); 179 free($2); 180 } 181 | PORT NUMBER { 182 if ($2 <= 0 || $2 > (int)USHRT_MAX) { 183 yyerror("invalid port: %lld", $2); 184 YYERROR; 185 } 186 $$ = $2; 187 } 188 | /* empty */ { 189 $$ = 0; 190 } 191 ; 192 193 opcode : GROUP { $$ = 0; } 194 | PASSWD { $$ = 1; } 195 ; 196 197 198 attribute : NAME { $$ = 0; } 199 | PASSWD { $$ = 1; } 200 | UID { $$ = 2; } 201 | GID { $$ = 3; } 202 | CLASS { $$ = 4; } 203 | CHANGE { $$ = 5; } 204 | EXPIRE { $$ = 6; } 205 | GECOS { $$ = 7; } 206 | HOME { $$ = 8; } 207 | SHELL { $$ = 9; } 208 | GROUPNAME { $$ = 10; } 209 | GROUPPASSWD { $$ = 11; } 210 | GROUPGID { $$ = 12; } 211 | GROUPMEMBERS { $$ = 13; } 212 ; 213 214 diropt : BINDDN STRING { 215 idm->idm_flags |= F_NEEDAUTH; 216 if (strlcpy(idm->idm_binddn, $2, 217 sizeof(idm->idm_binddn)) >= 218 sizeof(idm->idm_binddn)) { 219 yyerror("directory binddn truncated"); 220 free($2); 221 YYERROR; 222 } 223 free($2); 224 } 225 | BINDCRED STRING { 226 idm->idm_flags |= F_NEEDAUTH; 227 if (strlcpy(idm->idm_bindcred, $2, 228 sizeof(idm->idm_bindcred)) >= 229 sizeof(idm->idm_bindcred)) { 230 yyerror("directory bindcred truncated"); 231 free($2); 232 YYERROR; 233 } 234 free($2); 235 } 236 | BASEDN STRING { 237 if (strlcpy(idm->idm_basedn, $2, 238 sizeof(idm->idm_basedn)) >= 239 sizeof(idm->idm_basedn)) { 240 yyerror("directory basedn truncated"); 241 free($2); 242 YYERROR; 243 } 244 free($2); 245 } 246 | GROUPDN STRING { 247 if(strlcpy(idm->idm_groupdn, $2, 248 sizeof(idm->idm_groupdn)) >= 249 sizeof(idm->idm_groupdn)) { 250 yyerror("directory groupdn truncated"); 251 free($2); 252 YYERROR; 253 } 254 free($2); 255 } 256 | opcode FILTER STRING { 257 if (strlcpy(idm->idm_filters[$1], $3, 258 sizeof(idm->idm_filters[$1])) >= 259 sizeof(idm->idm_filters[$1])) { 260 yyerror("filter truncated"); 261 free($3); 262 YYERROR; 263 } 264 free($3); 265 } 266 | ATTRIBUTE attribute MAPS TO STRING { 267 if (strlcpy(idm->idm_attrs[$2], $5, 268 sizeof(idm->idm_attrs[$2])) >= 269 sizeof(idm->idm_attrs[$2])) { 270 yyerror("attribute truncated"); 271 free($5); 272 YYERROR; 273 } 274 free($5); 275 } 276 | FIXED ATTRIBUTE attribute STRING { 277 if (strlcpy(idm->idm_attrs[$3], $4, 278 sizeof(idm->idm_attrs[$3])) >= 279 sizeof(idm->idm_attrs[$3])) { 280 yyerror("attribute truncated"); 281 free($4); 282 YYERROR; 283 } 284 idm->idm_flags |= F_FIXED_ATTR($3); 285 free($4); 286 } 287 | LIST attribute MAPS TO STRING { 288 if (strlcpy(idm->idm_attrs[$2], $5, 289 sizeof(idm->idm_attrs[$2])) >= 290 sizeof(idm->idm_attrs[$2])) { 291 yyerror("attribute truncated"); 292 free($5); 293 YYERROR; 294 } 295 idm->idm_list |= F_LIST($2); 296 free($5); 297 } 298 ; 299 300 ssl : /* empty */ { $$ = 0; } 301 | LDAPS { $$ = F_SSL; } 302 | TLS { $$ = F_STARTTLS; } 303 ; 304 305 directory : DIRECTORY STRING port ssl { 306 if ((idm = calloc(1, sizeof(*idm))) == NULL) 307 fatal(NULL); 308 idm->idm_id = conf->sc_maxid++; 309 310 if (strlcpy(idm->idm_name, $2, 311 sizeof(idm->idm_name)) >= 312 sizeof(idm->idm_name)) { 313 yyerror("attribute truncated"); 314 free($2); 315 YYERROR; 316 } 317 free($2); 318 319 idm->idm_port = $3; 320 321 if ($4 != 0) { 322 if (tls_init()) { 323 yyerror("tls init failed"); 324 YYERROR; 325 } 326 327 idm->idm_flags |= $4; 328 idm->idm_tls_config = tls_config_new(); 329 if (idm->idm_tls_config == NULL) { 330 yyerror("tls config failed"); 331 YYERROR; 332 } 333 334 if (tls_config_set_protocols( 335 idm->idm_tls_config, 336 TLS_PROTOCOLS_ALL) == -1) { 337 yyerror("tls set protocols failed: %s", 338 tls_config_error( 339 idm->idm_tls_config)); 340 tls_config_free(idm->idm_tls_config); 341 idm->idm_tls_config = NULL; 342 YYERROR; 343 } 344 if (tls_config_set_ciphers(idm->idm_tls_config, 345 "compat") == -1) { 346 yyerror("tls set ciphers failed: %s", 347 tls_config_error( 348 idm->idm_tls_config)); 349 tls_config_free(idm->idm_tls_config); 350 idm->idm_tls_config = NULL; 351 YYERROR; 352 } 353 354 if (tls_config_set_ca_file(idm->idm_tls_config, 355 conf->sc_cafile) == -1) { 356 yyerror("tls set CA bundle failed: %s", 357 tls_config_error( 358 idm->idm_tls_config)); 359 tls_config_free(idm->idm_tls_config); 360 idm->idm_tls_config = NULL; 361 YYERROR; 362 } 363 } 364 365 } '{' optnl diropts '}' { 366 TAILQ_INSERT_TAIL(&conf->sc_idms, idm, idm_entry); 367 idm = NULL; 368 } 369 ; 370 371 main : INTERVAL NUMBER { 372 conf->sc_conf_tv.tv_sec = $2; 373 conf->sc_conf_tv.tv_usec = 0; 374 } 375 | DOMAIN STRING { 376 if (strlcpy(conf->sc_domainname, $2, 377 sizeof(conf->sc_domainname)) >= 378 sizeof(conf->sc_domainname)) { 379 yyerror("domainname truncated"); 380 free($2); 381 YYERROR; 382 } 383 free($2); 384 } 385 | PROVIDE MAP STRING { 386 if (strcmp($3, "passwd.byname") == 0) 387 conf->sc_flags |= YPMAP_PASSWD_BYNAME; 388 else if (strcmp($3, "passwd.byuid") == 0) 389 conf->sc_flags |= YPMAP_PASSWD_BYUID; 390 else if (strcmp($3, "master.passwd.byname") == 0) 391 conf->sc_flags |= YPMAP_MASTER_PASSWD_BYNAME; 392 else if (strcmp($3, "master.passwd.byuid") == 0) 393 conf->sc_flags |= YPMAP_MASTER_PASSWD_BYUID; 394 else if (strcmp($3, "group.byname") == 0) 395 conf->sc_flags |= YPMAP_GROUP_BYNAME; 396 else if (strcmp($3, "group.bygid") == 0) 397 conf->sc_flags |= YPMAP_GROUP_BYGID; 398 else if (strcmp($3, "netid.byname") == 0) 399 conf->sc_flags |= YPMAP_NETID_BYNAME; 400 else { 401 yyerror("unsupported map type: %s", $3); 402 free($3); 403 YYERROR; 404 } 405 free($3); 406 } 407 | CAFILE STRING { 408 free(conf->sc_cafile); 409 conf->sc_cafile = $2; 410 } 411 ; 412 413 diropts : diropts diropt nl 414 | diropt optnl 415 ; 416 417 %% 418 419 struct keywords { 420 const char *k_name; 421 int k_val; 422 }; 423 424 int 425 yyerror(const char *fmt, ...) 426 { 427 va_list ap; 428 char *msg; 429 430 file->errors++; 431 va_start(ap, fmt); 432 if (vasprintf(&msg, fmt, ap) == -1) 433 fatalx("yyerror vasprintf"); 434 va_end(ap); 435 logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg); 436 free(msg); 437 return (0); 438 } 439 440 int 441 kw_cmp(const void *k, const void *e) 442 { 443 return (strcmp(k, ((const struct keywords *)e)->k_name)); 444 } 445 446 int 447 lookup(char *s) 448 { 449 /* this has to be sorted always */ 450 static const struct keywords keywords[] = { 451 { "attribute", ATTRIBUTE }, 452 { "basedn", BASEDN }, 453 { "bindcred", BINDCRED }, 454 { "binddn", BINDDN }, 455 { "cafile", CAFILE }, 456 { "change", CHANGE }, 457 { "class", CLASS }, 458 { "directory", DIRECTORY }, 459 { "domain", DOMAIN }, 460 { "expire", EXPIRE }, 461 { "filter", FILTER }, 462 { "fixed", FIXED }, 463 { "gecos", GECOS }, 464 { "gid", GID }, 465 { "group", GROUP }, 466 { "groupdn", GROUPDN }, 467 { "groupgid", GROUPGID }, 468 { "groupmembers", GROUPMEMBERS }, 469 { "groupname", GROUPNAME }, 470 { "grouppasswd", GROUPPASSWD }, 471 { "home", HOME }, 472 { "include", INCLUDE }, 473 { "interval", INTERVAL }, 474 { "ldaps", LDAPS }, 475 { "list", LIST }, 476 { "map", MAP }, 477 { "maps", MAPS }, 478 { "name", NAME }, 479 { "passwd", PASSWD }, 480 { "port", PORT }, 481 { "provide", PROVIDE }, 482 { "server", SERVER }, 483 { "shell", SHELL }, 484 { "tls", TLS }, 485 { "to", TO }, 486 { "uid", UID }, 487 { "user", USER }, 488 }; 489 const struct keywords *p; 490 491 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 492 sizeof(keywords[0]), kw_cmp); 493 494 if (p) 495 return (p->k_val); 496 else 497 return (STRING); 498 } 499 500 #define START_EXPAND 1 501 #define DONE_EXPAND 2 502 503 static int expanding; 504 505 int 506 igetc(void) 507 { 508 int c; 509 510 while (1) { 511 if (file->ungetpos > 0) 512 c = file->ungetbuf[--file->ungetpos]; 513 else 514 c = getc(file->stream); 515 516 if (c == START_EXPAND) 517 expanding = 1; 518 else if (c == DONE_EXPAND) 519 expanding = 0; 520 else 521 break; 522 } 523 return (c); 524 } 525 526 int 527 lgetc(int quotec) 528 { 529 int c, next; 530 531 if (quotec) { 532 if ((c = igetc()) == EOF) { 533 yyerror("reached end of file while parsing " 534 "quoted string"); 535 if (file == topfile || popfile() == EOF) 536 return (EOF); 537 return (quotec); 538 } 539 return (c); 540 } 541 542 while ((c = igetc()) == '\\') { 543 next = igetc(); 544 if (next != '\n') { 545 c = next; 546 break; 547 } 548 yylval.lineno = file->lineno; 549 file->lineno++; 550 } 551 552 if (c == EOF) { 553 /* 554 * Fake EOL when hit EOF for the first time. This gets line 555 * count right if last line in included file is syntactically 556 * invalid and has no newline. 557 */ 558 if (file->eof_reached == 0) { 559 file->eof_reached = 1; 560 return ('\n'); 561 } 562 while (c == EOF) { 563 if (file == topfile || popfile() == EOF) 564 return (EOF); 565 c = igetc(); 566 } 567 } 568 return (c); 569 } 570 571 void 572 lungetc(int c) 573 { 574 if (c == EOF) 575 return; 576 577 if (file->ungetpos >= file->ungetsize) { 578 void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); 579 if (p == NULL) 580 err(1, "lungetc"); 581 file->ungetbuf = p; 582 file->ungetsize *= 2; 583 } 584 file->ungetbuf[file->ungetpos++] = c; 585 } 586 587 int 588 findeol(void) 589 { 590 int c; 591 592 /* skip to either EOF or the first real EOL */ 593 while (1) { 594 c = lgetc(0); 595 if (c == '\n') { 596 file->lineno++; 597 break; 598 } 599 if (c == EOF) 600 break; 601 } 602 return (ERROR); 603 } 604 605 int 606 yylex(void) 607 { 608 u_char buf[8096]; 609 u_char *p, *val; 610 int quotec, next, c; 611 int token; 612 613 top: 614 p = buf; 615 while ((c = lgetc(0)) == ' ' || c == '\t') 616 ; /* nothing */ 617 618 yylval.lineno = file->lineno; 619 if (c == '#') 620 while ((c = lgetc(0)) != '\n' && c != EOF) 621 ; /* nothing */ 622 if (c == '$' && !expanding) { 623 while (1) { 624 if ((c = lgetc(0)) == EOF) 625 return (0); 626 627 if (p + 1 >= buf + sizeof(buf) - 1) { 628 yyerror("string too long"); 629 return (findeol()); 630 } 631 if (isalnum(c) || c == '_') { 632 *p++ = c; 633 continue; 634 } 635 *p = '\0'; 636 lungetc(c); 637 break; 638 } 639 val = symget(buf); 640 if (val == NULL) { 641 yyerror("macro '%s' not defined", buf); 642 return (findeol()); 643 } 644 p = val + strlen(val) - 1; 645 lungetc(DONE_EXPAND); 646 while (p >= val) { 647 lungetc(*p); 648 p--; 649 } 650 lungetc(START_EXPAND); 651 goto top; 652 } 653 654 switch (c) { 655 case '\'': 656 case '"': 657 quotec = c; 658 while (1) { 659 if ((c = lgetc(quotec)) == EOF) 660 return (0); 661 if (c == '\n') { 662 file->lineno++; 663 continue; 664 } else if (c == '\\') { 665 if ((next = lgetc(quotec)) == EOF) 666 return (0); 667 if (next == quotec || next == ' ' || 668 next == '\t') 669 c = next; 670 else if (next == '\n') { 671 file->lineno++; 672 continue; 673 } else 674 lungetc(next); 675 } else if (c == quotec) { 676 *p = '\0'; 677 break; 678 } else if (c == '\0') { 679 yyerror("syntax error"); 680 return (findeol()); 681 } 682 if (p + 1 >= buf + sizeof(buf) - 1) { 683 yyerror("string too long"); 684 return (findeol()); 685 } 686 *p++ = c; 687 } 688 yylval.v.string = strdup(buf); 689 if (yylval.v.string == NULL) 690 err(1, "%s", __func__); 691 return (STRING); 692 } 693 694 #define allowed_to_end_number(x) \ 695 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 696 697 if (c == '-' || isdigit(c)) { 698 do { 699 *p++ = c; 700 if ((size_t)(p-buf) >= sizeof(buf)) { 701 yyerror("string too long"); 702 return (findeol()); 703 } 704 } while ((c = lgetc(0)) != EOF && isdigit(c)); 705 lungetc(c); 706 if (p == buf + 1 && buf[0] == '-') 707 goto nodigits; 708 if (c == EOF || allowed_to_end_number(c)) { 709 const char *errstr = NULL; 710 711 *p = '\0'; 712 yylval.v.number = strtonum(buf, LLONG_MIN, 713 LLONG_MAX, &errstr); 714 if (errstr) { 715 yyerror("\"%s\" invalid number: %s", 716 buf, errstr); 717 return (findeol()); 718 } 719 return (NUMBER); 720 } else { 721 nodigits: 722 while (p > buf + 1) 723 lungetc(*--p); 724 c = *--p; 725 if (c == '-') 726 return (c); 727 } 728 } 729 730 #define allowed_in_string(x) \ 731 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 732 x != '{' && x != '}' && x != '<' && x != '>' && \ 733 x != '!' && x != '=' && x != '#' && \ 734 x != ',')) 735 736 if (isalnum(c) || c == ':' || c == '_') { 737 do { 738 *p++ = c; 739 if ((size_t)(p-buf) >= sizeof(buf)) { 740 yyerror("string too long"); 741 return (findeol()); 742 } 743 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 744 lungetc(c); 745 *p = '\0'; 746 if ((token = lookup(buf)) == STRING) 747 if ((yylval.v.string = strdup(buf)) == NULL) 748 err(1, "%s", __func__); 749 return (token); 750 } 751 if (c == '\n') { 752 yylval.lineno = file->lineno; 753 file->lineno++; 754 } 755 if (c == EOF) 756 return (0); 757 return (c); 758 } 759 760 int 761 check_file_secrecy(int fd, const char *fname) 762 { 763 struct stat st; 764 765 if (fstat(fd, &st)) { 766 log_warn("cannot stat %s", fname); 767 return (-1); 768 } 769 if (st.st_uid != 0 && st.st_uid != getuid()) { 770 log_warnx("%s: owner not root or current user", fname); 771 return (-1); 772 } 773 if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) { 774 log_warnx("%s: group writable or world read/writable", fname); 775 return (-1); 776 } 777 return (0); 778 } 779 780 struct file * 781 pushfile(const char *name, int secret) 782 { 783 struct file *nfile; 784 785 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 786 log_warn("%s", __func__); 787 return (NULL); 788 } 789 if ((nfile->name = strdup(name)) == NULL) { 790 log_warn("%s", __func__); 791 free(nfile); 792 return (NULL); 793 } 794 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 795 log_warn("%s: %s", __func__, nfile->name); 796 free(nfile->name); 797 free(nfile); 798 return (NULL); 799 } else if (secret && 800 check_file_secrecy(fileno(nfile->stream), nfile->name)) { 801 fclose(nfile->stream); 802 free(nfile->name); 803 free(nfile); 804 return (NULL); 805 } 806 nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0; 807 nfile->ungetsize = 16; 808 nfile->ungetbuf = malloc(nfile->ungetsize); 809 if (nfile->ungetbuf == NULL) { 810 log_warn("%s", __func__); 811 fclose(nfile->stream); 812 free(nfile->name); 813 free(nfile); 814 return (NULL); 815 } 816 TAILQ_INSERT_TAIL(&files, nfile, entry); 817 return (nfile); 818 } 819 820 int 821 popfile(void) 822 { 823 struct file *prev; 824 825 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 826 prev->errors += file->errors; 827 828 TAILQ_REMOVE(&files, file, entry); 829 fclose(file->stream); 830 free(file->name); 831 free(file->ungetbuf); 832 free(file); 833 file = prev; 834 return (file ? 0 : EOF); 835 } 836 837 int 838 parse_config(struct env *x_conf, const char *filename, int opts) 839 { 840 struct sym *sym, *next; 841 842 conf = x_conf; 843 bzero(conf, sizeof(*conf)); 844 845 TAILQ_INIT(&conf->sc_idms); 846 conf->sc_conf_tv.tv_sec = DEFAULT_INTERVAL; 847 conf->sc_conf_tv.tv_usec = 0; 848 conf->sc_cafile = strdup(tls_default_ca_cert_file()); 849 if (conf->sc_cafile == NULL) { 850 log_warn("%s", __func__); 851 return (-1); 852 } 853 854 errors = 0; 855 856 if ((file = pushfile(filename, 1)) == NULL) { 857 return (-1); 858 } 859 topfile = file; 860 861 /* 862 * parse configuration 863 */ 864 setservent(1); 865 yyparse(); 866 endservent(); 867 errors = file->errors; 868 popfile(); 869 870 /* Free macros and check which have not been used. */ 871 TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) { 872 if ((opts & YPLDAP_OPT_VERBOSE) && !sym->used) 873 fprintf(stderr, "warning: macro '%s' not " 874 "used\n", sym->nam); 875 if (!sym->persist) { 876 free(sym->nam); 877 free(sym->val); 878 TAILQ_REMOVE(&symhead, sym, entry); 879 free(sym); 880 } 881 } 882 883 if (errors) { 884 return (-1); 885 } 886 887 return (0); 888 } 889 890 int 891 symset(const char *nam, const char *val, int persist) 892 { 893 struct sym *sym; 894 895 TAILQ_FOREACH(sym, &symhead, entry) { 896 if (strcmp(nam, sym->nam) == 0) 897 break; 898 } 899 900 if (sym != NULL) { 901 if (sym->persist == 1) 902 return (0); 903 else { 904 free(sym->nam); 905 free(sym->val); 906 TAILQ_REMOVE(&symhead, sym, entry); 907 free(sym); 908 } 909 } 910 if ((sym = calloc(1, sizeof(*sym))) == NULL) 911 return (-1); 912 913 sym->nam = strdup(nam); 914 if (sym->nam == NULL) { 915 free(sym); 916 return (-1); 917 } 918 sym->val = strdup(val); 919 if (sym->val == NULL) { 920 free(sym->nam); 921 free(sym); 922 return (-1); 923 } 924 sym->used = 0; 925 sym->persist = persist; 926 TAILQ_INSERT_TAIL(&symhead, sym, entry); 927 return (0); 928 } 929 930 int 931 cmdline_symset(char *s) 932 { 933 char *sym, *val; 934 int ret; 935 936 if ((val = strrchr(s, '=')) == NULL) 937 return (-1); 938 sym = strndup(s, val - s); 939 if (sym == NULL) 940 errx(1, "%s: strndup", __func__); 941 ret = symset(sym, val + 1, 1); 942 free(sym); 943 944 return (ret); 945 } 946 947 char * 948 symget(const char *nam) 949 { 950 struct sym *sym; 951 952 TAILQ_FOREACH(sym, &symhead, entry) { 953 if (strcmp(nam, sym->nam) == 0) { 954 sym->used = 1; 955 return (sym->val); 956 } 957 } 958 return (NULL); 959 } 960