parse.y revision 1.40
1/* $OpenBSD: parse.y,v 1.40 2021/05/02 14:39:05 martijn Exp $ */ 2 3/* 4 * Copyright (c) 2009, 2010 Martin Hedenfalk <martinh@openbsd.org> 5 * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> 6 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> 7 * Copyright (c) 2001 Markus Friedl. All rights reserved. 8 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 9 * Copyright (c) 2001 Theo de Raadt. All rights reserved. 10 * 11 * Permission to use, copy, modify, and distribute this software for any 12 * purpose with or without fee is hereby granted, provided that the above 13 * copyright notice and this permission notice appear in all copies. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 */ 23 24%{ 25#include <sys/types.h> 26#include <sys/queue.h> 27#include <sys/tree.h> 28#include <sys/socket.h> 29#include <sys/stat.h> 30#include <sys/un.h> 31#include <netinet/in.h> 32#include <arpa/inet.h> 33 34#include <ctype.h> 35#include <err.h> 36#include <errno.h> 37#include <ifaddrs.h> 38#include <limits.h> 39#include <netdb.h> 40#include <stdarg.h> 41#include <stdio.h> 42#include <stdlib.h> 43#include <string.h> 44#include <syslog.h> 45#include <unistd.h> 46 47#include "ldapd.h" 48#include "log.h" 49 50TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 51static struct file { 52 TAILQ_ENTRY(file) entry; 53 FILE *stream; 54 char *name; 55 size_t ungetpos; 56 size_t ungetsize; 57 u_char *ungetbuf; 58 int eof_reached; 59 int lineno; 60 int errors; 61} *file, *topfile; 62struct file *pushfile(const char *, int); 63int popfile(void); 64int check_file_secrecy(int, const char *); 65int yyparse(void); 66int yylex(void); 67int yyerror(const char *, ...) 68 __attribute__((__format__ (printf, 1, 2))) 69 __attribute__((__nonnull__ (1))); 70int kw_cmp(const void *, const void *); 71int lookup(char *); 72int igetc(void); 73int lgetc(int); 74void lungetc(int); 75int findeol(void); 76 77struct listener *host_unix(const char *path); 78struct listener *host_v4(const char *, in_port_t); 79struct listener *host_v6(const char *, in_port_t); 80int host_dns(const char *, const char *, 81 struct listenerlist *, in_port_t, u_int8_t); 82int host(const char *, const char *, 83 struct listenerlist *, in_port_t, u_int8_t); 84int interface(const char *, const char *, 85 struct listenerlist *, in_port_t, u_int8_t); 86int load_certfile(struct ldapd_config *, const char *, u_int8_t, u_int8_t); 87 88TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 89struct sym { 90 TAILQ_ENTRY(sym) entry; 91 int used; 92 int persist; 93 char *nam; 94 char *val; 95}; 96int symset(const char *, const char *, int); 97char *symget(const char *); 98 99struct ldapd_config *conf; 100 101SPLAY_GENERATE(ssltree, ssl, ssl_nodes, ssl_cmp); 102 103static struct aci *mk_aci(int type, int rights, enum scope scope, 104 char *target, char *subject, char *attr); 105 106typedef struct { 107 union { 108 int64_t number; 109 char *string; 110 struct aci *aci; 111 } v; 112 int lineno; 113} YYSTYPE; 114 115static struct namespace *current_ns = NULL; 116 117%} 118 119%token ERROR LISTEN ON LEGACY TLS LDAPS PORT NAMESPACE ROOTDN ROOTPW INDEX 120%token SECURE RELAX STRICT SCHEMA USE COMPRESSION LEVEL 121%token INCLUDE CERTIFICATE FSYNC CACHE_SIZE INDEX_CACHE_SIZE 122%token DENY ALLOW READ WRITE BIND ACCESS TO ROOT REFERRAL 123%token ANY CHILDREN OF ATTRIBUTE IN SUBTREE BY SELF 124%token <v.string> STRING 125%token <v.number> NUMBER 126%type <v.number> port ssl boolean comp_level legacy protocol 127%type <v.number> aci_type aci_access aci_rights aci_right aci_scope 128%type <v.string> aci_target aci_attr aci_subject certname 129%type <v.aci> aci 130 131%% 132 133grammar : /* empty */ 134 | grammar '\n' 135 | grammar include '\n' 136 | grammar varset '\n' 137 | grammar conf_main '\n' 138 | grammar error '\n' { file->errors++; } 139 | grammar namespace '\n' 140 | grammar aci '\n' { 141 SIMPLEQ_INSERT_TAIL(&conf->acl, $2, entry); 142 } 143 | grammar schema '\n' 144 ; 145 146legacy : /* empty */ { $$ = 0; } 147 | LEGACY { $$ = F_LEGACY; } 148 ; 149 150protocol : /* empty */ { $$ = 0; } 151 | TLS { $$ = F_STARTTLS; } 152 | LDAPS { $$ = F_LDAPS; } 153 | SECURE { $$ = F_SECURE; } 154 ; 155 156ssl : legacy protocol { $$ = $1 | $2; } 157 ; 158 159certname : /* empty */ { $$ = NULL; } 160 | CERTIFICATE STRING { $$ = $2; } 161 ; 162 163port : PORT STRING { 164 struct servent *servent; 165 166 servent = getservbyname($2, "tcp"); 167 if (servent == NULL) { 168 yyerror("port %s is invalid", $2); 169 free($2); 170 YYERROR; 171 } 172 $$ = servent->s_port; 173 free($2); 174 } 175 | PORT NUMBER { 176 if ($2 <= 0 || $2 > (int)USHRT_MAX) { 177 yyerror("invalid port: %lld", $2); 178 YYERROR; 179 } 180 $$ = htons($2); 181 } 182 | /* empty */ { 183 $$ = 0; 184 } 185 ; 186 187conf_main : LISTEN ON STRING port ssl certname { 188 char *cert; 189 190 if ($4 == 0) { 191 if ($5 & F_LDAPS) 192 $4 = htons(LDAPS_PORT); 193 else 194 $4 = htons(LDAP_PORT); 195 } 196 197 cert = ($6 != NULL) ? $6 : $3; 198 199 if (($5 & F_SSL) && 200 load_certfile(conf, cert, F_SCERT, $5) < 0) { 201 yyerror("cannot load certificate: %s", cert); 202 free($6); 203 free($3); 204 YYERROR; 205 } 206 207 if (! interface($3, cert, &conf->listeners, 208 $4, $5)) { 209 if (host($3, cert, &conf->listeners, 210 $4, $5) <= 0) { 211 yyerror("invalid virtual ip or interface: %s", $3); 212 free($6); 213 free($3); 214 YYERROR; 215 } 216 } 217 free($6); 218 free($3); 219 } 220 | REFERRAL STRING { 221 struct referral *ref; 222 if ((ref = calloc(1, sizeof(*ref))) == NULL) { 223 yyerror("calloc"); 224 free($2); 225 YYERROR; 226 } 227 ref->url = $2; 228 SLIST_INSERT_HEAD(&conf->referrals, ref, next); 229 } 230 | ROOTDN STRING { 231 conf->rootdn = $2; 232 normalize_dn(conf->rootdn); 233 } 234 | ROOTPW STRING { conf->rootpw = $2; } 235 ; 236 237namespace : NAMESPACE STRING '{' '\n' { 238 log_debug("parsing namespace %s", $2); 239 current_ns = namespace_new($2); 240 free($2); 241 TAILQ_INSERT_TAIL(&conf->namespaces, current_ns, next); 242 } ns_opts '}' { current_ns = NULL; } 243 ; 244 245boolean : STRING { 246 if (strcasecmp($1, "true") == 0 || 247 strcasecmp($1, "yes") == 0) 248 $$ = 1; 249 else if (strcasecmp($1, "false") == 0 || 250 strcasecmp($1, "off") == 0 || 251 strcasecmp($1, "no") == 0) 252 $$ = 0; 253 else { 254 yyerror("invalid boolean value '%s'", $1); 255 free($1); 256 YYERROR; 257 } 258 free($1); 259 } 260 | ON { $$ = 1; } 261 ; 262 263ns_opts : /* empty */ 264 | ns_opts '\n' 265 | ns_opts ns_opt '\n' 266 ; 267 268ns_opt : ROOTDN STRING { 269 current_ns->rootdn = $2; 270 normalize_dn(current_ns->rootdn); 271 } 272 | ROOTPW STRING { current_ns->rootpw = $2; } 273 | INDEX STRING { 274 struct attr_index *ai; 275 if ((ai = calloc(1, sizeof(*ai))) == NULL) { 276 yyerror("calloc"); 277 free($2); 278 YYERROR; 279 } 280 ai->attr = $2; 281 ai->type = INDEX_EQUAL; 282 TAILQ_INSERT_TAIL(¤t_ns->indices, ai, next); 283 } 284 | CACHE_SIZE NUMBER { current_ns->cache_size = $2; } 285 | INDEX_CACHE_SIZE NUMBER { current_ns->index_cache_size = $2; } 286 | FSYNC boolean { current_ns->sync = $2; } 287 | aci { 288 SIMPLEQ_INSERT_TAIL(¤t_ns->acl, $1, entry); 289 } 290 | RELAX SCHEMA { current_ns->relax = 1; } 291 | STRICT SCHEMA { current_ns->relax = 0; } 292 | USE COMPRESSION comp_level { current_ns->compression_level = $3; } 293 | REFERRAL STRING { 294 struct referral *ref; 295 if ((ref = calloc(1, sizeof(*ref))) == NULL) { 296 yyerror("calloc"); 297 free($2); 298 YYERROR; 299 } 300 ref->url = $2; 301 SLIST_INSERT_HEAD(¤t_ns->referrals, ref, next); 302 } 303 ; 304 305comp_level : /* empty */ { $$ = 6; } 306 | LEVEL NUMBER { $$ = $2; } 307 ; 308 309aci : aci_type aci_access TO aci_scope aci_target aci_attr aci_subject { 310 if (($$ = mk_aci($1, $2, $4, $5, $6, $7)) == NULL) { 311 free($5); 312 free($6); 313 YYERROR; 314 } 315 } 316 | aci_type aci_access { 317 if (($$ = mk_aci($1, $2, LDAP_SCOPE_SUBTREE, NULL, 318 NULL, NULL)) == NULL) { 319 YYERROR; 320 } 321 } 322 ; 323 324aci_type : DENY { $$ = ACI_DENY; } 325 | ALLOW { $$ = ACI_ALLOW; } 326 ; 327 328aci_access : /* empty */ { $$ = ACI_ALL; } 329 | ACCESS { $$ = ACI_ALL; } 330 | aci_rights ACCESS { $$ = $1; } 331 ; 332 333aci_rights : aci_right { $$ = $1; } 334 | aci_rights ',' aci_right { $$ = $1 | $3; } 335 ; 336 337aci_right : READ { $$ = ACI_READ; } 338 | WRITE { $$ = ACI_WRITE; } 339 | BIND { $$ = ACI_BIND; } 340 ; 341 342 343aci_scope : /* empty */ { $$ = LDAP_SCOPE_BASE; } 344 | SUBTREE { $$ = LDAP_SCOPE_SUBTREE; } 345 | CHILDREN OF { $$ = LDAP_SCOPE_ONELEVEL; } 346 ; 347 348aci_target : ANY { $$ = NULL; } 349 | ROOT { $$ = strdup(""); } 350 | STRING { $$ = $1; normalize_dn($$); } 351 ; 352 353aci_attr : /* empty */ { $$ = NULL; } 354 | ATTRIBUTE STRING { $$ = $2; } 355 ; 356 357aci_subject : /* empty */ { $$ = NULL; } 358 | BY ANY { $$ = NULL; } 359 | BY STRING { $$ = $2; normalize_dn($$); } 360 | BY SELF { $$ = strdup("@"); } 361 ; 362 363include : INCLUDE STRING { 364 struct file *nfile; 365 366 if ((nfile = pushfile($2, 1)) == NULL) { 367 yyerror("failed to include file %s", $2); 368 free($2); 369 YYERROR; 370 } 371 free($2); 372 373 file = nfile; 374 lungetc('\n'); 375 } 376 ; 377 378varset : STRING '=' STRING { 379 char *s = $1; 380 while (*s++) { 381 if (isspace((unsigned char)*s)) { 382 yyerror("macro name cannot contain " 383 "whitespace"); 384 free($1); 385 free($3); 386 YYERROR; 387 } 388 } 389 if (symset($1, $3, 0) == -1) 390 fatal("cannot store variable"); 391 free($1); 392 free($3); 393 } 394 ; 395 396schema : SCHEMA STRING { 397 int ret; 398 399 ret = schema_parse(conf->schema, $2); 400 free($2); 401 if (ret != 0) { 402 YYERROR; 403 } 404 } 405 ; 406 407%% 408 409struct keywords { 410 const char *k_name; 411 int k_val; 412}; 413 414int 415yyerror(const char *fmt, ...) 416{ 417 va_list ap; 418 char *msg; 419 420 file->errors++; 421 va_start(ap, fmt); 422 if (vasprintf(&msg, fmt, ap) == -1) 423 fatalx("yyerror vasprintf"); 424 va_end(ap); 425 logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg); 426 free(msg); 427 return (0); 428} 429 430int 431kw_cmp(const void *k, const void *e) 432{ 433 return (strcmp(k, ((const struct keywords *)e)->k_name)); 434} 435 436int 437lookup(char *s) 438{ 439 /* this has to be sorted always */ 440 static const struct keywords keywords[] = { 441 { "access", ACCESS }, 442 { "allow", ALLOW }, 443 { "any", ANY }, 444 { "attribute", ATTRIBUTE }, 445 { "bind", BIND }, 446 { "by", BY }, 447 { "cache-size", CACHE_SIZE }, 448 { "certificate", CERTIFICATE }, 449 { "children", CHILDREN }, 450 { "compression", COMPRESSION }, 451 { "deny", DENY }, 452 { "fsync", FSYNC }, 453 { "in", IN }, 454 { "include", INCLUDE }, 455 { "index", INDEX }, 456 { "index-cache-size", INDEX_CACHE_SIZE }, 457 { "ldaps", LDAPS }, 458 { "legacy", LEGACY }, 459 { "level", LEVEL }, 460 { "listen", LISTEN }, 461 { "namespace", NAMESPACE }, 462 { "of", OF }, 463 { "on", ON }, 464 { "port", PORT }, 465 { "read", READ }, 466 { "referral", REFERRAL }, 467 { "relax", RELAX }, 468 { "root", ROOT }, 469 { "rootdn", ROOTDN }, 470 { "rootpw", ROOTPW }, 471 { "schema", SCHEMA }, 472 { "secure", SECURE }, 473 { "self", SELF }, 474 { "strict", STRICT }, 475 { "subtree", SUBTREE }, 476 { "tls", TLS }, 477 { "to", TO }, 478 { "use", USE }, 479 { "write", WRITE }, 480 481 }; 482 const struct keywords *p; 483 484 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 485 sizeof(keywords[0]), kw_cmp); 486 487 if (p) 488 return (p->k_val); 489 else 490 return (STRING); 491} 492 493#define START_EXPAND 1 494#define DONE_EXPAND 2 495 496static int expanding; 497 498int 499igetc(void) 500{ 501 int c; 502 503 while (1) { 504 if (file->ungetpos > 0) 505 c = file->ungetbuf[--file->ungetpos]; 506 else 507 c = getc(file->stream); 508 509 if (c == START_EXPAND) 510 expanding = 1; 511 else if (c == DONE_EXPAND) 512 expanding = 0; 513 else 514 break; 515 } 516 return (c); 517} 518 519int 520lgetc(int quotec) 521{ 522 int c, next; 523 524 if (quotec) { 525 if ((c = igetc()) == EOF) { 526 yyerror("reached end of file while parsing " 527 "quoted string"); 528 if (file == topfile || popfile() == EOF) 529 return (EOF); 530 return (quotec); 531 } 532 return (c); 533 } 534 535 while ((c = igetc()) == '\\') { 536 next = igetc(); 537 if (next != '\n') { 538 c = next; 539 break; 540 } 541 yylval.lineno = file->lineno; 542 file->lineno++; 543 } 544 545 if (c == EOF) { 546 /* 547 * Fake EOL when hit EOF for the first time. This gets line 548 * count right if last line in included file is syntactically 549 * invalid and has no newline. 550 */ 551 if (file->eof_reached == 0) { 552 file->eof_reached = 1; 553 return ('\n'); 554 } 555 while (c == EOF) { 556 if (file == topfile || popfile() == EOF) 557 return (EOF); 558 c = igetc(); 559 } 560 } 561 return (c); 562} 563 564void 565lungetc(int c) 566{ 567 if (c == EOF) 568 return; 569 570 if (file->ungetpos >= file->ungetsize) { 571 void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); 572 if (p == NULL) 573 err(1, "%s", __func__); 574 file->ungetbuf = p; 575 file->ungetsize *= 2; 576 } 577 file->ungetbuf[file->ungetpos++] = c; 578} 579 580int 581findeol(void) 582{ 583 int c; 584 585 /* skip to either EOF or the first real EOL */ 586 while (1) { 587 c = lgetc(0); 588 if (c == '\n') { 589 file->lineno++; 590 break; 591 } 592 if (c == EOF) 593 break; 594 } 595 return (ERROR); 596} 597 598int 599yylex(void) 600{ 601 u_char buf[4096]; 602 u_char *p, *val; 603 int quotec, next, c; 604 int token; 605 606top: 607 p = buf; 608 while ((c = lgetc(0)) == ' ' || c == '\t') 609 ; /* nothing */ 610 611 yylval.lineno = file->lineno; 612 if (c == '#') 613 while ((c = lgetc(0)) != '\n' && c != EOF) 614 ; /* nothing */ 615 if (c == '$' && !expanding) { 616 while (1) { 617 if ((c = lgetc(0)) == EOF) 618 return (0); 619 620 if (p + 1 >= buf + sizeof(buf) - 1) { 621 yyerror("string too long"); 622 return (findeol()); 623 } 624 if (isalnum(c) || c == '_') { 625 *p++ = c; 626 continue; 627 } 628 *p = '\0'; 629 lungetc(c); 630 break; 631 } 632 val = symget(buf); 633 if (val == NULL) { 634 yyerror("macro '%s' not defined", buf); 635 return (findeol()); 636 } 637 p = val + strlen(val) - 1; 638 lungetc(DONE_EXPAND); 639 while (p >= val) { 640 lungetc(*p); 641 p--; 642 } 643 lungetc(START_EXPAND); 644 goto top; 645 } 646 647 switch (c) { 648 case '\'': 649 case '"': 650 quotec = c; 651 while (1) { 652 if ((c = lgetc(quotec)) == EOF) 653 return (0); 654 if (c == '\n') { 655 file->lineno++; 656 continue; 657 } else if (c == '\\') { 658 if ((next = lgetc(quotec)) == EOF) 659 return (0); 660 if (next == quotec || next == ' ' || 661 next == '\t') 662 c = next; 663 else if (next == '\n') { 664 file->lineno++; 665 continue; 666 } else 667 lungetc(next); 668 } else if (c == quotec) { 669 *p = '\0'; 670 break; 671 } else if (c == '\0') { 672 yyerror("syntax error"); 673 return (findeol()); 674 } 675 if (p + 1 >= buf + sizeof(buf) - 1) { 676 log_warnx("string too long"); 677 return (findeol()); 678 } 679 *p++ = c; 680 } 681 yylval.v.string = strdup(buf); 682 if (yylval.v.string == NULL) 683 fatal("yylex: strdup"); 684 return (STRING); 685 } 686 687#define allowed_to_end_number(x) \ 688 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 689 690 if (c == '-' || isdigit(c)) { 691 do { 692 *p++ = c; 693 if ((size_t)(p-buf) >= sizeof(buf)) { 694 yyerror("string too long"); 695 return (findeol()); 696 } 697 } while ((c = lgetc(0)) != EOF && isdigit(c)); 698 lungetc(c); 699 if (p == buf + 1 && buf[0] == '-') 700 goto nodigits; 701 if (c == EOF || allowed_to_end_number(c)) { 702 const char *errstr = NULL; 703 704 *p = '\0'; 705 yylval.v.number = strtonum(buf, LLONG_MIN, 706 LLONG_MAX, &errstr); 707 if (errstr) { 708 yyerror("\"%s\" invalid number: %s", 709 buf, errstr); 710 return (findeol()); 711 } 712 return (NUMBER); 713 } else { 714nodigits: 715 while (p > buf + 1) 716 lungetc(*--p); 717 c = *--p; 718 if (c == '-') 719 return (c); 720 } 721 } 722 723#define allowed_in_string(x) \ 724 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 725 x != '{' && x != '}' && x != '<' && x != '>' && \ 726 x != '!' && x != '=' && x != '/' && x != '#' && \ 727 x != ',')) 728 729 if (isalnum(c) || c == ':' || c == '_' || c == '*') { 730 do { 731 *p++ = c; 732 if ((size_t)(p-buf) >= sizeof(buf)) { 733 yyerror("string too long"); 734 return (findeol()); 735 } 736 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 737 lungetc(c); 738 *p = '\0'; 739 if ((token = lookup(buf)) == STRING) 740 if ((yylval.v.string = strdup(buf)) == NULL) 741 fatal("yylex: strdup"); 742 return (token); 743 } 744 if (c == '\n') { 745 yylval.lineno = file->lineno; 746 file->lineno++; 747 } 748 if (c == EOF) 749 return (0); 750 return (c); 751} 752 753int 754check_file_secrecy(int fd, const char *fname) 755{ 756 struct stat st; 757 758 if (fstat(fd, &st)) { 759 log_warn("cannot stat %s", fname); 760 return (-1); 761 } 762 if (st.st_uid != 0 && st.st_uid != getuid()) { 763 log_warnx("%s: owner not root or current user", fname); 764 return (-1); 765 } 766 if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) { 767 log_warnx("%s: group writable or world read/writable", fname); 768 return (-1); 769 } 770 return (0); 771} 772 773struct file * 774pushfile(const char *name, int secret) 775{ 776 struct file *nfile; 777 778 log_debug("parsing config %s", name); 779 780 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 781 log_warn("%s", __func__); 782 return (NULL); 783 } 784 if ((nfile->name = strdup(name)) == NULL) { 785 log_warn("%s", __func__); 786 free(nfile); 787 return (NULL); 788 } 789 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 790 log_warn("%s: %s", __func__, nfile->name); 791 free(nfile->name); 792 free(nfile); 793 return (NULL); 794 } 795 if (secret && 796 check_file_secrecy(fileno(nfile->stream), nfile->name)) { 797 fclose(nfile->stream); 798 free(nfile->name); 799 free(nfile); 800 return (NULL); 801 } 802 nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0; 803 nfile->ungetsize = 16; 804 nfile->ungetbuf = malloc(nfile->ungetsize); 805 if (nfile->ungetbuf == NULL) { 806 log_warn("%s", __func__); 807 fclose(nfile->stream); 808 free(nfile->name); 809 free(nfile); 810 return (NULL); 811 } 812 TAILQ_INSERT_TAIL(&files, nfile, entry); 813 return (nfile); 814} 815 816int 817popfile(void) 818{ 819 struct file *prev; 820 821 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 822 prev->errors += file->errors; 823 824 TAILQ_REMOVE(&files, file, entry); 825 fclose(file->stream); 826 free(file->name); 827 free(file->ungetbuf); 828 free(file); 829 file = prev; 830 return (file ? 0 : EOF); 831} 832 833int 834parse_config(char *filename) 835{ 836 struct sym *sym, *next; 837 int errors = 0; 838 839 if ((conf = calloc(1, sizeof(struct ldapd_config))) == NULL) 840 fatal(NULL); 841 842 conf->schema = schema_new(); 843 if (conf->schema == NULL) 844 fatal("schema_new"); 845 846 TAILQ_INIT(&conf->namespaces); 847 TAILQ_INIT(&conf->listeners); 848 if ((conf->sc_ssl = calloc(1, sizeof(*conf->sc_ssl))) == NULL) 849 fatal(NULL); 850 SPLAY_INIT(conf->sc_ssl); 851 SIMPLEQ_INIT(&conf->acl); 852 SLIST_INIT(&conf->referrals); 853 854 if ((file = pushfile(filename, 1)) == NULL) { 855 free(conf); 856 return (-1); 857 } 858 topfile = file; 859 860 yyparse(); 861 errors = file->errors; 862 popfile(); 863 864 /* Free macros and check which have not been used. */ 865 TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) { 866 log_debug("warning: macro \"%s\" not used", sym->nam); 867 if (!sym->persist) { 868 free(sym->nam); 869 free(sym->val); 870 TAILQ_REMOVE(&symhead, sym, entry); 871 free(sym); 872 } 873 } 874 875 return (errors ? -1 : 0); 876} 877 878int 879symset(const char *nam, const char *val, int persist) 880{ 881 struct sym *sym; 882 883 TAILQ_FOREACH(sym, &symhead, entry) { 884 if (strcmp(nam, sym->nam) == 0) 885 break; 886 } 887 888 if (sym != NULL) { 889 if (sym->persist == 1) 890 return (0); 891 else { 892 free(sym->nam); 893 free(sym->val); 894 TAILQ_REMOVE(&symhead, sym, entry); 895 free(sym); 896 } 897 } 898 if ((sym = calloc(1, sizeof(*sym))) == NULL) 899 return (-1); 900 901 sym->nam = strdup(nam); 902 if (sym->nam == NULL) { 903 free(sym); 904 return (-1); 905 } 906 sym->val = strdup(val); 907 if (sym->val == NULL) { 908 free(sym->nam); 909 free(sym); 910 return (-1); 911 } 912 sym->used = 0; 913 sym->persist = persist; 914 TAILQ_INSERT_TAIL(&symhead, sym, entry); 915 return (0); 916} 917 918int 919cmdline_symset(char *s) 920{ 921 char *sym, *val; 922 int ret; 923 924 if ((val = strrchr(s, '=')) == NULL) 925 return (-1); 926 sym = strndup(s, val - s); 927 if (sym == NULL) 928 fatal("%s: strndup", __func__); 929 ret = symset(sym, val + 1, 1); 930 free(sym); 931 932 return (ret); 933} 934 935char * 936symget(const char *nam) 937{ 938 struct sym *sym; 939 940 TAILQ_FOREACH(sym, &symhead, entry) { 941 if (strcmp(nam, sym->nam) == 0) { 942 sym->used = 1; 943 return (sym->val); 944 } 945 } 946 return (NULL); 947} 948 949struct listener * 950host_unix(const char *path) 951{ 952 struct sockaddr_un *saun; 953 struct listener *h; 954 955 if (*path != '/') 956 return (NULL); 957 958 if ((h = calloc(1, sizeof(*h))) == NULL) 959 fatal(NULL); 960 saun = (struct sockaddr_un *)&h->ss; 961 saun->sun_len = sizeof(struct sockaddr_un); 962 saun->sun_family = AF_UNIX; 963 if (strlcpy(saun->sun_path, path, sizeof(saun->sun_path)) >= 964 sizeof(saun->sun_path)) 965 fatal("socket path too long"); 966 h->flags = F_SECURE; 967 968 return (h); 969} 970 971struct listener * 972host_v4(const char *s, in_port_t port) 973{ 974 struct in_addr ina; 975 struct sockaddr_in *sain; 976 struct listener *h; 977 978 memset(&ina, 0, sizeof(ina)); 979 if (inet_pton(AF_INET, s, &ina) != 1) 980 return (NULL); 981 982 if ((h = calloc(1, sizeof(*h))) == NULL) 983 fatal(NULL); 984 sain = (struct sockaddr_in *)&h->ss; 985 sain->sin_len = sizeof(struct sockaddr_in); 986 sain->sin_family = AF_INET; 987 sain->sin_addr.s_addr = ina.s_addr; 988 sain->sin_port = port; 989 990 return (h); 991} 992 993struct listener * 994host_v6(const char *s, in_port_t port) 995{ 996 struct in6_addr ina6; 997 struct sockaddr_in6 *sin6; 998 struct listener *h; 999 1000 memset(&ina6, 0, sizeof(ina6)); 1001 if (inet_pton(AF_INET6, s, &ina6) != 1) 1002 return (NULL); 1003 1004 if ((h = calloc(1, sizeof(*h))) == NULL) 1005 fatal(NULL); 1006 sin6 = (struct sockaddr_in6 *)&h->ss; 1007 sin6->sin6_len = sizeof(struct sockaddr_in6); 1008 sin6->sin6_family = AF_INET6; 1009 sin6->sin6_port = port; 1010 memcpy(&sin6->sin6_addr, &ina6, sizeof(ina6)); 1011 1012 return (h); 1013} 1014 1015int 1016host_dns(const char *s, const char *cert, 1017 struct listenerlist *al, in_port_t port, u_int8_t flags) 1018{ 1019 struct addrinfo hints, *res0, *res; 1020 int error; 1021 struct sockaddr_in *sain; 1022 struct sockaddr_in6 *sin6; 1023 struct listener *h; 1024 1025 memset(&hints, 0, sizeof(hints)); 1026 hints.ai_family = PF_UNSPEC; 1027 hints.ai_socktype = SOCK_DGRAM; /* DUMMY */ 1028 error = getaddrinfo(s, NULL, &hints, &res0); 1029 if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME) 1030 return (0); 1031 if (error) { 1032 log_warnx("host_dns: could not parse \"%s\": %s", s, 1033 gai_strerror(error)); 1034 return (-1); 1035 } 1036 1037 for (res = res0; res; res = res->ai_next) { 1038 if (res->ai_family != AF_INET && 1039 res->ai_family != AF_INET6) 1040 continue; 1041 if ((h = calloc(1, sizeof(*h))) == NULL) 1042 fatal(NULL); 1043 1044 h->port = port; 1045 h->flags = flags; 1046 h->ss.ss_family = res->ai_family; 1047 h->ssl = NULL; 1048 h->ssl_cert_name[0] = '\0'; 1049 if (cert != NULL) 1050 (void)strlcpy(h->ssl_cert_name, cert, sizeof(h->ssl_cert_name)); 1051 1052 if (res->ai_family == AF_INET) { 1053 sain = (struct sockaddr_in *)&h->ss; 1054 sain->sin_len = sizeof(struct sockaddr_in); 1055 sain->sin_addr.s_addr = ((struct sockaddr_in *) 1056 res->ai_addr)->sin_addr.s_addr; 1057 sain->sin_port = port; 1058 } else { 1059 sin6 = (struct sockaddr_in6 *)&h->ss; 1060 sin6->sin6_len = sizeof(struct sockaddr_in6); 1061 memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *) 1062 res->ai_addr)->sin6_addr, sizeof(struct in6_addr)); 1063 sin6->sin6_port = port; 1064 } 1065 1066 TAILQ_INSERT_HEAD(al, h, entry); 1067 } 1068 freeaddrinfo(res0); 1069 return 1; 1070} 1071 1072int 1073host(const char *s, const char *cert, struct listenerlist *al, 1074 in_port_t port, u_int8_t flags) 1075{ 1076 struct listener *h; 1077 1078 /* Unix socket path? */ 1079 h = host_unix(s); 1080 1081 /* IPv4 address? */ 1082 if (h == NULL) 1083 h = host_v4(s, port); 1084 1085 /* IPv6 address? */ 1086 if (h == NULL) 1087 h = host_v6(s, port); 1088 1089 if (h != NULL) { 1090 h->port = port; 1091 h->flags |= flags; 1092 h->ssl = NULL; 1093 h->ssl_cert_name[0] = '\0'; 1094 if (cert != NULL) 1095 strlcpy(h->ssl_cert_name, cert, sizeof(h->ssl_cert_name)); 1096 1097 TAILQ_INSERT_HEAD(al, h, entry); 1098 return (1); 1099 } 1100 1101 return (host_dns(s, cert, al, port, flags)); 1102} 1103 1104int 1105interface(const char *s, const char *cert, 1106 struct listenerlist *al, in_port_t port, u_int8_t flags) 1107{ 1108 int ret = 0; 1109 struct ifaddrs *ifap, *p; 1110 struct sockaddr_in *sain; 1111 struct sockaddr_in6 *sin6; 1112 struct listener *h; 1113 1114 if (getifaddrs(&ifap) == -1) 1115 fatal("getifaddrs"); 1116 1117 for (p = ifap; p != NULL; p = p->ifa_next) { 1118 if (strcmp(s, p->ifa_name) != 0) 1119 continue; 1120 if (p->ifa_addr == NULL) 1121 continue; 1122 1123 switch (p->ifa_addr->sa_family) { 1124 case AF_INET: 1125 if ((h = calloc(1, sizeof(*h))) == NULL) 1126 fatal(NULL); 1127 sain = (struct sockaddr_in *)&h->ss; 1128 *sain = *(struct sockaddr_in *)p->ifa_addr; 1129 sain->sin_len = sizeof(struct sockaddr_in); 1130 sain->sin_port = port; 1131 1132 h->fd = -1; 1133 h->port = port; 1134 h->flags = flags; 1135 h->ssl = NULL; 1136 h->ssl_cert_name[0] = '\0'; 1137 if (cert != NULL) 1138 (void)strlcpy(h->ssl_cert_name, cert, sizeof(h->ssl_cert_name)); 1139 1140 ret = 1; 1141 TAILQ_INSERT_HEAD(al, h, entry); 1142 1143 break; 1144 1145 case AF_INET6: 1146 if ((h = calloc(1, sizeof(*h))) == NULL) 1147 fatal(NULL); 1148 sin6 = (struct sockaddr_in6 *)&h->ss; 1149 *sin6 = *(struct sockaddr_in6 *)p->ifa_addr; 1150 sin6->sin6_len = sizeof(struct sockaddr_in6); 1151 sin6->sin6_port = port; 1152 1153 h->fd = -1; 1154 h->port = port; 1155 h->flags = flags; 1156 h->ssl = NULL; 1157 h->ssl_cert_name[0] = '\0'; 1158 if (cert != NULL) 1159 (void)strlcpy(h->ssl_cert_name, cert, sizeof(h->ssl_cert_name)); 1160 1161 ret = 1; 1162 TAILQ_INSERT_HEAD(al, h, entry); 1163 1164 break; 1165 } 1166 } 1167 1168 freeifaddrs(ifap); 1169 1170 return ret; 1171} 1172 1173static struct aci * 1174mk_aci(int type, int rights, enum scope scope, char *target, char *attr, 1175 char *subject) 1176{ 1177 struct aci *aci; 1178 1179 if ((aci = calloc(1, sizeof(*aci))) == NULL) { 1180 yyerror("calloc"); 1181 return NULL; 1182 } 1183 aci->type = type; 1184 aci->rights = rights; 1185 aci->scope = scope; 1186 aci->target = target; 1187 aci->attribute = attr; 1188 aci->subject = subject; 1189 1190 log_debug("%s %02X access to %s%s%s scope %d by %s", 1191 aci->type == ACI_DENY ? "deny" : "allow", 1192 aci->rights, 1193 aci->target ? aci->target : "any", 1194 aci->attribute ? " attribute " : "", 1195 aci->attribute ? aci->attribute : "", 1196 aci->scope, 1197 aci->subject ? aci->subject : "any"); 1198 1199 return aci; 1200} 1201 1202struct namespace * 1203namespace_new(const char *suffix) 1204{ 1205 struct namespace *ns; 1206 1207 if ((ns = calloc(1, sizeof(*ns))) == NULL) 1208 return NULL; 1209 ns->suffix = strdup(suffix); 1210 ns->sync = 1; 1211 ns->cache_size = 1024; 1212 ns->index_cache_size = 512; 1213 if (ns->suffix == NULL) { 1214 free(ns->suffix); 1215 free(ns); 1216 return NULL; 1217 } 1218 TAILQ_INIT(&ns->indices); 1219 TAILQ_INIT(&ns->request_queue); 1220 SIMPLEQ_INIT(&ns->acl); 1221 SLIST_INIT(&ns->referrals); 1222 1223 return ns; 1224} 1225 1226int 1227ssl_cmp(struct ssl *s1, struct ssl *s2) 1228{ 1229 return (strcmp(s1->ssl_name, s2->ssl_name)); 1230} 1231 1232int 1233load_certfile(struct ldapd_config *env, const char *name, u_int8_t flags, 1234 u_int8_t protocol) 1235{ 1236 struct ssl *s; 1237 struct ssl key; 1238 char certfile[PATH_MAX]; 1239 uint32_t tls_protocols = TLS_PROTOCOLS_DEFAULT; 1240 const char *tls_ciphers = "default"; 1241 1242 if (strlcpy(key.ssl_name, name, sizeof(key.ssl_name)) 1243 >= sizeof(key.ssl_name)) { 1244 log_warn("load_certfile: certificate name truncated"); 1245 return -1; 1246 } 1247 1248 s = SPLAY_FIND(ssltree, env->sc_ssl, &key); 1249 if (s != NULL) { 1250 s->flags |= flags; 1251 return 0; 1252 } 1253 1254 if ((s = calloc(1, sizeof(*s))) == NULL) 1255 fatal(NULL); 1256 1257 s->flags = flags; 1258 (void)strlcpy(s->ssl_name, key.ssl_name, sizeof(s->ssl_name)); 1259 1260 s->config = tls_config_new(); 1261 if (s->config == NULL) 1262 goto err; 1263 1264 if (protocol & F_LEGACY) { 1265 tls_protocols = TLS_PROTOCOLS_ALL; 1266 tls_ciphers = "all"; 1267 } 1268 if (tls_config_set_protocols(s->config, tls_protocols) != 0) { 1269 log_warn("load_certfile: failed to set tls protocols: %s", 1270 tls_config_error(s->config)); 1271 goto err; 1272 } 1273 if (tls_config_set_ciphers(s->config, tls_ciphers)) { 1274 log_warn("load_certfile: failed to set tls ciphers: %s", 1275 tls_config_error(s->config)); 1276 goto err; 1277 } 1278 1279 if (name[0] == '/') { 1280 if (!bsnprintf(certfile, sizeof(certfile), "%s.crt", name)) { 1281 log_warn("load_certfile: path truncated"); 1282 goto err; 1283 } 1284 } else { 1285 if (!bsnprintf(certfile, sizeof(certfile), 1286 "/etc/ldap/certs/%s.crt", name)) { 1287 log_warn("load_certfile: path truncated"); 1288 goto err; 1289 } 1290 } 1291 1292 log_debug("loading certificate file %s", certfile); 1293 s->ssl_cert = tls_load_file(certfile, &s->ssl_cert_len, NULL); 1294 if (s->ssl_cert == NULL) 1295 goto err; 1296 1297 if (tls_config_set_cert_mem(s->config, s->ssl_cert, s->ssl_cert_len)) { 1298 log_warn("load_certfile: failed to set tls certificate: %s", 1299 tls_config_error(s->config)); 1300 goto err; 1301 } 1302 1303 if (name[0] == '/') { 1304 if (!bsnprintf(certfile, sizeof(certfile), "%s.key", name)) { 1305 log_warn("load_certfile: path truncated"); 1306 goto err; 1307 } 1308 } else { 1309 if (!bsnprintf(certfile, sizeof(certfile), 1310 "/etc/ldap/certs/%s.key", name)) { 1311 log_warn("load_certfile: path truncated"); 1312 goto err; 1313 } 1314 } 1315 1316 log_debug("loading key file %s", certfile); 1317 s->ssl_key = tls_load_file(certfile, &s->ssl_key_len, NULL); 1318 if (s->ssl_key == NULL) 1319 goto err; 1320 1321 if (tls_config_set_key_mem(s->config, s->ssl_key, s->ssl_key_len)) { 1322 log_warn("load_certfile: failed to set tls key: %s", 1323 tls_config_error(s->config)); 1324 goto err; 1325 } 1326 1327 SPLAY_INSERT(ssltree, env->sc_ssl, s); 1328 1329 return (0); 1330err: 1331 free(s->ssl_cert); 1332 free(s->ssl_key); 1333 tls_config_free(s->config); 1334 free(s); 1335 return (-1); 1336} 1337