1 /* $OpenBSD: parse.y,v 1.63 2022/12/28 21:30:16 jmc 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 : /* empty */
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
kw_cmp(const void * a,const void * b)1243 kw_cmp(const void *a, const void *b)
1244 {
1245 return strcmp(a, ((const struct keywords *)b)->k_name);
1246 }
1247
1248 int
lookup(char * token)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
igetc(void)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
lgetc(int quotec)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
lungetc(int c)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
findeol(void)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
yylex(void)1448 yylex(void)
1449 {
1450 char buf[8096];
1451 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((unsigned char)*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((unsigned char)*--p);
1590 c = (unsigned char)*--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
symset(const char * nam,const char * val,int persist)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
hostapd_parse_symset(char * s)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 *
symget(const char * nam)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
check_file_secrecy(int fd,const char * fname)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 *
pushfile(const char * name,int secret)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
popfile(void)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
hostapd_parse_file(struct hostapd_config * cfg)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
yyerror(const char * fmt,...)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