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 yylex(void);
92
93extern char *__libipsecyytext;	/*XXX*/
94
95%}
96
97%union {
98	u_int num;
99	struct _val {
100		int len;
101		char *buf;
102	} val;
103}
104
105%token DIR ACTION PROTOCOL MODE LEVEL LEVEL_SPECIFY
106%token IPADDRESS
107%token ME ANY
108%token SLASH HYPHEN
109%type <num> DIR ACTION PROTOCOL MODE LEVEL
110%type <val> IPADDRESS LEVEL_SPECIFY
111
112%%
113policy_spec
114	:	DIR ACTION
115		{
116			p_dir = $1;
117			p_type = $2;
118
119			if (init_x_policy())
120				return -1;
121		}
122		rules
123	|	DIR
124		{
125			p_dir = $1;
126			p_type = 0;	/* ignored it by kernel */
127
128			if (init_x_policy())
129				return -1;
130		}
131	;
132
133rules
134	:	/*NOTHING*/
135	|	rules rule {
136			if (rule_check() < 0)
137				return -1;
138
139			if (set_x_request(p_src, p_dst) < 0)
140				return -1;
141
142			policy_parse_request_init();
143		}
144	;
145
146rule
147	:	protocol SLASH mode SLASH addresses SLASH level
148	|	protocol SLASH mode SLASH addresses SLASH
149	|	protocol SLASH mode SLASH addresses
150	|	protocol SLASH mode SLASH
151	|	protocol SLASH mode SLASH SLASH level
152	|	protocol SLASH mode
153	|	protocol SLASH {
154			__ipsec_errcode = EIPSEC_FEW_ARGUMENTS;
155			return -1;
156		}
157	|	protocol {
158			__ipsec_errcode = EIPSEC_FEW_ARGUMENTS;
159			return -1;
160		}
161	;
162
163protocol
164	:	PROTOCOL { p_protocol = $1; }
165	;
166
167mode
168	:	MODE { p_mode = $1; }
169	;
170
171level
172	:	LEVEL {
173			p_level = $1;
174			p_reqid = 0;
175		}
176	|	LEVEL_SPECIFY {
177			p_level = IPSEC_LEVEL_UNIQUE;
178			p_reqid = atol($1.buf);	/* atol() is good. */
179		}
180	;
181
182addresses
183	:	IPADDRESS {
184			p_src = parse_sockaddr(&$1);
185			if (p_src == NULL)
186				return -1;
187		}
188		HYPHEN
189		IPADDRESS {
190			p_dst = parse_sockaddr(&$4);
191			if (p_dst == NULL)
192				return -1;
193		}
194	|	ME HYPHEN ANY {
195			if (p_dir != IPSEC_DIR_OUTBOUND) {
196				__ipsec_errcode = EIPSEC_INVAL_DIR;
197				return -1;
198			}
199		}
200	|	ANY HYPHEN ME {
201			if (p_dir != IPSEC_DIR_INBOUND) {
202				__ipsec_errcode = EIPSEC_INVAL_DIR;
203				return -1;
204			}
205		}
206		/*
207	|	ME HYPHEN ME
208		*/
209	;
210
211%%
212
213void
214yyerror(msg)
215	char *msg;
216{
217	fprintf(stderr, "libipsec: %s while parsing \"%s\"\n",
218		msg, __libipsecyytext);
219
220	return;
221}
222
223static struct sockaddr *
224parse_sockaddr(buf)
225	struct _val *buf;
226{
227	struct addrinfo hints, *res;
228	char *serv = NULL;
229	int error;
230	struct sockaddr *newaddr = NULL;
231
232	memset(&hints, 0, sizeof(hints));
233	hints.ai_family = PF_UNSPEC;
234	hints.ai_flags = AI_NUMERICHOST;
235	error = getaddrinfo(buf->buf, serv, &hints, &res);
236	if (error != 0) {
237		yyerror("invalid IP address");
238		__ipsec_set_strerror(gai_strerror(error));
239		return NULL;
240	}
241
242	if (res->ai_addr == NULL) {
243		yyerror("invalid IP address");
244		__ipsec_set_strerror(gai_strerror(error));
245		return NULL;
246	}
247
248	newaddr = malloc(res->ai_addr->sa_len);
249	if (newaddr == NULL) {
250		__ipsec_errcode = EIPSEC_NO_BUFS;
251		freeaddrinfo(res);
252		return NULL;
253	}
254	memcpy(newaddr, res->ai_addr, res->ai_addr->sa_len);
255
256	freeaddrinfo(res);
257
258	__ipsec_errcode = EIPSEC_NO_ERROR;
259	return newaddr;
260}
261
262static int
263rule_check()
264{
265	if (p_type == IPSEC_POLICY_IPSEC) {
266		if (p_protocol == IPPROTO_IP) {
267			__ipsec_errcode = EIPSEC_NO_PROTO;
268			return -1;
269		}
270
271		if (p_mode != IPSEC_MODE_TRANSPORT
272		 && p_mode != IPSEC_MODE_TUNNEL) {
273			__ipsec_errcode = EIPSEC_INVAL_MODE;
274			return -1;
275		}
276
277		if (p_src == NULL && p_dst == NULL) {
278			 if (p_mode != IPSEC_MODE_TRANSPORT) {
279				__ipsec_errcode = EIPSEC_INVAL_ADDRESS;
280				return -1;
281			}
282		}
283		else if (p_src->sa_family != p_dst->sa_family) {
284			__ipsec_errcode = EIPSEC_FAMILY_MISMATCH;
285			return -1;
286		}
287	}
288
289	__ipsec_errcode = EIPSEC_NO_ERROR;
290	return 0;
291}
292
293static int
294init_x_policy()
295{
296	struct sadb_x_policy *p;
297
298	tlen = sizeof(struct sadb_x_policy);
299
300	pbuf = malloc(tlen);
301	if (pbuf == NULL) {
302		__ipsec_errcode = EIPSEC_NO_BUFS;
303		return -1;
304	}
305	memset(pbuf, 0, tlen);
306	p = (struct sadb_x_policy *)pbuf;
307	p->sadb_x_policy_len = 0;	/* must update later */
308	p->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
309	p->sadb_x_policy_type = p_type;
310	p->sadb_x_policy_dir = p_dir;
311	p->sadb_x_policy_id = 0;
312
313	offset = tlen;
314
315	__ipsec_errcode = EIPSEC_NO_ERROR;
316	return 0;
317}
318
319static int
320set_x_request(src, dst)
321	struct sockaddr *src, *dst;
322{
323	struct sadb_x_ipsecrequest *p;
324	int reqlen;
325
326	reqlen = sizeof(*p)
327		+ (src ? src->sa_len : 0)
328		+ (dst ? dst->sa_len : 0);
329	tlen += reqlen;		/* increment to total length */
330
331	pbuf = realloc(pbuf, tlen);
332	if (pbuf == NULL) {
333		__ipsec_errcode = EIPSEC_NO_BUFS;
334		return -1;
335	}
336	p = (struct sadb_x_ipsecrequest *)&pbuf[offset];
337	p->sadb_x_ipsecrequest_len = reqlen;
338	p->sadb_x_ipsecrequest_proto = p_protocol;
339	p->sadb_x_ipsecrequest_mode = p_mode;
340	p->sadb_x_ipsecrequest_level = p_level;
341	p->sadb_x_ipsecrequest_reqid = p_reqid;
342	offset += sizeof(*p);
343
344	if (set_sockaddr(src) || set_sockaddr(dst))
345		return -1;
346
347	__ipsec_errcode = EIPSEC_NO_ERROR;
348	return 0;
349}
350
351static int
352set_sockaddr(addr)
353	struct sockaddr *addr;
354{
355	if (addr == NULL) {
356		__ipsec_errcode = EIPSEC_NO_ERROR;
357		return 0;
358	}
359
360	/* tlen has already incremented */
361
362	memcpy(&pbuf[offset], addr, addr->sa_len);
363
364	offset += addr->sa_len;
365
366	__ipsec_errcode = EIPSEC_NO_ERROR;
367	return 0;
368}
369
370static void
371policy_parse_request_init()
372{
373	p_protocol = IPPROTO_IP;
374	p_mode = IPSEC_MODE_ANY;
375	p_level = IPSEC_LEVEL_DEFAULT;
376	p_reqid = 0;
377	if (p_src != NULL) {
378		free(p_src);
379		p_src = NULL;
380	}
381	if (p_dst != NULL) {
382		free(p_dst);
383		p_dst = NULL;
384	}
385
386	return;
387}
388
389static caddr_t
390policy_parse(msg, msglen)
391	char *msg;
392	int msglen;
393{
394	int error;
395	pbuf = NULL;
396	tlen = 0;
397
398	/* initialize */
399	p_dir = IPSEC_DIR_INVALID;
400	p_type = IPSEC_POLICY_DISCARD;
401	policy_parse_request_init();
402	__policy__strbuffer__init__(msg);
403
404	error = yyparse();	/* it must be set errcode. */
405	__policy__strbuffer__free__();
406
407	if (error) {
408		if (pbuf != NULL)
409			free(pbuf);
410		return NULL;
411	}
412
413	/* update total length */
414	((struct sadb_x_policy *)pbuf)->sadb_x_policy_len = PFKEY_UNIT64(tlen);
415
416	__ipsec_errcode = EIPSEC_NO_ERROR;
417
418	return pbuf;
419}
420
421caddr_t
422ipsec_set_policy(msg, msglen)
423	char *msg;
424	int msglen;
425{
426	caddr_t policy;
427
428	policy = policy_parse(msg, msglen);
429	if (policy == NULL) {
430		if (__ipsec_errcode == EIPSEC_NO_ERROR)
431			__ipsec_errcode = EIPSEC_INVAL_ARGUMENT;
432		return NULL;
433	}
434
435	__ipsec_errcode = EIPSEC_NO_ERROR;
436	return policy;
437}
438
439