| /* $NetBSD: policy_parse.y,v 1.11 2009/02/16 18:36:21 tteras Exp $ */ |
| |
| /* $KAME: policy_parse.y,v 1.21 2003/12/12 08:01:26 itojun Exp $ */ |
| |
| /* |
| * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the project nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| /* |
| * IN/OUT bound policy configuration take place such below: |
| * in <priority> <policy> |
| * out <priority> <policy> |
| * |
| * <priority> is one of the following: |
| * priority <signed int> where the integer is an offset from the default |
| * priority, where negative numbers indicate lower |
| * priority (towards end of list) and positive numbers |
| * indicate higher priority (towards beginning of list) |
| * |
| * priority {low,def,high} {+,-} <unsigned int> where low and high are |
| * constants which are closer |
| * to the end of the list and |
| * beginning of the list, |
| * respectively |
| * |
| * <policy> is one of following: |
| * "discard", "none", "ipsec <requests>", "entrust", "bypass", |
| * |
| * The following requests are accepted as <requests>: |
| * |
| * protocol/mode/src-dst/level |
| * protocol/mode/src-dst parsed as protocol/mode/src-dst/default |
| * protocol/mode/src-dst/ parsed as protocol/mode/src-dst/default |
| * protocol/transport parsed as protocol/mode/any-any/default |
| * protocol/transport//level parsed as protocol/mode/any-any/level |
| * |
| * You can concatenate these requests with either ' '(single space) or '\n'. |
| */ |
| |
| %{ |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <sys/types.h> |
| #include <sys/param.h> |
| #include <sys/socket.h> |
| |
| #include <netinet/in.h> |
| #include PATH_IPSEC_H |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <netdb.h> |
| |
| #include <errno.h> |
| |
| #include "config.h" |
| |
| #include "ipsec_strerror.h" |
| #include "libpfkey.h" |
| |
| #ifndef INT32_MAX |
| #define INT32_MAX (0xffffffff) |
| #endif |
| |
| #ifndef INT32_MIN |
| #define INT32_MIN (-INT32_MAX-1) |
| #endif |
| |
| #define ATOX(c) \ |
| (isdigit(c) ? (c - '0') : (isupper(c) ? (c - 'A' + 10) : (c - 'a' + 10) )) |
| |
| static u_int8_t *pbuf = NULL; /* sadb_x_policy buffer */ |
| static int tlen = 0; /* total length of pbuf */ |
| static int offset = 0; /* offset of pbuf */ |
| static int p_dir, p_type, p_protocol, p_mode, p_level, p_reqid; |
| static u_int32_t p_priority = 0; |
| static long p_priority_offset = 0; |
| static struct sockaddr *p_src = NULL; |
| static struct sockaddr *p_dst = NULL; |
| |
| struct _val; |
| extern void yyerror __P((char *msg)); |
| static struct sockaddr *parse_sockaddr __P((struct _val *addrbuf, |
| struct _val *portbuf)); |
| static int rule_check __P((void)); |
| static int init_x_policy __P((void)); |
| static int set_x_request __P((struct sockaddr *, struct sockaddr *)); |
| static int set_sockaddr __P((struct sockaddr *)); |
| static void policy_parse_request_init __P((void)); |
| static void *policy_parse __P((const char *, int)); |
| |
| extern void __policy__strbuffer__init__ __P((const char *)); |
| extern void __policy__strbuffer__free__ __P((void)); |
| extern int yyparse __P((void)); |
| extern int yylex __P((void)); |
| |
| extern char *__libipsectext; /*XXX*/ |
| |
| %} |
| |
| %union { |
| u_int num; |
| u_int32_t num32; |
| struct _val { |
| int len; |
| char *buf; |
| } val; |
| } |
| |
| %token DIR |
| %token PRIORITY PLUS |
| %token <num32> PRIO_BASE |
| %token <val> PRIO_OFFSET |
| %token ACTION PROTOCOL MODE LEVEL LEVEL_SPECIFY IPADDRESS PORT |
| %token ME ANY |
| %token SLASH HYPHEN |
| %type <num> DIR PRIORITY ACTION PROTOCOL MODE LEVEL |
| %type <val> IPADDRESS LEVEL_SPECIFY PORT |
| |
| %% |
| policy_spec |
| : DIR ACTION |
| { |
| p_dir = $1; |
| p_type = $2; |
| |
| #ifdef HAVE_PFKEY_POLICY_PRIORITY |
| p_priority = PRIORITY_DEFAULT; |
| #else |
| p_priority = 0; |
| #endif |
| |
| if (init_x_policy()) |
| return -1; |
| } |
| rules |
| | DIR PRIORITY PRIO_OFFSET ACTION |
| { |
| p_dir = $1; |
| p_type = $4; |
| p_priority_offset = -atol($3.buf); |
| |
| errno = 0; |
| if (errno != 0 || p_priority_offset < INT32_MIN) |
| { |
| __ipsec_errcode = EIPSEC_INVAL_PRIORITY_OFFSET; |
| return -1; |
| } |
| |
| p_priority = PRIORITY_DEFAULT + (u_int32_t) p_priority_offset; |
| |
| if (init_x_policy()) |
| return -1; |
| } |
| rules |
| | DIR PRIORITY HYPHEN PRIO_OFFSET ACTION |
| { |
| p_dir = $1; |
| p_type = $5; |
| |
| errno = 0; |
| p_priority_offset = atol($4.buf); |
| |
| if (errno != 0 || p_priority_offset > INT32_MAX) |
| { |
| __ipsec_errcode = EIPSEC_INVAL_PRIORITY_OFFSET; |
| return -1; |
| } |
| |
| /* negative input value means lower priority, therefore higher |
| actual value so that is closer to the end of the list */ |
| p_priority = PRIORITY_DEFAULT + (u_int32_t) p_priority_offset; |
| |
| if (init_x_policy()) |
| return -1; |
| } |
| rules |
| | DIR PRIORITY PRIO_BASE ACTION |
| { |
| p_dir = $1; |
| p_type = $4; |
| |
| p_priority = $3; |
| |
| if (init_x_policy()) |
| return -1; |
| } |
| rules |
| | DIR PRIORITY PRIO_BASE PLUS PRIO_OFFSET ACTION |
| { |
| p_dir = $1; |
| p_type = $6; |
| |
| errno = 0; |
| p_priority_offset = atol($5.buf); |
| |
| if (errno != 0 || p_priority_offset > PRIORITY_OFFSET_NEGATIVE_MAX) |
| { |
| __ipsec_errcode = EIPSEC_INVAL_PRIORITY_BASE_OFFSET; |
| return -1; |
| } |
| |
| /* adding value means higher priority, therefore lower |
| actual value so that is closer to the beginning of the list */ |
| p_priority = $3 - (u_int32_t) p_priority_offset; |
| |
| if (init_x_policy()) |
| return -1; |
| } |
| rules |
| | DIR PRIORITY PRIO_BASE HYPHEN PRIO_OFFSET ACTION |
| { |
| p_dir = $1; |
| p_type = $6; |
| |
| errno = 0; |
| p_priority_offset = atol($5.buf); |
| |
| if (errno != 0 || p_priority_offset > PRIORITY_OFFSET_POSITIVE_MAX) |
| { |
| __ipsec_errcode = EIPSEC_INVAL_PRIORITY_BASE_OFFSET; |
| return -1; |
| } |
| |
| /* subtracting value means lower priority, therefore higher |
| actual value so that is closer to the end of the list */ |
| p_priority = $3 + (u_int32_t) p_priority_offset; |
| |
| if (init_x_policy()) |
| return -1; |
| } |
| rules |
| | DIR |
| { |
| p_dir = $1; |
| p_type = 0; /* ignored it by kernel */ |
| |
| p_priority = 0; |
| |
| if (init_x_policy()) |
| return -1; |
| } |
| ; |
| |
| rules |
| : /*NOTHING*/ |
| | rules rule { |
| if (rule_check() < 0) |
| return -1; |
| |
| if (set_x_request(p_src, p_dst) < 0) |
| return -1; |
| |
| policy_parse_request_init(); |
| } |
| ; |
| |
| rule |
| : protocol SLASH mode SLASH addresses SLASH level |
| | protocol SLASH mode SLASH addresses SLASH |
| | protocol SLASH mode SLASH addresses |
| | protocol SLASH mode SLASH |
| | protocol SLASH mode SLASH SLASH level |
| | protocol SLASH mode |
| | protocol SLASH { |
| __ipsec_errcode = EIPSEC_FEW_ARGUMENTS; |
| return -1; |
| } |
| | protocol { |
| __ipsec_errcode = EIPSEC_FEW_ARGUMENTS; |
| return -1; |
| } |
| ; |
| |
| protocol |
| : PROTOCOL { p_protocol = $1; } |
| ; |
| |
| mode |
| : MODE { p_mode = $1; } |
| ; |
| |
| level |
| : LEVEL { |
| p_level = $1; |
| p_reqid = 0; |
| } |
| | LEVEL_SPECIFY { |
| p_level = IPSEC_LEVEL_UNIQUE; |
| p_reqid = atol($1.buf); /* atol() is good. */ |
| } |
| ; |
| |
| addresses |
| : IPADDRESS { |
| p_src = parse_sockaddr(&$1, NULL); |
| if (p_src == NULL) |
| return -1; |
| } |
| HYPHEN |
| IPADDRESS { |
| p_dst = parse_sockaddr(&$4, NULL); |
| if (p_dst == NULL) |
| return -1; |
| } |
| | IPADDRESS PORT { |
| p_src = parse_sockaddr(&$1, &$2); |
| if (p_src == NULL) |
| return -1; |
| } |
| HYPHEN |
| IPADDRESS PORT { |
| p_dst = parse_sockaddr(&$5, &$6); |
| if (p_dst == NULL) |
| return -1; |
| } |
| | ME HYPHEN ANY { |
| if (p_dir != IPSEC_DIR_OUTBOUND) { |
| __ipsec_errcode = EIPSEC_INVAL_DIR; |
| return -1; |
| } |
| } |
| | ANY HYPHEN ME { |
| if (p_dir != IPSEC_DIR_INBOUND) { |
| __ipsec_errcode = EIPSEC_INVAL_DIR; |
| return -1; |
| } |
| } |
| /* |
| | ME HYPHEN ME |
| */ |
| ; |
| |
| %% |
| |
| void |
| yyerror(msg) |
| char *msg; |
| { |
| fprintf(stderr, "libipsec: %s while parsing \"%s\"\n", |
| msg, __libipsectext); |
| |
| return; |
| } |
| |
| static struct sockaddr * |
| parse_sockaddr(addrbuf, portbuf) |
| struct _val *addrbuf; |
| struct _val *portbuf; |
| { |
| struct addrinfo hints, *res; |
| char *addr; |
| char *serv = NULL; |
| int error; |
| struct sockaddr *newaddr = NULL; |
| |
| if ((addr = malloc(addrbuf->len + 1)) == NULL) { |
| yyerror("malloc failed"); |
| __ipsec_set_strerror(strerror(errno)); |
| return NULL; |
| } |
| |
| if (portbuf && ((serv = malloc(portbuf->len + 1)) == NULL)) { |
| free(addr); |
| yyerror("malloc failed"); |
| __ipsec_set_strerror(strerror(errno)); |
| return NULL; |
| } |
| |
| strncpy(addr, addrbuf->buf, addrbuf->len); |
| addr[addrbuf->len] = '\0'; |
| |
| if (portbuf) { |
| strncpy(serv, portbuf->buf, portbuf->len); |
| serv[portbuf->len] = '\0'; |
| } |
| |
| memset(&hints, 0, sizeof(hints)); |
| hints.ai_family = PF_UNSPEC; |
| hints.ai_flags = AI_NUMERICHOST; |
| hints.ai_socktype = SOCK_DGRAM; |
| error = getaddrinfo(addr, serv, &hints, &res); |
| free(addr); |
| if (serv != NULL) |
| free(serv); |
| if (error != 0) { |
| yyerror("invalid IP address"); |
| __ipsec_set_strerror(gai_strerror(error)); |
| return NULL; |
| } |
| |
| if (res->ai_addr == NULL) { |
| yyerror("invalid IP address"); |
| __ipsec_set_strerror(gai_strerror(error)); |
| return NULL; |
| } |
| |
| newaddr = malloc(res->ai_addrlen); |
| if (newaddr == NULL) { |
| __ipsec_errcode = EIPSEC_NO_BUFS; |
| freeaddrinfo(res); |
| return NULL; |
| } |
| memcpy(newaddr, res->ai_addr, res->ai_addrlen); |
| |
| freeaddrinfo(res); |
| |
| __ipsec_errcode = EIPSEC_NO_ERROR; |
| return newaddr; |
| } |
| |
| static int |
| rule_check() |
| { |
| if (p_type == IPSEC_POLICY_IPSEC) { |
| if (p_protocol == IPPROTO_IP) { |
| __ipsec_errcode = EIPSEC_NO_PROTO; |
| return -1; |
| } |
| |
| if (p_mode != IPSEC_MODE_TRANSPORT |
| && p_mode != IPSEC_MODE_TUNNEL) { |
| __ipsec_errcode = EIPSEC_INVAL_MODE; |
| return -1; |
| } |
| |
| if (p_src == NULL && p_dst == NULL) { |
| if (p_mode != IPSEC_MODE_TRANSPORT) { |
| __ipsec_errcode = EIPSEC_INVAL_ADDRESS; |
| return -1; |
| } |
| } |
| else if (p_src->sa_family != p_dst->sa_family) { |
| __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; |
| return -1; |
| } |
| } |
| |
| __ipsec_errcode = EIPSEC_NO_ERROR; |
| return 0; |
| } |
| |
| static int |
| init_x_policy() |
| { |
| struct sadb_x_policy *p; |
| |
| if (pbuf) { |
| free(pbuf); |
| tlen = 0; |
| } |
| pbuf = malloc(sizeof(struct sadb_x_policy)); |
| if (pbuf == NULL) { |
| __ipsec_errcode = EIPSEC_NO_BUFS; |
| return -1; |
| } |
| tlen = sizeof(struct sadb_x_policy); |
| |
| memset(pbuf, 0, tlen); |
| p = (struct sadb_x_policy *)pbuf; |
| p->sadb_x_policy_len = 0; /* must update later */ |
| p->sadb_x_policy_exttype = SADB_X_EXT_POLICY; |
| p->sadb_x_policy_type = p_type; |
| p->sadb_x_policy_dir = p_dir; |
| p->sadb_x_policy_id = 0; |
| #ifdef HAVE_PFKEY_POLICY_PRIORITY |
| p->sadb_x_policy_priority = p_priority; |
| #else |
| /* fail if given a priority and libipsec was not compiled with |
| priority support */ |
| if (p_priority != 0) |
| { |
| __ipsec_errcode = EIPSEC_PRIORITY_NOT_COMPILED; |
| return -1; |
| } |
| #endif |
| |
| offset = tlen; |
| |
| __ipsec_errcode = EIPSEC_NO_ERROR; |
| return 0; |
| } |
| |
| static int |
| set_x_request(src, dst) |
| struct sockaddr *src, *dst; |
| { |
| struct sadb_x_ipsecrequest *p; |
| int reqlen; |
| u_int8_t *n; |
| |
| reqlen = sizeof(*p) |
| + (src ? sysdep_sa_len(src) : 0) |
| + (dst ? sysdep_sa_len(dst) : 0); |
| tlen += reqlen; /* increment to total length */ |
| |
| n = realloc(pbuf, tlen); |
| if (n == NULL) { |
| __ipsec_errcode = EIPSEC_NO_BUFS; |
| return -1; |
| } |
| pbuf = n; |
| |
| p = (struct sadb_x_ipsecrequest *)&pbuf[offset]; |
| p->sadb_x_ipsecrequest_len = reqlen; |
| p->sadb_x_ipsecrequest_proto = p_protocol; |
| p->sadb_x_ipsecrequest_mode = p_mode; |
| p->sadb_x_ipsecrequest_level = p_level; |
| p->sadb_x_ipsecrequest_reqid = p_reqid; |
| offset += sizeof(*p); |
| |
| if (set_sockaddr(src) || set_sockaddr(dst)) |
| return -1; |
| |
| __ipsec_errcode = EIPSEC_NO_ERROR; |
| return 0; |
| } |
| |
| static int |
| set_sockaddr(addr) |
| struct sockaddr *addr; |
| { |
| if (addr == NULL) { |
| __ipsec_errcode = EIPSEC_NO_ERROR; |
| return 0; |
| } |
| |
| /* tlen has already incremented */ |
| |
| memcpy(&pbuf[offset], addr, sysdep_sa_len(addr)); |
| |
| offset += sysdep_sa_len(addr); |
| |
| __ipsec_errcode = EIPSEC_NO_ERROR; |
| return 0; |
| } |
| |
| static void |
| policy_parse_request_init() |
| { |
| p_protocol = IPPROTO_IP; |
| p_mode = IPSEC_MODE_ANY; |
| p_level = IPSEC_LEVEL_DEFAULT; |
| p_reqid = 0; |
| if (p_src != NULL) { |
| free(p_src); |
| p_src = NULL; |
| } |
| if (p_dst != NULL) { |
| free(p_dst); |
| p_dst = NULL; |
| } |
| |
| return; |
| } |
| |
| static void * |
| policy_parse(msg, msglen) |
| const char *msg; |
| int msglen; |
| { |
| int error; |
| |
| pbuf = NULL; |
| tlen = 0; |
| |
| /* initialize */ |
| p_dir = IPSEC_DIR_INVALID; |
| p_type = IPSEC_POLICY_DISCARD; |
| policy_parse_request_init(); |
| __policy__strbuffer__init__(msg); |
| |
| error = yyparse(); /* it must be set errcode. */ |
| __policy__strbuffer__free__(); |
| |
| if (error) { |
| if (pbuf != NULL) |
| free(pbuf); |
| return NULL; |
| } |
| |
| /* update total length */ |
| ((struct sadb_x_policy *)pbuf)->sadb_x_policy_len = PFKEY_UNIT64(tlen); |
| |
| __ipsec_errcode = EIPSEC_NO_ERROR; |
| |
| return pbuf; |
| } |
| |
| ipsec_policy_t |
| ipsec_set_policy(msg, msglen) |
| __ipsec_const char *msg; |
| int msglen; |
| { |
| caddr_t policy; |
| |
| policy = policy_parse(msg, msglen); |
| if (policy == NULL) { |
| if (__ipsec_errcode == EIPSEC_NO_ERROR) |
| __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; |
| return NULL; |
| } |
| |
| __ipsec_errcode = EIPSEC_NO_ERROR; |
| return policy; |
| } |