1/*	$KAME: policy_parse.y,v 1.14 2003/06/27 03:39:20 itojun Exp $	*/
2
3/*
4 * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
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 * 3. Neither the name of the project nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32/*
33 * IN/OUT bound policy configuration take place such below:
34 *	in <policy>
35 *	out <policy>
36 *
37 * <policy> is one of following:
38 *	"discard", "none", "ipsec <requests>", "entrust", "bypass",
39 *
40 * The following requests are accepted as <requests>:
41 *
42 *	protocol/mode/src-dst/level
43 *	protocol/mode/src-dst		parsed as protocol/mode/src-dst/default
44 *	protocol/mode/src-dst/		parsed as protocol/mode/src-dst/default
45 *	protocol/transport		parsed as protocol/mode/any-any/default
46 *	protocol/transport//level	parsed as protocol/mode/any-any/level
47 *
48 * You can concatenate these requests with either ' '(single space) or '\n'.
49 */
50
51%{
52#include <sys/cdefs.h>
53__FBSDID("$FreeBSD$");
54
55#include <sys/types.h>
56#include <sys/param.h>
57#include <sys/socket.h>
58
59#include <netinet/in.h>
60#include <netipsec/ipsec.h>
61
62#include <stdlib.h>
63#include <stdio.h>
64#include <string.h>
65#include <netdb.h>
66
67#include "ipsec_strerror.h"
68
69#define ATOX(c) \
70  (isdigit(c) ? (c - '0') : (isupper(c) ? (c - 'A' + 10) : (c - 'a' + 10) ))
71
72static caddr_t pbuf = NULL;		/* sadb_x_policy buffer */
73static int tlen = 0;			/* total length of pbuf */
74static int offset = 0;			/* offset of pbuf */
75static int p_dir, p_type, p_protocol, p_mode, p_level, p_reqid;
76static struct sockaddr *p_src = NULL;
77static struct sockaddr *p_dst = NULL;
78
79struct _val;
80extern void yyerror(char *msg);
81static struct sockaddr *parse_sockaddr(struct _val *buf);
82static int rule_check(void);
83static int init_x_policy(void);
84static int set_x_request(struct sockaddr *src, struct sockaddr *dst);
85static int set_sockaddr(struct sockaddr *addr);
86static void policy_parse_request_init(void);
87static caddr_t policy_parse(char *msg, int msglen);
88
89extern void __policy__strbuffer__init__(char *msg);
90extern void __policy__strbuffer__free__(void);
91extern int yyparse(void);
92extern int yylex(void);
93
94extern char *__libipsecyytext;	/*XXX*/
95
96%}
97
98%union {
99	u_int num;
100	struct _val {
101		int len;
102		char *buf;
103	} val;
104}
105
106%token DIR ACTION PROTOCOL MODE LEVEL LEVEL_SPECIFY
107%token IPADDRESS
108%token ME ANY
109%token SLASH HYPHEN
110%type <num> DIR ACTION PROTOCOL MODE LEVEL
111%type <val> IPADDRESS LEVEL_SPECIFY
112
113%%
114policy_spec
115	:	DIR ACTION
116		{
117			p_dir = $1;
118			p_type = $2;
119
120			if (init_x_policy())
121				return -1;
122		}
123		rules
124	|	DIR
125		{
126			p_dir = $1;
127			p_type = 0;	/* ignored it by kernel */
128
129			if (init_x_policy())
130				return -1;
131		}
132	;
133
134rules
135	:	/*NOTHING*/
136	|	rules rule {
137			if (rule_check() < 0)
138				return -1;
139
140			if (set_x_request(p_src, p_dst) < 0)
141				return -1;
142
143			policy_parse_request_init();
144		}
145	;
146
147rule
148	:	protocol SLASH mode SLASH addresses SLASH level
149	|	protocol SLASH mode SLASH addresses SLASH
150	|	protocol SLASH mode SLASH addresses
151	|	protocol SLASH mode SLASH
152	|	protocol SLASH mode SLASH SLASH level
153	|	protocol SLASH mode
154	|	protocol SLASH {
155			__ipsec_errcode = EIPSEC_FEW_ARGUMENTS;
156			return -1;
157		}
158	|	protocol {
159			__ipsec_errcode = EIPSEC_FEW_ARGUMENTS;
160			return -1;
161		}
162	;
163
164protocol
165	:	PROTOCOL { p_protocol = $1; }
166	;
167
168mode
169	:	MODE { p_mode = $1; }
170	;
171
172level
173	:	LEVEL {
174			p_level = $1;
175			p_reqid = 0;
176		}
177	|	LEVEL_SPECIFY {
178			p_level = IPSEC_LEVEL_UNIQUE;
179			p_reqid = atol($1.buf);	/* atol() is good. */
180		}
181	;
182
183addresses
184	:	IPADDRESS {
185			p_src = parse_sockaddr(&$1);
186			if (p_src == NULL)
187				return -1;
188		}
189		HYPHEN
190		IPADDRESS {
191			p_dst = parse_sockaddr(&$4);
192			if (p_dst == NULL)
193				return -1;
194		}
195	|	ME HYPHEN ANY {
196			if (p_dir != IPSEC_DIR_OUTBOUND) {
197				__ipsec_errcode = EIPSEC_INVAL_DIR;
198				return -1;
199			}
200		}
201	|	ANY HYPHEN ME {
202			if (p_dir != IPSEC_DIR_INBOUND) {
203				__ipsec_errcode = EIPSEC_INVAL_DIR;
204				return -1;
205			}
206		}
207		/*
208	|	ME HYPHEN ME
209		*/
210	;
211
212%%
213
214void
215yyerror(msg)
216	char *msg;
217{
218	fprintf(stderr, "libipsec: %s while parsing \"%s\"\n",
219		msg, __libipsecyytext);
220
221	return;
222}
223
224static struct sockaddr *
225parse_sockaddr(buf)
226	struct _val *buf;
227{
228	struct addrinfo hints, *res;
229	char *serv = NULL;
230	int error;
231	struct sockaddr *newaddr = NULL;
232
233	memset(&hints, 0, sizeof(hints));
234	hints.ai_family = PF_UNSPEC;
235	hints.ai_flags = AI_NUMERICHOST;
236	error = getaddrinfo(buf->buf, serv, &hints, &res);
237	if (error != 0) {
238		yyerror("invalid IP address");
239		__ipsec_set_strerror(gai_strerror(error));
240		return NULL;
241	}
242
243	if (res->ai_addr == NULL) {
244		yyerror("invalid IP address");
245		__ipsec_set_strerror(gai_strerror(error));
246		return NULL;
247	}
248
249	newaddr = malloc(res->ai_addr->sa_len);
250	if (newaddr == NULL) {
251		__ipsec_errcode = EIPSEC_NO_BUFS;
252		freeaddrinfo(res);
253		return NULL;
254	}
255	memcpy(newaddr, res->ai_addr, res->ai_addr->sa_len);
256
257	freeaddrinfo(res);
258
259	__ipsec_errcode = EIPSEC_NO_ERROR;
260	return newaddr;
261}
262
263static int
264rule_check()
265{
266	if (p_type == IPSEC_POLICY_IPSEC) {
267		if (p_protocol == IPPROTO_IP) {
268			__ipsec_errcode = EIPSEC_NO_PROTO;
269			return -1;
270		}
271
272		if (p_mode != IPSEC_MODE_TRANSPORT
273		 && p_mode != IPSEC_MODE_TUNNEL) {
274			__ipsec_errcode = EIPSEC_INVAL_MODE;
275			return -1;
276		}
277
278		if (p_src == NULL && p_dst == NULL) {
279			 if (p_mode != IPSEC_MODE_TRANSPORT) {
280				__ipsec_errcode = EIPSEC_INVAL_ADDRESS;
281				return -1;
282			}
283		}
284		else if (p_src->sa_family != p_dst->sa_family) {
285			__ipsec_errcode = EIPSEC_FAMILY_MISMATCH;
286			return -1;
287		}
288	}
289
290	__ipsec_errcode = EIPSEC_NO_ERROR;
291	return 0;
292}
293
294static int
295init_x_policy()
296{
297	struct sadb_x_policy *p;
298
299	tlen = sizeof(struct sadb_x_policy);
300
301	pbuf = malloc(tlen);
302	if (pbuf == NULL) {
303		__ipsec_errcode = EIPSEC_NO_BUFS;
304		return -1;
305	}
306	memset(pbuf, 0, tlen);
307	p = (struct sadb_x_policy *)pbuf;
308	p->sadb_x_policy_len = 0;	/* must update later */
309	p->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
310	p->sadb_x_policy_type = p_type;
311	p->sadb_x_policy_dir = p_dir;
312	p->sadb_x_policy_id = 0;
313
314	offset = tlen;
315
316	__ipsec_errcode = EIPSEC_NO_ERROR;
317	return 0;
318}
319
320static int
321set_x_request(src, dst)
322	struct sockaddr *src, *dst;
323{
324	struct sadb_x_ipsecrequest *p;
325	int reqlen;
326
327	reqlen = sizeof(*p)
328		+ (src ? src->sa_len : 0)
329		+ (dst ? dst->sa_len : 0);
330	tlen += reqlen;		/* increment to total length */
331
332	pbuf = realloc(pbuf, tlen);
333	if (pbuf == NULL) {
334		__ipsec_errcode = EIPSEC_NO_BUFS;
335		return -1;
336	}
337	p = (struct sadb_x_ipsecrequest *)&pbuf[offset];
338	p->sadb_x_ipsecrequest_len = reqlen;
339	p->sadb_x_ipsecrequest_proto = p_protocol;
340	p->sadb_x_ipsecrequest_mode = p_mode;
341	p->sadb_x_ipsecrequest_level = p_level;
342	p->sadb_x_ipsecrequest_reqid = p_reqid;
343	offset += sizeof(*p);
344
345	if (set_sockaddr(src) || set_sockaddr(dst))
346		return -1;
347
348	__ipsec_errcode = EIPSEC_NO_ERROR;
349	return 0;
350}
351
352static int
353set_sockaddr(addr)
354	struct sockaddr *addr;
355{
356	if (addr == NULL) {
357		__ipsec_errcode = EIPSEC_NO_ERROR;
358		return 0;
359	}
360
361	/* tlen has already incremented */
362
363	memcpy(&pbuf[offset], addr, addr->sa_len);
364
365	offset += addr->sa_len;
366
367	__ipsec_errcode = EIPSEC_NO_ERROR;
368	return 0;
369}
370
371static void
372policy_parse_request_init()
373{
374	p_protocol = IPPROTO_IP;
375	p_mode = IPSEC_MODE_ANY;
376	p_level = IPSEC_LEVEL_DEFAULT;
377	p_reqid = 0;
378	if (p_src != NULL) {
379		free(p_src);
380		p_src = NULL;
381	}
382	if (p_dst != NULL) {
383		free(p_dst);
384		p_dst = NULL;
385	}
386
387	return;
388}
389
390static caddr_t
391policy_parse(msg, msglen)
392	char *msg;
393	int msglen;
394{
395	int error;
396	pbuf = NULL;
397	tlen = 0;
398
399	/* initialize */
400	p_dir = IPSEC_DIR_INVALID;
401	p_type = IPSEC_POLICY_DISCARD;
402	policy_parse_request_init();
403	__policy__strbuffer__init__(msg);
404
405	error = yyparse();	/* it must be set errcode. */
406	__policy__strbuffer__free__();
407
408	if (error) {
409		if (pbuf != NULL)
410			free(pbuf);
411		return NULL;
412	}
413
414	/* update total length */
415	((struct sadb_x_policy *)pbuf)->sadb_x_policy_len = PFKEY_UNIT64(tlen);
416
417	__ipsec_errcode = EIPSEC_NO_ERROR;
418
419	return pbuf;
420}
421
422caddr_t
423ipsec_set_policy(msg, msglen)
424	char *msg;
425	int msglen;
426{
427	caddr_t policy;
428
429	policy = policy_parse(msg, msglen);
430	if (policy == NULL) {
431		if (__ipsec_errcode == EIPSEC_NO_ERROR)
432			__ipsec_errcode = EIPSEC_INVAL_ARGUMENT;
433		return NULL;
434	}
435
436	__ipsec_errcode = EIPSEC_NO_ERROR;
437	return policy;
438}
439
440