%{
#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;
static int tlen = 0;
static int offset = 0;
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(const char *msg);
static struct sockaddr *parse_sockaddr(struct _val *addrbuf,
struct _val *portbuf);
static int rule_check(void);
static int init_x_policy(void);
static int set_x_request(struct sockaddr *, struct sockaddr *);
static int set_sockaddr(struct sockaddr *);
static void policy_parse_request_init(void);
static void *policy_parse(const char *, int);
extern void __policy__strbuffer__init__(const char *);
extern void __policy__strbuffer__free__(void);
extern int yyparse(void);
extern int yylex(void);
extern char *__libipsectext;
%}
%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;
}
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;
}
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;
}
p_priority = $3 + (u_int32_t) p_priority_offset;
if (init_x_policy())
return -1;
}
rules
| DIR
{
p_dir = $1;
p_type = 0;
p_priority = 0;
if (init_x_policy())
return -1;
}
;
rules
:
| 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);
}
;
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;
}
}
;
%%
void
yyerror(const char *msg)
{
fprintf(stderr, "libipsec: %s while parsing \"%s\"\n",
msg, __libipsectext);
return;
}
static struct sockaddr *
parse_sockaddr(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(void)
{
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(void)
{
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;
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
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(struct sockaddr *src, struct sockaddr *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;
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(struct sockaddr *addr)
{
if (addr == NULL) {
__ipsec_errcode = EIPSEC_NO_ERROR;
return 0;
}
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(void)
{
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(const char *msg, int msglen)
{
int error;
pbuf = NULL;
tlen = 0;
p_dir = IPSEC_DIR_INVALID;
p_type = IPSEC_POLICY_DISCARD;
policy_parse_request_init();
__policy__strbuffer__init__(msg);
error = yyparse();
__policy__strbuffer__free__();
if (error) {
if (pbuf != NULL)
free(pbuf);
return NULL;
}
((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(__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;
}