xref: /openbsd/usr.sbin/hostapd/parse.y (revision 3a50f0a9)
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