1/*	$KAME: policy_parse.y,v 1.11 2001/08/31 09:44:18 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/types.h>
53#include <sys/param.h>
54#include <sys/socket.h>
55
56#include <netinet/in.h>
57#include <netinet/ipsec.h>
58
59#include <stdlib.h>
60#include <stdio.h>
61#include <string.h>
62#include <netdb.h>
63
64#include "ipsec_strerror.h"
65#include "libpfkey.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
91extern char *__libyytext;	/*XXX*/
92
93%}
94
95%union {
96	u_int num;
97	struct _val {
98		int len;
99		char *buf;
100	} val;
101}
102
103%token DIR ACTION PROTOCOL MODE LEVEL LEVEL_SPECIFY
104%token IPADDRESS
105%token ME ANY
106%token SLASH HYPHEN
107%type <num> DIR ACTION PROTOCOL MODE LEVEL
108%type <val> IPADDRESS LEVEL_SPECIFY
109
110%%
111policy_spec
112	:	DIR ACTION
113		{
114			p_dir = $1;
115			p_type = $2;
116
117			if (init_x_policy())
118				return -1;
119		}
120		rules
121	|	DIR
122		{
123			p_dir = $1;
124			p_type = 0;	/* ignored it by kernel */
125
126			if (init_x_policy())
127				return -1;
128		}
129	;
130
131rules
132	:	/*NOTHING*/
133	|	rules rule {
134			if (rule_check() < 0)
135				return -1;
136
137			if (set_x_request(p_src, p_dst) < 0)
138				return -1;
139
140			policy_parse_request_init();
141		}
142	;
143
144rule
145	:	protocol SLASH mode SLASH addresses SLASH level
146	|	protocol SLASH mode SLASH addresses SLASH
147	|	protocol SLASH mode SLASH addresses
148	|	protocol SLASH mode SLASH
149	|	protocol SLASH mode SLASH SLASH level
150	|	protocol SLASH mode
151	|	protocol SLASH {
152			__ipsec_errcode = EIPSEC_FEW_ARGUMENTS;
153			return -1;
154		}
155	|	protocol {
156			__ipsec_errcode = EIPSEC_FEW_ARGUMENTS;
157			return -1;
158		}
159	;
160
161protocol
162	:	PROTOCOL { p_protocol = $1; }
163	;
164
165mode
166	:	MODE { p_mode = $1; }
167	;
168
169level
170	:	LEVEL {
171			p_level = $1;
172			p_reqid = 0;
173		}
174	|	LEVEL_SPECIFY {
175			p_level = IPSEC_LEVEL_UNIQUE;
176			p_reqid = atol($1.buf);	/* atol() is good. */
177		}
178	;
179
180addresses
181	:	IPADDRESS {
182			p_src = parse_sockaddr(&$1);
183			if (p_src == NULL)
184				return -1;
185		}
186		HYPHEN
187		IPADDRESS {
188			p_dst = parse_sockaddr(&$4);
189			if (p_dst == NULL)
190				return -1;
191		}
192	|	ME HYPHEN ANY {
193			if (p_dir != IPSEC_DIR_OUTBOUND) {
194				__ipsec_errcode = EIPSEC_INVAL_DIR;
195				return -1;
196			}
197		}
198	|	ANY HYPHEN ME {
199			if (p_dir != IPSEC_DIR_INBOUND) {
200				__ipsec_errcode = EIPSEC_INVAL_DIR;
201				return -1;
202			}
203		}
204		/*
205	|	ME HYPHEN ME
206		*/
207	;
208
209%%
210
211void
212yyerror(msg)
213	char *msg;
214{
215	fprintf(stderr, "libipsec: %s while parsing \"%s\"\n",
216		msg, __libyytext);
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_addrlen);
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_addrlen);
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 ? sysdep_sa_len(src) : 0)
324		+ (dst ? sysdep_sa_len(dst) : 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, sysdep_sa_len(addr));
359
360	offset += sysdep_sa_len(addr);
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