xref: /netbsd/usr.sbin/pf/pfs/parse.y (revision 321a73e4)
1 /* $NetBSD: parse.y,v 1.4 2023/03/17 17:12:54 andvar Exp $ */
2 
3 /*-
4  * Copyright (c) 2010 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 %{
30 #include <sys/cdefs.h>
31 
32 #ifndef lint
33 __RCSID("$NetBSD: parse.y,v 1.4 2023/03/17 17:12:54 andvar Exp $");
34 #endif
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <stdint.h>
40 #include <stdbool.h>
41 #include <inttypes.h>
42 #include <errno.h>
43 
44 #include <net/if.h>
45 #include <netinet/in.h>
46 #include <net/pfvar.h>
47 #include <arpa/inet.h>
48 #include <netdb.h>
49 #include <netinet/tcp_fsm.h>
50 
51 #include "parser.h"
52 
53 int lineno;
54 
55 struct pfioc_states* states;
56 size_t allocated;
57 
58 // XXX it is really correct ?
59 extern const char * const tcpstates[];
60 
61 
62 struct pfsync_state global_state;
63 struct pfsync_state_peer *src_peer, *dst_peer;
64 struct pfsync_state_peer current_peer;
65 
66 static void parse_init(void);
67 static void add_state(void);
68 static bool get_pfsync_host(const char*, struct pfsync_state_host*, sa_family_t*);
69 static uint8_t retrieve_peer_state(const char*, int);
70 static bool retrieve_seq(const char*, struct pfsync_state_peer*);
71 static bool strtou32(const char*, uint32_t*);
72 
73 %}
74 
75 %union {
76 	uintmax_t num;
77 	char* str;
78 }
79 
80 %token STATE
81 %token IN OUT
82 %token ON PROTO
83 %token FROM TO USING
84 %token ID CID EXPIRE TIMEOUT
85 %token SRC DST
86 %token SEQ  MAX_WIN WSCALE MSS
87 %token NOSCRUB SCRUB FLAGS TTL MODE
88 %token NUMBER STRING
89 
90 %type <str> STRING
91 %type <num> NUMBER
92 %%
93 
94 states
95 	: /* NOTHING */
96 	| state states  { parse_init(); }
97 	;
98 
99 state
100 	: STATE direction iface proto addrs id cid expire timeout src_peer dst_peer {
101 			add_state();
102 		}
103 	;
104 
105 direction
106 	: IN {
107 		   global_state.direction = PF_IN;
108 		   src_peer = &global_state.dst;
109 		   dst_peer = &global_state.src;
110 		}
111 	| OUT {
112 			 global_state.direction = PF_OUT;
113 			 src_peer = &global_state.src;
114 			 dst_peer = &global_state.dst;
115 		}
116 	;
117 
118 iface
119 	: ON STRING {
120 			strlcpy(global_state.ifname, $2, sizeof(global_state.ifname));
121 			free($2);
122 		}
123 	;
124 
125 proto
126 	: PROTO STRING {
127 			struct protoent *p;
128 			p = getprotobyname($2);
129 			if (p == NULL)
130 				yyfatal("Invalid protocol name");
131 			global_state.proto = p->p_proto;
132 			free($2);
133 			}
134 	| PROTO NUMBER {
135 			// check that the number may be valid proto ?
136 			global_state.proto = $2;
137 			}
138 	;
139 
140 addrs
141 	: FROM STRING TO STRING {
142 		get_pfsync_host($2, &global_state.lan, &global_state.af);
143 		get_pfsync_host($4, &global_state.ext, &global_state.af);
144 		memcpy(&global_state.gwy, &global_state.lan, sizeof(struct pfsync_state_host));
145 		free($2);
146 		free($4);
147 		}
148 	| FROM STRING TO STRING USING STRING {
149 		get_pfsync_host($2, &global_state.lan, &global_state.af);
150 		get_pfsync_host($4, &global_state.ext, &global_state.af);
151 		get_pfsync_host($6, &global_state.gwy, &global_state.af);
152 		free($2);
153 		free($4);
154 		free($6);
155 		}
156 	;
157 
158 id
159 	: ID NUMBER {
160 			if ( $2 > UINT64_MAX)
161 				yyfatal("id is too big");
162 			uint64_t value = (uint64_t)$2;
163 			memcpy(global_state.id, &value, sizeof(global_state.id));
164 		}
165 	;
166 
167 cid
168 	: CID NUMBER {
169 			if ( $2 > UINT32_MAX)
170 				yyfatal("creator id is too big");
171 			global_state.creatorid = (uint32_t)$2;
172 		}
173 	;
174 
175 expire
176 	: EXPIRE NUMBER {
177 			if ( $2 > UINT32_MAX)
178 				yyfatal("expire time is too big");
179 			global_state.expire = (uint32_t) $2;
180 		}
181 	;
182 
183 timeout
184 	: TIMEOUT NUMBER {
185 			if ($2 > UINT8_MAX)
186 				yyfatal("timeout time is too big");
187 			global_state.timeout = (uint8_t) $2;
188 		}
189 	;
190 
191 src_peer
192 	: SRC peer {
193 			memcpy(src_peer, &current_peer, sizeof(current_peer));
194 		}
195 	;
196 
197 dst_peer
198 	: DST peer {
199 			memcpy(dst_peer, &current_peer, sizeof(current_peer));
200 		}
201 	;
202 
203 peer
204 	: peer_state scrub
205 	| peer_state tcp_options scrub
206 	;
207 
208 peer_state
209 	: STATE STRING {
210 			current_peer.state = retrieve_peer_state($2, global_state.proto);
211 			free($2);
212 		}
213 	| STATE	NUMBER {
214 		if ( $2 > UINT8_MAX)
215 			yyfatal("peer state is too big");
216 		current_peer.state = $2;
217 		}
218 	;
219 
220 tcp_options
221 	: SEQ seqs MAX_WIN NUMBER WSCALE NUMBER {
222 			if ($4 > UINT16_MAX)
223 				yyfatal("max_win is too big");
224 			current_peer.max_win = $4;
225 
226 			if ($6 > UINT8_MAX)
227 				yyfatal("wscale is too big");
228 			current_peer.wscale = $6;
229 		}
230 	| SEQ seqs MAX_WIN NUMBER WSCALE NUMBER MSS NUMBER {
231 			if ($4 > UINT16_MAX)
232 				yyfatal("max_win is too big");
233 			current_peer.max_win = $4;
234 
235 			if ($6 > UINT8_MAX)
236 				yyfatal("wscale is too big");
237 			current_peer.wscale = $6;
238 
239 			if ($8 > UINT16_MAX)
240 				yyfatal("mss is too big");
241 			current_peer.mss = $8;
242 		}
243 	;
244 
245 seqs
246 	: STRING {
247 		if (!retrieve_seq($1, &current_peer))
248 			yyfatal("invalid seq number");
249 
250 		free($1);
251 		}
252 	;
253 
254 scrub
255 	: NOSCRUB { current_peer.scrub.scrub_flag= 0;}
256 	| SCRUB FLAGS NUMBER MODE NUMBER TTL NUMBER {
257 			current_peer.scrub.scrub_flag= PFSYNC_SCRUB_FLAG_VALID;
258 			if ($3 > UINT16_MAX)
259 				yyfatal("scrub flags is too big");
260 			current_peer.scrub.pfss_flags = $3;
261 
262 			if ($5 > UINT32_MAX)
263 				yyfatal("scrub mode is too big");
264 			current_peer.scrub.pfss_ts_mod = $5;
265 
266 			if ($7 > UINT8_MAX)
267 				yyfatal("scrub ttl is too big");
268 			current_peer.scrub.pfss_ttl = $7;
269 		}
270 	;
271 
272 
273 %%
274 
275 static void
276 parse_init(void)
277 {
278 	memset(&global_state, 0, sizeof(global_state));
279 	memset(&current_peer, 0, sizeof(current_peer));
280 	src_peer = NULL;
281 	dst_peer = NULL;
282 }
283 
284 static bool
get_pfsync_host(const char * str,struct pfsync_state_host * host,sa_family_t * af)285 get_pfsync_host(const char* str, struct pfsync_state_host* host, sa_family_t* af)
286 {
287 	size_t count_colon, addr_len, port_len;
288 	const char* p, *last_colon, *first_bracket, *last_bracket;
289 	char buf[48];
290 	char buf_port[6];
291 
292 	if (str == NULL || *str == '\0')
293 		return false;
294 
295 	p = str;
296 	last_colon = NULL;
297 	count_colon = 0;
298 
299 	while (*p != '\0') {
300 		if (*p == ':') {
301 			count_colon++;
302 			last_colon = p;
303 		}
304 		p++;
305 	}
306 
307 	/*
308 	 * If no colon, it is not an expected addr
309 	 * If there are more than one colon, we guess that af = AF_INET6
310 	 */
311 
312 	if (count_colon == 0)
313 		return false;
314 
315 	if (count_colon == 1)
316 		*af = AF_INET;
317 	else
318 		*af = AF_INET6;
319 
320 	/*
321 	 * First bracket must be next character after last colon
322 	 * Last bracket must be the last character
323 	 * distance between both must be <= 7
324 	 */
325 
326 	if (*(last_colon+1) == '[')
327 		first_bracket = last_colon + 1;
328 	else
329 		return false;
330 
331 	last_bracket = str + (strlen(str) - 1);
332 	if (*last_bracket != ']')
333 		return false;
334 
335 	port_len = last_bracket - first_bracket;
336 	if (last_bracket - first_bracket > 7)
337 		return false;
338 
339 	memcpy(buf_port, first_bracket +1, port_len - 1);
340 	buf_port[port_len-1]= '\0';
341 
342 	addr_len = last_colon - str;
343 	if (addr_len >= sizeof(buf))
344 		return false;
345 	memcpy(buf, str, addr_len);
346 	buf[addr_len] = '\0';
347 
348 	if (inet_pton(*af, buf, &host->addr) != 1)
349 		return false;
350 
351 	host->port = htons(atoi(buf_port));
352 
353 	return true;
354 }
355 
356 static uint8_t
retrieve_peer_state(const char * str,int proto)357 retrieve_peer_state(const char* str, int proto)
358 {
359 	uint8_t i;
360 
361 	if (proto == IPPROTO_TCP) {
362 		i = 0;
363 		while (i < TCP_NSTATES) {
364 			if (strcmp(str, tcpstates[i]) == 0)
365 				return i;
366 			i++;
367 		}
368 		yyfatal("Invalid peer state");
369 
370 	} else {
371 		if (proto == IPPROTO_UDP) {
372 			const char* mystates[] = PFUDPS_NAMES;
373 			i = 0;
374 
375 			while (i < PFUDPS_NSTATES) {
376 				if (strcmp(str, mystates[i]) == 0)
377 					return i;
378 				i++;
379 			}
380 
381 			yyfatal("Invalid peer state");
382 		} else {
383 			const char *mystates[] = PFOTHERS_NAMES;
384 			i = 0;
385 
386 			while (i < PFOTHERS_NSTATES) {
387 				if (strcmp(str, mystates[i]) == 0)
388 					return i;
389 				i++;
390 			}
391 
392 			yyfatal("Invalid peer state");
393 		}
394 	}
395      /*NOTREACHED*/
396 	return 0;
397 }
398 
399 static bool
strtou32(const char * str,uint32_t * res)400 strtou32(const char* str, uint32_t* res)
401 {
402 	uintmax_t u;
403 	errno = 0;
404 	u = strtoumax(str, NULL, 10);
405 	if (errno == ERANGE && u == UINTMAX_MAX)
406 		return false;
407 	if (u > UINT32_MAX)
408 		return false;
409 	*res = (uint32_t) u;
410 	return true;
411 }
412 
413 static bool
retrieve_seq(const char * str,struct pfsync_state_peer * peer)414 retrieve_seq(const char* str, struct pfsync_state_peer* peer)
415 {
416 	const char* p, *p_colon, *p_comma;
417 	char buf[100];
418 	size_t size;
419 
420 	if (str == NULL || *str == '\0')
421 		return false;
422 
423 	if (*str != '[' || *(str+(strlen(str) -1)) != ']')
424 		return false;
425 
426 	p = str;
427 	p_colon = NULL;
428 	p_comma = NULL;
429 	while (*p != '\0') {
430 		if (*p == ':') {
431 			if (p_colon !=NULL)
432 				return false;
433 			else
434 				p_colon = p;
435 		}
436 
437 		if (*p == ',') {
438 			if (p_comma != NULL)
439 				return false;
440 			else
441 				p_comma = p;
442 		}
443 		p++;
444 	}
445 
446 	size = p_colon - str;
447 	if (size > sizeof(buf))
448 		return false;
449 	memcpy(buf, str+1, size-1);
450 	buf[size-1] = '\0';
451 
452 	if (!strtou32(buf, &peer->seqlo))
453 		return false;
454 
455 
456 	if (p_comma == NULL)
457 		size = str + strlen(str) - 1 - p_colon;
458 	else
459 		size = p_comma - p_colon;
460 
461 	if (size > sizeof(buf))
462 		return false;
463 	memcpy(buf, p_colon+1, size -1);
464 	buf[size-1] = '\0';
465 
466 	if (!strtou32(buf, &peer->seqhi))
467 		return false;
468 
469 	if (p_comma == NULL) {
470 		peer->seqdiff = 0;
471 	} else {
472 		size = str + strlen(str) - 1 - p_comma;
473 		if (size > sizeof(buf))
474 			return false;
475 		memcpy(buf, p_comma +1, size -1);
476 		buf[size-1] = '\0';
477 
478 		if (!strtou32(buf, &peer->seqdiff))
479 			return false;
480 	}
481 
482 	return true;
483 }
484 
485 static void
add_state(void)486 add_state(void)
487 {
488 
489 	if (allocated == 0) {
490 		allocated = 5;
491 		states->ps_buf = malloc(allocated * sizeof(struct pfsync_state));
492 		if (states->ps_buf == NULL)
493 			yyfatal("Not enough memory");
494 	}
495 
496 	if (allocated == (states->ps_len / sizeof(struct pfsync_state))) {
497 		void *buf;
498 		allocated = allocated * 2 + 1;
499 		buf = realloc(states->ps_buf, allocated * sizeof(struct pfsync_state));
500 		if (buf == NULL) {
501 			free(states->ps_buf);
502 			yyfatal("Not enough memory");
503 		}
504 		states->ps_buf = buf;
505 	}
506 
507 }
508