1 /* $OpenBSD: parse.y,v 1.27 2024/08/15 07:24:28 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_module *conf_module = NULL; 42 static struct radiusd_client client; 43 44 static struct radiusd_authentication 45 *create_authen(const char *, char **, int, char **); 46 static struct radiusd_module 47 *find_module(const char *); 48 static void free_str_l(void *); 49 static struct radiusd_module_ref *create_module_ref(const char *); 50 static void radiusd_authentication_init(struct radiusd_authentication *); 51 static void radiusd_client_init(struct radiusd_client *); 52 static const char 53 *default_module_path(const char *); 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 int lineno; 61 int errors; 62 } *file, *topfile; 63 struct file *pushfile(const char *); 64 int popfile(void); 65 int yyparse(void); 66 int yylex(void); 67 int yyerror(const char *, ...) 68 __attribute__((__format__ (printf, 1, 2))) 69 __attribute__((__nonnull__ (1))); 70 int kw_cmp(const void *, const void *); 71 int lookup(char *); 72 int lgetc(int); 73 int lungetc(int); 74 int findeol(void); 75 76 typedef struct { 77 union { 78 int64_t number; 79 char *string; 80 struct radiusd_listen listen; 81 int yesno; 82 struct { 83 char **v; 84 int c; 85 } str_l; 86 struct { 87 int af; 88 struct radiusd_addr addr; 89 struct radiusd_addr mask; 90 } prefix; 91 } v; 92 int lineno; 93 } YYSTYPE; 94 95 %} 96 97 %token INCLUDE LISTEN ON PORT CLIENT SECRET LOAD MODULE MSGAUTH_REQUIRED 98 %token ACCOUNT ACCOUNTING AUTHENTICATE AUTHENTICATE_BY AUTHENTICATION_FILTER 99 %token BY DECORATE_BY QUICK SET TO ERROR YES NO 100 %token <v.string> STRING 101 %token <v.number> NUMBER 102 %type <v.number> optport optacct 103 %type <v.listen> listen_addr 104 %type <v.str_l> str_l optdeco 105 %type <v.prefix> prefix 106 %type <v.yesno> yesno optquick 107 %type <v.string> strnum 108 %type <v.string> key 109 %type <v.string> optstring 110 %% 111 112 grammar : /* empty */ 113 | grammar '\n' 114 | grammar include '\n' 115 | grammar listen '\n' 116 | grammar client '\n' 117 | grammar module '\n' 118 | grammar authenticate '\n' 119 | grammar account '\n' 120 | grammar error '\n' 121 ; 122 123 include : INCLUDE STRING { 124 struct file *nfile; 125 126 if ((nfile = pushfile($2)) == NULL) { 127 yyerror("failed to include file %s", $2); 128 free($2); 129 YYERROR; 130 } 131 free($2); 132 133 file = nfile; 134 lungetc('\n'); 135 nfile->lineno--; 136 } 137 ; 138 listen : LISTEN ON listen_addr { 139 struct radiusd_listen *n; 140 141 if ((n = calloc(1, sizeof(struct radiusd_listen))) 142 == NULL) { 143 outofmemory: 144 yyerror("Out of memory: %s", strerror(errno)); 145 YYERROR; 146 } 147 *n = $3; 148 TAILQ_INSERT_TAIL(&conf->listen, n, next); 149 } 150 listen_addr : STRING optacct optport { 151 int gai_errno; 152 struct addrinfo hints, *res; 153 154 memset(&hints, 0, sizeof(hints)); 155 hints.ai_family = PF_UNSPEC; 156 hints.ai_socktype = SOCK_DGRAM; 157 hints.ai_flags = AI_PASSIVE; 158 hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV; 159 160 if ((gai_errno = 161 getaddrinfo($1, NULL, &hints, &res)) != 0 || 162 res->ai_addrlen > sizeof($$.addr)) { 163 yyerror("Could not parse the address: %s: %s", 164 $1, gai_strerror(gai_errno)); 165 free($1); 166 YYERROR; 167 } 168 free($1); 169 $$.stype = res->ai_socktype; 170 $$.sproto = res->ai_protocol; 171 $$.accounting = $2; 172 memcpy(&$$.addr, res->ai_addr, res->ai_addrlen); 173 if ($3 != 0) 174 $$.addr.ipv4.sin_port = htons($3); 175 else if ($2) 176 $$.addr.ipv4.sin_port = 177 htons(RADIUS_ACCT_DEFAULT_PORT); 178 else 179 $$.addr.ipv4.sin_port = 180 htons(RADIUS_DEFAULT_PORT); 181 182 freeaddrinfo(res); 183 } 184 optacct : ACCOUNTING { $$ = 1; } 185 | { $$ = 0; } 186 ; 187 optport : { $$ = 0; } 188 | PORT NUMBER { $$ = $2; } 189 ; 190 client : CLIENT { 191 radiusd_client_init(&client); 192 } prefix optnl '{' clientopts '}' { 193 struct radiusd_client *client0; 194 195 if (client.secret[0] == '\0') { 196 yyerror("secret is required for client"); 197 YYERROR; 198 } 199 200 client0 = calloc(1, sizeof(struct radiusd_client)); 201 if (client0 == NULL) 202 goto outofmemory; 203 strlcpy(client0->secret, client.secret, 204 sizeof(client0->secret)); 205 client0->msgauth_required = client.msgauth_required; 206 client0->af = $3.af; 207 client0->addr = $3.addr; 208 client0->mask = $3.mask; 209 TAILQ_INSERT_TAIL(&conf->client, client0, next); 210 } 211 212 clientopts : clientopts '\n' clientopt 213 | clientopt 214 ; 215 216 clientopt : SECRET STRING { 217 if (client.secret[0] != '\0') { 218 free($2); 219 yyerror("secret is specified already"); 220 YYERROR; 221 } else if (strlcpy(client.secret, $2, 222 sizeof(client.secret)) >= sizeof(client.secret)) { 223 free($2); 224 yyerror("secret is too long"); 225 YYERROR; 226 } 227 free($2); 228 } 229 | MSGAUTH_REQUIRED yesno { 230 client.msgauth_required = $2; 231 } 232 | 233 ; 234 235 prefix : STRING '/' NUMBER { 236 int gai_errno, q, r; 237 struct addrinfo hints, *res; 238 239 memset(&hints, 0, sizeof(hints)); 240 hints.ai_family = PF_UNSPEC; 241 hints.ai_socktype = SOCK_DGRAM; /* dummy */ 242 hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV; 243 244 if ((gai_errno = getaddrinfo($1, NULL, &hints, &res)) 245 != 0) { 246 yyerror("Could not parse the address: %s: %s", 247 $1, gai_strerror(gai_errno)); 248 free($1); 249 YYERROR; 250 } 251 free($1); 252 q = $3 >> 3; 253 r = $3 & 7; 254 switch (res->ai_family) { 255 case AF_INET: 256 if ($3 < 0 || 32 < $3) { 257 yyerror("mask len %lld is out of range", 258 (long long)$3); 259 YYERROR; 260 } 261 $$.addr.addr.ipv4 = ((struct sockaddr_in *) 262 res->ai_addr)->sin_addr; 263 $$.mask.addr.ipv4.s_addr = htonl((uint32_t) 264 ((0xffffffffffULL) << (32 - $3))); 265 break; 266 case AF_INET6: 267 if ($3 < 0 || 128 < $3) { 268 yyerror("mask len %lld is out of range", 269 (long long)$3); 270 YYERROR; 271 } 272 $$.addr.addr.ipv6 = ((struct sockaddr_in6 *) 273 res->ai_addr)->sin6_addr; 274 memset(&$$.mask.addr.ipv6, 0, 275 sizeof($$.mask.addr.ipv6)); 276 if (q > 0) 277 memset(&$$.mask.addr.ipv6, 0xff, q); 278 if (r > 0) 279 *((u_char *)&$$.mask.addr.ipv6 + q) = 280 (0xff00 >> r) & 0xff; 281 break; 282 } 283 $$.af = res->ai_family; 284 freeaddrinfo(res); 285 } 286 ; 287 module : MODULE STRING optstring { 288 const char *path = $3; 289 if (path == NULL && (path = default_module_path($2)) 290 == NULL) { 291 yyerror("default path for `%s' is unknown.", 292 $2); 293 free($2); 294 free($3); 295 YYERROR; 296 } 297 conf_module = radiusd_module_load(conf, path, $2); 298 free($2); 299 free($3); 300 if (conf_module == NULL) 301 YYERROR; 302 TAILQ_INSERT_TAIL(&conf->module, conf_module, next); 303 conf_module = NULL; 304 } 305 | MODULE STRING optstring { 306 const char *path = $3; 307 if (path == NULL && (path = default_module_path($2)) 308 == NULL) { 309 yyerror("default path for `%s' is unknown.", 310 $2); 311 free($2); 312 free($3); 313 YYERROR; 314 } 315 conf_module = radiusd_module_load(conf, path, $2); 316 free($2); 317 free($3); 318 if (conf_module == NULL) 319 YYERROR; 320 } '{' moduleopts '}' { 321 TAILQ_INSERT_TAIL(&conf->module, conf_module, next); 322 conf_module = NULL; 323 } 324 /* following syntaxes are for backward compatilities */ 325 | MODULE LOAD STRING STRING { 326 struct radiusd_module *module; 327 if ((module = radiusd_module_load(conf, $4, $3)) 328 == NULL) { 329 free($3); 330 free($4); 331 YYERROR; 332 } 333 free($3); 334 free($4); 335 TAILQ_INSERT_TAIL(&conf->module, module, next); 336 } 337 | MODULE SET STRING key str_l { 338 struct radiusd_module *module; 339 340 module = find_module($3); 341 if (module == NULL) { 342 yyerror("module `%s' is not found", $3); 343 setstrerr: 344 free($3); 345 free($4); 346 free_str_l(&$5); 347 YYERROR; 348 } 349 if ($4[0] == '_') { 350 yyerror("setting `%s' is not allowed", $4); 351 goto setstrerr; 352 } 353 if (radiusd_module_set(module, $4, $5.c, $5.v)) { 354 yyerror("syntax error by module `%s'", $3); 355 goto setstrerr; 356 } 357 free($3); 358 free($4); 359 free_str_l(&$5); 360 } 361 ; 362 363 moduleopts : moduleopts '\n' moduleopt 364 | moduleopt 365 ; 366 moduleopt : /* empty */ 367 | SET key str_l { 368 if ($2[0] == '_') { 369 yyerror("setting `%s' is not allowed", $2); 370 free($2); 371 free_str_l(&$3); 372 YYERROR; 373 } 374 if (radiusd_module_set(conf_module, $2, $3.c, $3.v)) { 375 yyerror("syntax error by module `%s'", 376 conf_module->name); 377 free($2); 378 free_str_l(&$3); 379 YYERROR; 380 } 381 free($2); 382 free_str_l(&$3); 383 } 384 ; 385 386 key : STRING 387 | SECRET { $$ = strdup("secret"); } 388 ; 389 390 authenticate : AUTHENTICATE str_l BY STRING optdeco { 391 struct radiusd_authentication *auth; 392 393 auth = create_authen($4, $2.v, $5.c, $5.v); 394 free($4); 395 free_str_l(&$5); 396 if (auth == NULL) { 397 free_str_l(&$2); 398 YYERROR; 399 } else 400 TAILQ_INSERT_TAIL(&conf->authen, auth, next); 401 } 402 | AUTHENTICATION_FILTER str_l BY STRING optdeco { 403 struct radiusd_authentication *auth; 404 405 auth = create_authen($4, $2.v, $5.c, $5.v); 406 free($4); 407 free_str_l(&$5); 408 if (auth == NULL) { 409 free_str_l(&$2); 410 YYERROR; 411 } else { 412 auth->isfilter = true; 413 TAILQ_INSERT_TAIL(&conf->authen, auth, next); 414 } 415 } 416 /* the followings are for backward compatibilities */ 417 | AUTHENTICATE str_l optnl '{' { 418 radiusd_authentication_init(&authen); 419 authen.username = $2.v; 420 } authopts '}' { 421 int i; 422 struct radiusd_authentication *a; 423 424 if (authen.auth == NULL) { 425 yyerror("no authentication module specified"); 426 for (i = 0; authen.username[i] != NULL; i++) 427 free(authen.username[i]); 428 free(authen.username); 429 YYERROR; 430 } 431 if ((a = calloc(1, 432 sizeof(struct radiusd_authentication))) == NULL) { 433 for (i = 0; authen.username[i] != NULL; i++) 434 free(authen.username[i]); 435 free(authen.username); 436 goto outofmemory; 437 } 438 a->auth = authen.auth; 439 authen.auth = NULL; 440 a->deco = authen.deco; 441 a->username = authen.username; 442 TAILQ_INSERT_TAIL(&conf->authen, a, next); 443 } 444 ; 445 446 optdeco : { $$.c = 0; $$.v = NULL; } 447 | DECORATE_BY str_l { $$ = $2; } 448 ; 449 450 authopts : authopts '\n' authopt 451 | authopt 452 ; 453 454 authopt : /* empty */ 455 | AUTHENTICATE_BY STRING { 456 struct radiusd_module_ref *modref; 457 458 if (authen.auth != NULL) { 459 free($2); 460 yyerror("authenticate is specified already"); 461 YYERROR; 462 } 463 modref = create_module_ref($2); 464 free($2); 465 if (modref == NULL) 466 YYERROR; 467 authen.auth = modref; 468 } 469 | DECORATE_BY str_l { 470 int i; 471 struct radiusd_module_ref *modref; 472 473 for (i = 0; i < $2.c; i++) { 474 if ((modref = create_module_ref($2.v[i])) 475 == NULL) { 476 free_str_l(&$2); 477 YYERROR; 478 } 479 TAILQ_INSERT_TAIL(&authen.deco, modref, next); 480 } 481 free_str_l(&$2); 482 } 483 ; 484 485 account : ACCOUNT optquick str_l TO STRING optdeco { 486 int i, error = 1; 487 struct radiusd_accounting *acct; 488 struct radiusd_module_ref *modref, *modreft; 489 490 if ((acct = calloc(1, 491 sizeof(struct radiusd_accounting))) == NULL) { 492 yyerror("Out of memory: %s", strerror(errno)); 493 goto account_error; 494 } 495 if ((acct->acct = create_module_ref($5)) == NULL) 496 goto account_error; 497 acct->username = $3.v; 498 acct->quick = $2; 499 TAILQ_INIT(&acct->deco); 500 for (i = 0; i < $6.c; i++) { 501 if ((modref = create_module_ref($6.v[i])) 502 == NULL) 503 goto account_error; 504 TAILQ_INSERT_TAIL(&acct->deco, modref, next); 505 } 506 TAILQ_INSERT_TAIL(&conf->account, acct, next); 507 acct = NULL; 508 error = 0; 509 account_error: 510 if (acct != NULL) { 511 free(acct->acct); 512 TAILQ_FOREACH_SAFE(modref, &acct->deco, next, 513 modreft) { 514 TAILQ_REMOVE(&acct->deco, modref, next); 515 free(modref); 516 } 517 free_str_l(&$3); 518 } 519 free(acct); 520 free($5); 521 free_str_l(&$6); 522 if (error > 0) 523 YYERROR; 524 } 525 ; 526 527 optquick : { $$ = 0; } 528 | QUICK { $$ = 1; } 529 530 str_l : str_l strnum { 531 int i; 532 char **v; 533 if ((v = calloc(sizeof(char **), $$.c + 2)) == NULL) 534 goto outofmemory; 535 for (i = 0; i < $$.c; i++) 536 v[i] = $$.v[i]; 537 v[i++] = $2; 538 v[i] = NULL; 539 $$.c++; 540 free($$.v); 541 $$.v = v; 542 } 543 | strnum { 544 if (($$.v = calloc(sizeof(char **), 2)) == NULL) 545 goto outofmemory; 546 $$.v[0] = $1; 547 $$.v[1] = NULL; 548 $$.c = 1; 549 } 550 ; 551 strnum : STRING { $$ = $1; } 552 | NUMBER { 553 /* Treat number as a string */ 554 asprintf(&($$), "%jd", (intmax_t)$1); 555 if ($$ == NULL) 556 goto outofmemory; 557 } 558 ; 559 optnl : 560 | '\n' 561 ; 562 optstring : { $$ = NULL; } 563 | STRING { $$ = $1; } 564 ; 565 yesno : YES { $$ = true; } 566 | NO { $$ = false; } 567 ; 568 %% 569 570 struct keywords { 571 const char *k_name; 572 int k_val; 573 }; 574 575 int 576 yyerror(const char *fmt, ...) 577 { 578 va_list ap; 579 char *msg; 580 581 file->errors++; 582 va_start(ap, fmt); 583 if (vasprintf(&msg, fmt, ap) == -1) 584 fatalx("yyerror vasprintf"); 585 va_end(ap); 586 logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg); 587 free(msg); 588 return (0); 589 } 590 591 int 592 kw_cmp(const void *k, const void *e) 593 { 594 return (strcmp(k, ((const struct keywords *)e)->k_name)); 595 } 596 597 int 598 lookup(char *s) 599 { 600 /* this has to be sorted always */ 601 static const struct keywords keywords[] = { 602 { "account", ACCOUNT}, 603 { "accounting", ACCOUNTING}, 604 { "authenticate", AUTHENTICATE}, 605 { "authenticate-by", AUTHENTICATE_BY}, 606 { "authentication-filter", AUTHENTICATION_FILTER}, 607 { "by", BY}, 608 { "client", CLIENT}, 609 { "decorate-by", DECORATE_BY}, 610 { "include", INCLUDE}, 611 { "listen", LISTEN}, 612 { "load", LOAD}, 613 { "module", MODULE}, 614 { "msgauth-required", MSGAUTH_REQUIRED}, 615 { "no", NO}, 616 { "on", ON}, 617 { "port", PORT}, 618 { "quick", QUICK}, 619 { "secret", SECRET}, 620 { "set", SET}, 621 { "to", TO}, 622 { "yes", YES}, 623 }; 624 const struct keywords *p; 625 626 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 627 sizeof(keywords[0]), kw_cmp); 628 629 if (p) 630 return (p->k_val); 631 else 632 return (STRING); 633 } 634 635 #define MAXPUSHBACK 128 636 637 char *parsebuf; 638 int parseindex; 639 char pushback_buffer[MAXPUSHBACK]; 640 int pushback_index = 0; 641 642 int 643 lgetc(int quotec) 644 { 645 int c, next; 646 647 if (parsebuf) { 648 /* Read character from the parsebuffer instead of input. */ 649 if (parseindex >= 0) { 650 c = (unsigned char)parsebuf[parseindex++]; 651 if (c != '\0') 652 return (c); 653 parsebuf = NULL; 654 } else 655 parseindex++; 656 } 657 658 if (pushback_index) 659 return ((unsigned char)pushback_buffer[--pushback_index]); 660 661 if (quotec) { 662 if ((c = getc(file->stream)) == EOF) { 663 yyerror("reached end of file while parsing " 664 "quoted string"); 665 if (file == topfile || popfile() == EOF) 666 return (EOF); 667 return (quotec); 668 } 669 return (c); 670 } 671 672 while ((c = getc(file->stream)) == '\\') { 673 next = getc(file->stream); 674 if (next != '\n') { 675 c = next; 676 break; 677 } 678 yylval.lineno = file->lineno; 679 file->lineno++; 680 } 681 682 while (c == EOF) { 683 if (file == topfile || popfile() == EOF) 684 return (EOF); 685 c = getc(file->stream); 686 } 687 return (c); 688 } 689 690 int 691 lungetc(int c) 692 { 693 if (c == EOF) 694 return (EOF); 695 if (parsebuf) { 696 parseindex--; 697 if (parseindex >= 0) 698 return (c); 699 } 700 if (pushback_index + 1 >= MAXPUSHBACK) 701 return (EOF); 702 pushback_buffer[pushback_index++] = c; 703 return (c); 704 } 705 706 int 707 findeol(void) 708 { 709 int c; 710 711 parsebuf = NULL; 712 713 /* skip to either EOF or the first real EOL */ 714 while (1) { 715 if (pushback_index) 716 c = (unsigned char)pushback_buffer[--pushback_index]; 717 else 718 c = lgetc(0); 719 if (c == '\n') { 720 file->lineno++; 721 break; 722 } 723 if (c == EOF) 724 break; 725 } 726 return (ERROR); 727 } 728 729 int 730 yylex(void) 731 { 732 char buf[8096]; 733 char *p; 734 int quotec, next, c; 735 int token; 736 737 p = buf; 738 while ((c = lgetc(0)) == ' ' || c == '\t') 739 ; /* nothing */ 740 741 yylval.lineno = file->lineno; 742 if (c == '#') 743 while ((c = lgetc(0)) != '\n' && c != EOF) 744 ; /* nothing */ 745 746 switch (c) { 747 case '\'': 748 case '"': 749 quotec = c; 750 while (1) { 751 if ((c = lgetc(quotec)) == EOF) 752 return (0); 753 if (c == '\n') { 754 file->lineno++; 755 continue; 756 } else if (c == '\\') { 757 if ((next = lgetc(quotec)) == EOF) 758 return (0); 759 if (next == quotec || next == ' ' || 760 next == '\t') 761 c = next; 762 else if (next == '\n') { 763 file->lineno++; 764 continue; 765 } else 766 lungetc(next); 767 } else if (c == quotec) { 768 *p = '\0'; 769 break; 770 } else if (c == '\0') { 771 yyerror("syntax error"); 772 return (findeol()); 773 } 774 if (p + 1 >= buf + sizeof(buf) - 1) { 775 yyerror("string too long"); 776 return (findeol()); 777 } 778 *p++ = c; 779 } 780 yylval.v.string = strdup(buf); 781 if (yylval.v.string == NULL) 782 fatal("yylex: strdup"); 783 return (STRING); 784 } 785 786 #define allowed_to_end_number(x) \ 787 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 788 789 if (c == '-' || isdigit(c)) { 790 do { 791 *p++ = c; 792 if ((size_t)(p-buf) >= sizeof(buf)) { 793 yyerror("string too long"); 794 return (findeol()); 795 } 796 } while ((c = lgetc(0)) != EOF && isdigit(c)); 797 lungetc(c); 798 if (p == buf + 1 && buf[0] == '-') 799 goto nodigits; 800 if (c == EOF || allowed_to_end_number(c)) { 801 const char *errstr = NULL; 802 803 *p = '\0'; 804 yylval.v.number = strtonum(buf, LLONG_MIN, 805 LLONG_MAX, &errstr); 806 if (errstr) { 807 yyerror("\"%s\" invalid number: %s", 808 buf, errstr); 809 return (findeol()); 810 } 811 return (NUMBER); 812 } else { 813 nodigits: 814 while (p > buf + 1) 815 lungetc((unsigned char)*--p); 816 c = (unsigned char)*--p; 817 if (c == '-') 818 return (c); 819 } 820 } 821 822 #define allowed_in_string(x) \ 823 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 824 x != '{' && x != '}' && x != '<' && x != '>' && \ 825 x != '!' && x != '=' && x != '/' && x != '#' && \ 826 x != ',')) 827 828 if (isalnum(c) || c == ':' || c == '_' || c == '*') { 829 do { 830 *p++ = c; 831 if ((size_t)(p-buf) >= sizeof(buf)) { 832 yyerror("string too long"); 833 return (findeol()); 834 } 835 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 836 lungetc(c); 837 *p = '\0'; 838 if ((token = lookup(buf)) == STRING) 839 if ((yylval.v.string = strdup(buf)) == NULL) 840 fatal("yylex: strdup"); 841 return (token); 842 } 843 if (c == '\n') { 844 yylval.lineno = file->lineno; 845 file->lineno++; 846 } 847 if (c == EOF) 848 return (0); 849 return (c); 850 } 851 852 struct file * 853 pushfile(const char *name) 854 { 855 struct file *nfile; 856 857 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 858 log_warn("%s", __func__); 859 return (NULL); 860 } 861 if ((nfile->name = strdup(name)) == NULL) { 862 log_warn("%s", __func__); 863 free(nfile); 864 return (NULL); 865 } 866 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 867 log_warn("%s: %s", __func__, nfile->name); 868 free(nfile->name); 869 free(nfile); 870 return (NULL); 871 } 872 nfile->lineno = 1; 873 TAILQ_INSERT_TAIL(&files, nfile, entry); 874 return (nfile); 875 } 876 877 int 878 popfile(void) 879 { 880 struct file *prev; 881 882 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 883 prev->errors += file->errors; 884 885 TAILQ_REMOVE(&files, file, entry); 886 fclose(file->stream); 887 free(file->name); 888 free(file); 889 file = prev; 890 return (file ? 0 : EOF); 891 } 892 893 int 894 parse_config(const char *filename, struct radiusd *radiusd) 895 { 896 int errors = 0; 897 struct radiusd_listen *l; 898 899 conf = radiusd; 900 radiusd_conf_init(conf); 901 radiusd_authentication_init(&authen); 902 radiusd_client_init(&client); 903 904 if ((file = pushfile(filename)) == NULL) { 905 errors++; 906 goto out; 907 } 908 topfile = file; 909 910 yyparse(); 911 errors = file->errors; 912 popfile(); 913 914 if (TAILQ_EMPTY(&conf->listen)) { 915 if ((l = calloc(1, sizeof(struct radiusd_listen))) == NULL) { 916 log_warn("Out of memory"); 917 return (-1); 918 } 919 l->stype = SOCK_DGRAM; 920 l->sproto = IPPROTO_UDP; 921 l->addr.ipv4.sin_family = AF_INET; 922 l->addr.ipv4.sin_len = sizeof(struct sockaddr_in); 923 l->addr.ipv4.sin_addr.s_addr = htonl(0x7F000001L); 924 l->addr.ipv4.sin_port = htons(RADIUS_DEFAULT_PORT); 925 TAILQ_INSERT_TAIL(&conf->listen, l, next); 926 } 927 TAILQ_FOREACH(l, &conf->listen, next) { 928 l->sock = -1; 929 } 930 radiusd_authentication_init(&authen); 931 if (conf_module != NULL) 932 radiusd_module_unload(conf_module); 933 out: 934 conf = NULL; 935 return (errors ? -1 : 0); 936 } 937 938 static struct radiusd_authentication * 939 create_authen(const char *byname, char **username, int decoc, char **deco) 940 { 941 int i; 942 struct radiusd_authentication *auth; 943 struct radiusd_module_ref *modref, *modreft; 944 945 if ((auth = calloc(1, sizeof(struct radiusd_authentication))) 946 == NULL) { 947 yyerror("Out of memory: %s", strerror(errno)); 948 return (NULL); 949 } 950 if ((auth->auth = create_module_ref(byname)) == NULL) 951 goto on_error; 952 953 auth->username = username; 954 TAILQ_INIT(&auth->deco); 955 for (i = 0; i < decoc; i++) { 956 if ((modref = create_module_ref(deco[i])) == NULL) 957 goto on_error; 958 TAILQ_INSERT_TAIL(&auth->deco, modref, next); 959 } 960 return (auth); 961 on_error: 962 TAILQ_FOREACH_SAFE(modref, &auth->deco, next, modreft) { 963 TAILQ_REMOVE(&auth->deco, modref, next); 964 free(modref); 965 } 966 free(auth); 967 return (NULL); 968 } 969 970 static struct radiusd_module * 971 find_module(const char *name) 972 { 973 struct radiusd_module *module; 974 975 TAILQ_FOREACH(module, &conf->module, next) { 976 if (strcmp(name, module->name) == 0) 977 return (module); 978 } 979 980 return (NULL); 981 } 982 983 static void 984 free_str_l(void *str_l0) 985 { 986 int i; 987 struct { 988 char **v; 989 int c; 990 } *str_l = str_l0; 991 992 for (i = 0; i < str_l->c; i++) 993 free(str_l->v[i]); 994 free(str_l->v); 995 } 996 997 static struct radiusd_module_ref * 998 create_module_ref(const char *modulename) 999 { 1000 struct radiusd_module *module; 1001 struct radiusd_module_ref *modref; 1002 1003 if ((module = find_module(modulename)) == NULL) { 1004 yyerror("module `%s' is not found", modulename); 1005 return (NULL); 1006 } 1007 if ((modref = calloc(1, sizeof(struct radiusd_module_ref))) == NULL) { 1008 yyerror("Out of memory: %s", strerror(errno)); 1009 return (NULL); 1010 } 1011 modref->module = module; 1012 1013 return (modref); 1014 } 1015 1016 static void 1017 radiusd_authentication_init(struct radiusd_authentication *auth) 1018 { 1019 free(auth->auth); 1020 memset(auth, 0, sizeof(struct radiusd_authentication)); 1021 TAILQ_INIT(&auth->deco); 1022 } 1023 1024 static void 1025 radiusd_client_init(struct radiusd_client *clnt) 1026 { 1027 memset(clnt, 0, sizeof(struct radiusd_client)); 1028 clnt->msgauth_required = true; 1029 } 1030 1031 static const char * 1032 default_module_path(const char *name) 1033 { 1034 unsigned i; 1035 struct { 1036 const char *name; 1037 const char *path; 1038 } module_paths[] = { 1039 { "bsdauth", "/usr/libexec/radiusd/radiusd_bsdauth" }, 1040 { "eap2mschap", "/usr/libexec/radiusd/radiusd_eap2mschap" }, 1041 { "file", "/usr/libexec/radiusd/radiusd_file" }, 1042 { "ipcp", "/usr/libexec/radiusd/radiusd_ipcp" }, 1043 { "radius", "/usr/libexec/radiusd/radiusd_radius" }, 1044 { "standard", "/usr/libexec/radiusd/radiusd_standard" } 1045 }; 1046 1047 for (i = 0; i < nitems(module_paths); i++) { 1048 if (strcmp(name, module_paths[i].name) == 0) 1049 return (module_paths[i].path); 1050 } 1051 1052 return (NULL); 1053 } 1054