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