1122107Sume/*	$KAME: policy_parse.y,v 1.14 2003/06/27 03:39:20 itojun Exp $	*/
262583Sitojun
355505Sshin/*
455505Sshin * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
555505Sshin * All rights reserved.
655505Sshin *
755505Sshin * Redistribution and use in source and binary forms, with or without
855505Sshin * modification, are permitted provided that the following conditions
955505Sshin * are met:
1055505Sshin * 1. Redistributions of source code must retain the above copyright
1155505Sshin *    notice, this list of conditions and the following disclaimer.
1255505Sshin * 2. Redistributions in binary form must reproduce the above copyright
1355505Sshin *    notice, this list of conditions and the following disclaimer in the
1455505Sshin *    documentation and/or other materials provided with the distribution.
1555505Sshin * 3. Neither the name of the project nor the names of its contributors
1655505Sshin *    may be used to endorse or promote products derived from this software
1755505Sshin *    without specific prior written permission.
1855505Sshin *
1955505Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2055505Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2155505Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2255505Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2355505Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2455505Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2555505Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2655505Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2755505Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2855505Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2955505Sshin * SUCH DAMAGE.
3055505Sshin */
3155505Sshin
3255505Sshin/*
3355505Sshin * IN/OUT bound policy configuration take place such below:
3455505Sshin *	in <policy>
3555505Sshin *	out <policy>
3655505Sshin *
3755505Sshin * <policy> is one of following:
3855505Sshin *	"discard", "none", "ipsec <requests>", "entrust", "bypass",
3955505Sshin *
4055505Sshin * The following requests are accepted as <requests>:
4155505Sshin *
4255505Sshin *	protocol/mode/src-dst/level
4355505Sshin *	protocol/mode/src-dst		parsed as protocol/mode/src-dst/default
4455505Sshin *	protocol/mode/src-dst/		parsed as protocol/mode/src-dst/default
4555505Sshin *	protocol/transport		parsed as protocol/mode/any-any/default
4655505Sshin *	protocol/transport//level	parsed as protocol/mode/any-any/level
4755505Sshin *
4855505Sshin * You can concatenate these requests with either ' '(single space) or '\n'.
4955505Sshin */
5055505Sshin
5155505Sshin%{
52122107Sume#include <sys/cdefs.h>
53122107Sume__FBSDID("$FreeBSD$");
54122107Sume
5555505Sshin#include <sys/types.h>
5655505Sshin#include <sys/param.h>
5755505Sshin#include <sys/socket.h>
5855505Sshin
5955505Sshin#include <netinet/in.h>
60171135Sgnn#include <netipsec/ipsec.h>
6155505Sshin
6255505Sshin#include <stdlib.h>
6355505Sshin#include <stdio.h>
6455505Sshin#include <string.h>
6555505Sshin#include <netdb.h>
6655505Sshin
6755505Sshin#include "ipsec_strerror.h"
6855505Sshin
6962583Sitojun#define ATOX(c) \
7055505Sshin  (isdigit(c) ? (c - '0') : (isupper(c) ? (c - 'A' + 10) : (c - 'a' + 10) ))
7155505Sshin
7255505Sshinstatic caddr_t pbuf = NULL;		/* sadb_x_policy buffer */
7355505Sshinstatic int tlen = 0;			/* total length of pbuf */
7455505Sshinstatic int offset = 0;			/* offset of pbuf */
7562583Sitojunstatic int p_dir, p_type, p_protocol, p_mode, p_level, p_reqid;
7655505Sshinstatic struct sockaddr *p_src = NULL;
7755505Sshinstatic struct sockaddr *p_dst = NULL;
7855505Sshin
7962583Sitojunstruct _val;
8092917Sobrienextern void yyerror(char *msg);
8192917Sobrienstatic struct sockaddr *parse_sockaddr(struct _val *buf);
8292917Sobrienstatic int rule_check(void);
8392917Sobrienstatic int init_x_policy(void);
8492917Sobrienstatic int set_x_request(struct sockaddr *src, struct sockaddr *dst);
8592917Sobrienstatic int set_sockaddr(struct sockaddr *addr);
8692917Sobrienstatic void policy_parse_request_init(void);
8792917Sobrienstatic caddr_t policy_parse(char *msg, int msglen);
8855505Sshin
8992917Sobrienextern void __policy__strbuffer__init__(char *msg);
90122107Sumeextern void __policy__strbuffer__free__(void);
9192917Sobrienextern int yylex(void);
9255505Sshin
93122107Sumeextern char *__libipsecyytext;	/*XXX*/
94122107Sume
9555505Sshin%}
9655505Sshin
9755505Sshin%union {
9855505Sshin	u_int num;
9955505Sshin	struct _val {
10055505Sshin		int len;
10155505Sshin		char *buf;
10255505Sshin	} val;
10355505Sshin}
10455505Sshin
10562583Sitojun%token DIR ACTION PROTOCOL MODE LEVEL LEVEL_SPECIFY
10655505Sshin%token IPADDRESS
10755505Sshin%token ME ANY
10855505Sshin%token SLASH HYPHEN
10955505Sshin%type <num> DIR ACTION PROTOCOL MODE LEVEL
11062583Sitojun%type <val> IPADDRESS LEVEL_SPECIFY
11155505Sshin
11255505Sshin%%
11355505Sshinpolicy_spec
11455505Sshin	:	DIR ACTION
11555505Sshin		{
11655505Sshin			p_dir = $1;
11755505Sshin			p_type = $2;
11855505Sshin
11955505Sshin			if (init_x_policy())
12055505Sshin				return -1;
12155505Sshin		}
12255505Sshin		rules
12362583Sitojun	|	DIR
12462583Sitojun		{
12562583Sitojun			p_dir = $1;
12662583Sitojun			p_type = 0;	/* ignored it by kernel */
12762583Sitojun
12862583Sitojun			if (init_x_policy())
12962583Sitojun				return -1;
13062583Sitojun		}
13155505Sshin	;
13255505Sshin
13355505Sshinrules
13455505Sshin	:	/*NOTHING*/
13555505Sshin	|	rules rule {
13655505Sshin			if (rule_check() < 0)
13755505Sshin				return -1;
13855505Sshin
13955505Sshin			if (set_x_request(p_src, p_dst) < 0)
14055505Sshin				return -1;
14155505Sshin
14255505Sshin			policy_parse_request_init();
14355505Sshin		}
14455505Sshin	;
14555505Sshin
14655505Sshinrule
14755505Sshin	:	protocol SLASH mode SLASH addresses SLASH level
14855505Sshin	|	protocol SLASH mode SLASH addresses SLASH
14955505Sshin	|	protocol SLASH mode SLASH addresses
15055505Sshin	|	protocol SLASH mode SLASH
15155505Sshin	|	protocol SLASH mode SLASH SLASH level
15255505Sshin	|	protocol SLASH mode
15355505Sshin	|	protocol SLASH {
15462583Sitojun			__ipsec_errcode = EIPSEC_FEW_ARGUMENTS;
15555505Sshin			return -1;
15655505Sshin		}
15755505Sshin	|	protocol {
15862583Sitojun			__ipsec_errcode = EIPSEC_FEW_ARGUMENTS;
15955505Sshin			return -1;
16055505Sshin		}
16155505Sshin	;
16255505Sshin
16355505Sshinprotocol
16455505Sshin	:	PROTOCOL { p_protocol = $1; }
16555505Sshin	;
16655505Sshin
16755505Sshinmode
16855505Sshin	:	MODE { p_mode = $1; }
16955505Sshin	;
17055505Sshin
17155505Sshinlevel
17262583Sitojun	:	LEVEL {
17362583Sitojun			p_level = $1;
17462583Sitojun			p_reqid = 0;
17562583Sitojun		}
17662583Sitojun	|	LEVEL_SPECIFY {
17762583Sitojun			p_level = IPSEC_LEVEL_UNIQUE;
17862583Sitojun			p_reqid = atol($1.buf);	/* atol() is good. */
17962583Sitojun		}
18055505Sshin	;
18155505Sshin
18255505Sshinaddresses
18355505Sshin	:	IPADDRESS {
18455505Sshin			p_src = parse_sockaddr(&$1);
18555505Sshin			if (p_src == NULL)
18655505Sshin				return -1;
18755505Sshin		}
18855505Sshin		HYPHEN
18955505Sshin		IPADDRESS {
19055505Sshin			p_dst = parse_sockaddr(&$4);
19155505Sshin			if (p_dst == NULL)
19255505Sshin				return -1;
19355505Sshin		}
19455505Sshin	|	ME HYPHEN ANY {
19555505Sshin			if (p_dir != IPSEC_DIR_OUTBOUND) {
19662583Sitojun				__ipsec_errcode = EIPSEC_INVAL_DIR;
19755505Sshin				return -1;
19855505Sshin			}
19955505Sshin		}
20055505Sshin	|	ANY HYPHEN ME {
20155505Sshin			if (p_dir != IPSEC_DIR_INBOUND) {
20262583Sitojun				__ipsec_errcode = EIPSEC_INVAL_DIR;
20355505Sshin				return -1;
20455505Sshin			}
20555505Sshin		}
20655505Sshin		/*
20755505Sshin	|	ME HYPHEN ME
20855505Sshin		*/
20955505Sshin	;
21055505Sshin
21155505Sshin%%
21255505Sshin
21355505Sshinvoid
21455505Sshinyyerror(msg)
21555505Sshin	char *msg;
21655505Sshin{
21762583Sitojun	fprintf(stderr, "libipsec: %s while parsing \"%s\"\n",
21862583Sitojun		msg, __libipsecyytext);
21962583Sitojun
22055505Sshin	return;
22155505Sshin}
22255505Sshin
22355505Sshinstatic struct sockaddr *
22455505Sshinparse_sockaddr(buf)
22555505Sshin	struct _val *buf;
22655505Sshin{
22755505Sshin	struct addrinfo hints, *res;
22855505Sshin	char *serv = NULL;
22955505Sshin	int error;
23055505Sshin	struct sockaddr *newaddr = NULL;
23155505Sshin
23255505Sshin	memset(&hints, 0, sizeof(hints));
23355505Sshin	hints.ai_family = PF_UNSPEC;
23455505Sshin	hints.ai_flags = AI_NUMERICHOST;
23555505Sshin	error = getaddrinfo(buf->buf, serv, &hints, &res);
23662583Sitojun	if (error != 0) {
23762583Sitojun		yyerror("invalid IP address");
23862583Sitojun		__ipsec_set_strerror(gai_strerror(error));
23955505Sshin		return NULL;
24055505Sshin	}
24155505Sshin
24255505Sshin	if (res->ai_addr == NULL) {
24362583Sitojun		yyerror("invalid IP address");
24462583Sitojun		__ipsec_set_strerror(gai_strerror(error));
24555505Sshin		return NULL;
24655505Sshin	}
24755505Sshin
24855505Sshin	newaddr = malloc(res->ai_addr->sa_len);
24955505Sshin	if (newaddr == NULL) {
25062583Sitojun		__ipsec_errcode = EIPSEC_NO_BUFS;
25155505Sshin		freeaddrinfo(res);
25255505Sshin		return NULL;
25355505Sshin	}
25455505Sshin	memcpy(newaddr, res->ai_addr, res->ai_addr->sa_len);
25555505Sshin
25655505Sshin	freeaddrinfo(res);
25755505Sshin
25862583Sitojun	__ipsec_errcode = EIPSEC_NO_ERROR;
25955505Sshin	return newaddr;
26055505Sshin}
26155505Sshin
26255505Sshinstatic int
26355505Sshinrule_check()
26455505Sshin{
26555505Sshin	if (p_type == IPSEC_POLICY_IPSEC) {
26655505Sshin		if (p_protocol == IPPROTO_IP) {
26762583Sitojun			__ipsec_errcode = EIPSEC_NO_PROTO;
26855505Sshin			return -1;
26955505Sshin		}
27055505Sshin
27155505Sshin		if (p_mode != IPSEC_MODE_TRANSPORT
27255505Sshin		 && p_mode != IPSEC_MODE_TUNNEL) {
27362583Sitojun			__ipsec_errcode = EIPSEC_INVAL_MODE;
27455505Sshin			return -1;
27555505Sshin		}
27655505Sshin
27755505Sshin		if (p_src == NULL && p_dst == NULL) {
27855505Sshin			 if (p_mode != IPSEC_MODE_TRANSPORT) {
27962583Sitojun				__ipsec_errcode = EIPSEC_INVAL_ADDRESS;
28055505Sshin				return -1;
28155505Sshin			}
28255505Sshin		}
28355505Sshin		else if (p_src->sa_family != p_dst->sa_family) {
28462583Sitojun			__ipsec_errcode = EIPSEC_FAMILY_MISMATCH;
28555505Sshin			return -1;
28655505Sshin		}
28755505Sshin	}
28855505Sshin
28962583Sitojun	__ipsec_errcode = EIPSEC_NO_ERROR;
29055505Sshin	return 0;
29155505Sshin}
29255505Sshin
29355505Sshinstatic int
29455505Sshininit_x_policy()
29555505Sshin{
29655505Sshin	struct sadb_x_policy *p;
29755505Sshin
29855505Sshin	tlen = sizeof(struct sadb_x_policy);
29955505Sshin
30055505Sshin	pbuf = malloc(tlen);
30155505Sshin	if (pbuf == NULL) {
30262583Sitojun		__ipsec_errcode = EIPSEC_NO_BUFS;
30355505Sshin		return -1;
30455505Sshin	}
305122107Sume	memset(pbuf, 0, tlen);
30655505Sshin	p = (struct sadb_x_policy *)pbuf;
30755505Sshin	p->sadb_x_policy_len = 0;	/* must update later */
30855505Sshin	p->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
30955505Sshin	p->sadb_x_policy_type = p_type;
31055505Sshin	p->sadb_x_policy_dir = p_dir;
311122107Sume	p->sadb_x_policy_id = 0;
312122107Sume
31355505Sshin	offset = tlen;
31455505Sshin
31562583Sitojun	__ipsec_errcode = EIPSEC_NO_ERROR;
31655505Sshin	return 0;
31755505Sshin}
31855505Sshin
31955505Sshinstatic int
32055505Sshinset_x_request(src, dst)
32155505Sshin	struct sockaddr *src, *dst;
32255505Sshin{
32355505Sshin	struct sadb_x_ipsecrequest *p;
32455505Sshin	int reqlen;
32555505Sshin
32655505Sshin	reqlen = sizeof(*p)
32755505Sshin		+ (src ? src->sa_len : 0)
32855505Sshin		+ (dst ? dst->sa_len : 0);
32955505Sshin	tlen += reqlen;		/* increment to total length */
33055505Sshin
33155505Sshin	pbuf = realloc(pbuf, tlen);
33255505Sshin	if (pbuf == NULL) {
33362583Sitojun		__ipsec_errcode = EIPSEC_NO_BUFS;
33455505Sshin		return -1;
33555505Sshin	}
33655505Sshin	p = (struct sadb_x_ipsecrequest *)&pbuf[offset];
33755505Sshin	p->sadb_x_ipsecrequest_len = reqlen;
33855505Sshin	p->sadb_x_ipsecrequest_proto = p_protocol;
33955505Sshin	p->sadb_x_ipsecrequest_mode = p_mode;
34055505Sshin	p->sadb_x_ipsecrequest_level = p_level;
34162583Sitojun	p->sadb_x_ipsecrequest_reqid = p_reqid;
34255505Sshin	offset += sizeof(*p);
34355505Sshin
34455505Sshin	if (set_sockaddr(src) || set_sockaddr(dst))
34555505Sshin		return -1;
34655505Sshin
34762583Sitojun	__ipsec_errcode = EIPSEC_NO_ERROR;
34855505Sshin	return 0;
34955505Sshin}
35055505Sshin
35155505Sshinstatic int
35255505Sshinset_sockaddr(addr)
35355505Sshin	struct sockaddr *addr;
35455505Sshin{
35555505Sshin	if (addr == NULL) {
35662583Sitojun		__ipsec_errcode = EIPSEC_NO_ERROR;
35755505Sshin		return 0;
35855505Sshin	}
35955505Sshin
36055505Sshin	/* tlen has already incremented */
36155505Sshin
36255505Sshin	memcpy(&pbuf[offset], addr, addr->sa_len);
36355505Sshin
36455505Sshin	offset += addr->sa_len;
36555505Sshin
36662583Sitojun	__ipsec_errcode = EIPSEC_NO_ERROR;
36755505Sshin	return 0;
36855505Sshin}
36955505Sshin
37055505Sshinstatic void
37155505Sshinpolicy_parse_request_init()
37255505Sshin{
37355505Sshin	p_protocol = IPPROTO_IP;
37455505Sshin	p_mode = IPSEC_MODE_ANY;
37555505Sshin	p_level = IPSEC_LEVEL_DEFAULT;
37662583Sitojun	p_reqid = 0;
37755505Sshin	if (p_src != NULL) {
37855505Sshin		free(p_src);
37955505Sshin		p_src = NULL;
38055505Sshin	}
38155505Sshin	if (p_dst != NULL) {
38255505Sshin		free(p_dst);
38355505Sshin		p_dst = NULL;
38455505Sshin	}
38555505Sshin
38655505Sshin	return;
38755505Sshin}
38855505Sshin
38955505Sshinstatic caddr_t
39055505Sshinpolicy_parse(msg, msglen)
39155505Sshin	char *msg;
39255505Sshin	int msglen;
39355505Sshin{
39455505Sshin	int error;
39555505Sshin	pbuf = NULL;
39655505Sshin	tlen = 0;
39755505Sshin
39855505Sshin	/* initialize */
39955505Sshin	p_dir = IPSEC_DIR_INVALID;
40055505Sshin	p_type = IPSEC_POLICY_DISCARD;
40155505Sshin	policy_parse_request_init();
40255505Sshin	__policy__strbuffer__init__(msg);
40355505Sshin
40455505Sshin	error = yyparse();	/* it must be set errcode. */
405122107Sume	__policy__strbuffer__free__();
406122107Sume
40755505Sshin	if (error) {
40855505Sshin		if (pbuf != NULL)
40955505Sshin			free(pbuf);
41055505Sshin		return NULL;
41155505Sshin	}
41255505Sshin
41355505Sshin	/* update total length */
41455505Sshin	((struct sadb_x_policy *)pbuf)->sadb_x_policy_len = PFKEY_UNIT64(tlen);
41555505Sshin
41662583Sitojun	__ipsec_errcode = EIPSEC_NO_ERROR;
41755505Sshin
41855505Sshin	return pbuf;
41955505Sshin}
42055505Sshin
42155505Sshincaddr_t
42255505Sshinipsec_set_policy(msg, msglen)
42355505Sshin	char *msg;
42455505Sshin	int msglen;
42555505Sshin{
42655505Sshin	caddr_t policy;
42755505Sshin
42855505Sshin	policy = policy_parse(msg, msglen);
42955505Sshin	if (policy == NULL) {
43062583Sitojun		if (__ipsec_errcode == EIPSEC_NO_ERROR)
43162583Sitojun			__ipsec_errcode = EIPSEC_INVAL_ARGUMENT;
43255505Sshin		return NULL;
43355505Sshin	}
43455505Sshin
43562583Sitojun	__ipsec_errcode = EIPSEC_NO_ERROR;
43655505Sshin	return policy;
43755505Sshin}
43855505Sshin
439