1/*	$NetBSD: policy_parse.y,v 1.9.6.2 2009/02/16 18:38:26 tteras 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#include PATH_IPSEC_H
76
77#include <stdlib.h>
78#include <stdio.h>
79#include <string.h>
80#include <netdb.h>
81
82#include <errno.h>
83
84#include "config.h"
85
86#include "ipsec_strerror.h"
87#include "libpfkey.h"
88
89#ifndef INT32_MAX
90#define INT32_MAX	(0xffffffff)
91#endif
92
93#ifndef INT32_MIN
94#define INT32_MIN	(-INT32_MAX-1)
95#endif
96
97#define ATOX(c) \
98  (isdigit(c) ? (c - '0') : (isupper(c) ? (c - 'A' + 10) : (c - 'a' + 10) ))
99
100static u_int8_t *pbuf = NULL;		/* sadb_x_policy buffer */
101static int tlen = 0;			/* total length of pbuf */
102static int offset = 0;			/* offset of pbuf */
103static int p_dir, p_type, p_protocol, p_mode, p_level, p_reqid;
104static u_int32_t p_priority = 0;
105static long p_priority_offset = 0;
106static struct sockaddr *p_src = NULL;
107static struct sockaddr *p_dst = NULL;
108
109struct _val;
110extern void yyerror __P((char *msg));
111static struct sockaddr *parse_sockaddr __P((struct _val *addrbuf,
112    struct _val *portbuf));
113static int rule_check __P((void));
114static int init_x_policy __P((void));
115static int set_x_request __P((struct sockaddr *, struct sockaddr *));
116static int set_sockaddr __P((struct sockaddr *));
117static void policy_parse_request_init __P((void));
118static void *policy_parse __P((const char *, int));
119
120extern void __policy__strbuffer__init__ __P((const char *));
121extern void __policy__strbuffer__free__ __P((void));
122extern int yyparse __P((void));
123extern int yylex __P((void));
124
125extern char *__libipsectext;	/*XXX*/
126
127%}
128
129%union {
130	u_int num;
131	u_int32_t num32;
132	struct _val {
133		int len;
134		char *buf;
135	} val;
136}
137
138%token DIR
139%token PRIORITY PLUS
140%token <num32> PRIO_BASE
141%token <val> PRIO_OFFSET
142%token ACTION PROTOCOL MODE LEVEL LEVEL_SPECIFY IPADDRESS PORT
143%token ME ANY
144%token SLASH HYPHEN
145%type <num> DIR PRIORITY ACTION PROTOCOL MODE LEVEL
146%type <val> IPADDRESS LEVEL_SPECIFY PORT
147
148%%
149policy_spec
150	:	DIR ACTION
151		{
152			p_dir = $1;
153			p_type = $2;
154
155#ifdef HAVE_PFKEY_POLICY_PRIORITY
156			p_priority = PRIORITY_DEFAULT;
157#else
158			p_priority = 0;
159#endif
160
161			if (init_x_policy())
162				return -1;
163		}
164		rules
165	|	DIR PRIORITY PRIO_OFFSET ACTION
166		{
167			p_dir = $1;
168			p_type = $4;
169			p_priority_offset = -atol($3.buf);
170
171			errno = 0;
172			if (errno != 0 || p_priority_offset < INT32_MIN)
173			{
174				__ipsec_errcode = EIPSEC_INVAL_PRIORITY_OFFSET;
175				return -1;
176			}
177
178			p_priority = PRIORITY_DEFAULT + (u_int32_t) p_priority_offset;
179
180			if (init_x_policy())
181				return -1;
182		}
183		rules
184	|	DIR PRIORITY HYPHEN PRIO_OFFSET ACTION
185		{
186			p_dir = $1;
187			p_type = $5;
188
189			errno = 0;
190			p_priority_offset = atol($4.buf);
191
192			if (errno != 0 || p_priority_offset > INT32_MAX)
193			{
194				__ipsec_errcode = EIPSEC_INVAL_PRIORITY_OFFSET;
195				return -1;
196			}
197
198			/* negative input value means lower priority, therefore higher
199			   actual value so that is closer to the end of the list */
200			p_priority = PRIORITY_DEFAULT + (u_int32_t) p_priority_offset;
201
202			if (init_x_policy())
203				return -1;
204		}
205		rules
206	|	DIR PRIORITY PRIO_BASE ACTION
207		{
208			p_dir = $1;
209			p_type = $4;
210
211			p_priority = $3;
212
213			if (init_x_policy())
214				return -1;
215		}
216		rules
217	|	DIR PRIORITY PRIO_BASE PLUS PRIO_OFFSET ACTION
218		{
219			p_dir = $1;
220			p_type = $6;
221
222			errno = 0;
223			p_priority_offset = atol($5.buf);
224
225			if (errno != 0 || p_priority_offset > PRIORITY_OFFSET_NEGATIVE_MAX)
226			{
227				__ipsec_errcode = EIPSEC_INVAL_PRIORITY_BASE_OFFSET;
228				return -1;
229			}
230
231			/* adding value means higher priority, therefore lower
232			   actual value so that is closer to the beginning of the list */
233			p_priority = $3 - (u_int32_t) p_priority_offset;
234
235			if (init_x_policy())
236				return -1;
237		}
238		rules
239	|	DIR PRIORITY PRIO_BASE HYPHEN PRIO_OFFSET ACTION
240		{
241			p_dir = $1;
242			p_type = $6;
243
244			errno = 0;
245			p_priority_offset = atol($5.buf);
246
247			if (errno != 0 || p_priority_offset > PRIORITY_OFFSET_POSITIVE_MAX)
248			{
249				__ipsec_errcode = EIPSEC_INVAL_PRIORITY_BASE_OFFSET;
250				return -1;
251			}
252
253			/* subtracting value means lower priority, therefore higher
254			   actual value so that is closer to the end of the list */
255			p_priority = $3 + (u_int32_t) p_priority_offset;
256
257			if (init_x_policy())
258				return -1;
259		}
260		rules
261	|	DIR
262		{
263			p_dir = $1;
264			p_type = 0;	/* ignored it by kernel */
265
266			p_priority = 0;
267
268			if (init_x_policy())
269				return -1;
270		}
271	;
272
273rules
274	:	/*NOTHING*/
275	|	rules rule {
276			if (rule_check() < 0)
277				return -1;
278
279			if (set_x_request(p_src, p_dst) < 0)
280				return -1;
281
282			policy_parse_request_init();
283		}
284	;
285
286rule
287	:	protocol SLASH mode SLASH addresses SLASH level
288	|	protocol SLASH mode SLASH addresses SLASH
289	|	protocol SLASH mode SLASH addresses
290	|	protocol SLASH mode SLASH
291	|	protocol SLASH mode SLASH SLASH level
292	|	protocol SLASH mode
293	|	protocol SLASH {
294			__ipsec_errcode = EIPSEC_FEW_ARGUMENTS;
295			return -1;
296		}
297	|	protocol {
298			__ipsec_errcode = EIPSEC_FEW_ARGUMENTS;
299			return -1;
300		}
301	;
302
303protocol
304	:	PROTOCOL { p_protocol = $1; }
305	;
306
307mode
308	:	MODE { p_mode = $1; }
309	;
310
311level
312	:	LEVEL {
313			p_level = $1;
314			p_reqid = 0;
315		}
316	|	LEVEL_SPECIFY {
317			p_level = IPSEC_LEVEL_UNIQUE;
318			p_reqid = atol($1.buf);	/* atol() is good. */
319		}
320	;
321
322addresses
323	:	IPADDRESS {
324			p_src = parse_sockaddr(&$1, NULL);
325			if (p_src == NULL)
326				return -1;
327		}
328		HYPHEN
329		IPADDRESS {
330			p_dst = parse_sockaddr(&$4, NULL);
331			if (p_dst == NULL)
332				return -1;
333		}
334	|	IPADDRESS PORT {
335			p_src = parse_sockaddr(&$1, &$2);
336			if (p_src == NULL)
337				return -1;
338		}
339		HYPHEN
340		IPADDRESS PORT {
341			p_dst = parse_sockaddr(&$5, &$6);
342			if (p_dst == NULL)
343				return -1;
344		}
345	|	ME HYPHEN ANY {
346			if (p_dir != IPSEC_DIR_OUTBOUND) {
347				__ipsec_errcode = EIPSEC_INVAL_DIR;
348				return -1;
349			}
350		}
351	|	ANY HYPHEN ME {
352			if (p_dir != IPSEC_DIR_INBOUND) {
353				__ipsec_errcode = EIPSEC_INVAL_DIR;
354				return -1;
355			}
356		}
357		/*
358	|	ME HYPHEN ME
359		*/
360	;
361
362%%
363
364void
365yyerror(msg)
366	char *msg;
367{
368	fprintf(stderr, "libipsec: %s while parsing \"%s\"\n",
369		msg, __libipsectext);
370
371	return;
372}
373
374static struct sockaddr *
375parse_sockaddr(addrbuf, portbuf)
376	struct _val *addrbuf;
377	struct _val *portbuf;
378{
379	struct addrinfo hints, *res;
380	char *addr;
381	char *serv = NULL;
382	int error;
383	struct sockaddr *newaddr = NULL;
384
385	if ((addr = malloc(addrbuf->len + 1)) == NULL) {
386		yyerror("malloc failed");
387		__ipsec_set_strerror(strerror(errno));
388		return NULL;
389	}
390
391	if (portbuf && ((serv = malloc(portbuf->len + 1)) == NULL)) {
392		free(addr);
393		yyerror("malloc failed");
394		__ipsec_set_strerror(strerror(errno));
395		return NULL;
396	}
397
398	strncpy(addr, addrbuf->buf, addrbuf->len);
399	addr[addrbuf->len] = '\0';
400
401	if (portbuf) {
402		strncpy(serv, portbuf->buf, portbuf->len);
403		serv[portbuf->len] = '\0';
404	}
405
406	memset(&hints, 0, sizeof(hints));
407	hints.ai_family = PF_UNSPEC;
408	hints.ai_flags = AI_NUMERICHOST;
409	hints.ai_socktype = SOCK_DGRAM;
410	error = getaddrinfo(addr, serv, &hints, &res);
411	free(addr);
412	if (serv != NULL)
413		free(serv);
414	if (error != 0) {
415		yyerror("invalid IP address");
416		__ipsec_set_strerror(gai_strerror(error));
417		return NULL;
418	}
419
420	if (res->ai_addr == NULL) {
421		yyerror("invalid IP address");
422		__ipsec_set_strerror(gai_strerror(error));
423		return NULL;
424	}
425
426	newaddr = malloc(res->ai_addrlen);
427	if (newaddr == NULL) {
428		__ipsec_errcode = EIPSEC_NO_BUFS;
429		freeaddrinfo(res);
430		return NULL;
431	}
432	memcpy(newaddr, res->ai_addr, res->ai_addrlen);
433
434	freeaddrinfo(res);
435
436	__ipsec_errcode = EIPSEC_NO_ERROR;
437	return newaddr;
438}
439
440static int
441rule_check()
442{
443	if (p_type == IPSEC_POLICY_IPSEC) {
444		if (p_protocol == IPPROTO_IP) {
445			__ipsec_errcode = EIPSEC_NO_PROTO;
446			return -1;
447		}
448
449		if (p_mode != IPSEC_MODE_TRANSPORT
450		 && p_mode != IPSEC_MODE_TUNNEL) {
451			__ipsec_errcode = EIPSEC_INVAL_MODE;
452			return -1;
453		}
454
455		if (p_src == NULL && p_dst == NULL) {
456			 if (p_mode != IPSEC_MODE_TRANSPORT) {
457				__ipsec_errcode = EIPSEC_INVAL_ADDRESS;
458				return -1;
459			}
460		}
461		else if (p_src->sa_family != p_dst->sa_family) {
462			__ipsec_errcode = EIPSEC_FAMILY_MISMATCH;
463			return -1;
464		}
465	}
466
467	__ipsec_errcode = EIPSEC_NO_ERROR;
468	return 0;
469}
470
471static int
472init_x_policy()
473{
474	struct sadb_x_policy *p;
475
476	if (pbuf) {
477		free(pbuf);
478		tlen = 0;
479	}
480	pbuf = malloc(sizeof(struct sadb_x_policy));
481	if (pbuf == NULL) {
482		__ipsec_errcode = EIPSEC_NO_BUFS;
483		return -1;
484	}
485	tlen = sizeof(struct sadb_x_policy);
486
487	memset(pbuf, 0, tlen);
488	p = (struct sadb_x_policy *)pbuf;
489	p->sadb_x_policy_len = 0;	/* must update later */
490	p->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
491	p->sadb_x_policy_type = p_type;
492	p->sadb_x_policy_dir = p_dir;
493	p->sadb_x_policy_id = 0;
494#ifdef HAVE_PFKEY_POLICY_PRIORITY
495	p->sadb_x_policy_priority = p_priority;
496#else
497    /* fail if given a priority and libipsec was not compiled with
498	   priority support */
499	if (p_priority != 0)
500	{
501		__ipsec_errcode = EIPSEC_PRIORITY_NOT_COMPILED;
502		return -1;
503	}
504#endif
505
506	offset = tlen;
507
508	__ipsec_errcode = EIPSEC_NO_ERROR;
509	return 0;
510}
511
512static int
513set_x_request(src, dst)
514	struct sockaddr *src, *dst;
515{
516	struct sadb_x_ipsecrequest *p;
517	int reqlen;
518	u_int8_t *n;
519
520	reqlen = sizeof(*p)
521		+ (src ? sysdep_sa_len(src) : 0)
522		+ (dst ? sysdep_sa_len(dst) : 0);
523	tlen += reqlen;		/* increment to total length */
524
525	n = realloc(pbuf, tlen);
526	if (n == NULL) {
527		__ipsec_errcode = EIPSEC_NO_BUFS;
528		return -1;
529	}
530	pbuf = n;
531
532	p = (struct sadb_x_ipsecrequest *)&pbuf[offset];
533	p->sadb_x_ipsecrequest_len = reqlen;
534	p->sadb_x_ipsecrequest_proto = p_protocol;
535	p->sadb_x_ipsecrequest_mode = p_mode;
536	p->sadb_x_ipsecrequest_level = p_level;
537	p->sadb_x_ipsecrequest_reqid = p_reqid;
538	offset += sizeof(*p);
539
540	if (set_sockaddr(src) || set_sockaddr(dst))
541		return -1;
542
543	__ipsec_errcode = EIPSEC_NO_ERROR;
544	return 0;
545}
546
547static int
548set_sockaddr(addr)
549	struct sockaddr *addr;
550{
551	if (addr == NULL) {
552		__ipsec_errcode = EIPSEC_NO_ERROR;
553		return 0;
554	}
555
556	/* tlen has already incremented */
557
558	memcpy(&pbuf[offset], addr, sysdep_sa_len(addr));
559
560	offset += sysdep_sa_len(addr);
561
562	__ipsec_errcode = EIPSEC_NO_ERROR;
563	return 0;
564}
565
566static void
567policy_parse_request_init()
568{
569	p_protocol = IPPROTO_IP;
570	p_mode = IPSEC_MODE_ANY;
571	p_level = IPSEC_LEVEL_DEFAULT;
572	p_reqid = 0;
573	if (p_src != NULL) {
574		free(p_src);
575		p_src = NULL;
576	}
577	if (p_dst != NULL) {
578		free(p_dst);
579		p_dst = NULL;
580	}
581
582	return;
583}
584
585static void *
586policy_parse(msg, msglen)
587	const char *msg;
588	int msglen;
589{
590	int error;
591
592	pbuf = NULL;
593	tlen = 0;
594
595	/* initialize */
596	p_dir = IPSEC_DIR_INVALID;
597	p_type = IPSEC_POLICY_DISCARD;
598	policy_parse_request_init();
599	__policy__strbuffer__init__(msg);
600
601	error = yyparse();	/* it must be set errcode. */
602	__policy__strbuffer__free__();
603
604	if (error) {
605		if (pbuf != NULL)
606			free(pbuf);
607		return NULL;
608	}
609
610	/* update total length */
611	((struct sadb_x_policy *)pbuf)->sadb_x_policy_len = PFKEY_UNIT64(tlen);
612
613	__ipsec_errcode = EIPSEC_NO_ERROR;
614
615	return pbuf;
616}
617
618ipsec_policy_t
619ipsec_set_policy(msg, msglen)
620	__ipsec_const char *msg;
621	int msglen;
622{
623	caddr_t policy;
624
625	policy = policy_parse(msg, msglen);
626	if (policy == NULL) {
627		if (__ipsec_errcode == EIPSEC_NO_ERROR)
628			__ipsec_errcode = EIPSEC_INVAL_ARGUMENT;
629		return NULL;
630	}
631
632	__ipsec_errcode = EIPSEC_NO_ERROR;
633	return policy;
634}
635