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