xref: /netbsd-src/crypto/dist/ipsec-tools/src/libipsec/policy_parse.y (revision aa73cae19608873cc4d1f712c4a0f8f8435f1ffa)
1 /*	$NetBSD: policy_parse.y,v 1.1.1.2 2005/02/23 14:54:09 manu Exp $	*/
2 
3 /*	$KAME: policy_parse.y,v 1.21 2003/12/12 08:01:26 itojun Exp $	*/
4 
5 /*
6  * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the project nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 /*
35  * IN/OUT bound policy configuration take place such below:
36  *	in <priority> <policy>
37  *	out <priority> <policy>
38  *
39  * <priority> is one of the following:
40  * priority <signed int> where the integer is an offset from the default
41  *                       priority, where negative numbers indicate lower
42  *                       priority (towards end of list) and positive numbers
43  *                       indicate higher priority (towards beginning of list)
44  *
45  * priority {low,def,high} {+,-} <unsigned int>  where low and high are
46  *                                               constants which are closer
47  *                                               to the end of the list and
48  *                                               beginning of the list,
49  *                                               respectively
50  *
51  * <policy> is one of following:
52  *	"discard", "none", "ipsec <requests>", "entrust", "bypass",
53  *
54  * The following requests are accepted as <requests>:
55  *
56  *	protocol/mode/src-dst/level
57  *	protocol/mode/src-dst		parsed as protocol/mode/src-dst/default
58  *	protocol/mode/src-dst/		parsed as protocol/mode/src-dst/default
59  *	protocol/transport		parsed as protocol/mode/any-any/default
60  *	protocol/transport//level	parsed as protocol/mode/any-any/level
61  *
62  * You can concatenate these requests with either ' '(single space) or '\n'.
63  */
64 
65 %{
66 #ifdef HAVE_CONFIG_H
67 #include "config.h"
68 #endif
69 
70 #include <sys/types.h>
71 #include <sys/param.h>
72 #include <sys/socket.h>
73 
74 #include <netinet/in.h>
75 #ifdef HAVE_NETINET6_IPSEC
76 #  include <netinet6/ipsec.h>
77 #else
78 #  include <netinet/ipsec.h>
79 #endif
80 
81 #include <stdlib.h>
82 #include <stdio.h>
83 #include <string.h>
84 #include <netdb.h>
85 
86 #include <errno.h>
87 
88 #include "config.h"
89 
90 #include "ipsec_strerror.h"
91 #include "libpfkey.h"
92 
93 #ifndef INT32_MAX
94 #define INT32_MAX	(0xffffffff)
95 #endif
96 
97 #ifndef INT32_MIN
98 #define INT32_MIN	(-INT32_MAX-1)
99 #endif
100 
101 #define ATOX(c) \
102   (isdigit(c) ? (c - '0') : (isupper(c) ? (c - 'A' + 10) : (c - 'a' + 10) ))
103 
104 static u_int8_t *pbuf = NULL;		/* sadb_x_policy buffer */
105 static int tlen = 0;			/* total length of pbuf */
106 static int offset = 0;			/* offset of pbuf */
107 static int p_dir, p_type, p_protocol, p_mode, p_level, p_reqid;
108 static u_int32_t p_priority = 0;
109 static long p_priority_offset = 0;
110 static struct sockaddr *p_src = NULL;
111 static struct sockaddr *p_dst = NULL;
112 
113 struct _val;
114 extern void yyerror __P((char *msg));
115 static struct sockaddr *parse_sockaddr __P((struct _val *buf));
116 static int rule_check __P((void));
117 static int init_x_policy __P((void));
118 static int set_x_request __P((struct sockaddr *src, struct sockaddr *dst));
119 static int set_sockaddr __P((struct sockaddr *addr));
120 static void policy_parse_request_init __P((void));
121 static caddr_t policy_parse __P((char *msg, int msglen));
122 
123 extern void __policy__strbuffer__init__ __P((char *msg));
124 extern void __policy__strbuffer__free__ __P((void));
125 extern int yyparse __P((void));
126 extern int yylex __P((void));
127 
128 extern char *__libipsectext;	/*XXX*/
129 
130 %}
131 
132 %union {
133 	u_int num;
134 	u_int32_t num32;
135 	struct _val {
136 		int len;
137 		char *buf;
138 	} val;
139 }
140 
141 %token DIR
142 %token PRIORITY PLUS
143 %token <num32> PRIO_BASE
144 %token <val> PRIO_OFFSET
145 %token ACTION PROTOCOL MODE LEVEL LEVEL_SPECIFY IPADDRESS
146 %token ME ANY
147 %token SLASH HYPHEN
148 %type <num> DIR PRIORITY ACTION PROTOCOL MODE LEVEL
149 %type <val> IPADDRESS LEVEL_SPECIFY
150 
151 %%
152 policy_spec
153 	:	DIR ACTION
154 		{
155 			p_dir = $1;
156 			p_type = $2;
157 
158 #ifdef HAVE_PFKEY_POLICY_PRIORITY
159 			p_priority = PRIORITY_DEFAULT;
160 #else
161 			p_priority = 0;
162 #endif
163 
164 			if (init_x_policy())
165 				return -1;
166 		}
167 		rules
168 	|	DIR PRIORITY PRIO_OFFSET ACTION
169 		{
170 			char *offset_buf;
171 
172 			p_dir = $1;
173 			p_type = $4;
174 
175 			/* buffer big enough to hold a prepended negative sign */
176 			offset_buf = malloc($3.len + 2);
177 			if (offset_buf == NULL)
178 			{
179 				__ipsec_errcode = EIPSEC_NO_BUFS;
180 				return -1;
181 			}
182 
183 			/* positive input value means higher priority, therefore lower
184 			   actual value so that is closer to the beginning of the list */
185 			sprintf (offset_buf, "-%s", $3.buf);
186 
187 			errno = 0;
188 			p_priority_offset = atol(offset_buf);
189 
190 			free(offset_buf);
191 
192 			if (errno != 0 || p_priority_offset < INT32_MIN)
193 			{
194 				__ipsec_errcode = EIPSEC_INVAL_PRIORITY_OFFSET;
195 				return -1;
196 			}
197 
198 			p_priority = PRIORITY_DEFAULT + (u_int32_t) p_priority_offset;
199 
200 			if (init_x_policy())
201 				return -1;
202 		}
203 		rules
204 	|	DIR PRIORITY HYPHEN PRIO_OFFSET ACTION
205 		{
206 			p_dir = $1;
207 			p_type = $5;
208 
209 			errno = 0;
210 			p_priority_offset = atol($4.buf);
211 
212 			if (errno != 0 || p_priority_offset > INT32_MAX)
213 			{
214 				__ipsec_errcode = EIPSEC_INVAL_PRIORITY_OFFSET;
215 				return -1;
216 			}
217 
218 			/* negative input value means lower priority, therefore higher
219 			   actual value so that is closer to the end of the list */
220 			p_priority = PRIORITY_DEFAULT + (u_int32_t) p_priority_offset;
221 
222 			if (init_x_policy())
223 				return -1;
224 		}
225 		rules
226 	|	DIR PRIORITY PRIO_BASE ACTION
227 		{
228 			p_dir = $1;
229 			p_type = $4;
230 
231 			p_priority = $3;
232 
233 			if (init_x_policy())
234 				return -1;
235 		}
236 		rules
237 	|	DIR PRIORITY PRIO_BASE PLUS PRIO_OFFSET ACTION
238 		{
239 			p_dir = $1;
240 			p_type = $6;
241 
242 			errno = 0;
243 			p_priority_offset = atol($5.buf);
244 
245 			if (errno != 0 || p_priority_offset > PRIORITY_OFFSET_NEGATIVE_MAX)
246 			{
247 				__ipsec_errcode = EIPSEC_INVAL_PRIORITY_BASE_OFFSET;
248 				return -1;
249 			}
250 
251 			/* adding value means higher priority, therefore lower
252 			   actual value so that is closer to the beginning of the list */
253 			p_priority = $3 - (u_int32_t) p_priority_offset;
254 
255 			if (init_x_policy())
256 				return -1;
257 		}
258 		rules
259 	|	DIR PRIORITY PRIO_BASE HYPHEN PRIO_OFFSET ACTION
260 		{
261 			p_dir = $1;
262 			p_type = $6;
263 
264 			errno = 0;
265 			p_priority_offset = atol($5.buf);
266 
267 			if (errno != 0 || p_priority_offset > PRIORITY_OFFSET_POSITIVE_MAX)
268 			{
269 				__ipsec_errcode = EIPSEC_INVAL_PRIORITY_BASE_OFFSET;
270 				return -1;
271 			}
272 
273 			/* subtracting value means lower priority, therefore higher
274 			   actual value so that is closer to the end of the list */
275 			p_priority = $3 + (u_int32_t) p_priority_offset;
276 
277 			if (init_x_policy())
278 				return -1;
279 		}
280 		rules
281 	|	DIR
282 		{
283 			p_dir = $1;
284 			p_type = 0;	/* ignored it by kernel */
285 
286 			p_priority = 0;
287 
288 			if (init_x_policy())
289 				return -1;
290 		}
291 	;
292 
293 rules
294 	:	/*NOTHING*/
295 	|	rules rule {
296 			if (rule_check() < 0)
297 				return -1;
298 
299 			if (set_x_request(p_src, p_dst) < 0)
300 				return -1;
301 
302 			policy_parse_request_init();
303 		}
304 	;
305 
306 rule
307 	:	protocol SLASH mode SLASH addresses SLASH level
308 	|	protocol SLASH mode SLASH addresses SLASH
309 	|	protocol SLASH mode SLASH addresses
310 	|	protocol SLASH mode SLASH
311 	|	protocol SLASH mode SLASH SLASH level
312 	|	protocol SLASH mode
313 	|	protocol SLASH {
314 			__ipsec_errcode = EIPSEC_FEW_ARGUMENTS;
315 			return -1;
316 		}
317 	|	protocol {
318 			__ipsec_errcode = EIPSEC_FEW_ARGUMENTS;
319 			return -1;
320 		}
321 	;
322 
323 protocol
324 	:	PROTOCOL { p_protocol = $1; }
325 	;
326 
327 mode
328 	:	MODE { p_mode = $1; }
329 	;
330 
331 level
332 	:	LEVEL {
333 			p_level = $1;
334 			p_reqid = 0;
335 		}
336 	|	LEVEL_SPECIFY {
337 			p_level = IPSEC_LEVEL_UNIQUE;
338 			p_reqid = atol($1.buf);	/* atol() is good. */
339 		}
340 	;
341 
342 addresses
343 	:	IPADDRESS {
344 			p_src = parse_sockaddr(&$1);
345 			if (p_src == NULL)
346 				return -1;
347 		}
348 		HYPHEN
349 		IPADDRESS {
350 			p_dst = parse_sockaddr(&$4);
351 			if (p_dst == NULL)
352 				return -1;
353 		}
354 	|	ME HYPHEN ANY {
355 			if (p_dir != IPSEC_DIR_OUTBOUND) {
356 				__ipsec_errcode = EIPSEC_INVAL_DIR;
357 				return -1;
358 			}
359 		}
360 	|	ANY HYPHEN ME {
361 			if (p_dir != IPSEC_DIR_INBOUND) {
362 				__ipsec_errcode = EIPSEC_INVAL_DIR;
363 				return -1;
364 			}
365 		}
366 		/*
367 	|	ME HYPHEN ME
368 		*/
369 	;
370 
371 %%
372 
373 void
374 yyerror(msg)
375 	char *msg;
376 {
377 	fprintf(stderr, "libipsec: %s while parsing \"%s\"\n",
378 		msg, __libipsectext);
379 
380 	return;
381 }
382 
383 static struct sockaddr *
384 parse_sockaddr(buf)
385 	struct _val *buf;
386 {
387 	struct addrinfo hints, *res;
388 	char *serv = NULL;
389 	int error;
390 	struct sockaddr *newaddr = NULL;
391 
392 	memset(&hints, 0, sizeof(hints));
393 	hints.ai_family = PF_UNSPEC;
394 	hints.ai_flags = AI_NUMERICHOST;
395 	error = getaddrinfo(buf->buf, serv, &hints, &res);
396 	if (error != 0) {
397 		yyerror("invalid IP address");
398 		__ipsec_set_strerror(gai_strerror(error));
399 		return NULL;
400 	}
401 
402 	if (res->ai_addr == NULL) {
403 		yyerror("invalid IP address");
404 		__ipsec_set_strerror(gai_strerror(error));
405 		return NULL;
406 	}
407 
408 	newaddr = malloc(res->ai_addrlen);
409 	if (newaddr == NULL) {
410 		__ipsec_errcode = EIPSEC_NO_BUFS;
411 		freeaddrinfo(res);
412 		return NULL;
413 	}
414 	memcpy(newaddr, res->ai_addr, res->ai_addrlen);
415 
416 	freeaddrinfo(res);
417 
418 	__ipsec_errcode = EIPSEC_NO_ERROR;
419 	return newaddr;
420 }
421 
422 static int
423 rule_check()
424 {
425 	if (p_type == IPSEC_POLICY_IPSEC) {
426 		if (p_protocol == IPPROTO_IP) {
427 			__ipsec_errcode = EIPSEC_NO_PROTO;
428 			return -1;
429 		}
430 
431 		if (p_mode != IPSEC_MODE_TRANSPORT
432 		 && p_mode != IPSEC_MODE_TUNNEL) {
433 			__ipsec_errcode = EIPSEC_INVAL_MODE;
434 			return -1;
435 		}
436 
437 		if (p_src == NULL && p_dst == NULL) {
438 			 if (p_mode != IPSEC_MODE_TRANSPORT) {
439 				__ipsec_errcode = EIPSEC_INVAL_ADDRESS;
440 				return -1;
441 			}
442 		}
443 		else if (p_src->sa_family != p_dst->sa_family) {
444 			__ipsec_errcode = EIPSEC_FAMILY_MISMATCH;
445 			return -1;
446 		}
447 	}
448 
449 	__ipsec_errcode = EIPSEC_NO_ERROR;
450 	return 0;
451 }
452 
453 static int
454 init_x_policy()
455 {
456 	struct sadb_x_policy *p;
457 
458 	if (pbuf) {
459 		free(pbuf);
460 		tlen = 0;
461 	}
462 	pbuf = malloc(sizeof(struct sadb_x_policy));
463 	if (pbuf == NULL) {
464 		__ipsec_errcode = EIPSEC_NO_BUFS;
465 		return -1;
466 	}
467 	tlen = sizeof(struct sadb_x_policy);
468 
469 	memset(pbuf, 0, tlen);
470 	p = (struct sadb_x_policy *)pbuf;
471 	p->sadb_x_policy_len = 0;	/* must update later */
472 	p->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
473 	p->sadb_x_policy_type = p_type;
474 	p->sadb_x_policy_dir = p_dir;
475 	p->sadb_x_policy_id = 0;
476 #ifdef HAVE_PFKEY_POLICY_PRIORITY
477 	p->sadb_x_policy_priority = p_priority;
478 #else
479     /* fail if given a priority and libipsec was not compiled with
480 	   priority support */
481 	if (p_priority != 0)
482 	{
483 		__ipsec_errcode = EIPSEC_PRIORITY_NOT_COMPILED;
484 		return -1;
485 	}
486 #endif
487 
488 	offset = tlen;
489 
490 	__ipsec_errcode = EIPSEC_NO_ERROR;
491 	return 0;
492 }
493 
494 static int
495 set_x_request(src, dst)
496 	struct sockaddr *src, *dst;
497 {
498 	struct sadb_x_ipsecrequest *p;
499 	int reqlen;
500 	caddr_t n;
501 
502 	reqlen = sizeof(*p)
503 		+ (src ? sysdep_sa_len(src) : 0)
504 		+ (dst ? sysdep_sa_len(dst) : 0);
505 	tlen += reqlen;		/* increment to total length */
506 
507 	n = realloc(pbuf, tlen);
508 	if (n == NULL) {
509 		__ipsec_errcode = EIPSEC_NO_BUFS;
510 		return -1;
511 	}
512 	pbuf = n;
513 	p = (struct sadb_x_ipsecrequest *)&pbuf[offset];
514 	p->sadb_x_ipsecrequest_len = reqlen;
515 	p->sadb_x_ipsecrequest_proto = p_protocol;
516 	p->sadb_x_ipsecrequest_mode = p_mode;
517 	p->sadb_x_ipsecrequest_level = p_level;
518 	p->sadb_x_ipsecrequest_reqid = p_reqid;
519 	offset += sizeof(*p);
520 
521 	if (set_sockaddr(src) || set_sockaddr(dst))
522 		return -1;
523 
524 	__ipsec_errcode = EIPSEC_NO_ERROR;
525 	return 0;
526 }
527 
528 static int
529 set_sockaddr(addr)
530 	struct sockaddr *addr;
531 {
532 	if (addr == NULL) {
533 		__ipsec_errcode = EIPSEC_NO_ERROR;
534 		return 0;
535 	}
536 
537 	/* tlen has already incremented */
538 
539 	memcpy(&pbuf[offset], addr, sysdep_sa_len(addr));
540 
541 	offset += sysdep_sa_len(addr);
542 
543 	__ipsec_errcode = EIPSEC_NO_ERROR;
544 	return 0;
545 }
546 
547 static void
548 policy_parse_request_init()
549 {
550 	p_protocol = IPPROTO_IP;
551 	p_mode = IPSEC_MODE_ANY;
552 	p_level = IPSEC_LEVEL_DEFAULT;
553 	p_reqid = 0;
554 	if (p_src != NULL) {
555 		free(p_src);
556 		p_src = NULL;
557 	}
558 	if (p_dst != NULL) {
559 		free(p_dst);
560 		p_dst = NULL;
561 	}
562 
563 	return;
564 }
565 
566 static caddr_t
567 policy_parse(msg, msglen)
568 	char *msg;
569 	int msglen;
570 {
571 	int error;
572 
573 	pbuf = NULL;
574 	tlen = 0;
575 
576 	/* initialize */
577 	p_dir = IPSEC_DIR_INVALID;
578 	p_type = IPSEC_POLICY_DISCARD;
579 	policy_parse_request_init();
580 	__policy__strbuffer__init__(msg);
581 
582 	error = yyparse();	/* it must be set errcode. */
583 	__policy__strbuffer__free__();
584 
585 	if (error) {
586 		if (pbuf != NULL)
587 			free(pbuf);
588 		return NULL;
589 	}
590 
591 	/* update total length */
592 	((struct sadb_x_policy *)pbuf)->sadb_x_policy_len = PFKEY_UNIT64(tlen);
593 
594 	__ipsec_errcode = EIPSEC_NO_ERROR;
595 
596 	return pbuf;
597 }
598 
599 caddr_t
600 ipsec_set_policy(msg, msglen)
601 	char *msg;
602 	int msglen;
603 {
604 	caddr_t policy;
605 
606 	policy = policy_parse(msg, msglen);
607 	if (policy == NULL) {
608 		if (__ipsec_errcode == EIPSEC_NO_ERROR)
609 			__ipsec_errcode = EIPSEC_INVAL_ARGUMENT;
610 		return NULL;
611 	}
612 
613 	__ipsec_errcode = EIPSEC_NO_ERROR;
614 	return policy;
615 }
616 
617