1 /* $OpenBSD: parse.y,v 1.4 2016/08/27 09:04:20 yasuoka Exp $ */ 2 3 /* 4 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> 5 * Copyright (c) 2001 Markus Friedl. All rights reserved. 6 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 7 * Copyright (c) 2001 Theo de Raadt. All rights reserved. 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 %{ 23 #include <sys/types.h> 24 #include <sys/queue.h> 25 #include <sys/socket.h> 26 27 #include <ctype.h> 28 #include <errno.h> 29 #include <limits.h> 30 #include <netdb.h> 31 #include <stdarg.h> 32 #include <stdio.h> 33 #include <syslog.h> 34 35 #include "radiusd.h" 36 #include "radiusd_local.h" 37 #include "log.h" 38 39 static struct radiusd *conf; 40 static struct radiusd_authentication authen; 41 static struct radiusd_client client; 42 43 static struct radiusd_module *find_module (const char *); 44 static void free_str_l (void *); 45 static struct radiusd_module_ref *create_module_ref (const char *); 46 static void radiusd_authentication_init (struct radiusd_authentication *); 47 static void radiusd_client_init (struct radiusd_client *); 48 49 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 50 static struct file { 51 TAILQ_ENTRY(file) entry; 52 FILE *stream; 53 char *name; 54 int lineno; 55 int errors; 56 } *file, *topfile; 57 struct file *pushfile(const char *); 58 int popfile(void); 59 int yyparse(void); 60 int yylex(void); 61 int yyerror(const char *, ...); 62 int kw_cmp(const void *, const void *); 63 int lookup(char *); 64 int lgetc(int); 65 int lungetc(int); 66 int findeol(void); 67 68 typedef struct { 69 union { 70 int64_t number; 71 char *string; 72 struct radiusd_listen listen; 73 int yesno; 74 struct { 75 char **v; 76 int c; 77 } str_l; 78 struct { 79 int af; 80 struct radiusd_addr addr; 81 struct radiusd_addr mask; 82 } prefix; 83 } v; 84 int lineno; 85 } YYSTYPE; 86 87 %} 88 89 %token INCLUDE LISTEN ON PORT CLIENT SECRET LOAD MODULE MSGAUTH_REQUIRED 90 %token AUTHENTICATE AUTHENTICATE_BY DECORATE_BY SET 91 %token ERROR YES NO 92 %token <v.string> STRING 93 %token <v.number> NUMBER 94 %type <v.number> optport 95 %type <v.listen> listen_addr 96 %type <v.str_l> str_l 97 %type <v.prefix> prefix 98 %type <v.yesno> yesno 99 %type <v.string> strnum 100 %% 101 102 grammar : /* empty */ 103 | grammar '\n' 104 | grammar include '\n' 105 | grammar listen '\n' 106 | grammar client '\n' 107 | grammar module '\n' 108 | grammar authenticate '\n' 109 | grammar error '\n' 110 ; 111 112 include : INCLUDE STRING { 113 struct file *nfile; 114 115 if ((nfile = pushfile($2)) == NULL) { 116 yyerror("failed to include file %s", $2); 117 free($2); 118 YYERROR; 119 } 120 free($2); 121 122 file = nfile; 123 lungetc('\n'); 124 nfile->lineno--; 125 } 126 ; 127 listen : LISTEN ON listen_addr { 128 struct radiusd_listen *n; 129 130 if ((n = malloc(sizeof(struct radiusd_listen))) 131 == NULL) { 132 outofmemory: 133 yyerror("Out of memory: %s", strerror(errno)); 134 YYERROR; 135 } 136 *n = $3; 137 TAILQ_INSERT_TAIL(&conf->listen, n, next); 138 } 139 listen_addr : STRING optport { 140 int gai_errno; 141 struct addrinfo hints, *res; 142 143 memset(&hints, 0, sizeof(hints)); 144 hints.ai_family = PF_UNSPEC; 145 hints.ai_socktype = SOCK_DGRAM; 146 hints.ai_flags = AI_PASSIVE; 147 hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV; 148 149 if ((gai_errno = 150 getaddrinfo($1, NULL, &hints, &res)) != 0 || 151 res->ai_addrlen > sizeof($$.addr)) { 152 yyerror("Could not parse the address: %s: %s", 153 $1, gai_strerror(gai_errno)); 154 free($1); 155 YYERROR; 156 } 157 free($1); 158 $$.stype = res->ai_socktype; 159 $$.sproto = res->ai_protocol; 160 memcpy(&$$.addr, res->ai_addr, res->ai_addrlen); 161 $$.addr.ipv4.sin_port = ($2 == 0)? 162 htons(RADIUS_DEFAULT_PORT) : htons($2); 163 freeaddrinfo(res); 164 } 165 optport : { $$ = 0; } 166 | PORT NUMBER { $$ = $2; } 167 ; 168 client : CLIENT prefix optnl clientopts_b { 169 struct radiusd_client *client0; 170 171 client0 = calloc(1, sizeof(struct radiusd_client)); 172 if (client0 == NULL) 173 goto outofmemory; 174 strlcpy(client0->secret, client.secret, 175 sizeof(client0->secret)); 176 client0->msgauth_required = client.msgauth_required; 177 client0->af = $2.af; 178 client0->addr = $2.addr; 179 client0->mask = $2.mask; 180 TAILQ_INSERT_TAIL(&conf->client, client0, next); 181 radiusd_client_init(&client); 182 } 183 184 clientopts_b : '{' optnl_l clientopts_l optnl_l '}' 185 | '{' optnl_l '}' /* allow empty block */ 186 ; 187 188 clientopts_l : clientopts_l nl clientopts 189 | clientopts 190 ; 191 192 clientopts : SECRET STRING { 193 if (strlcpy(client.secret, $2, sizeof(client.secret)) 194 >= sizeof(client.secret)) { 195 yyerror("secret is too long"); 196 YYERROR; 197 } 198 } 199 | MSGAUTH_REQUIRED yesno { 200 client.msgauth_required = $2; 201 } 202 ; 203 204 prefix : STRING '/' NUMBER { 205 int gai_errno, q, r; 206 struct addrinfo hints, *res; 207 208 memset(&hints, 0, sizeof(hints)); 209 hints.ai_family = PF_UNSPEC; 210 hints.ai_socktype = SOCK_DGRAM; /* dummy */ 211 hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV; 212 213 if ((gai_errno = getaddrinfo($1, NULL, &hints, &res)) 214 != 0) { 215 yyerror("Could not parse the address: %s: %s", 216 $1, gai_strerror(gai_errno)); 217 free($1); 218 YYERROR; 219 } 220 free($1); 221 q = $3 >> 3; 222 r = $3 & 7; 223 switch (res->ai_family) { 224 case AF_INET: 225 if ($3 < 0 || 32 < $3) { 226 yyerror("mask len %d is out of range", 227 $3); 228 YYERROR; 229 } 230 $$.addr.addr.ipv4 = ((struct sockaddr_in *) 231 res->ai_addr)->sin_addr; 232 $$.mask.addr.ipv4.s_addr = htonl((uint32_t) 233 ((0xffffffffffULL) << (32 - $3))); 234 break; 235 case AF_INET6: 236 if ($3 < 0 || 128 < $3) { 237 yyerror("mask len %d is out of range", 238 $3); 239 YYERROR; 240 } 241 $$.addr.addr.ipv6 = ((struct sockaddr_in6 *) 242 res->ai_addr)->sin6_addr; 243 memset(&$$.mask.addr.ipv6, 0, 244 sizeof($$.mask.addr.ipv6)); 245 if (q > 0) 246 memset(&$$.mask.addr.ipv6, 0xff, q); 247 if (r > 0) 248 *((u_char *)&$$.mask.addr.ipv6 + q) = 249 (0xff00 >> r) & 0xff; 250 break; 251 } 252 $$.af = res->ai_family; 253 freeaddrinfo(res); 254 } 255 ; 256 module : MODULE LOAD STRING STRING { 257 struct radiusd_module *module; 258 if ((module = radiusd_module_load(conf, $4, $3)) 259 == NULL) { 260 free($3); 261 free($4); 262 YYERROR; 263 } 264 free($3); 265 free($4); 266 TAILQ_INSERT_TAIL(&conf->module, module, next); 267 } 268 | MODULE SET STRING STRING str_l { 269 struct radiusd_module *module; 270 271 module = find_module($3); 272 if (module == NULL) { 273 yyerror("module `%s' is not found", $3); 274 free($3); 275 free($4); 276 free_str_l(&$5); 277 YYERROR; 278 } 279 if (radiusd_module_set(module, $4, $5.c, $5.v)) { 280 yyerror("syntax error by module `%s'", $3); 281 free($3); 282 free($4); 283 free_str_l(&$5); 284 YYERROR; 285 } 286 free($3); 287 free($4); 288 free_str_l(&$5); 289 } 290 ; 291 authenticate : AUTHENTICATE str_l optnl authopts_b { 292 struct radiusd_authentication *a; 293 294 if ((a = calloc(1, 295 sizeof(struct radiusd_authentication))) == NULL) { 296 free_str_l(&$2); 297 goto outofmemory; 298 } 299 a->auth = authen.auth; 300 a->deco = authen.deco; 301 a->username = $2.v; 302 303 TAILQ_INSERT_TAIL(&conf->authen, a, next); 304 radiusd_authentication_init(&authen); 305 } 306 ; 307 308 authopts_b : '{' optnl_l authopts_l optnl_l '}' 309 | '{' optnl_l '}' /* empty options */ 310 ; 311 312 authopts_l : authopts_l nl authopts 313 | authopts 314 ; 315 316 authopts : AUTHENTICATE_BY STRING { 317 struct radiusd_module_ref *modref; 318 319 modref = create_module_ref($2); 320 free($2); 321 if (modref == NULL) 322 YYERROR; 323 authen.auth = modref; 324 } 325 /* XXX decoration doesn't work for this moment. */ 326 | DECORATE_BY str_l { 327 int i; 328 struct radiusd_module_ref *modref; 329 330 for (i = 0; i < $2.c; i++) { 331 if ((modref = create_module_ref($2.v[i])) 332 == NULL) { 333 free_str_l(&$2); 334 YYERROR; 335 } 336 TAILQ_INSERT_TAIL(&authen.deco, modref, next); 337 } 338 free_str_l(&$2); 339 } 340 ; 341 str_l : str_l strnum { 342 int i; 343 char **v; 344 if ((v = calloc(sizeof(char **), $$.c + 2)) == NULL) 345 goto outofmemory; 346 for (i = 0; i < $$.c; i++) 347 v[i] = $$.v[i]; 348 v[i++] = $2; 349 v[i] = NULL; 350 $$.c++; 351 free($$.v); 352 $$.v = v; 353 } 354 | strnum { 355 if (($$.v = calloc(sizeof(char **), 2)) == NULL) 356 goto outofmemory; 357 $$.v[0] = $1; 358 $$.v[1] = NULL; 359 $$.c = 1; 360 } 361 ; 362 strnum : STRING { $$ = $1; } 363 | NUMBER { 364 /* Treat number as a string */ 365 asprintf(&($$), "%jd", (intmax_t)$1); 366 if ($$ == NULL) 367 goto outofmemory; 368 } 369 ; 370 optnl : 371 | '\n' 372 ; 373 nl : '\n' optnl /* one new line or more */ 374 ; 375 optnl_l : 376 | '\n' optnl_l 377 ; 378 yesno : YES { $$ = true; } 379 | NO { $$ = false; } 380 ; 381 %% 382 383 struct keywords { 384 const char *k_name; 385 int k_val; 386 }; 387 388 int 389 yyerror(const char *fmt, ...) 390 { 391 va_list ap; 392 char *msg; 393 394 file->errors++; 395 va_start(ap, fmt); 396 if (vasprintf(&msg, fmt, ap) == -1) 397 fatalx("yyerror vasprintf"); 398 va_end(ap); 399 logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg); 400 free(msg); 401 return (0); 402 } 403 404 int 405 kw_cmp(const void *k, const void *e) 406 { 407 return (strcmp(k, ((const struct keywords *)e)->k_name)); 408 } 409 410 int 411 lookup(char *s) 412 { 413 /* this has to be sorted always */ 414 static const struct keywords keywords[] = { 415 { "authenticate", AUTHENTICATE}, 416 { "authenticate-by", AUTHENTICATE_BY}, 417 { "client", CLIENT}, 418 { "decorate-by", DECORATE_BY}, 419 { "include", INCLUDE}, 420 { "listen", LISTEN}, 421 { "load", LOAD}, 422 { "module", MODULE}, 423 { "msgauth-required", MSGAUTH_REQUIRED}, 424 { "no", NO}, 425 { "on", ON}, 426 { "port", PORT}, 427 { "secret", SECRET}, 428 { "set", SET}, 429 { "yes", YES}, 430 }; 431 const struct keywords *p; 432 433 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 434 sizeof(keywords[0]), kw_cmp); 435 436 if (p) 437 return (p->k_val); 438 else 439 return (STRING); 440 } 441 442 #define MAXPUSHBACK 128 443 444 char *parsebuf; 445 int parseindex; 446 char pushback_buffer[MAXPUSHBACK]; 447 int pushback_index = 0; 448 449 int 450 lgetc(int quotec) 451 { 452 int c, next; 453 454 if (parsebuf) { 455 /* Read character from the parsebuffer instead of input. */ 456 if (parseindex >= 0) { 457 c = parsebuf[parseindex++]; 458 if (c != '\0') 459 return (c); 460 parsebuf = NULL; 461 } else 462 parseindex++; 463 } 464 465 if (pushback_index) 466 return (pushback_buffer[--pushback_index]); 467 468 if (quotec) { 469 if ((c = getc(file->stream)) == EOF) { 470 yyerror("reached end of file while parsing " 471 "quoted string"); 472 if (file == topfile || popfile() == EOF) 473 return (EOF); 474 return (quotec); 475 } 476 return (c); 477 } 478 479 while ((c = getc(file->stream)) == '\\') { 480 next = getc(file->stream); 481 if (next != '\n') { 482 c = next; 483 break; 484 } 485 yylval.lineno = file->lineno; 486 file->lineno++; 487 } 488 489 while (c == EOF) { 490 if (file == topfile || popfile() == EOF) 491 return (EOF); 492 c = getc(file->stream); 493 } 494 return (c); 495 } 496 497 int 498 lungetc(int c) 499 { 500 if (c == EOF) 501 return (EOF); 502 if (parsebuf) { 503 parseindex--; 504 if (parseindex >= 0) 505 return (c); 506 } 507 if (pushback_index < MAXPUSHBACK-1) 508 return (pushback_buffer[pushback_index++] = c); 509 else 510 return (EOF); 511 } 512 513 int 514 findeol(void) 515 { 516 int c; 517 518 parsebuf = NULL; 519 520 /* skip to either EOF or the first real EOL */ 521 while (1) { 522 if (pushback_index) 523 c = pushback_buffer[--pushback_index]; 524 else 525 c = lgetc(0); 526 if (c == '\n') { 527 file->lineno++; 528 break; 529 } 530 if (c == EOF) 531 break; 532 } 533 return (ERROR); 534 } 535 536 int 537 yylex(void) 538 { 539 char buf[8096]; 540 char *p; 541 int quotec, next, c; 542 int token; 543 544 p = buf; 545 while ((c = lgetc(0)) == ' ' || c == '\t') 546 ; /* nothing */ 547 548 yylval.lineno = file->lineno; 549 if (c == '#') 550 while ((c = lgetc(0)) != '\n' && c != EOF) 551 ; /* nothing */ 552 553 switch (c) { 554 case '\'': 555 case '"': 556 quotec = c; 557 while (1) { 558 if ((c = lgetc(quotec)) == EOF) 559 return (0); 560 if (c == '\n') { 561 file->lineno++; 562 continue; 563 } else if (c == '\\') { 564 if ((next = lgetc(quotec)) == EOF) 565 return (0); 566 if (next == quotec || c == ' ' || c == '\t') 567 c = next; 568 else if (next == '\n') { 569 file->lineno++; 570 continue; 571 } else 572 lungetc(next); 573 } else if (c == quotec) { 574 *p = '\0'; 575 break; 576 } else if (c == '\0') { 577 yyerror("syntax error"); 578 return (findeol()); 579 } 580 if (p + 1 >= buf + sizeof(buf) - 1) { 581 yyerror("string too long"); 582 return (findeol()); 583 } 584 *p++ = (char)c; 585 } 586 yylval.v.string = strdup(buf); 587 if (yylval.v.string == NULL) 588 fatal("yylex: strdup"); 589 return (STRING); 590 } 591 592 #define allowed_to_end_number(x) \ 593 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 594 595 if (c == '-' || isdigit(c)) { 596 do { 597 *p++ = c; 598 if ((unsigned)(p-buf) >= sizeof(buf)) { 599 yyerror("string too long"); 600 return (findeol()); 601 } 602 } while ((c = lgetc(0)) != EOF && isdigit(c)); 603 lungetc(c); 604 if (p == buf + 1 && buf[0] == '-') 605 goto nodigits; 606 if (c == EOF || allowed_to_end_number(c)) { 607 const char *errstr = NULL; 608 609 *p = '\0'; 610 yylval.v.number = strtonum(buf, LLONG_MIN, 611 LLONG_MAX, &errstr); 612 if (errstr) { 613 yyerror("\"%s\" invalid number: %s", 614 buf, errstr); 615 return (findeol()); 616 } 617 return (NUMBER); 618 } else { 619 nodigits: 620 while (p > buf + 1) 621 lungetc(*--p); 622 c = *--p; 623 if (c == '-') 624 return (c); 625 } 626 } 627 628 #define allowed_in_string(x) \ 629 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 630 x != '{' && x != '}' && x != '<' && x != '>' && \ 631 x != '!' && x != '=' && x != '/' && x != '#' && \ 632 x != ',')) 633 634 if (isalnum(c) || c == ':' || c == '_' || c == '*') { 635 do { 636 *p++ = c; 637 if ((unsigned)(p-buf) >= sizeof(buf)) { 638 yyerror("string too long"); 639 return (findeol()); 640 } 641 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 642 lungetc(c); 643 *p = '\0'; 644 if ((token = lookup(buf)) == STRING) 645 if ((yylval.v.string = strdup(buf)) == NULL) 646 fatal("yylex: strdup"); 647 return (token); 648 } 649 if (c == '\n') { 650 yylval.lineno = file->lineno; 651 file->lineno++; 652 } 653 if (c == EOF) 654 return (0); 655 return (c); 656 } 657 658 struct file * 659 pushfile(const char *name) 660 { 661 struct file *nfile; 662 663 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 664 log_warn("malloc"); 665 return (NULL); 666 } 667 if ((nfile->name = strdup(name)) == NULL) { 668 log_warn("malloc"); 669 free(nfile); 670 return (NULL); 671 } 672 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 673 log_warn("%s", nfile->name); 674 free(nfile->name); 675 free(nfile); 676 return (NULL); 677 } 678 nfile->lineno = 1; 679 TAILQ_INSERT_TAIL(&files, nfile, entry); 680 return (nfile); 681 } 682 683 int 684 popfile(void) 685 { 686 struct file *prev; 687 688 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 689 prev->errors += file->errors; 690 691 TAILQ_REMOVE(&files, file, entry); 692 fclose(file->stream); 693 free(file->name); 694 free(file); 695 file = prev; 696 return (file ? 0 : EOF); 697 } 698 699 int 700 parse_config(const char *filename, struct radiusd *radiusd) 701 { 702 int errors = 0; 703 struct radiusd_listen *l; 704 struct radiusd_module_ref *m, *mt; 705 706 conf = radiusd; 707 radiusd_conf_init(conf); 708 radiusd_authentication_init(&authen); 709 radiusd_client_init(&client); 710 authen.auth = NULL; 711 712 if ((file = pushfile(filename)) == NULL) { 713 errors++; 714 goto out; 715 } 716 topfile = file; 717 718 yyparse(); 719 errors = file->errors; 720 popfile(); 721 722 if (TAILQ_EMPTY(&conf->listen)) { 723 if ((l = malloc(sizeof(struct radiusd_listen))) == NULL) { 724 log_warn("Out of memory"); 725 return (-1); 726 } 727 l->stype = SOCK_DGRAM; 728 l->sproto = IPPROTO_UDP; 729 l->addr.ipv4.sin_family = AF_INET; 730 l->addr.ipv4.sin_len = sizeof(struct sockaddr_in); 731 l->addr.ipv4.sin_addr.s_addr = htonl(0x7F000001L); 732 l->addr.ipv4.sin_port = htons(RADIUS_DEFAULT_PORT); 733 TAILQ_INSERT_TAIL(&conf->listen, l, next); 734 } 735 TAILQ_FOREACH(l, &conf->listen, next) { 736 l->sock = -1; 737 } 738 if (authen.auth != NULL) 739 free(authen.auth); 740 TAILQ_FOREACH_SAFE(m, &authen.deco, next, mt) { 741 TAILQ_REMOVE(&authen.deco, m, next); 742 free(m); 743 } 744 out: 745 conf = NULL; 746 return (errors ? -1 : 0); 747 } 748 749 static struct radiusd_module * 750 find_module(const char *name) 751 { 752 struct radiusd_module *module; 753 754 TAILQ_FOREACH(module, &conf->module, next) { 755 if (strcmp(name, module->name) == 0) 756 return (module); 757 } 758 759 return (NULL); 760 } 761 762 static void 763 free_str_l(void *str_l0) 764 { 765 int i; 766 struct { 767 char **v; 768 int c; 769 } *str_l = str_l0; 770 771 for (i = 0; i < str_l->c; i++) 772 free(str_l->v[i]); 773 free(str_l->v); 774 } 775 776 static struct radiusd_module_ref * 777 create_module_ref(const char *modulename) 778 { 779 struct radiusd_module *module; 780 struct radiusd_module_ref *modref; 781 782 if ((module = find_module(modulename)) == NULL) { 783 yyerror("module `%s' is not found", modulename); 784 return (NULL); 785 } 786 if ((modref = calloc(1, sizeof(struct radiusd_module_ref))) == NULL) { 787 yyerror("Out of memory: %s", strerror(errno)); 788 return (NULL); 789 } 790 modref->module = module; 791 792 return (modref); 793 } 794 795 static void 796 radiusd_authentication_init(struct radiusd_authentication *auth) 797 { 798 memset(auth, 0, sizeof(struct radiusd_authentication)); 799 TAILQ_INIT(&auth->deco); 800 } 801 802 static void 803 radiusd_client_init(struct radiusd_client *clnt) 804 { 805 memset(clnt, 0, sizeof(struct radiusd_client)); 806 clnt->msgauth_required = true; 807 } 808