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