1 /* $OpenBSD: parse.y,v 1.61 2019/05/10 01:29:31 guenther Exp $ */ 2 3 /* 4 * Copyright (c) 2004, 2005, 2006 Reyk Floeter <reyk@openbsd.org> 5 * Copyright (c) 2002 - 2005 Henning Brauer <henning@openbsd.org> 6 * Copyright (c) 2001 Markus Friedl. All rights reserved. 7 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 8 * Copyright (c) 2001 Theo de Raadt. All rights reserved. 9 * 10 * Permission to use, copy, modify, and distribute this software for any 11 * purpose with or without fee is hereby granted, provided that the above 12 * copyright notice and this permission notice appear in all copies. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 */ 22 23 %{ 24 #include <sys/ioctl.h> 25 #include <sys/types.h> 26 #include <sys/socket.h> 27 #include <sys/time.h> 28 #include <sys/queue.h> 29 #include <sys/stat.h> 30 31 #include <net/if.h> 32 #include <net/if_media.h> 33 #include <net/if_arp.h> 34 #include <net/if_llc.h> 35 #include <net/bpf.h> 36 37 #include <netinet/in.h> 38 #include <netinet/if_ether.h> 39 #include <arpa/inet.h> 40 41 #include <net80211/ieee80211.h> 42 #include <net80211/ieee80211_radiotap.h> 43 44 #include <ctype.h> 45 #include <errno.h> 46 #include <event.h> 47 #include <fcntl.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <stdarg.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include <limits.h> 54 #include <stdint.h> 55 #include <err.h> 56 57 #include "hostapd.h" 58 59 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 60 static struct file { 61 TAILQ_ENTRY(file) entry; 62 FILE *stream; 63 char *name; 64 size_t ungetpos; 65 size_t ungetsize; 66 u_char *ungetbuf; 67 int eof_reached; 68 int lineno; 69 int errors; 70 } *file, *topfile; 71 struct file *pushfile(const char *, int); 72 int popfile(void); 73 int check_file_secrecy(int, const char *); 74 int yyparse(void); 75 int yylex(void); 76 int yyerror(const char *, ...) 77 __attribute__((__format__ (printf, 1, 2))) 78 __attribute__((__nonnull__ (1))); 79 int kw_cmp(const void *, const void *); 80 int lookup(char *); 81 int igetc(void); 82 int lgetc(int); 83 void lungetc(int); 84 int findeol(void); 85 86 TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 87 struct sym { 88 TAILQ_ENTRY(sym) entry; 89 int used; 90 int persist; 91 char *nam; 92 char *val; 93 }; 94 int symset(const char *, const char *, int); 95 char *symget(const char *); 96 97 extern struct hostapd_config hostapd_cfg; 98 99 typedef struct { 100 union { 101 struct { 102 u_int8_t lladdr[IEEE80211_ADDR_LEN]; 103 struct hostapd_table *table; 104 u_int32_t flags; 105 } reflladdr; 106 struct { 107 u_int16_t alg; 108 u_int16_t transaction; 109 } authalg; 110 struct in_addr in; 111 char *string; 112 int64_t number; 113 u_int16_t reason; 114 enum hostapd_op op; 115 struct timeval timeout; 116 } v; 117 int lineno; 118 } YYSTYPE; 119 120 struct hostapd_apme *apme; 121 struct hostapd_table *table; 122 struct hostapd_entry *entry; 123 struct hostapd_frame frame, *frame_ptr; 124 struct hostapd_ieee80211_frame *frame_ieee80211; 125 126 #define HOSTAPD_MATCH(_m, _not) { \ 127 frame.f_flags |= (_not) ? \ 128 HOSTAPD_FRAME_F_##_m##_N : HOSTAPD_FRAME_F_##_m; \ 129 } 130 #define HOSTAPD_MATCH_TABLE(_m, _not) { \ 131 frame.f_flags |= HOSTAPD_FRAME_F_##_m##_TABLE | ((_not) ? \ 132 HOSTAPD_FRAME_F_##_m##_N : HOSTAPD_FRAME_F_##_m); \ 133 } 134 #define HOSTAPD_MATCH_RADIOTAP(_x) { \ 135 if (hostapd_cfg.c_apme_dlt == DLT_IEEE802_11 || \ 136 (hostapd_cfg.c_apme_dlt == 0 && \ 137 HOSTAPD_DLT == DLT_IEEE802_11)) { \ 138 yyerror("option %s requires radiotap headers", #_x); \ 139 YYERROR; \ 140 } \ 141 frame.f_radiotap |= HOSTAPD_RADIOTAP_F(RSSI); \ 142 frame.f_flags |= HOSTAPD_FRAME_F_##_x; \ 143 } 144 #define HOSTAPD_IAPP_FLAG(_f, _not) { \ 145 if (_not) \ 146 hostapd_cfg.c_iapp.i_flags &= ~(HOSTAPD_IAPP_F_##_f); \ 147 else \ 148 hostapd_cfg.c_iapp.i_flags |= (HOSTAPD_IAPP_F_##_f); \ 149 } 150 151 %} 152 153 %token MODE INTERFACE IAPP HOSTAP MULTICAST BROADCAST SET SEC USEC 154 %token HANDLE TYPE SUBTYPE FROM TO BSSID WITH FRAME RADIOTAP NWID PASSIVE 155 %token MANAGEMENT DATA PROBE BEACON ATIM ANY DS NO DIR RESEND RANDOM 156 %token AUTH DEAUTH ASSOC DISASSOC REASSOC REQUEST RESPONSE PCAP RATE 157 %token ERROR CONST TABLE NODE DELETE ADD LOG VERBOSE LIMIT QUICK SKIP 158 %token REASON UNSPECIFIED EXPIRE LEAVE ASSOC TOOMANY NOT AUTHED ASSOCED 159 %token RESERVED RSN REQUIRED INCONSISTENT IE INVALID MIC FAILURE OPEN 160 %token ADDRESS PORT ON NOTIFY TTL INCLUDE ROUTE ROAMING RSSI TXRATE FREQ 161 %token HOPPER DELAY NE LE GE ARROW 162 %token <v.string> STRING 163 %token <v.number> NUMBER 164 %type <v.in> ipv4addr 165 %type <v.reflladdr> refaddr, lladdr, randaddr, frmactionaddr, frmmatchaddr 166 %type <v.reason> frmreason_l 167 %type <v.string> table 168 %type <v.string> string 169 %type <v.authalg> authalg 170 %type <v.op> unaryop 171 %type <v.number> percent 172 %type <v.number> txrate 173 %type <v.number> freq 174 %type <v.number> not 175 %type <v.timeout> timeout 176 177 %% 178 179 /* 180 * Configuration grammar 181 */ 182 183 grammar : /* empty */ 184 | grammar '\n' 185 | grammar include '\n' 186 | grammar tabledef '\n' 187 | grammar option '\n' 188 | grammar event '\n' 189 | grammar varset '\n' 190 | grammar error '\n' { file->errors++; } 191 ; 192 193 include : INCLUDE STRING 194 { 195 struct file *nfile; 196 197 if ((nfile = pushfile($2, 1)) == NULL) { 198 yyerror("failed to include file %s", $2); 199 free($2); 200 YYERROR; 201 } 202 free($2); 203 204 file = nfile; 205 lungetc('\n'); 206 } 207 208 option : SET HOSTAP INTERFACE hostapifaces 209 { 210 if (!TAILQ_EMPTY(&hostapd_cfg.c_apmes)) 211 hostapd_cfg.c_flags |= HOSTAPD_CFG_F_APME; 212 } 213 | SET HOSTAP HOPPER INTERFACE hopperifaces 214 | SET HOSTAP HOPPER DELAY timeout 215 { 216 bcopy(&$5, &hostapd_cfg.c_apme_hopdelay, 217 sizeof(struct timeval)); 218 } 219 | SET HOSTAP MODE hostapmode 220 | SET IAPP INTERFACE STRING passive 221 { 222 if (strlcpy(hostapd_cfg.c_iapp.i_iface, $4, 223 sizeof(hostapd_cfg.c_iapp.i_iface)) >= 224 sizeof(hostapd_cfg.c_iapp.i_iface)) { 225 yyerror("invalid interface %s", $4); 226 free($4); 227 YYERROR; 228 } 229 230 hostapd_cfg.c_flags |= HOSTAPD_CFG_F_IAPP; 231 232 hostapd_log(HOSTAPD_LOG_DEBUG, 233 "%s: IAPP interface added", $4); 234 235 free($4); 236 } 237 | SET IAPP MODE iappmode 238 | SET IAPP ADDRESS ROAMING TABLE table 239 { 240 if ((hostapd_cfg.c_iapp.i_addr_tbl = 241 hostapd_table_lookup(&hostapd_cfg, $6)) == NULL) { 242 yyerror("undefined table <%s>", $6); 243 free($6); 244 YYERROR; 245 } 246 free($6); 247 } 248 | SET IAPP ROUTE ROAMING TABLE table 249 { 250 if ((hostapd_cfg.c_iapp.i_route_tbl = 251 hostapd_table_lookup(&hostapd_cfg, $6)) == NULL) { 252 yyerror("undefined table <%s>", $6); 253 free($6); 254 YYERROR; 255 } 256 free($6); 257 } 258 | SET IAPP HANDLE SUBTYPE iappsubtypes 259 ; 260 261 iappmode : MULTICAST iappmodeaddr iappmodeport iappmodettl 262 { 263 hostapd_cfg.c_flags &= ~HOSTAPD_CFG_F_BRDCAST; 264 } 265 | BROADCAST iappmodeport 266 { 267 hostapd_cfg.c_flags |= HOSTAPD_CFG_F_BRDCAST; 268 } 269 ; 270 271 iappmodeaddr : /* empty */ 272 | ADDRESS ipv4addr 273 { 274 bcopy(&$2, &hostapd_cfg.c_iapp.i_multicast.sin_addr, 275 sizeof(struct in_addr)); 276 } 277 ; 278 279 iappmodeport : /* empty */ 280 | PORT NUMBER 281 { 282 if ($2 < 0 || $2 > UINT16_MAX) { 283 yyerror("port out of range: %lld", $2); 284 YYERROR; 285 } 286 hostapd_cfg.c_iapp.i_addr.sin_port = htons($2); 287 } 288 ; 289 290 iappmodettl : /* empty */ 291 | TTL NUMBER 292 { 293 if ($2 < 1 || $2 > UINT8_MAX) { 294 yyerror("ttl out of range: %lld", $2); 295 YYERROR; 296 } 297 hostapd_cfg.c_iapp.i_ttl = $2; 298 } 299 ; 300 301 hostapmode : RADIOTAP 302 { 303 hostapd_cfg.c_apme_dlt = DLT_IEEE802_11_RADIO; 304 } 305 | PCAP 306 { 307 hostapd_cfg.c_apme_dlt = DLT_IEEE802_11; 308 } 309 ; 310 311 hostapifaces : '{' optnl hostapifacelist optnl '}' 312 | hostapiface 313 ; 314 315 hostapifacelist : hostapiface 316 | hostapifacelist comma hostapiface 317 ; 318 319 hostapiface : STRING 320 { 321 if (hostapd_apme_add(&hostapd_cfg, $1) != 0) { 322 yyerror("failed to add hostap interface"); 323 YYERROR; 324 } 325 free($1); 326 } 327 ; 328 329 hopperifaces : '{' optnl hopperifacelist optnl '}' 330 | hopperiface 331 ; 332 333 hopperifacelist : hopperiface 334 | hopperifacelist comma hopperiface 335 ; 336 337 hopperiface : STRING 338 { 339 if ((apme = hostapd_apme_addhopper(&hostapd_cfg, 340 $1)) == NULL) { 341 yyerror("failed to add hopper %s", $1); 342 free($1); 343 YYERROR; 344 } 345 free($1); 346 } 347 ; 348 349 hostapmatch : /* empty */ 350 | ON not STRING 351 { 352 if ((frame.f_apme = 353 hostapd_apme_lookup(&hostapd_cfg, $3)) == NULL) { 354 yyerror("undefined hostap interface"); 355 free($3); 356 YYERROR; 357 } 358 free($3); 359 360 HOSTAPD_MATCH(APME, $2); 361 } 362 ; 363 364 event : HOSTAP HANDLE 365 { 366 bzero(&frame, sizeof(struct hostapd_frame)); 367 /* IEEE 802.11 frame to match */ 368 frame_ieee80211 = &frame.f_frame; 369 } eventopt hostapmatch frmmatch { 370 /* IEEE 802.11 raw frame to send as an action */ 371 frame_ieee80211 = &frame.f_action_data.a_frame; 372 } action limit rate { 373 if ((frame_ptr = calloc(1, sizeof(struct hostapd_frame))) 374 == NULL) { 375 yyerror("calloc"); 376 YYERROR; 377 } 378 379 if (gettimeofday(&frame.f_last, NULL) == -1) 380 hostapd_fatal("gettimeofday"); 381 timeradd(&frame.f_last, &frame.f_limit, &frame.f_then); 382 383 bcopy(&frame, frame_ptr, sizeof(struct hostapd_frame)); 384 TAILQ_INSERT_TAIL(&hostapd_cfg.c_frames, 385 frame_ptr, f_entries); 386 } 387 ; 388 389 iappsubtypes : '{' optnl iappsubtypelist optnl '}' 390 | iappsubtype 391 ; 392 393 iappsubtypelist : iappsubtype 394 | iappsubtypelist comma iappsubtype 395 ; 396 397 iappsubtype : not ADD NOTIFY 398 { 399 HOSTAPD_IAPP_FLAG(ADD_NOTIFY, $1); 400 } 401 | not RADIOTAP 402 { 403 HOSTAPD_IAPP_FLAG(RADIOTAP, $1); 404 } 405 | not ROUTE ROAMING 406 { 407 HOSTAPD_IAPP_FLAG(ROAMING_ROUTE, $1); 408 } 409 | not ADDRESS ROAMING 410 { 411 HOSTAPD_IAPP_FLAG(ROAMING_ADDRESS, $1); 412 } 413 ; 414 415 eventopt : /* empty */ 416 { 417 frame.f_flags |= HOSTAPD_FRAME_F_RET_OK; 418 } 419 | QUICK 420 { 421 frame.f_flags |= HOSTAPD_FRAME_F_RET_QUICK; 422 } 423 | SKIP 424 { 425 frame.f_flags |= HOSTAPD_FRAME_F_RET_SKIP; 426 } 427 ; 428 429 action : /* empty */ 430 { 431 frame.f_action = HOSTAPD_ACTION_NONE; 432 } 433 | WITH LOG verbose 434 { 435 frame.f_action = HOSTAPD_ACTION_LOG; 436 } 437 | WITH FRAME frmaction 438 { 439 frame.f_action = HOSTAPD_ACTION_FRAME; 440 } 441 | WITH IAPP iapp 442 | WITH NODE nodeopt frmactionaddr 443 { 444 if (($4.flags & HOSTAPD_ACTION_F_REF_M) == 0) { 445 bcopy($4.lladdr, frame.f_action_data.a_lladdr, 446 IEEE80211_ADDR_LEN); 447 } else 448 frame.f_action_data.a_flags |= $4.flags; 449 } 450 | WITH RESEND 451 { 452 frame.f_action = HOSTAPD_ACTION_RESEND; 453 } 454 ; 455 456 verbose : /* empty */ 457 | VERBOSE 458 { 459 frame.f_action_flags |= HOSTAPD_ACTION_VERBOSE; 460 } 461 ; 462 463 iapp : TYPE RADIOTAP verbose 464 { 465 frame.f_action = HOSTAPD_ACTION_RADIOTAP; 466 } 467 ; 468 469 nodeopt : DELETE 470 { 471 frame.f_action = HOSTAPD_ACTION_DELNODE; 472 } 473 | ADD 474 { 475 frame.f_action = HOSTAPD_ACTION_ADDNODE; 476 } 477 ; 478 479 frmmatch : ANY 480 | frm frmmatchtype frmmatchdir frmmatchfrom frmmatchto 481 frmmatchbssid frmmatchrtap 482 ; 483 484 frm : /* empty */ 485 | FRAME 486 ; 487 488 frmaction : frmactiontype frmactiondir frmactionfrom frmactionto frmactionbssid 489 ; 490 491 limit : /* empty */ 492 | LIMIT NUMBER SEC 493 { 494 if ($2 < 0 || $2 > LONG_MAX) { 495 yyerror("limit out of range: %lld sec", $2); 496 YYERROR; 497 } 498 frame.f_limit.tv_sec = $2; 499 } 500 | LIMIT NUMBER USEC 501 { 502 if ($2 < 0 || $2 > LONG_MAX) { 503 yyerror("limit out of range: %lld usec", $2); 504 YYERROR; 505 } 506 frame.f_limit.tv_sec = $2 / 1000000; 507 frame.f_limit.tv_usec = $2 % 1000000; 508 } 509 ; 510 511 rate : /* empty */ 512 | RATE NUMBER '/' NUMBER SEC 513 { 514 if (($2 < 1 || $2 > LONG_MAX) || 515 ($4 < 1 || $4 > LONG_MAX)) { 516 yyerror("rate out of range: %lld/%lld sec", 517 $2, $4); 518 YYERROR; 519 } 520 521 if (!($2 && $4)) { 522 yyerror("invalid rate"); 523 YYERROR; 524 } 525 526 frame.f_rate = $2; 527 frame.f_rate_intval = $4; 528 } 529 ; 530 531 frmmatchtype : /* any */ 532 | TYPE ANY 533 | TYPE not DATA 534 { 535 frame_ieee80211->i_fc[0] |= 536 IEEE80211_FC0_TYPE_DATA; 537 HOSTAPD_MATCH(TYPE, $2); 538 } 539 | TYPE not MANAGEMENT frmmatchmgmt 540 { 541 frame_ieee80211->i_fc[0] |= 542 IEEE80211_FC0_TYPE_MGT; 543 HOSTAPD_MATCH(TYPE, $2); 544 } 545 ; 546 547 frmmatchmgmt : /* any */ 548 | SUBTYPE ANY 549 | SUBTYPE not frmsubtype 550 { 551 HOSTAPD_MATCH(SUBTYPE, $2); 552 } 553 ; 554 555 frmsubtype : PROBE REQUEST frmelems 556 { 557 frame_ieee80211->i_fc[0] |= 558 IEEE80211_FC0_SUBTYPE_PROBE_REQ; 559 } 560 | PROBE RESPONSE frmelems 561 { 562 frame_ieee80211->i_fc[0] |= 563 IEEE80211_FC0_SUBTYPE_PROBE_RESP; 564 } 565 | BEACON frmelems 566 { 567 frame_ieee80211->i_fc[0] |= 568 IEEE80211_FC0_SUBTYPE_BEACON; 569 } 570 | ATIM 571 { 572 frame_ieee80211->i_fc[0] |= 573 IEEE80211_FC0_SUBTYPE_ATIM; 574 } 575 | AUTH frmauth 576 { 577 frame_ieee80211->i_fc[0] |= 578 IEEE80211_FC0_SUBTYPE_AUTH; 579 } 580 | DEAUTH frmreason 581 { 582 frame_ieee80211->i_fc[0] |= 583 IEEE80211_FC0_SUBTYPE_DEAUTH; 584 } 585 | ASSOC REQUEST 586 { 587 frame_ieee80211->i_fc[0] |= 588 IEEE80211_FC0_SUBTYPE_ASSOC_REQ; 589 } 590 | DISASSOC frmreason 591 { 592 frame_ieee80211->i_fc[0] |= 593 IEEE80211_FC0_SUBTYPE_DISASSOC; 594 } 595 | ASSOC RESPONSE 596 { 597 frame_ieee80211->i_fc[0] |= 598 IEEE80211_FC0_SUBTYPE_ASSOC_RESP; 599 } 600 | REASSOC REQUEST 601 { 602 frame_ieee80211->i_fc[0] |= 603 IEEE80211_FC0_SUBTYPE_REASSOC_REQ; 604 } 605 | REASSOC RESPONSE 606 { 607 frame_ieee80211->i_fc[0] |= 608 IEEE80211_FC0_SUBTYPE_REASSOC_RESP; 609 } 610 ; 611 612 frmelems : /* empty */ 613 | frmelems_l 614 ; 615 616 frmelems_l : frmelems_l frmelem 617 | frmelem 618 ; 619 620 frmelem : NWID not STRING 621 ; 622 623 frmauth : /* empty */ 624 | authalg 625 { 626 if ((frame_ieee80211->i_data = malloc(6)) == NULL) { 627 yyerror("failed to allocate auth"); 628 YYERROR; 629 } 630 ((u_int16_t *)frame_ieee80211->i_data)[0] = 631 $1.alg; 632 ((u_int16_t *)frame_ieee80211->i_data)[1] = 633 $1.transaction; 634 ((u_int16_t *)frame_ieee80211->i_data)[0] = 0; 635 frame_ieee80211->i_data_len = 6; 636 } 637 ; 638 639 authalg : OPEN REQUEST 640 { 641 $$.alg = htole16(IEEE80211_AUTH_ALG_OPEN); 642 $$.transaction = htole16(IEEE80211_AUTH_OPEN_REQUEST); 643 } 644 | OPEN RESPONSE 645 { 646 $$.alg = htole16(IEEE80211_AUTH_ALG_OPEN); 647 $$.transaction = htole16(IEEE80211_AUTH_OPEN_RESPONSE); 648 } 649 ; 650 651 frmreason : frmreason_l 652 { 653 if ($1 != 0) { 654 if ((frame_ieee80211->i_data = 655 malloc(sizeof(u_int16_t))) == NULL) { 656 yyerror("failed to allocate " 657 "reason code %u", $1); 658 YYERROR; 659 } 660 *(u_int16_t *)frame_ieee80211->i_data = 661 htole16($1); 662 frame_ieee80211->i_data_len = sizeof(u_int16_t); 663 } 664 } 665 ; 666 667 frmreason_l : /* empty */ 668 { 669 $$ = 0; 670 } 671 | REASON UNSPECIFIED 672 { 673 $$ = IEEE80211_REASON_UNSPECIFIED; 674 } 675 | REASON AUTH EXPIRE 676 { 677 $$ = IEEE80211_REASON_AUTH_EXPIRE; 678 } 679 | REASON AUTH LEAVE 680 { 681 $$ = IEEE80211_REASON_AUTH_LEAVE; 682 } 683 | REASON ASSOC EXPIRE 684 { 685 $$ = IEEE80211_REASON_ASSOC_EXPIRE; 686 } 687 | REASON ASSOC TOOMANY 688 { 689 $$ = IEEE80211_REASON_ASSOC_TOOMANY; 690 } 691 | REASON NOT AUTHED 692 { 693 $$ = IEEE80211_REASON_NOT_AUTHED; 694 } 695 | REASON NOT ASSOCED 696 { 697 $$ = IEEE80211_REASON_NOT_ASSOCED; 698 } 699 | REASON ASSOC LEAVE 700 { 701 $$ = IEEE80211_REASON_ASSOC_LEAVE; 702 } 703 | REASON ASSOC NOT AUTHED 704 { 705 $$ = IEEE80211_REASON_NOT_AUTHED; 706 } 707 | REASON RESERVED 708 { 709 $$ = 10; /* XXX unknown */ 710 } 711 | REASON RSN REQUIRED 712 { 713 $$ = IEEE80211_REASON_RSN_REQUIRED; 714 } 715 | REASON RSN INCONSISTENT 716 { 717 $$ = IEEE80211_REASON_RSN_INCONSISTENT; 718 } 719 | REASON IE INVALID 720 { 721 $$ = IEEE80211_REASON_IE_INVALID; 722 } 723 | REASON MIC FAILURE 724 { 725 $$ = IEEE80211_REASON_MIC_FAILURE; 726 } 727 ; 728 729 frmmatchdir : /* any */ 730 | DIR ANY 731 | DIR not frmdir 732 { 733 HOSTAPD_MATCH(DIR, $2); 734 } 735 ; 736 737 frmdir : NO DS 738 { 739 frame_ieee80211->i_fc[1] |= IEEE80211_FC1_DIR_NODS; 740 } 741 | TO DS 742 { 743 frame_ieee80211->i_fc[1] |= IEEE80211_FC1_DIR_TODS; 744 } 745 | FROM DS 746 { 747 frame_ieee80211->i_fc[1] |= IEEE80211_FC1_DIR_FROMDS; 748 } 749 | DS TO DS 750 { 751 frame_ieee80211->i_fc[1] |= IEEE80211_FC1_DIR_DSTODS; 752 } 753 ; 754 755 frmmatchfrom : /* any */ 756 | FROM ANY 757 | FROM not frmmatchaddr 758 { 759 if (($3.flags & HOSTAPD_ACTION_F_OPT_TABLE) == 0) { 760 bcopy($3.lladdr, &frame_ieee80211->i_from, 761 IEEE80211_ADDR_LEN); 762 HOSTAPD_MATCH(FROM, $2); 763 } else { 764 frame.f_from = $3.table; 765 HOSTAPD_MATCH_TABLE(FROM, $2); 766 } 767 } 768 ; 769 770 frmmatchto : /* any */ 771 | TO ANY 772 | TO not frmmatchaddr 773 { 774 if (($3.flags & HOSTAPD_ACTION_F_OPT_TABLE) == 0) { 775 bcopy($3.lladdr, &frame_ieee80211->i_to, 776 IEEE80211_ADDR_LEN); 777 HOSTAPD_MATCH(TO, $2); 778 } else { 779 frame.f_to = $3.table; 780 HOSTAPD_MATCH_TABLE(TO, $2); 781 } 782 } 783 ; 784 785 frmmatchbssid : /* any */ 786 | BSSID ANY 787 | BSSID not frmmatchaddr 788 { 789 if (($3.flags & HOSTAPD_ACTION_F_OPT_TABLE) == 0) { 790 bcopy($3.lladdr, &frame_ieee80211->i_bssid, 791 IEEE80211_ADDR_LEN); 792 HOSTAPD_MATCH(BSSID, $2); 793 } else { 794 frame.f_bssid = $3.table; 795 HOSTAPD_MATCH_TABLE(BSSID, $2); 796 } 797 } 798 ; 799 800 frmmatchrtap : /* empty */ 801 | frmmatchrtap_l 802 ; 803 804 frmmatchrtap_l : frmmatchrtap_l frmmatchrtapopt 805 | frmmatchrtapopt 806 ; 807 808 frmmatchrtapopt : RSSI unaryop percent 809 { 810 if (($2 == HOSTAPD_OP_GT && $3 == 100) || 811 ($2 == HOSTAPD_OP_LE && $3 == 100) || 812 ($2 == HOSTAPD_OP_LT && $3 == 0) || 813 ($2 == HOSTAPD_OP_GE && $3 == 0)) { 814 yyerror("absurd unary comparison"); 815 YYERROR; 816 } 817 818 frame.f_rssi_op = $2; 819 frame.f_rssi = $3; 820 HOSTAPD_MATCH_RADIOTAP(RSSI); 821 } 822 | TXRATE unaryop txrate 823 { 824 frame.f_txrate_op = $2; 825 frame.f_txrate = $3; 826 HOSTAPD_MATCH_RADIOTAP(RATE); 827 } 828 | FREQ unaryop freq 829 { 830 frame.f_chan_op = $2; 831 frame.f_chan = $3; 832 HOSTAPD_MATCH_RADIOTAP(CHANNEL); 833 } 834 ; 835 836 frmmatchaddr : table 837 { 838 if (($$.table = 839 hostapd_table_lookup(&hostapd_cfg, $1)) == NULL) { 840 yyerror("undefined table <%s>", $1); 841 free($1); 842 YYERROR; 843 } 844 $$.flags = HOSTAPD_ACTION_F_OPT_TABLE; 845 free($1); 846 } 847 | lladdr 848 { 849 bcopy($1.lladdr, $$.lladdr, IEEE80211_ADDR_LEN); 850 $$.flags = HOSTAPD_ACTION_F_OPT_LLADDR; 851 } 852 ; 853 854 frmactiontype : TYPE DATA 855 { 856 frame_ieee80211->i_fc[0] |= IEEE80211_FC0_TYPE_DATA; 857 } 858 | TYPE MANAGEMENT frmactionmgmt 859 { 860 frame_ieee80211->i_fc[0] |= IEEE80211_FC0_TYPE_MGT; 861 } 862 ; 863 864 frmactionmgmt : SUBTYPE frmsubtype 865 ; 866 867 frmactiondir : /* empty */ 868 { 869 frame.f_action_data.a_flags |= 870 HOSTAPD_ACTION_F_OPT_DIR_AUTO; 871 } 872 | DIR frmdir 873 ; 874 875 frmactionfrom : FROM frmactionaddr 876 { 877 if (($2.flags & HOSTAPD_ACTION_F_REF_M) == 0) { 878 bcopy($2.lladdr, frame_ieee80211->i_from, 879 IEEE80211_ADDR_LEN); 880 } else 881 frame.f_action_data.a_flags |= 882 ($2.flags << HOSTAPD_ACTION_F_REF_FROM_S); 883 } 884 ; 885 886 frmactionto : TO frmactionaddr 887 { 888 if (($2.flags & HOSTAPD_ACTION_F_REF_M) == 0) { 889 bcopy($2.lladdr, frame_ieee80211->i_to, 890 IEEE80211_ADDR_LEN); 891 } else 892 frame.f_action_data.a_flags |= 893 ($2.flags << HOSTAPD_ACTION_F_REF_TO_S); 894 } 895 ; 896 897 frmactionbssid : BSSID frmactionaddr 898 { 899 if (($2.flags & HOSTAPD_ACTION_F_REF_M) == 0) { 900 bcopy($2.lladdr, frame_ieee80211->i_bssid, 901 IEEE80211_ADDR_LEN); 902 } else 903 frame.f_action_data.a_flags |= 904 ($2.flags << HOSTAPD_ACTION_F_REF_BSSID_S); 905 } 906 ; 907 908 frmactionaddr : lladdr 909 { 910 bcopy($1.lladdr, $$.lladdr, IEEE80211_ADDR_LEN); 911 $$.flags = $1.flags; 912 } 913 | randaddr 914 { 915 $$.flags = $1.flags; 916 } 917 | refaddr 918 { 919 $$.flags = $1.flags; 920 } 921 ; 922 923 table : '<' STRING '>' { 924 if (strlen($2) >= HOSTAPD_TABLE_NAMELEN) { 925 yyerror("table name %s too long, max %u", 926 $2, HOSTAPD_TABLE_NAMELEN - 1); 927 free($2); 928 YYERROR; 929 } 930 $$ = $2; 931 } 932 ; 933 934 tabledef : TABLE table { 935 if ((table = 936 hostapd_table_add(&hostapd_cfg, $2)) == NULL) { 937 yyerror("failed to add table: %s", $2); 938 free($2); 939 YYERROR; 940 } 941 free($2); 942 } tableopts { 943 table = NULL; 944 } 945 ; 946 947 tableopts : /* empty */ 948 | tableopts_l 949 ; 950 951 tableopts_l : tableopts_l tableopt 952 | tableopt 953 ; 954 955 tableopt : CONST { 956 if (table->t_flags & HOSTAPD_TABLE_F_CONST) { 957 yyerror("option already specified"); 958 YYERROR; 959 } 960 table->t_flags |= HOSTAPD_TABLE_F_CONST; 961 } 962 | '{' optnl '}' 963 | '{' optnl tableaddrlist optnl '}' 964 ; 965 966 string : string STRING 967 { 968 if (asprintf(&$$, "%s %s", $1, $2) == -1) 969 hostapd_fatal("string: asprintf"); 970 free($1); 971 free($2); 972 } 973 | STRING 974 ; 975 976 varset : STRING '=' string 977 { 978 char *s = $1; 979 while (*s++) { 980 if (isspace((unsigned char)*s)) { 981 yyerror("macro name cannot contain " 982 "whitespace"); 983 free($1); 984 free($3); 985 YYERROR; 986 } 987 } 988 if (symset($1, $3, 0) == -1) 989 hostapd_fatal("cannot store variable"); 990 free($1); 991 free($3); 992 } 993 ; 994 995 refaddr : '&' FROM 996 { 997 $$.flags |= HOSTAPD_ACTION_F_REF_FROM; 998 } 999 | '&' TO 1000 { 1001 $$.flags |= HOSTAPD_ACTION_F_REF_TO; 1002 } 1003 | '&' BSSID 1004 { 1005 $$.flags |= HOSTAPD_ACTION_F_REF_BSSID; 1006 } 1007 ; 1008 1009 tableaddrlist : tableaddrentry 1010 | tableaddrlist comma tableaddrentry 1011 ; 1012 1013 tableaddrentry : lladdr 1014 { 1015 if ((entry = hostapd_entry_add(table, 1016 $1.lladdr)) == NULL) { 1017 yyerror("failed to add entry: %s", 1018 etheraddr_string($1.lladdr)); 1019 YYERROR; 1020 } 1021 } tableaddropt { 1022 entry = NULL; 1023 } 1024 ; 1025 1026 tableaddropt : /* empty */ 1027 | assign ipv4addr ipv4netmask 1028 { 1029 entry->e_flags |= HOSTAPD_ENTRY_F_INADDR; 1030 entry->e_inaddr.in_af = AF_INET; 1031 bcopy(&$2, &entry->e_inaddr.in_v4, 1032 sizeof(struct in_addr)); 1033 } 1034 | mask lladdr 1035 { 1036 entry->e_flags |= HOSTAPD_ENTRY_F_MASK; 1037 bcopy($2.lladdr, entry->e_mask, IEEE80211_ADDR_LEN); 1038 1039 /* Update entry position in the table */ 1040 hostapd_entry_update(table, entry); 1041 } 1042 ; 1043 1044 ipv4addr : STRING 1045 { 1046 if (inet_net_pton(AF_INET, $1, &$$, sizeof($$)) == -1) { 1047 yyerror("invalid address: %s\n", $1); 1048 free($1); 1049 YYERROR; 1050 } 1051 free($1); 1052 } 1053 ; 1054 1055 ipv4netmask : /* empty */ 1056 { 1057 entry->e_inaddr.in_netmask = -1; 1058 } 1059 | '/' NUMBER 1060 { 1061 if ($2 < 0 || $2 > 32) { 1062 yyerror("netmask out of range: %lld", $2); 1063 YYERROR; 1064 } 1065 entry->e_inaddr.in_netmask = $2; 1066 } 1067 ; 1068 1069 lladdr : STRING 1070 { 1071 struct ether_addr *ea; 1072 1073 if ((ea = ether_aton($1)) == NULL) { 1074 yyerror("invalid address: %s\n", $1); 1075 free($1); 1076 YYERROR; 1077 } 1078 free($1); 1079 1080 bcopy(ea, $$.lladdr, IEEE80211_ADDR_LEN); 1081 $$.flags = HOSTAPD_ACTION_F_OPT_LLADDR; 1082 } 1083 ; 1084 1085 randaddr : RANDOM 1086 { 1087 $$.flags |= HOSTAPD_ACTION_F_REF_RANDOM; 1088 } 1089 ; 1090 1091 passive : /* empty */ 1092 | PASSIVE 1093 { 1094 hostapd_cfg.c_flags |= HOSTAPD_CFG_F_IAPP_PASSIVE; 1095 } 1096 ; 1097 1098 assign : ARROW 1099 ; 1100 1101 mask : '&' 1102 ; 1103 1104 comma : /* emtpy */ 1105 | ',' optnl 1106 ; 1107 1108 optnl : /* empty */ 1109 | '\n' 1110 ; 1111 1112 not : /* empty */ 1113 { 1114 $$ = 0; 1115 } 1116 | '!' 1117 { 1118 $$ = 1; 1119 } 1120 | NOT 1121 { 1122 $$ = 1; 1123 } 1124 ; 1125 1126 unaryop : /* any */ 1127 { 1128 $$ = HOSTAPD_OP_EQ; 1129 } 1130 | '=' 1131 { 1132 $$ = HOSTAPD_OP_EQ; 1133 } 1134 | '==' 1135 { 1136 $$ = HOSTAPD_OP_EQ; 1137 } 1138 | '!' 1139 { 1140 $$ = HOSTAPD_OP_NE; 1141 } 1142 | NE 1143 { 1144 $$ = HOSTAPD_OP_NE; 1145 } 1146 | LE 1147 { 1148 $$ = HOSTAPD_OP_LE; 1149 } 1150 | '<' 1151 { 1152 $$ = HOSTAPD_OP_LT; 1153 } 1154 | GE 1155 { 1156 $$ = HOSTAPD_OP_GE; 1157 } 1158 | '>' 1159 { 1160 $$ = HOSTAPD_OP_GT; 1161 } 1162 ; 1163 1164 percent : STRING 1165 { 1166 double val; 1167 char *cp; 1168 1169 val = strtod($1, &cp); 1170 if (cp == NULL || strcmp(cp, "%") != 0 || 1171 val < 0 || val > 100) { 1172 yyerror("invalid percentage: %s", $1); 1173 free($1); 1174 YYERROR; 1175 } 1176 free($1); 1177 $$ = val; 1178 } 1179 ; 1180 1181 txrate : STRING 1182 { 1183 double val; 1184 char *cp; 1185 1186 val = strtod($1, &cp) * 2; 1187 if (cp == NULL || strcasecmp(cp, "mb") != 0 || 1188 val != (int)val) { 1189 yyerror("invalid rate: %s", $1); 1190 free($1); 1191 YYERROR; 1192 } 1193 free($1); 1194 $$ = val; 1195 } 1196 ; 1197 1198 freq : STRING 1199 { 1200 double val; 1201 char *cp; 1202 1203 val = strtod($1, &cp); 1204 if (cp != NULL) { 1205 if (strcasecmp(cp, "ghz") == 0) { 1206 $$ = val * 1000; 1207 } else if (strcasecmp(cp, "mhz") == 0) { 1208 $$ = val; 1209 } else 1210 cp = NULL; 1211 } 1212 if (cp == NULL) { 1213 yyerror("invalid frequency: %s", $1); 1214 free($1); 1215 YYERROR; 1216 } 1217 free($1); 1218 } 1219 ; 1220 1221 timeout : NUMBER 1222 { 1223 if ($1 < 1 || $1 > LONG_MAX) { 1224 yyerror("timeout out of range: %lld", $1); 1225 YYERROR; 1226 } 1227 $$.tv_sec = $1 / 1000; 1228 $$.tv_usec = ($1 % 1000) * 1000; 1229 } 1230 ; 1231 %% 1232 1233 /* 1234 * Parser and lexer 1235 */ 1236 1237 struct keywords { 1238 char *k_name; 1239 int k_val; 1240 }; 1241 1242 int 1243 kw_cmp(const void *a, const void *b) 1244 { 1245 return strcmp(a, ((const struct keywords *)b)->k_name); 1246 } 1247 1248 int 1249 lookup(char *token) 1250 { 1251 /* Keep this list sorted */ 1252 static const struct keywords keywords[] = { 1253 { "add", ADD }, 1254 { "address", ADDRESS }, 1255 { "any", ANY }, 1256 { "assoc", ASSOC }, 1257 { "assoced", ASSOCED }, 1258 { "atim", ATIM }, 1259 { "auth", AUTH }, 1260 { "authed", AUTHED }, 1261 { "beacon", BEACON }, 1262 { "broadcast", BROADCAST }, 1263 { "bssid", BSSID }, 1264 { "const", CONST }, 1265 { "data", DATA }, 1266 { "deauth", DEAUTH }, 1267 { "delay", DELAY }, 1268 { "delete", DELETE }, 1269 { "dir", DIR }, 1270 { "disassoc", DISASSOC }, 1271 { "ds", DS }, 1272 { "expire", EXPIRE }, 1273 { "failure", FAILURE }, 1274 { "frame", FRAME }, 1275 { "freq", FREQ }, 1276 { "from", FROM }, 1277 { "handle", HANDLE }, 1278 { "hopper", HOPPER }, 1279 { "hostap", HOSTAP }, 1280 { "iapp", IAPP }, 1281 { "ie", IE }, 1282 { "include", INCLUDE }, 1283 { "inconsistent", INCONSISTENT }, 1284 { "interface", INTERFACE }, 1285 { "invalid", INVALID }, 1286 { "leave", LEAVE }, 1287 { "limit", LIMIT }, 1288 { "log", LOG }, 1289 { "management", MANAGEMENT }, 1290 { "mic", MIC }, 1291 { "mode", MODE }, 1292 { "multicast", MULTICAST }, 1293 { "no", NO }, 1294 { "node", NODE }, 1295 { "not", NOT }, 1296 { "notify", NOTIFY }, 1297 { "nwid", NWID }, 1298 { "on", ON }, 1299 { "open", OPEN }, 1300 { "passive", PASSIVE }, 1301 { "pcap", PCAP }, 1302 { "port", PORT }, 1303 { "probe", PROBE }, 1304 { "quick", QUICK }, 1305 { "radiotap", RADIOTAP }, 1306 { "random", RANDOM }, 1307 { "rate", RATE }, 1308 { "reason", REASON }, 1309 { "reassoc", REASSOC }, 1310 { "request", REQUEST }, 1311 { "required", REQUIRED }, 1312 { "resend", RESEND }, 1313 { "reserved", RESERVED }, 1314 { "response", RESPONSE }, 1315 { "roaming", ROAMING }, 1316 { "route", ROUTE }, 1317 { "rsn", RSN }, 1318 { "sec", SEC }, 1319 { "set", SET }, 1320 { "signal", RSSI }, 1321 { "skip", SKIP }, 1322 { "subtype", SUBTYPE }, 1323 { "table", TABLE }, 1324 { "to", TO }, 1325 { "toomany", TOOMANY }, 1326 { "ttl", TTL }, 1327 { "txrate", TXRATE }, 1328 { "type", TYPE }, 1329 { "unspecified", UNSPECIFIED }, 1330 { "usec", USEC }, 1331 { "verbose", VERBOSE }, 1332 { "with", WITH } 1333 }; 1334 const struct keywords *p; 1335 1336 p = bsearch(token, keywords, sizeof(keywords) / sizeof(keywords[0]), 1337 sizeof(keywords[0]), kw_cmp); 1338 1339 return (p == NULL ? STRING : p->k_val); 1340 } 1341 1342 #define START_EXPAND 1 1343 #define DONE_EXPAND 2 1344 1345 static int expanding; 1346 1347 int 1348 igetc(void) 1349 { 1350 int c; 1351 1352 while (1) { 1353 if (file->ungetpos > 0) 1354 c = file->ungetbuf[--file->ungetpos]; 1355 else 1356 c = getc(file->stream); 1357 1358 if (c == START_EXPAND) 1359 expanding = 1; 1360 else if (c == DONE_EXPAND) 1361 expanding = 0; 1362 else 1363 break; 1364 } 1365 return (c); 1366 } 1367 1368 int 1369 lgetc(int quotec) 1370 { 1371 int c, next; 1372 1373 if (quotec) { 1374 if ((c = igetc()) == EOF) { 1375 yyerror("reached end of file while parsing " 1376 "quoted string"); 1377 if (file == topfile || popfile() == EOF) 1378 return (EOF); 1379 return (quotec); 1380 } 1381 return (c); 1382 } 1383 1384 while ((c = igetc()) == '\\') { 1385 next = igetc(); 1386 if (next != '\n') { 1387 c = next; 1388 break; 1389 } 1390 yylval.lineno = file->lineno; 1391 file->lineno++; 1392 } 1393 1394 if (c == EOF) { 1395 /* 1396 * Fake EOL when hit EOF for the first time. This gets line 1397 * count right if last line in included file is syntactically 1398 * invalid and has no newline. 1399 */ 1400 if (file->eof_reached == 0) { 1401 file->eof_reached = 1; 1402 return ('\n'); 1403 } 1404 while (c == EOF) { 1405 if (file == topfile || popfile() == EOF) 1406 return (EOF); 1407 c = igetc(); 1408 } 1409 } 1410 return (c); 1411 } 1412 1413 void 1414 lungetc(int c) 1415 { 1416 if (c == EOF) 1417 return; 1418 1419 if (file->ungetpos >= file->ungetsize) { 1420 void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); 1421 if (p == NULL) 1422 err(1, "%s", __func__); 1423 file->ungetbuf = p; 1424 file->ungetsize *= 2; 1425 } 1426 file->ungetbuf[file->ungetpos++] = c; 1427 } 1428 1429 int 1430 findeol(void) 1431 { 1432 int c; 1433 1434 /* skip to either EOF or the first real EOL */ 1435 while (1) { 1436 c = lgetc(0); 1437 if (c == '\n') { 1438 file->lineno++; 1439 break; 1440 } 1441 if (c == EOF) 1442 break; 1443 } 1444 return (ERROR); 1445 } 1446 1447 int 1448 yylex(void) 1449 { 1450 u_char buf[8096]; 1451 u_char *p, *val; 1452 int quotec, next, c; 1453 int token; 1454 1455 top: 1456 p = buf; 1457 while ((c = lgetc(0)) == ' ' || c == '\t') 1458 ; /* nothing */ 1459 1460 yylval.lineno = file->lineno; 1461 if (c == '#') 1462 while ((c = lgetc(0)) != '\n' && c != EOF) 1463 ; /* nothing */ 1464 if (c == '$' && !expanding) { 1465 while (1) { 1466 if ((c = lgetc(0)) == EOF) 1467 return (0); 1468 1469 if (p + 1 >= buf + sizeof(buf) - 1) { 1470 yyerror("string too long"); 1471 return (findeol()); 1472 } 1473 if (isalnum(c) || c == '_') { 1474 *p++ = c; 1475 continue; 1476 } 1477 *p = '\0'; 1478 lungetc(c); 1479 break; 1480 } 1481 val = symget(buf); 1482 if (val == NULL) { 1483 yyerror("macro \"%s\" not defined", buf); 1484 return (findeol()); 1485 } 1486 p = val + strlen(val) - 1; 1487 lungetc(DONE_EXPAND); 1488 while (p >= val) { 1489 lungetc(*p); 1490 p--; 1491 } 1492 lungetc(START_EXPAND); 1493 goto top; 1494 } 1495 1496 switch (c) { 1497 case '\'': 1498 case '"': 1499 quotec = c; 1500 while (1) { 1501 if ((c = lgetc(quotec)) == EOF) 1502 return (0); 1503 if (c == '\n') { 1504 file->lineno++; 1505 continue; 1506 } else if (c == '\\') { 1507 if ((next = lgetc(quotec)) == EOF) 1508 return (0); 1509 if (next == quotec || next == ' ' || 1510 next == '\t') 1511 c = next; 1512 else if (next == '\n') { 1513 file->lineno++; 1514 continue; 1515 } else 1516 lungetc(next); 1517 } else if (c == quotec) { 1518 *p = '\0'; 1519 break; 1520 } else if (c == '\0') { 1521 yyerror("syntax error"); 1522 return (findeol()); 1523 } 1524 if (p + 1 >= buf + sizeof(buf) - 1) { 1525 yyerror("string too long"); 1526 return (findeol()); 1527 } 1528 *p++ = c; 1529 } 1530 yylval.v.string = strdup(buf); 1531 if (yylval.v.string == NULL) 1532 hostapd_fatal("yylex: strdup"); 1533 return (STRING); 1534 case '-': 1535 next = lgetc(0); 1536 if (next == '>') 1537 return (ARROW); 1538 lungetc(next); 1539 break; 1540 case '!': 1541 next = lgetc(0); 1542 if (next == '=') 1543 return (NE); 1544 lungetc(next); 1545 break; 1546 case '<': 1547 next = lgetc(0); 1548 if (next == '=') 1549 return (LE); 1550 lungetc(next); 1551 break; 1552 case '>': 1553 next = lgetc(0); 1554 if (next == '=') 1555 return (GE); 1556 lungetc(next); 1557 break; 1558 } 1559 1560 #define allowed_to_end_number(x) \ 1561 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 1562 1563 if (c == '-' || isdigit(c)) { 1564 do { 1565 *p++ = c; 1566 if ((size_t)(p-buf) >= sizeof(buf)) { 1567 yyerror("string too long"); 1568 return (findeol()); 1569 } 1570 } while ((c = lgetc(0)) != EOF && isdigit(c)); 1571 lungetc(c); 1572 if (p == buf + 1 && buf[0] == '-') 1573 goto nodigits; 1574 if (c == EOF || allowed_to_end_number(c)) { 1575 const char *errstr = NULL; 1576 1577 *p = '\0'; 1578 yylval.v.number = strtonum(buf, LLONG_MIN, 1579 LLONG_MAX, &errstr); 1580 if (errstr) { 1581 yyerror("\"%s\" invalid number: %s", 1582 buf, errstr); 1583 return (findeol()); 1584 } 1585 return (NUMBER); 1586 } else { 1587 nodigits: 1588 while (p > buf + 1) 1589 lungetc(*--p); 1590 c = *--p; 1591 if (c == '-') 1592 return (c); 1593 } 1594 } 1595 1596 #define allowed_in_string(x) \ 1597 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 1598 x != '{' && x != '}' && x != '<' && x != '>' && \ 1599 x != '!' && x != '=' && x != '/' && x != '#' && \ 1600 x != ',')) 1601 1602 if (isalnum(c) || c == ':' || c == '_' || c == '*') { 1603 do { 1604 *p++ = c; 1605 if ((size_t)(p-buf) >= sizeof(buf)) { 1606 yyerror("string too long"); 1607 return (findeol()); 1608 } 1609 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 1610 lungetc(c); 1611 *p = '\0'; 1612 if ((token = lookup(buf)) == STRING) 1613 if ((yylval.v.string = strdup(buf)) == NULL) 1614 hostapd_fatal("yylex: strdup"); 1615 return (token); 1616 } 1617 if (c == '\n') { 1618 yylval.lineno = file->lineno; 1619 file->lineno++; 1620 } 1621 if (c == EOF) 1622 return (0); 1623 return (c); 1624 } 1625 1626 int 1627 symset(const char *nam, const char *val, int persist) 1628 { 1629 struct sym *sym; 1630 1631 TAILQ_FOREACH(sym, &symhead, entry) { 1632 if (strcmp(nam, sym->nam) == 0) 1633 break; 1634 } 1635 1636 if (sym != NULL) { 1637 if (sym->persist == 1) 1638 return (0); 1639 else { 1640 free(sym->nam); 1641 free(sym->val); 1642 TAILQ_REMOVE(&symhead, sym, entry); 1643 free(sym); 1644 } 1645 } 1646 if ((sym = calloc(1, sizeof(*sym))) == NULL) 1647 return (-1); 1648 1649 sym->nam = strdup(nam); 1650 if (sym->nam == NULL) { 1651 free(sym); 1652 return (-1); 1653 } 1654 sym->val = strdup(val); 1655 if (sym->val == NULL) { 1656 free(sym->nam); 1657 free(sym); 1658 return (-1); 1659 } 1660 sym->used = 0; 1661 sym->persist = persist; 1662 TAILQ_INSERT_TAIL(&symhead, sym, entry); 1663 1664 hostapd_log(HOSTAPD_LOG_DEBUG, "%s = \"%s\"", sym->nam, sym->val); 1665 1666 return (0); 1667 } 1668 1669 int 1670 hostapd_parse_symset(char *s) 1671 { 1672 char *sym, *val; 1673 int ret; 1674 size_t len; 1675 1676 if ((val = strrchr(s, '=')) == NULL) 1677 return (-1); 1678 1679 len = strlen(s) - strlen(val) + 1; 1680 if ((sym = malloc(len)) == NULL) 1681 hostapd_fatal("cmdline_symset: malloc"); 1682 1683 (void)strlcpy(sym, s, len); 1684 1685 ret = symset(sym, val + 1, 1); 1686 1687 free(sym); 1688 1689 return (ret); 1690 } 1691 1692 char * 1693 symget(const char *nam) 1694 { 1695 struct sym *sym; 1696 1697 TAILQ_FOREACH(sym, &symhead, entry) { 1698 if (strcmp(nam, sym->nam) == 0) { 1699 sym->used = 1; 1700 return (sym->val); 1701 } 1702 } 1703 return (NULL); 1704 } 1705 1706 int 1707 check_file_secrecy(int fd, const char *fname) 1708 { 1709 struct stat st; 1710 1711 if (fstat(fd, &st)) { 1712 warn("cannot stat %s", fname); 1713 return (-1); 1714 } 1715 if (st.st_uid != 0 && st.st_uid != getuid()) { 1716 warnx("%s: owner not root or current user", fname); 1717 return (-1); 1718 } 1719 if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) { 1720 warnx("%s: group writable or world read/writable", fname); 1721 return (-1); 1722 } 1723 return (0); 1724 } 1725 1726 struct file * 1727 pushfile(const char *name, int secret) 1728 { 1729 struct file *nfile; 1730 1731 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 1732 warn("%s", __func__); 1733 return (NULL); 1734 } 1735 if ((nfile->name = strdup(name)) == NULL) { 1736 warn("%s", __func__); 1737 free(nfile); 1738 return (NULL); 1739 } 1740 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 1741 warn("%s: %s", __func__, nfile->name); 1742 free(nfile->name); 1743 free(nfile); 1744 return (NULL); 1745 } else if (secret && 1746 check_file_secrecy(fileno(nfile->stream), nfile->name)) { 1747 fclose(nfile->stream); 1748 free(nfile->name); 1749 free(nfile); 1750 return (NULL); 1751 } 1752 nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0; 1753 nfile->ungetsize = 16; 1754 nfile->ungetbuf = malloc(nfile->ungetsize); 1755 if (nfile->ungetbuf == NULL) { 1756 warn("%s", __func__); 1757 fclose(nfile->stream); 1758 free(nfile->name); 1759 free(nfile); 1760 return (NULL); 1761 } 1762 TAILQ_INSERT_TAIL(&files, nfile, entry); 1763 return (nfile); 1764 } 1765 1766 int 1767 popfile(void) 1768 { 1769 struct file *prev; 1770 1771 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 1772 prev->errors += file->errors; 1773 1774 TAILQ_REMOVE(&files, file, entry); 1775 fclose(file->stream); 1776 free(file->name); 1777 free(file->ungetbuf); 1778 free(file); 1779 file = prev; 1780 return (file ? 0 : EOF); 1781 } 1782 1783 int 1784 hostapd_parse_file(struct hostapd_config *cfg) 1785 { 1786 struct sym *sym, *next; 1787 int errors = 0; 1788 int ret; 1789 1790 if ((file = pushfile(cfg->c_config, 1)) == NULL) 1791 hostapd_fatal("failed to open the main config file: %s\n", 1792 cfg->c_config); 1793 topfile = file; 1794 1795 /* Init tables and data structures */ 1796 TAILQ_INIT(&cfg->c_apmes); 1797 TAILQ_INIT(&cfg->c_tables); 1798 TAILQ_INIT(&cfg->c_frames); 1799 cfg->c_iapp.i_multicast.sin_addr.s_addr = INADDR_ANY; 1800 cfg->c_iapp.i_flags = HOSTAPD_IAPP_F_DEFAULT; 1801 cfg->c_iapp.i_ttl = IP_DEFAULT_MULTICAST_TTL; 1802 cfg->c_apme_hopdelay.tv_sec = HOSTAPD_HOPPER_MDELAY / 1000; 1803 cfg->c_apme_hopdelay.tv_usec = (HOSTAPD_HOPPER_MDELAY % 1000) * 1000; 1804 1805 ret = yyparse(); 1806 errors = file->errors; 1807 popfile(); 1808 1809 /* Free macros and check which have not been used. */ 1810 TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) { 1811 if (!sym->used) 1812 hostapd_log(HOSTAPD_LOG_VERBOSE, 1813 "warning: macro '%s' not used", sym->nam); 1814 if (!sym->persist) { 1815 free(sym->nam); 1816 free(sym->val); 1817 TAILQ_REMOVE(&symhead, sym, entry); 1818 free(sym); 1819 } 1820 } 1821 1822 return (errors ? EINVAL : ret); 1823 } 1824 1825 int 1826 yyerror(const char *fmt, ...) 1827 { 1828 va_list ap; 1829 char *msg; 1830 1831 file->errors++; 1832 1833 va_start(ap, fmt); 1834 if (vasprintf(&msg, fmt, ap) == -1) 1835 hostapd_fatal("yyerror vasprintf"); 1836 va_end(ap); 1837 fprintf(stderr, "%s:%d: %s\n", file->name, yylval.lineno, msg); 1838 fflush(stderr); 1839 free(msg); 1840 1841 return (0); 1842 } 1843