3148 lines
74 KiB
Text
3148 lines
74 KiB
Text
/* $OpenBSD: parse.y,v 1.146 2024/04/25 14:24:54 jsg Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de>
|
|
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
|
|
* Copyright (c) 2004, 2005 Hans-Joerg Hoexer <hshoexer@openbsd.org>
|
|
* Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
|
|
* Copyright (c) 2001 Markus Friedl. All rights reserved.
|
|
* Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
|
|
* Copyright (c) 2001 Theo de Raadt. All rights reserved.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
%{
|
|
#include <sys/types.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <net/if.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/ip_ipsp.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include <ctype.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <ifaddrs.h>
|
|
#include <limits.h>
|
|
#include <netdb.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <unistd.h>
|
|
#include <event.h>
|
|
|
|
#include "iked.h"
|
|
#include "ikev2.h"
|
|
#include "eap.h"
|
|
|
|
TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
|
|
static struct file {
|
|
TAILQ_ENTRY(file) entry;
|
|
FILE *stream;
|
|
char *name;
|
|
size_t ungetpos;
|
|
size_t ungetsize;
|
|
u_char *ungetbuf;
|
|
int eof_reached;
|
|
int lineno;
|
|
int errors;
|
|
} *file, *topfile;
|
|
struct file *pushfile(const char *, int);
|
|
int popfile(void);
|
|
int check_file_secrecy(int, const char *);
|
|
int yyparse(void);
|
|
int yylex(void);
|
|
int yyerror(const char *, ...)
|
|
__attribute__((__format__ (printf, 1, 2)))
|
|
__attribute__((__nonnull__ (1)));
|
|
int kw_cmp(const void *, const void *);
|
|
int lookup(char *);
|
|
int igetc(void);
|
|
int lgetc(int);
|
|
void lungetc(int);
|
|
int findeol(void);
|
|
|
|
TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
|
|
struct sym {
|
|
TAILQ_ENTRY(sym) entry;
|
|
int used;
|
|
int persist;
|
|
char *nam;
|
|
char *val;
|
|
};
|
|
int symset(const char *, const char *, int);
|
|
char *symget(const char *);
|
|
|
|
#define KEYSIZE_LIMIT 1024
|
|
|
|
static struct iked *env = NULL;
|
|
static int debug = 0;
|
|
static int rules = 0;
|
|
static int passive = 0;
|
|
static int decouple = 0;
|
|
static int mobike = 1;
|
|
static int enforcesingleikesa = 0;
|
|
static int stickyaddress = 0;
|
|
static int fragmentation = 0;
|
|
static int vendorid = 1;
|
|
static int dpd_interval = IKED_IKE_SA_ALIVE_TIMEOUT;
|
|
static char *ocsp_url = NULL;
|
|
static long ocsp_tolerate = 0;
|
|
static long ocsp_maxage = -1;
|
|
static int cert_partial_chain = 0;
|
|
|
|
struct iked_transform ikev2_default_ike_transforms[] = {
|
|
{ IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 256 },
|
|
{ IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 192 },
|
|
{ IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 128 },
|
|
{ IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_3DES },
|
|
{ IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_SHA2_256 },
|
|
{ IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_SHA2_384 },
|
|
{ IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_SHA2_512 },
|
|
{ IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_SHA1 },
|
|
{ IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA2_256_128 },
|
|
{ IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA2_384_192 },
|
|
{ IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA2_512_256 },
|
|
{ IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA1_96 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_CURVE25519 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_ECP_521 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_ECP_384 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_ECP_256 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_4096 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_3072 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_2048 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_1536 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_1024 },
|
|
{ 0 }
|
|
};
|
|
size_t ikev2_default_nike_transforms = ((sizeof(ikev2_default_ike_transforms) /
|
|
sizeof(ikev2_default_ike_transforms[0])) - 1);
|
|
|
|
struct iked_transform ikev2_default_ike_transforms_noauth[] = {
|
|
{ IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_GCM_16, 128 },
|
|
{ IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_GCM_16, 256 },
|
|
{ IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_SHA2_256 },
|
|
{ IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_SHA2_384 },
|
|
{ IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_SHA2_512 },
|
|
{ IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_SHA1 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_CURVE25519 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_ECP_521 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_ECP_384 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_ECP_256 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_4096 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_3072 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_2048 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_1536 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_1024 },
|
|
{ 0 }
|
|
};
|
|
size_t ikev2_default_nike_transforms_noauth =
|
|
((sizeof(ikev2_default_ike_transforms_noauth) /
|
|
sizeof(ikev2_default_ike_transforms_noauth[0])) - 1);
|
|
|
|
struct iked_transform ikev2_default_esp_transforms[] = {
|
|
{ IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 256 },
|
|
{ IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 192 },
|
|
{ IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 128 },
|
|
{ IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA2_256_128 },
|
|
{ IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA2_384_192 },
|
|
{ IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA2_512_256 },
|
|
{ IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA1_96 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_NONE },
|
|
{ IKEV2_XFORMTYPE_ESN, IKEV2_XFORMESN_ESN },
|
|
{ IKEV2_XFORMTYPE_ESN, IKEV2_XFORMESN_NONE },
|
|
{ 0 }
|
|
};
|
|
size_t ikev2_default_nesp_transforms = ((sizeof(ikev2_default_esp_transforms) /
|
|
sizeof(ikev2_default_esp_transforms[0])) - 1);
|
|
|
|
struct iked_transform ikev2_default_esp_transforms_noauth[] = {
|
|
{ IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_GCM_16, 128 },
|
|
{ IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_GCM_16, 256 },
|
|
{ IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_NONE },
|
|
{ IKEV2_XFORMTYPE_ESN, IKEV2_XFORMESN_ESN },
|
|
{ IKEV2_XFORMTYPE_ESN, IKEV2_XFORMESN_NONE },
|
|
{ 0 }
|
|
};
|
|
size_t ikev2_default_nesp_transforms_noauth =
|
|
((sizeof(ikev2_default_esp_transforms_noauth) /
|
|
sizeof(ikev2_default_esp_transforms_noauth[0])) - 1);
|
|
|
|
const struct ipsec_xf authxfs[] = {
|
|
{ "hmac-md5", IKEV2_XFORMAUTH_HMAC_MD5_96, 16 },
|
|
{ "hmac-sha1", IKEV2_XFORMAUTH_HMAC_SHA1_96, 20 },
|
|
{ "hmac-sha2-256", IKEV2_XFORMAUTH_HMAC_SHA2_256_128, 32 },
|
|
{ "hmac-sha2-384", IKEV2_XFORMAUTH_HMAC_SHA2_384_192, 48 },
|
|
{ "hmac-sha2-512", IKEV2_XFORMAUTH_HMAC_SHA2_512_256, 64 },
|
|
{ NULL }
|
|
};
|
|
|
|
const struct ipsec_xf prfxfs[] = {
|
|
{ "hmac-md5", IKEV2_XFORMPRF_HMAC_MD5, 16 },
|
|
{ "hmac-sha1", IKEV2_XFORMPRF_HMAC_SHA1, 20 },
|
|
{ "hmac-sha2-256", IKEV2_XFORMPRF_HMAC_SHA2_256, 32 },
|
|
{ "hmac-sha2-384", IKEV2_XFORMPRF_HMAC_SHA2_384, 48 },
|
|
{ "hmac-sha2-512", IKEV2_XFORMPRF_HMAC_SHA2_512, 64 },
|
|
{ NULL }
|
|
};
|
|
|
|
const struct ipsec_xf *encxfs = NULL;
|
|
|
|
const struct ipsec_xf ikeencxfs[] = {
|
|
{ "3des", IKEV2_XFORMENCR_3DES, 24 },
|
|
{ "3des-cbc", IKEV2_XFORMENCR_3DES, 24 },
|
|
{ "aes-128", IKEV2_XFORMENCR_AES_CBC, 16, 16 },
|
|
{ "aes-192", IKEV2_XFORMENCR_AES_CBC, 24, 24 },
|
|
{ "aes-256", IKEV2_XFORMENCR_AES_CBC, 32, 32 },
|
|
{ "aes-128-gcm", IKEV2_XFORMENCR_AES_GCM_16, 16, 16, 4, 1 },
|
|
{ "aes-256-gcm", IKEV2_XFORMENCR_AES_GCM_16, 32, 32, 4, 1 },
|
|
{ "aes-128-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 16, 16, 4, 1 },
|
|
{ "aes-256-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 32, 32, 4, 1 },
|
|
{ NULL }
|
|
};
|
|
|
|
const struct ipsec_xf ipsecencxfs[] = {
|
|
{ "3des", IKEV2_XFORMENCR_3DES, 24 },
|
|
{ "3des-cbc", IKEV2_XFORMENCR_3DES, 24 },
|
|
{ "aes-128", IKEV2_XFORMENCR_AES_CBC, 16, 16 },
|
|
{ "aes-192", IKEV2_XFORMENCR_AES_CBC, 24, 24 },
|
|
{ "aes-256", IKEV2_XFORMENCR_AES_CBC, 32, 32 },
|
|
{ "aes-128-ctr", IKEV2_XFORMENCR_AES_CTR, 16, 16, 4 },
|
|
{ "aes-192-ctr", IKEV2_XFORMENCR_AES_CTR, 24, 24, 4 },
|
|
{ "aes-256-ctr", IKEV2_XFORMENCR_AES_CTR, 32, 32, 4 },
|
|
{ "aes-128-gcm", IKEV2_XFORMENCR_AES_GCM_16, 16, 16, 4, 1 },
|
|
{ "aes-192-gcm", IKEV2_XFORMENCR_AES_GCM_16, 24, 24, 4, 1 },
|
|
{ "aes-256-gcm", IKEV2_XFORMENCR_AES_GCM_16, 32, 32, 4, 1 },
|
|
{ "aes-128-gmac", IKEV2_XFORMENCR_NULL_AES_GMAC, 16, 16, 4, 1 },
|
|
{ "aes-192-gmac", IKEV2_XFORMENCR_NULL_AES_GMAC, 24, 24, 4, 1 },
|
|
{ "aes-256-gmac", IKEV2_XFORMENCR_NULL_AES_GMAC, 32, 32, 4, 1 },
|
|
{ "blowfish", IKEV2_XFORMENCR_BLOWFISH, 20, 20 },
|
|
{ "cast", IKEV2_XFORMENCR_CAST, 16, 16 },
|
|
{ "chacha20-poly1305", IKEV2_XFORMENCR_CHACHA20_POLY1305,
|
|
32, 32, 4, 1 },
|
|
{ "null", IKEV2_XFORMENCR_NULL, 0, 0 },
|
|
{ NULL }
|
|
};
|
|
|
|
const struct ipsec_xf groupxfs[] = {
|
|
{ "none", IKEV2_XFORMDH_NONE },
|
|
{ "modp768", IKEV2_XFORMDH_MODP_768 },
|
|
{ "grp1", IKEV2_XFORMDH_MODP_768 },
|
|
{ "modp1024", IKEV2_XFORMDH_MODP_1024 },
|
|
{ "grp2", IKEV2_XFORMDH_MODP_1024 },
|
|
{ "modp1536", IKEV2_XFORMDH_MODP_1536 },
|
|
{ "grp5", IKEV2_XFORMDH_MODP_1536 },
|
|
{ "modp2048", IKEV2_XFORMDH_MODP_2048 },
|
|
{ "grp14", IKEV2_XFORMDH_MODP_2048 },
|
|
{ "modp3072", IKEV2_XFORMDH_MODP_3072 },
|
|
{ "grp15", IKEV2_XFORMDH_MODP_3072 },
|
|
{ "modp4096", IKEV2_XFORMDH_MODP_4096 },
|
|
{ "grp16", IKEV2_XFORMDH_MODP_4096 },
|
|
{ "modp6144", IKEV2_XFORMDH_MODP_6144 },
|
|
{ "grp17", IKEV2_XFORMDH_MODP_6144 },
|
|
{ "modp8192", IKEV2_XFORMDH_MODP_8192 },
|
|
{ "grp18", IKEV2_XFORMDH_MODP_8192 },
|
|
{ "ecp256", IKEV2_XFORMDH_ECP_256 },
|
|
{ "grp19", IKEV2_XFORMDH_ECP_256 },
|
|
{ "ecp384", IKEV2_XFORMDH_ECP_384 },
|
|
{ "grp20", IKEV2_XFORMDH_ECP_384 },
|
|
{ "ecp521", IKEV2_XFORMDH_ECP_521 },
|
|
{ "grp21", IKEV2_XFORMDH_ECP_521 },
|
|
{ "ecp192", IKEV2_XFORMDH_ECP_192 },
|
|
{ "grp25", IKEV2_XFORMDH_ECP_192 },
|
|
{ "ecp224", IKEV2_XFORMDH_ECP_224 },
|
|
{ "grp26", IKEV2_XFORMDH_ECP_224 },
|
|
{ "brainpool224", IKEV2_XFORMDH_BRAINPOOL_P224R1 },
|
|
{ "grp27", IKEV2_XFORMDH_BRAINPOOL_P224R1 },
|
|
{ "brainpool256", IKEV2_XFORMDH_BRAINPOOL_P256R1 },
|
|
{ "grp28", IKEV2_XFORMDH_BRAINPOOL_P256R1 },
|
|
{ "brainpool384", IKEV2_XFORMDH_BRAINPOOL_P384R1 },
|
|
{ "grp29", IKEV2_XFORMDH_BRAINPOOL_P384R1 },
|
|
{ "brainpool512", IKEV2_XFORMDH_BRAINPOOL_P512R1 },
|
|
{ "grp30", IKEV2_XFORMDH_BRAINPOOL_P512R1 },
|
|
{ "curve25519", IKEV2_XFORMDH_CURVE25519 },
|
|
{ "grp31", IKEV2_XFORMDH_CURVE25519 },
|
|
{ "sntrup761x25519", IKEV2_XFORMDH_X_SNTRUP761X25519 },
|
|
{ NULL }
|
|
};
|
|
|
|
const struct ipsec_xf esnxfs[] = {
|
|
{ "esn", IKEV2_XFORMESN_ESN },
|
|
{ "noesn", IKEV2_XFORMESN_NONE },
|
|
{ NULL }
|
|
};
|
|
|
|
const struct ipsec_xf methodxfs[] = {
|
|
{ "none", IKEV2_AUTH_NONE },
|
|
{ "rsa", IKEV2_AUTH_RSA_SIG },
|
|
{ "ecdsa256", IKEV2_AUTH_ECDSA_256 },
|
|
{ "ecdsa384", IKEV2_AUTH_ECDSA_384 },
|
|
{ "ecdsa521", IKEV2_AUTH_ECDSA_521 },
|
|
{ "rfc7427", IKEV2_AUTH_SIG },
|
|
{ "signature", IKEV2_AUTH_SIG_ANY },
|
|
{ NULL }
|
|
};
|
|
|
|
const struct ipsec_xf saxfs[] = {
|
|
{ "esp", IKEV2_SAPROTO_ESP },
|
|
{ "ah", IKEV2_SAPROTO_AH },
|
|
{ NULL }
|
|
};
|
|
|
|
const struct ipsec_xf cpxfs[] = {
|
|
{ "address", IKEV2_CFG_INTERNAL_IP4_ADDRESS, AF_INET },
|
|
{ "netmask", IKEV2_CFG_INTERNAL_IP4_NETMASK, AF_INET },
|
|
{ "name-server", IKEV2_CFG_INTERNAL_IP4_DNS, AF_INET },
|
|
{ "netbios-server", IKEV2_CFG_INTERNAL_IP4_NBNS, AF_INET },
|
|
{ "dhcp-server", IKEV2_CFG_INTERNAL_IP4_DHCP, AF_INET },
|
|
{ "address", IKEV2_CFG_INTERNAL_IP6_ADDRESS, AF_INET6 },
|
|
{ "name-server", IKEV2_CFG_INTERNAL_IP6_DNS, AF_INET6 },
|
|
{ "netbios-server", IKEV2_CFG_INTERNAL_IP6_NBNS, AF_INET6 },
|
|
{ "dhcp-server", IKEV2_CFG_INTERNAL_IP6_DHCP, AF_INET6 },
|
|
{ "protected-subnet", IKEV2_CFG_INTERNAL_IP4_SUBNET, AF_INET },
|
|
{ "protected-subnet", IKEV2_CFG_INTERNAL_IP6_SUBNET, AF_INET6 },
|
|
{ "access-server", IKEV2_CFG_INTERNAL_IP4_SERVER, AF_INET },
|
|
{ "access-server", IKEV2_CFG_INTERNAL_IP6_SERVER, AF_INET6 },
|
|
{ NULL }
|
|
};
|
|
|
|
const struct iked_lifetime deflifetime = {
|
|
IKED_LIFETIME_BYTES,
|
|
IKED_LIFETIME_SECONDS
|
|
};
|
|
|
|
#define IPSEC_ADDR_ANY (0x1)
|
|
#define IPSEC_ADDR_DYNAMIC (0x2)
|
|
|
|
struct ipsec_addr_wrap {
|
|
struct sockaddr_storage address;
|
|
uint8_t mask;
|
|
int netaddress;
|
|
sa_family_t af;
|
|
unsigned int type;
|
|
unsigned int action;
|
|
uint16_t port;
|
|
char *name;
|
|
struct ipsec_addr_wrap *next;
|
|
struct ipsec_addr_wrap *tail;
|
|
struct ipsec_addr_wrap *srcnat;
|
|
};
|
|
|
|
struct ipsec_hosts {
|
|
struct ipsec_addr_wrap *src;
|
|
struct ipsec_addr_wrap *dst;
|
|
};
|
|
|
|
struct ipsec_filters {
|
|
char *tag;
|
|
unsigned int tap;
|
|
};
|
|
|
|
void copy_sockaddrtoipa(struct ipsec_addr_wrap *,
|
|
struct sockaddr *);
|
|
struct ipsec_addr_wrap *host(const char *);
|
|
struct ipsec_addr_wrap *host_ip(const char *, int);
|
|
struct ipsec_addr_wrap *host_dns(const char *, int);
|
|
struct ipsec_addr_wrap *host_if(const char *, int);
|
|
struct ipsec_addr_wrap *host_any(void);
|
|
struct ipsec_addr_wrap *host_dynamic(void);
|
|
void ifa_load(void);
|
|
int ifa_exists(const char *);
|
|
struct ipsec_addr_wrap *ifa_lookup(const char *ifa_name);
|
|
struct ipsec_addr_wrap *ifa_grouplookup(const char *);
|
|
void set_ipmask(struct ipsec_addr_wrap *, int);
|
|
const struct ipsec_xf *parse_xf(const char *, unsigned int,
|
|
const struct ipsec_xf *);
|
|
void copy_transforms(unsigned int,
|
|
const struct ipsec_xf **, unsigned int,
|
|
struct iked_transform **, unsigned int *,
|
|
struct iked_transform *, size_t);
|
|
int create_ike(char *, int, struct ipsec_addr_wrap *,
|
|
int, struct ipsec_hosts *,
|
|
struct ipsec_hosts *, struct ipsec_mode *,
|
|
struct ipsec_mode *, uint8_t,
|
|
uint8_t, char *, char *,
|
|
uint32_t, struct iked_lifetime *,
|
|
struct iked_auth *, struct ipsec_filters *,
|
|
struct ipsec_addr_wrap *, char *);
|
|
int create_user(const char *, const char *);
|
|
int get_id_type(char *);
|
|
uint8_t x2i(unsigned char *);
|
|
int parsekey(unsigned char *, size_t, struct iked_auth *);
|
|
int parsekeyfile(char *, struct iked_auth *);
|
|
void iaw_free(struct ipsec_addr_wrap *);
|
|
static int create_flow(struct iked_policy *pol, int, struct ipsec_addr_wrap *ipa,
|
|
struct ipsec_addr_wrap *ipb);
|
|
static int expand_flows(struct iked_policy *, int, struct ipsec_addr_wrap *,
|
|
struct ipsec_addr_wrap *);
|
|
static struct ipsec_addr_wrap *
|
|
expand_keyword(struct ipsec_addr_wrap *);
|
|
|
|
struct ipsec_transforms *ipsec_transforms;
|
|
struct ipsec_filters *ipsec_filters;
|
|
struct ipsec_mode *ipsec_mode;
|
|
/* interface lookup routintes */
|
|
struct ipsec_addr_wrap *iftab;
|
|
|
|
typedef struct {
|
|
union {
|
|
int64_t number;
|
|
uint8_t ikemode;
|
|
uint8_t dir;
|
|
uint8_t satype;
|
|
char *string;
|
|
uint16_t port;
|
|
struct ipsec_hosts *hosts;
|
|
struct ipsec_hosts peers;
|
|
struct ipsec_addr_wrap *anyhost;
|
|
struct ipsec_addr_wrap *host;
|
|
struct ipsec_addr_wrap *cfg;
|
|
struct ipsec_addr_wrap *proto;
|
|
struct {
|
|
char *srcid;
|
|
char *dstid;
|
|
} ids;
|
|
char *id;
|
|
uint8_t type;
|
|
struct iked_lifetime lifetime;
|
|
struct iked_auth ikeauth;
|
|
struct iked_auth ikekey;
|
|
struct ipsec_transforms *transforms;
|
|
struct ipsec_filters *filters;
|
|
struct ipsec_mode *mode;
|
|
} v;
|
|
int lineno;
|
|
} YYSTYPE;
|
|
|
|
%}
|
|
|
|
%token FROM ESP AH IN PEER ON OUT TO SRCID DSTID PSK PORT
|
|
%token FILENAME AUTHXF PRFXF ENCXF ERROR IKEV2 IKESA CHILDSA ESN NOESN
|
|
%token PASSIVE ACTIVE ANY TAG TAP PROTO LOCAL GROUP NAME CONFIG EAP USER
|
|
%token IKEV1 FLOW SA TCPMD5 TUNNEL TRANSPORT COUPLE DECOUPLE SET
|
|
%token INCLUDE LIFETIME BYTES INET INET6 QUICK SKIP DEFAULT
|
|
%token IPCOMP OCSP IKELIFETIME MOBIKE NOMOBIKE RDOMAIN
|
|
%token FRAGMENTATION NOFRAGMENTATION DPD_CHECK_INTERVAL
|
|
%token ENFORCESINGLEIKESA NOENFORCESINGLEIKESA
|
|
%token STICKYADDRESS NOSTICKYADDRESS
|
|
%token VENDORID NOVENDORID
|
|
%token TOLERATE MAXAGE DYNAMIC
|
|
%token CERTPARTIALCHAIN
|
|
%token REQUEST IFACE
|
|
%token <v.string> STRING
|
|
%token <v.number> NUMBER
|
|
%type <v.string> string
|
|
%type <v.satype> satype
|
|
%type <v.proto> proto proto_list protoval
|
|
%type <v.hosts> hosts hosts_list
|
|
%type <v.port> port
|
|
%type <v.number> portval af rdomain
|
|
%type <v.peers> peers
|
|
%type <v.anyhost> anyhost
|
|
%type <v.host> host host_spec
|
|
%type <v.ids> ids
|
|
%type <v.id> id
|
|
%type <v.transforms> transforms
|
|
%type <v.filters> filters
|
|
%type <v.ikemode> ikeflags ikematch ikemode ipcomp tmode
|
|
%type <v.ikeauth> ikeauth
|
|
%type <v.ikekey> keyspec
|
|
%type <v.mode> ike_sas child_sas
|
|
%type <v.lifetime> lifetime
|
|
%type <v.number> byte_spec time_spec ikelifetime
|
|
%type <v.string> name iface
|
|
%type <v.cfg> cfg ikecfg ikecfgvals
|
|
%type <v.string> transform_esn
|
|
%%
|
|
|
|
grammar : /* empty */
|
|
| grammar include '\n'
|
|
| grammar '\n'
|
|
| grammar set '\n'
|
|
| grammar user '\n'
|
|
| grammar ikev2rule '\n'
|
|
| grammar varset '\n'
|
|
| grammar otherrule skipline '\n'
|
|
| grammar error '\n' { file->errors++; }
|
|
;
|
|
|
|
comma : ','
|
|
| /* empty */
|
|
;
|
|
|
|
include : INCLUDE STRING {
|
|
struct file *nfile;
|
|
|
|
if ((nfile = pushfile($2, 1)) == NULL) {
|
|
yyerror("failed to include file %s", $2);
|
|
free($2);
|
|
YYERROR;
|
|
}
|
|
free($2);
|
|
|
|
file = nfile;
|
|
lungetc('\n');
|
|
}
|
|
;
|
|
|
|
set : SET ACTIVE { passive = 0; }
|
|
| SET PASSIVE { passive = 1; }
|
|
| SET COUPLE { decouple = 0; }
|
|
| SET DECOUPLE { decouple = 1; }
|
|
| SET FRAGMENTATION { fragmentation = 1; }
|
|
| SET NOFRAGMENTATION { fragmentation = 0; }
|
|
| SET MOBIKE { mobike = 1; }
|
|
| SET NOMOBIKE { mobike = 0; }
|
|
| SET VENDORID { vendorid = 1; }
|
|
| SET NOVENDORID { vendorid = 0; }
|
|
| SET ENFORCESINGLEIKESA { enforcesingleikesa = 1; }
|
|
| SET NOENFORCESINGLEIKESA { enforcesingleikesa = 0; }
|
|
| SET STICKYADDRESS { stickyaddress = 1; }
|
|
| SET NOSTICKYADDRESS { stickyaddress = 0; }
|
|
| SET OCSP STRING {
|
|
ocsp_url = $3;
|
|
}
|
|
| SET OCSP STRING TOLERATE time_spec {
|
|
ocsp_url = $3;
|
|
ocsp_tolerate = $5;
|
|
}
|
|
| SET OCSP STRING TOLERATE time_spec MAXAGE time_spec {
|
|
ocsp_url = $3;
|
|
ocsp_tolerate = $5;
|
|
ocsp_maxage = $7;
|
|
}
|
|
| SET CERTPARTIALCHAIN {
|
|
cert_partial_chain = 1;
|
|
}
|
|
| SET DPD_CHECK_INTERVAL NUMBER {
|
|
if ($3 < 0) {
|
|
yyerror("timeout outside range");
|
|
YYERROR;
|
|
}
|
|
dpd_interval = $3;
|
|
}
|
|
;
|
|
|
|
user : USER STRING STRING {
|
|
if (create_user($2, $3) == -1)
|
|
YYERROR;
|
|
free($2);
|
|
freezero($3, strlen($3));
|
|
}
|
|
;
|
|
|
|
ikev2rule : IKEV2 name ikeflags satype af proto rdomain hosts_list peers
|
|
ike_sas child_sas ids ikelifetime lifetime ikeauth ikecfg
|
|
iface filters {
|
|
if (create_ike($2, $5, $6, $7, $8, &$9, $10, $11, $4,
|
|
$3, $12.srcid, $12.dstid, $13, &$14, &$15,
|
|
$18, $16, $17) == -1) {
|
|
yyerror("create_ike failed");
|
|
YYERROR;
|
|
}
|
|
}
|
|
;
|
|
|
|
ikecfg : /* empty */ { $$ = NULL; }
|
|
| ikecfgvals { $$ = $1; }
|
|
;
|
|
|
|
ikecfgvals : cfg { $$ = $1; }
|
|
| ikecfgvals cfg {
|
|
if ($2 == NULL)
|
|
$$ = $1;
|
|
else if ($1 == NULL)
|
|
$$ = $2;
|
|
else {
|
|
$1->tail->next = $2;
|
|
$1->tail = $2->tail;
|
|
$$ = $1;
|
|
}
|
|
}
|
|
;
|
|
|
|
cfg : CONFIG STRING host_spec {
|
|
const struct ipsec_xf *xf;
|
|
|
|
if ((xf = parse_xf($2, $3->af, cpxfs)) == NULL) {
|
|
yyerror("not a valid ikecfg option");
|
|
free($2);
|
|
free($3);
|
|
YYERROR;
|
|
}
|
|
free($2);
|
|
$$ = $3;
|
|
$$->type = xf->id;
|
|
$$->action = IKEV2_CP_REPLY; /* XXX */
|
|
}
|
|
| REQUEST STRING anyhost {
|
|
const struct ipsec_xf *xf;
|
|
|
|
if ((xf = parse_xf($2, $3->af, cpxfs)) == NULL) {
|
|
yyerror("not a valid ikecfg option");
|
|
free($2);
|
|
free($3);
|
|
YYERROR;
|
|
}
|
|
free($2);
|
|
$$ = $3;
|
|
$$->type = xf->id;
|
|
$$->action = IKEV2_CP_REQUEST; /* XXX */
|
|
}
|
|
;
|
|
|
|
name : /* empty */ { $$ = NULL; }
|
|
| STRING {
|
|
$$ = $1;
|
|
}
|
|
|
|
satype : /* empty */ { $$ = IKEV2_SAPROTO_ESP; }
|
|
| ESP { $$ = IKEV2_SAPROTO_ESP; }
|
|
| AH { $$ = IKEV2_SAPROTO_AH; }
|
|
;
|
|
|
|
af : /* empty */ { $$ = AF_UNSPEC; }
|
|
| INET { $$ = AF_INET; }
|
|
| INET6 { $$ = AF_INET6; }
|
|
;
|
|
|
|
proto : /* empty */ { $$ = NULL; }
|
|
| PROTO protoval { $$ = $2; }
|
|
| PROTO '{' proto_list '}' { $$ = $3; }
|
|
;
|
|
|
|
proto_list : protoval { $$ = $1; }
|
|
| proto_list comma protoval {
|
|
if ($3 == NULL)
|
|
$$ = $1;
|
|
else if ($1 == NULL)
|
|
$$ = $3;
|
|
else {
|
|
$1->tail->next = $3;
|
|
$1->tail = $3->tail;
|
|
$$ = $1;
|
|
}
|
|
}
|
|
;
|
|
|
|
protoval : STRING {
|
|
struct protoent *p;
|
|
|
|
p = getprotobyname($1);
|
|
if (p == NULL) {
|
|
yyerror("unknown protocol: %s", $1);
|
|
YYERROR;
|
|
}
|
|
|
|
if (($$ = calloc(1, sizeof(*$$))) == NULL)
|
|
err(1, "protoval: calloc");
|
|
|
|
$$->type = p->p_proto;
|
|
$$->tail = $$;
|
|
free($1);
|
|
}
|
|
| NUMBER {
|
|
if ($1 > 255 || $1 < 0) {
|
|
yyerror("protocol outside range");
|
|
YYERROR;
|
|
}
|
|
if (($$ = calloc(1, sizeof(*$$))) == NULL)
|
|
err(1, "protoval: calloc");
|
|
|
|
$$->type = $1;
|
|
$$->tail = $$;
|
|
}
|
|
;
|
|
|
|
rdomain : /* empty */ { $$ = -1; }
|
|
| RDOMAIN NUMBER {
|
|
if ($2 > 255 || $2 < 0) {
|
|
yyerror("rdomain outside range");
|
|
YYERROR;
|
|
}
|
|
$$ = $2;
|
|
}
|
|
|
|
hosts_list : hosts { $$ = $1; }
|
|
| hosts_list comma hosts {
|
|
if ($3 == NULL)
|
|
$$ = $1;
|
|
else if ($1 == NULL)
|
|
$$ = $3;
|
|
else {
|
|
$1->src->tail->next = $3->src;
|
|
$1->src->tail = $3->src->tail;
|
|
$1->dst->tail->next = $3->dst;
|
|
$1->dst->tail = $3->dst->tail;
|
|
$$ = $1;
|
|
free($3);
|
|
}
|
|
}
|
|
;
|
|
|
|
hosts : FROM host port TO host port {
|
|
struct ipsec_addr_wrap *ipa;
|
|
for (ipa = $5; ipa; ipa = ipa->next) {
|
|
if (ipa->srcnat) {
|
|
yyerror("no flow NAT support for"
|
|
" destination network: %s",
|
|
ipa->name);
|
|
YYERROR;
|
|
}
|
|
}
|
|
|
|
if (($$ = calloc(1, sizeof(*$$))) == NULL)
|
|
err(1, "hosts: calloc");
|
|
|
|
$$->src = $2;
|
|
$$->src->port = $3;
|
|
$$->dst = $5;
|
|
$$->dst->port = $6;
|
|
}
|
|
| TO host port FROM host port {
|
|
struct ipsec_addr_wrap *ipa;
|
|
for (ipa = $2; ipa; ipa = ipa->next) {
|
|
if (ipa->srcnat) {
|
|
yyerror("no flow NAT support for"
|
|
" destination network: %s",
|
|
ipa->name);
|
|
YYERROR;
|
|
}
|
|
}
|
|
if (($$ = calloc(1, sizeof(*$$))) == NULL)
|
|
err(1, "hosts: calloc");
|
|
|
|
$$->src = $5;
|
|
$$->src->port = $6;
|
|
$$->dst = $2;
|
|
$$->dst->port = $3;
|
|
}
|
|
;
|
|
|
|
port : /* empty */ { $$ = 0; }
|
|
| PORT portval { $$ = $2; }
|
|
;
|
|
|
|
portval : STRING {
|
|
struct servent *s;
|
|
|
|
if ((s = getservbyname($1, "tcp")) != NULL ||
|
|
(s = getservbyname($1, "udp")) != NULL) {
|
|
$$ = s->s_port;
|
|
} else {
|
|
yyerror("unknown port: %s", $1);
|
|
YYERROR;
|
|
}
|
|
free($1);
|
|
}
|
|
| NUMBER {
|
|
if ($1 > USHRT_MAX || $1 < 0) {
|
|
yyerror("port outside range");
|
|
YYERROR;
|
|
}
|
|
$$ = htons($1);
|
|
}
|
|
;
|
|
|
|
peers : /* empty */ {
|
|
$$.dst = NULL;
|
|
$$.src = NULL;
|
|
}
|
|
| PEER anyhost LOCAL anyhost {
|
|
$$.dst = $2;
|
|
$$.src = $4;
|
|
}
|
|
| LOCAL anyhost PEER anyhost {
|
|
$$.dst = $4;
|
|
$$.src = $2;
|
|
}
|
|
| PEER anyhost {
|
|
$$.dst = $2;
|
|
$$.src = NULL;
|
|
}
|
|
| LOCAL anyhost {
|
|
$$.dst = NULL;
|
|
$$.src = $2;
|
|
}
|
|
;
|
|
|
|
anyhost : host_spec { $$ = $1; }
|
|
| ANY {
|
|
$$ = host_any();
|
|
}
|
|
|
|
host_spec : STRING {
|
|
if (($$ = host($1)) == NULL) {
|
|
free($1);
|
|
yyerror("could not parse host specification");
|
|
YYERROR;
|
|
}
|
|
free($1);
|
|
}
|
|
| STRING '/' NUMBER {
|
|
char *buf;
|
|
|
|
if (asprintf(&buf, "%s/%lld", $1, $3) == -1)
|
|
err(1, "host: asprintf");
|
|
free($1);
|
|
if (($$ = host(buf)) == NULL) {
|
|
free(buf);
|
|
yyerror("could not parse host specification");
|
|
YYERROR;
|
|
}
|
|
free(buf);
|
|
}
|
|
;
|
|
|
|
host : host_spec { $$ = $1; }
|
|
| host_spec '(' host_spec ')' {
|
|
if (($1->af != AF_UNSPEC) && ($3->af != AF_UNSPEC) &&
|
|
($3->af != $1->af)) {
|
|
yyerror("Flow NAT address family mismatch");
|
|
YYERROR;
|
|
}
|
|
$$ = $1;
|
|
$$->srcnat = $3;
|
|
}
|
|
| ANY {
|
|
$$ = host_any();
|
|
}
|
|
| DYNAMIC {
|
|
$$ = host_dynamic();
|
|
}
|
|
;
|
|
|
|
ids : /* empty */ {
|
|
$$.srcid = NULL;
|
|
$$.dstid = NULL;
|
|
}
|
|
| SRCID id DSTID id {
|
|
$$.srcid = $2;
|
|
$$.dstid = $4;
|
|
}
|
|
| SRCID id {
|
|
$$.srcid = $2;
|
|
$$.dstid = NULL;
|
|
}
|
|
| DSTID id {
|
|
$$.srcid = NULL;
|
|
$$.dstid = $2;
|
|
}
|
|
;
|
|
|
|
id : STRING { $$ = $1; }
|
|
;
|
|
|
|
transforms : {
|
|
if ((ipsec_transforms = calloc(1,
|
|
sizeof(struct ipsec_transforms))) == NULL)
|
|
err(1, "transforms: calloc");
|
|
}
|
|
transforms_l {
|
|
$$ = ipsec_transforms;
|
|
}
|
|
| /* empty */ {
|
|
$$ = NULL;
|
|
}
|
|
;
|
|
|
|
transforms_l : transforms_l transform
|
|
| transform
|
|
;
|
|
|
|
transform : AUTHXF STRING {
|
|
const struct ipsec_xf **xfs = ipsec_transforms->authxf;
|
|
size_t nxfs = ipsec_transforms->nauthxf;
|
|
xfs = recallocarray(xfs, nxfs, nxfs + 1,
|
|
sizeof(struct ipsec_xf *));
|
|
if (xfs == NULL)
|
|
err(1, "transform: recallocarray");
|
|
if ((xfs[nxfs] = parse_xf($2, 0, authxfs)) == NULL) {
|
|
yyerror("%s not a valid transform", $2);
|
|
YYERROR;
|
|
}
|
|
free($2);
|
|
ipsec_transforms->authxf = xfs;
|
|
ipsec_transforms->nauthxf++;
|
|
}
|
|
| ENCXF STRING {
|
|
const struct ipsec_xf **xfs = ipsec_transforms->encxf;
|
|
size_t nxfs = ipsec_transforms->nencxf;
|
|
xfs = recallocarray(xfs, nxfs, nxfs + 1,
|
|
sizeof(struct ipsec_xf *));
|
|
if (xfs == NULL)
|
|
err(1, "transform: recallocarray");
|
|
if ((xfs[nxfs] = parse_xf($2, 0, encxfs)) == NULL) {
|
|
yyerror("%s not a valid transform", $2);
|
|
YYERROR;
|
|
}
|
|
free($2);
|
|
ipsec_transforms->encxf = xfs;
|
|
ipsec_transforms->nencxf++;
|
|
}
|
|
| PRFXF STRING {
|
|
const struct ipsec_xf **xfs = ipsec_transforms->prfxf;
|
|
size_t nxfs = ipsec_transforms->nprfxf;
|
|
xfs = recallocarray(xfs, nxfs, nxfs + 1,
|
|
sizeof(struct ipsec_xf *));
|
|
if (xfs == NULL)
|
|
err(1, "transform: recallocarray");
|
|
if ((xfs[nxfs] = parse_xf($2, 0, prfxfs)) == NULL) {
|
|
yyerror("%s not a valid transform", $2);
|
|
YYERROR;
|
|
}
|
|
free($2);
|
|
ipsec_transforms->prfxf = xfs;
|
|
ipsec_transforms->nprfxf++;
|
|
}
|
|
| GROUP STRING {
|
|
const struct ipsec_xf **xfs = ipsec_transforms->groupxf;
|
|
size_t nxfs = ipsec_transforms->ngroupxf;
|
|
xfs = recallocarray(xfs, nxfs, nxfs + 1,
|
|
sizeof(struct ipsec_xf *));
|
|
if (xfs == NULL)
|
|
err(1, "transform: recallocarray");
|
|
if ((xfs[nxfs] = parse_xf($2, 0, groupxfs)) == NULL) {
|
|
yyerror("%s not a valid transform", $2);
|
|
YYERROR;
|
|
}
|
|
free($2);
|
|
ipsec_transforms->groupxf = xfs;
|
|
ipsec_transforms->ngroupxf++;
|
|
}
|
|
| transform_esn {
|
|
const struct ipsec_xf **xfs = ipsec_transforms->esnxf;
|
|
size_t nxfs = ipsec_transforms->nesnxf;
|
|
xfs = recallocarray(xfs, nxfs, nxfs + 1,
|
|
sizeof(struct ipsec_xf *));
|
|
if (xfs == NULL)
|
|
err(1, "transform: recallocarray");
|
|
if ((xfs[nxfs] = parse_xf($1, 0, esnxfs)) == NULL) {
|
|
yyerror("%s not a valid transform", $1);
|
|
YYERROR;
|
|
}
|
|
ipsec_transforms->esnxf = xfs;
|
|
ipsec_transforms->nesnxf++;
|
|
}
|
|
;
|
|
|
|
transform_esn : ESN { $$ = "esn"; }
|
|
| NOESN { $$ = "noesn"; }
|
|
;
|
|
|
|
ike_sas : {
|
|
if ((ipsec_mode = calloc(1,
|
|
sizeof(struct ipsec_mode))) == NULL)
|
|
err(1, "ike_sas: calloc");
|
|
}
|
|
ike_sas_l {
|
|
$$ = ipsec_mode;
|
|
}
|
|
| /* empty */ {
|
|
$$ = NULL;
|
|
}
|
|
;
|
|
|
|
ike_sas_l : ike_sas_l ike_sa
|
|
| ike_sa
|
|
;
|
|
|
|
ike_sa : IKESA {
|
|
if ((ipsec_mode->xfs = recallocarray(ipsec_mode->xfs,
|
|
ipsec_mode->nxfs, ipsec_mode->nxfs + 1,
|
|
sizeof(struct ipsec_transforms *))) == NULL)
|
|
err(1, "ike_sa: recallocarray");
|
|
ipsec_mode->nxfs++;
|
|
encxfs = ikeencxfs;
|
|
} transforms {
|
|
ipsec_mode->xfs[ipsec_mode->nxfs - 1] = $3;
|
|
}
|
|
;
|
|
|
|
child_sas : {
|
|
if ((ipsec_mode = calloc(1,
|
|
sizeof(struct ipsec_mode))) == NULL)
|
|
err(1, "child_sas: calloc");
|
|
}
|
|
child_sas_l {
|
|
$$ = ipsec_mode;
|
|
}
|
|
| /* empty */ {
|
|
$$ = NULL;
|
|
}
|
|
;
|
|
|
|
child_sas_l : child_sas_l child_sa
|
|
| child_sa
|
|
;
|
|
|
|
child_sa : CHILDSA {
|
|
if ((ipsec_mode->xfs = recallocarray(ipsec_mode->xfs,
|
|
ipsec_mode->nxfs, ipsec_mode->nxfs + 1,
|
|
sizeof(struct ipsec_transforms *))) == NULL)
|
|
err(1, "child_sa: recallocarray");
|
|
ipsec_mode->nxfs++;
|
|
encxfs = ipsecencxfs;
|
|
} transforms {
|
|
ipsec_mode->xfs[ipsec_mode->nxfs - 1] = $3;
|
|
}
|
|
;
|
|
|
|
ikeflags : ikematch ikemode ipcomp tmode { $$ = $1 | $2 | $3 | $4; }
|
|
;
|
|
|
|
ikematch : /* empty */ { $$ = 0; }
|
|
| QUICK { $$ = IKED_POLICY_QUICK; }
|
|
| SKIP { $$ = IKED_POLICY_SKIP; }
|
|
| DEFAULT { $$ = IKED_POLICY_DEFAULT; }
|
|
;
|
|
|
|
ikemode : /* empty */ { $$ = IKED_POLICY_PASSIVE; }
|
|
| PASSIVE { $$ = IKED_POLICY_PASSIVE; }
|
|
| ACTIVE { $$ = IKED_POLICY_ACTIVE; }
|
|
;
|
|
|
|
ipcomp : /* empty */ { $$ = 0; }
|
|
| IPCOMP { $$ = IKED_POLICY_IPCOMP; }
|
|
;
|
|
|
|
tmode : /* empty */ { $$ = 0; }
|
|
| TUNNEL { $$ = 0; }
|
|
| TRANSPORT { $$ = IKED_POLICY_TRANSPORT; }
|
|
;
|
|
|
|
ikeauth : /* empty */ {
|
|
$$.auth_method = IKEV2_AUTH_SIG_ANY; /* default */
|
|
$$.auth_eap = 0;
|
|
$$.auth_length = 0;
|
|
}
|
|
| PSK keyspec {
|
|
memcpy(&$$, &$2, sizeof($$));
|
|
$$.auth_method = IKEV2_AUTH_SHARED_KEY_MIC;
|
|
$$.auth_eap = 0;
|
|
explicit_bzero(&$2, sizeof($2));
|
|
}
|
|
| EAP STRING {
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < strlen($2); i++)
|
|
if ($2[i] == '-')
|
|
$2[i] = '_';
|
|
|
|
if (strcasecmp("mschap_v2", $2) != 0) {
|
|
yyerror("unsupported EAP method: %s", $2);
|
|
free($2);
|
|
YYERROR;
|
|
}
|
|
free($2);
|
|
|
|
$$.auth_method = IKEV2_AUTH_SIG_ANY;
|
|
$$.auth_eap = EAP_TYPE_MSCHAP_V2;
|
|
$$.auth_length = 0;
|
|
}
|
|
| STRING {
|
|
const struct ipsec_xf *xf;
|
|
|
|
if ((xf = parse_xf($1, 0, methodxfs)) == NULL ||
|
|
xf->id == IKEV2_AUTH_NONE) {
|
|
yyerror("not a valid authentication mode");
|
|
free($1);
|
|
YYERROR;
|
|
}
|
|
free($1);
|
|
|
|
$$.auth_method = xf->id;
|
|
$$.auth_eap = 0;
|
|
$$.auth_length = 0;
|
|
}
|
|
;
|
|
|
|
byte_spec : NUMBER {
|
|
$$ = $1;
|
|
}
|
|
| STRING {
|
|
uint64_t bytes = 0;
|
|
char unit = 0;
|
|
|
|
if (sscanf($1, "%llu%c", &bytes, &unit) != 2) {
|
|
yyerror("invalid byte specification: %s", $1);
|
|
YYERROR;
|
|
}
|
|
free($1);
|
|
switch (toupper((unsigned char)unit)) {
|
|
case 'K':
|
|
bytes *= 1024;
|
|
break;
|
|
case 'M':
|
|
bytes *= 1024 * 1024;
|
|
break;
|
|
case 'G':
|
|
bytes *= 1024 * 1024 * 1024;
|
|
break;
|
|
default:
|
|
yyerror("invalid byte unit");
|
|
YYERROR;
|
|
}
|
|
$$ = bytes;
|
|
}
|
|
;
|
|
|
|
time_spec : NUMBER {
|
|
$$ = $1;
|
|
}
|
|
| STRING {
|
|
uint64_t seconds = 0;
|
|
char unit = 0;
|
|
|
|
if (sscanf($1, "%llu%c", &seconds, &unit) != 2) {
|
|
yyerror("invalid time specification: %s", $1);
|
|
YYERROR;
|
|
}
|
|
free($1);
|
|
switch (tolower((unsigned char)unit)) {
|
|
case 'm':
|
|
seconds *= 60;
|
|
break;
|
|
case 'h':
|
|
seconds *= 60 * 60;
|
|
break;
|
|
default:
|
|
yyerror("invalid time unit");
|
|
YYERROR;
|
|
}
|
|
$$ = seconds;
|
|
}
|
|
;
|
|
|
|
lifetime : /* empty */ {
|
|
$$ = deflifetime;
|
|
}
|
|
| LIFETIME time_spec {
|
|
$$.lt_seconds = $2;
|
|
$$.lt_bytes = deflifetime.lt_bytes;
|
|
}
|
|
| LIFETIME time_spec BYTES byte_spec {
|
|
$$.lt_seconds = $2;
|
|
$$.lt_bytes = $4;
|
|
}
|
|
;
|
|
|
|
ikelifetime : /* empty */ {
|
|
$$ = 0;
|
|
}
|
|
| IKELIFETIME time_spec {
|
|
$$ = $2;
|
|
}
|
|
|
|
keyspec : STRING {
|
|
uint8_t *hex;
|
|
|
|
bzero(&$$, sizeof($$));
|
|
|
|
hex = $1;
|
|
if (strncmp(hex, "0x", 2) == 0) {
|
|
hex += 2;
|
|
if (parsekey(hex, strlen(hex), &$$) != 0) {
|
|
free($1);
|
|
YYERROR;
|
|
}
|
|
} else {
|
|
if (strlen($1) > sizeof($$.auth_data)) {
|
|
yyerror("psk too long");
|
|
free($1);
|
|
YYERROR;
|
|
}
|
|
strlcpy($$.auth_data, $1,
|
|
sizeof($$.auth_data));
|
|
$$.auth_length = strlen($1);
|
|
}
|
|
freezero($1, strlen($1));
|
|
}
|
|
| FILENAME STRING {
|
|
if (parsekeyfile($2, &$$) != 0) {
|
|
free($2);
|
|
YYERROR;
|
|
}
|
|
free($2);
|
|
}
|
|
;
|
|
|
|
filters : {
|
|
if ((ipsec_filters = calloc(1,
|
|
sizeof(struct ipsec_filters))) == NULL)
|
|
err(1, "filters: calloc");
|
|
}
|
|
filters_l {
|
|
$$ = ipsec_filters;
|
|
}
|
|
| /* empty */ {
|
|
$$ = NULL;
|
|
}
|
|
;
|
|
|
|
filters_l : filters_l filter
|
|
| filter
|
|
;
|
|
|
|
filter : TAG STRING
|
|
{
|
|
ipsec_filters->tag = $2;
|
|
}
|
|
| TAP STRING
|
|
{
|
|
const char *errstr = NULL;
|
|
size_t len;
|
|
|
|
len = strcspn($2, "0123456789");
|
|
if (strlen("enc") != len ||
|
|
strncmp("enc", $2, len) != 0) {
|
|
yyerror("invalid tap interface name: %s", $2);
|
|
free($2);
|
|
YYERROR;
|
|
}
|
|
ipsec_filters->tap =
|
|
strtonum($2 + len, 0, UINT_MAX, &errstr);
|
|
free($2);
|
|
if (errstr != NULL) {
|
|
yyerror("invalid tap interface unit: %s",
|
|
errstr);
|
|
YYERROR;
|
|
}
|
|
}
|
|
;
|
|
|
|
iface : {
|
|
$$ = NULL;
|
|
}
|
|
| IFACE STRING {
|
|
$$ = $2;
|
|
}
|
|
|
|
string : string STRING
|
|
{
|
|
if (asprintf(&$$, "%s %s", $1, $2) == -1)
|
|
err(1, "string: asprintf");
|
|
free($1);
|
|
free($2);
|
|
}
|
|
| STRING
|
|
;
|
|
|
|
varset : STRING '=' string
|
|
{
|
|
char *s = $1;
|
|
log_debug("%s = \"%s\"\n", $1, $3);
|
|
while (*s++) {
|
|
if (isspace((unsigned char)*s)) {
|
|
yyerror("macro name cannot contain "
|
|
"whitespace");
|
|
free($1);
|
|
free($3);
|
|
YYERROR;
|
|
}
|
|
}
|
|
if (symset($1, $3, 0) == -1)
|
|
err(1, "cannot store variable");
|
|
free($1);
|
|
free($3);
|
|
}
|
|
;
|
|
|
|
/*
|
|
* ignore IKEv1/manual keying rules in ipsec.conf
|
|
*/
|
|
otherrule : IKEV1
|
|
| sarule
|
|
| FLOW
|
|
| TCPMD5
|
|
;
|
|
|
|
/* manual keying SAs might start with the following keywords */
|
|
sarule : SA
|
|
| FROM
|
|
| TO
|
|
| TUNNEL
|
|
| TRANSPORT
|
|
;
|
|
|
|
/* ignore everything to the end of the line */
|
|
skipline :
|
|
{
|
|
int c;
|
|
|
|
while ((c = lgetc(0)) != '\n' && c != EOF)
|
|
; /* nothing */
|
|
if (c == '\n')
|
|
lungetc(c);
|
|
}
|
|
;
|
|
%%
|
|
|
|
struct keywords {
|
|
const char *k_name;
|
|
int k_val;
|
|
};
|
|
|
|
void
|
|
copy_sockaddrtoipa(struct ipsec_addr_wrap *ipa, struct sockaddr *sa)
|
|
{
|
|
if (sa->sa_family == AF_INET6)
|
|
memcpy(&ipa->address, sa, sizeof(struct sockaddr_in6));
|
|
else if (sa->sa_family == AF_INET)
|
|
memcpy(&ipa->address, sa, sizeof(struct sockaddr_in));
|
|
else
|
|
warnx("unhandled af %d", sa->sa_family);
|
|
}
|
|
|
|
int
|
|
yyerror(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
file->errors++;
|
|
va_start(ap, fmt);
|
|
fprintf(stderr, "%s: %d: ", file->name, yylval.lineno);
|
|
vfprintf(stderr, fmt, ap);
|
|
fprintf(stderr, "\n");
|
|
va_end(ap);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
kw_cmp(const void *k, const void *e)
|
|
{
|
|
return (strcmp(k, ((const struct keywords *)e)->k_name));
|
|
}
|
|
|
|
int
|
|
lookup(char *s)
|
|
{
|
|
/* this has to be sorted always */
|
|
static const struct keywords keywords[] = {
|
|
{ "active", ACTIVE },
|
|
{ "ah", AH },
|
|
{ "any", ANY },
|
|
{ "auth", AUTHXF },
|
|
{ "bytes", BYTES },
|
|
{ "cert_partial_chain", CERTPARTIALCHAIN },
|
|
{ "childsa", CHILDSA },
|
|
{ "config", CONFIG },
|
|
{ "couple", COUPLE },
|
|
{ "decouple", DECOUPLE },
|
|
{ "default", DEFAULT },
|
|
{ "dpd_check_interval", DPD_CHECK_INTERVAL },
|
|
{ "dstid", DSTID },
|
|
{ "dynamic", DYNAMIC },
|
|
{ "eap", EAP },
|
|
{ "enc", ENCXF },
|
|
{ "enforcesingleikesa", ENFORCESINGLEIKESA },
|
|
{ "esn", ESN },
|
|
{ "esp", ESP },
|
|
{ "file", FILENAME },
|
|
{ "flow", FLOW },
|
|
{ "fragmentation", FRAGMENTATION },
|
|
{ "from", FROM },
|
|
{ "group", GROUP },
|
|
{ "iface", IFACE },
|
|
{ "ike", IKEV1 },
|
|
{ "ikelifetime", IKELIFETIME },
|
|
{ "ikesa", IKESA },
|
|
{ "ikev2", IKEV2 },
|
|
{ "include", INCLUDE },
|
|
{ "inet", INET },
|
|
{ "inet6", INET6 },
|
|
{ "ipcomp", IPCOMP },
|
|
{ "lifetime", LIFETIME },
|
|
{ "local", LOCAL },
|
|
{ "maxage", MAXAGE },
|
|
{ "mobike", MOBIKE },
|
|
{ "name", NAME },
|
|
{ "noenforcesingleikesa", NOENFORCESINGLEIKESA },
|
|
{ "noesn", NOESN },
|
|
{ "nofragmentation", NOFRAGMENTATION },
|
|
{ "nomobike", NOMOBIKE },
|
|
{ "nostickyaddress", NOSTICKYADDRESS },
|
|
{ "novendorid", NOVENDORID },
|
|
{ "ocsp", OCSP },
|
|
{ "passive", PASSIVE },
|
|
{ "peer", PEER },
|
|
{ "port", PORT },
|
|
{ "prf", PRFXF },
|
|
{ "proto", PROTO },
|
|
{ "psk", PSK },
|
|
{ "quick", QUICK },
|
|
{ "rdomain", RDOMAIN },
|
|
{ "request", REQUEST },
|
|
{ "sa", SA },
|
|
{ "set", SET },
|
|
{ "skip", SKIP },
|
|
{ "srcid", SRCID },
|
|
{ "stickyaddress", STICKYADDRESS },
|
|
{ "tag", TAG },
|
|
{ "tap", TAP },
|
|
{ "tcpmd5", TCPMD5 },
|
|
{ "to", TO },
|
|
{ "tolerate", TOLERATE },
|
|
{ "transport", TRANSPORT },
|
|
{ "tunnel", TUNNEL },
|
|
{ "user", USER },
|
|
{ "vendorid", VENDORID }
|
|
};
|
|
const struct keywords *p;
|
|
|
|
p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
|
|
sizeof(keywords[0]), kw_cmp);
|
|
|
|
if (p) {
|
|
if (debug > 1)
|
|
fprintf(stderr, "%s: %d\n", s, p->k_val);
|
|
return (p->k_val);
|
|
} else {
|
|
if (debug > 1)
|
|
fprintf(stderr, "string: %s\n", s);
|
|
return (STRING);
|
|
}
|
|
}
|
|
|
|
#define START_EXPAND 1
|
|
#define DONE_EXPAND 2
|
|
|
|
static int expanding;
|
|
|
|
int
|
|
igetc(void)
|
|
{
|
|
int c;
|
|
|
|
while (1) {
|
|
if (file->ungetpos > 0)
|
|
c = file->ungetbuf[--file->ungetpos];
|
|
else
|
|
c = getc(file->stream);
|
|
|
|
if (c == START_EXPAND)
|
|
expanding = 1;
|
|
else if (c == DONE_EXPAND)
|
|
expanding = 0;
|
|
else
|
|
break;
|
|
}
|
|
return (c);
|
|
}
|
|
|
|
int
|
|
lgetc(int quotec)
|
|
{
|
|
int c, next;
|
|
|
|
if (quotec) {
|
|
if ((c = igetc()) == EOF) {
|
|
yyerror("reached end of file while parsing "
|
|
"quoted string");
|
|
if (file == topfile || popfile() == EOF)
|
|
return (EOF);
|
|
return (quotec);
|
|
}
|
|
return (c);
|
|
}
|
|
|
|
while ((c = igetc()) == '\\') {
|
|
next = igetc();
|
|
if (next != '\n') {
|
|
c = next;
|
|
break;
|
|
}
|
|
yylval.lineno = file->lineno;
|
|
file->lineno++;
|
|
}
|
|
|
|
while (c == EOF) {
|
|
/*
|
|
* Fake EOL when hit EOF for the first time. This gets line
|
|
* count right if last line in included file is syntactically
|
|
* invalid and has no newline.
|
|
*/
|
|
if (file->eof_reached == 0) {
|
|
file->eof_reached = 1;
|
|
return ('\n');
|
|
}
|
|
while (c == EOF) {
|
|
if (file == topfile || popfile() == EOF)
|
|
return (EOF);
|
|
c = igetc();
|
|
}
|
|
}
|
|
return (c);
|
|
}
|
|
|
|
void
|
|
lungetc(int c)
|
|
{
|
|
if (c == EOF)
|
|
return;
|
|
|
|
if (file->ungetpos >= file->ungetsize) {
|
|
void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
|
|
if (p == NULL)
|
|
err(1, "lungetc");
|
|
file->ungetbuf = p;
|
|
file->ungetsize *= 2;
|
|
}
|
|
file->ungetbuf[file->ungetpos++] = c;
|
|
}
|
|
|
|
int
|
|
findeol(void)
|
|
{
|
|
int c;
|
|
|
|
/* skip to either EOF or the first real EOL */
|
|
while (1) {
|
|
c = lgetc(0);
|
|
if (c == '\n') {
|
|
file->lineno++;
|
|
break;
|
|
}
|
|
if (c == EOF)
|
|
break;
|
|
}
|
|
return (ERROR);
|
|
}
|
|
|
|
int
|
|
yylex(void)
|
|
{
|
|
char buf[8096];
|
|
char *p, *val;
|
|
int quotec, next, c;
|
|
int token;
|
|
|
|
top:
|
|
p = buf;
|
|
while ((c = lgetc(0)) == ' ' || c == '\t')
|
|
; /* nothing */
|
|
|
|
yylval.lineno = file->lineno;
|
|
if (c == '#')
|
|
while ((c = lgetc(0)) != '\n' && c != EOF)
|
|
; /* nothing */
|
|
if (c == '$' && !expanding) {
|
|
while (1) {
|
|
if ((c = lgetc(0)) == EOF)
|
|
return (0);
|
|
|
|
if (p + 1 >= buf + sizeof(buf) - 1) {
|
|
yyerror("string too long");
|
|
return (findeol());
|
|
}
|
|
if (isalnum(c) || c == '_') {
|
|
*p++ = c;
|
|
continue;
|
|
}
|
|
*p = '\0';
|
|
lungetc(c);
|
|
break;
|
|
}
|
|
val = symget(buf);
|
|
if (val == NULL) {
|
|
yyerror("macro '%s' not defined", buf);
|
|
return (findeol());
|
|
}
|
|
p = val + strlen(val) - 1;
|
|
lungetc(DONE_EXPAND);
|
|
while (p >= val) {
|
|
lungetc((unsigned char)*p);
|
|
p--;
|
|
}
|
|
lungetc(START_EXPAND);
|
|
goto top;
|
|
}
|
|
|
|
switch (c) {
|
|
case '\'':
|
|
case '"':
|
|
quotec = c;
|
|
while (1) {
|
|
if ((c = lgetc(quotec)) == EOF)
|
|
return (0);
|
|
if (c == '\n') {
|
|
file->lineno++;
|
|
continue;
|
|
} else if (c == '\\') {
|
|
if ((next = lgetc(quotec)) == EOF)
|
|
return (0);
|
|
if (next == quotec || next == ' ' ||
|
|
next == '\t')
|
|
c = next;
|
|
else if (next == '\n') {
|
|
file->lineno++;
|
|
continue;
|
|
} else
|
|
lungetc(next);
|
|
} else if (c == quotec) {
|
|
*p = '\0';
|
|
break;
|
|
} else if (c == '\0') {
|
|
yyerror("syntax error");
|
|
return (findeol());
|
|
}
|
|
if (p + 1 >= buf + sizeof(buf) - 1) {
|
|
yyerror("string too long");
|
|
return (findeol());
|
|
}
|
|
*p++ = c;
|
|
}
|
|
yylval.v.string = strdup(buf);
|
|
if (yylval.v.string == NULL)
|
|
err(1, "%s", __func__);
|
|
return (STRING);
|
|
}
|
|
|
|
#define allowed_to_end_number(x) \
|
|
(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
|
|
|
|
if (c == '-' || isdigit(c)) {
|
|
do {
|
|
*p++ = c;
|
|
if ((size_t)(p-buf) >= sizeof(buf)) {
|
|
yyerror("string too long");
|
|
return (findeol());
|
|
}
|
|
} while ((c = lgetc(0)) != EOF && isdigit(c));
|
|
lungetc(c);
|
|
if (p == buf + 1 && buf[0] == '-')
|
|
goto nodigits;
|
|
if (c == EOF || allowed_to_end_number(c)) {
|
|
const char *errstr = NULL;
|
|
|
|
*p = '\0';
|
|
yylval.v.number = strtonum(buf, LLONG_MIN,
|
|
LLONG_MAX, &errstr);
|
|
if (errstr) {
|
|
yyerror("\"%s\" invalid number: %s",
|
|
buf, errstr);
|
|
return (findeol());
|
|
}
|
|
return (NUMBER);
|
|
} else {
|
|
nodigits:
|
|
while (p > buf + 1)
|
|
lungetc((unsigned char)*--p);
|
|
c = (unsigned char)*--p;
|
|
if (c == '-')
|
|
return (c);
|
|
}
|
|
}
|
|
|
|
#define allowed_in_string(x) \
|
|
(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
|
|
x != '{' && x != '}' && x != '<' && x != '>' && \
|
|
x != '!' && x != '=' && x != '/' && x != '#' && \
|
|
x != ','))
|
|
|
|
if (isalnum(c) || c == ':' || c == '_' || c == '*') {
|
|
do {
|
|
*p++ = c;
|
|
if ((size_t)(p-buf) >= sizeof(buf)) {
|
|
yyerror("string too long");
|
|
return (findeol());
|
|
}
|
|
} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
|
|
lungetc(c);
|
|
*p = '\0';
|
|
if ((token = lookup(buf)) == STRING)
|
|
if ((yylval.v.string = strdup(buf)) == NULL)
|
|
err(1, "%s", __func__);
|
|
return (token);
|
|
}
|
|
if (c == '\n') {
|
|
yylval.lineno = file->lineno;
|
|
file->lineno++;
|
|
}
|
|
if (c == EOF)
|
|
return (0);
|
|
return (c);
|
|
}
|
|
|
|
int
|
|
check_file_secrecy(int fd, const char *fname)
|
|
{
|
|
struct stat st;
|
|
|
|
if (fstat(fd, &st)) {
|
|
warn("cannot stat %s", fname);
|
|
return (-1);
|
|
}
|
|
if (st.st_uid != 0 && st.st_uid != getuid()) {
|
|
warnx("%s: owner not root or current user", fname);
|
|
return (-1);
|
|
}
|
|
if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
|
|
warnx("%s: group writable or world read/writable", fname);
|
|
return (-1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
struct file *
|
|
pushfile(const char *name, int secret)
|
|
{
|
|
struct file *nfile;
|
|
|
|
if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
|
|
warn("%s", __func__);
|
|
return (NULL);
|
|
}
|
|
if ((nfile->name = strdup(name)) == NULL) {
|
|
warn("%s", __func__);
|
|
free(nfile);
|
|
return (NULL);
|
|
}
|
|
if (TAILQ_FIRST(&files) == NULL && strcmp(nfile->name, "-") == 0) {
|
|
nfile->stream = stdin;
|
|
free(nfile->name);
|
|
if ((nfile->name = strdup("stdin")) == NULL) {
|
|
warn("%s", __func__);
|
|
free(nfile);
|
|
return (NULL);
|
|
}
|
|
} else if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
|
|
warn("%s: %s", __func__, nfile->name);
|
|
free(nfile->name);
|
|
free(nfile);
|
|
return (NULL);
|
|
} else if (secret &&
|
|
check_file_secrecy(fileno(nfile->stream), nfile->name)) {
|
|
fclose(nfile->stream);
|
|
free(nfile->name);
|
|
free(nfile);
|
|
return (NULL);
|
|
}
|
|
nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
|
|
nfile->ungetsize = 16;
|
|
nfile->ungetbuf = malloc(nfile->ungetsize);
|
|
if (nfile->ungetbuf == NULL) {
|
|
warn("%s", __func__);
|
|
fclose(nfile->stream);
|
|
free(nfile->name);
|
|
free(nfile);
|
|
return (NULL);
|
|
}
|
|
TAILQ_INSERT_TAIL(&files, nfile, entry);
|
|
return (nfile);
|
|
}
|
|
|
|
int
|
|
popfile(void)
|
|
{
|
|
struct file *prev;
|
|
|
|
if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
|
|
prev->errors += file->errors;
|
|
|
|
TAILQ_REMOVE(&files, file, entry);
|
|
fclose(file->stream);
|
|
free(file->name);
|
|
free(file->ungetbuf);
|
|
free(file);
|
|
file = prev;
|
|
|
|
return (file ? 0 : EOF);
|
|
}
|
|
|
|
int
|
|
parse_config(const char *filename, struct iked *x_env)
|
|
{
|
|
struct sym *sym;
|
|
int errors = 0;
|
|
|
|
env = x_env;
|
|
rules = 0;
|
|
|
|
if ((file = pushfile(filename, 1)) == NULL)
|
|
return (-1);
|
|
topfile = file;
|
|
|
|
free(ocsp_url);
|
|
|
|
mobike = 1;
|
|
enforcesingleikesa = stickyaddress = 0;
|
|
cert_partial_chain = decouple = passive = 0;
|
|
ocsp_tolerate = 0;
|
|
ocsp_url = NULL;
|
|
ocsp_maxage = -1;
|
|
fragmentation = 0;
|
|
dpd_interval = IKED_IKE_SA_ALIVE_TIMEOUT;
|
|
decouple = passive = 0;
|
|
ocsp_url = NULL;
|
|
|
|
if (env->sc_opts & IKED_OPT_PASSIVE)
|
|
passive = 1;
|
|
|
|
yyparse();
|
|
errors = file->errors;
|
|
popfile();
|
|
|
|
env->sc_passive = passive ? 1 : 0;
|
|
env->sc_decoupled = decouple ? 1 : 0;
|
|
env->sc_mobike = mobike;
|
|
env->sc_enforcesingleikesa = enforcesingleikesa;
|
|
env->sc_stickyaddress = stickyaddress;
|
|
env->sc_frag = fragmentation;
|
|
env->sc_alive_timeout = dpd_interval;
|
|
env->sc_ocsp_url = ocsp_url;
|
|
env->sc_ocsp_tolerate = ocsp_tolerate;
|
|
env->sc_ocsp_maxage = ocsp_maxage;
|
|
env->sc_cert_partial_chain = cert_partial_chain;
|
|
env->sc_vendorid = vendorid;
|
|
|
|
if (!rules)
|
|
log_warnx("%s: no valid configuration rules found",
|
|
filename);
|
|
else
|
|
log_debug("%s: loaded %d configuration rules",
|
|
filename, rules);
|
|
|
|
/* Free macros and check which have not been used. */
|
|
while ((sym = TAILQ_FIRST(&symhead))) {
|
|
if (!sym->used)
|
|
log_debug("warning: macro '%s' not "
|
|
"used\n", sym->nam);
|
|
free(sym->nam);
|
|
free(sym->val);
|
|
TAILQ_REMOVE(&symhead, sym, entry);
|
|
free(sym);
|
|
}
|
|
|
|
iaw_free(iftab);
|
|
iftab = NULL;
|
|
|
|
return (errors ? -1 : 0);
|
|
}
|
|
|
|
int
|
|
symset(const char *nam, const char *val, int persist)
|
|
{
|
|
struct sym *sym;
|
|
|
|
TAILQ_FOREACH(sym, &symhead, entry) {
|
|
if (strcmp(nam, sym->nam) == 0)
|
|
break;
|
|
}
|
|
|
|
if (sym != NULL) {
|
|
if (sym->persist == 1)
|
|
return (0);
|
|
else {
|
|
free(sym->nam);
|
|
free(sym->val);
|
|
TAILQ_REMOVE(&symhead, sym, entry);
|
|
free(sym);
|
|
}
|
|
}
|
|
if ((sym = calloc(1, sizeof(*sym))) == NULL)
|
|
return (-1);
|
|
|
|
sym->nam = strdup(nam);
|
|
if (sym->nam == NULL) {
|
|
free(sym);
|
|
return (-1);
|
|
}
|
|
sym->val = strdup(val);
|
|
if (sym->val == NULL) {
|
|
free(sym->nam);
|
|
free(sym);
|
|
return (-1);
|
|
}
|
|
sym->used = 0;
|
|
sym->persist = persist;
|
|
TAILQ_INSERT_TAIL(&symhead, sym, entry);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cmdline_symset(char *s)
|
|
{
|
|
char *sym, *val;
|
|
int ret;
|
|
|
|
if ((val = strrchr(s, '=')) == NULL)
|
|
return (-1);
|
|
|
|
sym = strndup(s, val - s);
|
|
if (sym == NULL)
|
|
err(1, "%s", __func__);
|
|
ret = symset(sym, val + 1, 1);
|
|
free(sym);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
char *
|
|
symget(const char *nam)
|
|
{
|
|
struct sym *sym;
|
|
|
|
TAILQ_FOREACH(sym, &symhead, entry) {
|
|
if (strcmp(nam, sym->nam) == 0) {
|
|
sym->used = 1;
|
|
return (sym->val);
|
|
}
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
uint8_t
|
|
x2i(unsigned char *s)
|
|
{
|
|
char ss[3];
|
|
|
|
ss[0] = s[0];
|
|
ss[1] = s[1];
|
|
ss[2] = 0;
|
|
|
|
if (!isxdigit(s[0]) || !isxdigit(s[1])) {
|
|
yyerror("keys need to be specified in hex digits");
|
|
return (-1);
|
|
}
|
|
return ((uint8_t)strtoul(ss, NULL, 16));
|
|
}
|
|
|
|
int
|
|
parsekey(unsigned char *hexkey, size_t len, struct iked_auth *auth)
|
|
{
|
|
unsigned int i;
|
|
|
|
bzero(auth, sizeof(*auth));
|
|
if ((len / 2) > sizeof(auth->auth_data))
|
|
return (-1);
|
|
auth->auth_length = len / 2;
|
|
|
|
for (i = 0; i < auth->auth_length; i++)
|
|
auth->auth_data[i] = x2i(hexkey + 2 * i);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
parsekeyfile(char *filename, struct iked_auth *auth)
|
|
{
|
|
struct stat sb;
|
|
int fd, ret;
|
|
unsigned char *hex;
|
|
|
|
if ((fd = open(filename, O_RDONLY)) == -1)
|
|
err(1, "open %s", filename);
|
|
if (check_file_secrecy(fd, filename) == -1)
|
|
exit(1);
|
|
if (fstat(fd, &sb) == -1)
|
|
err(1, "parsekeyfile: stat %s", filename);
|
|
if ((sb.st_size > KEYSIZE_LIMIT) || (sb.st_size == 0))
|
|
errx(1, "%s: key too %s", filename, sb.st_size ? "large" :
|
|
"small");
|
|
if ((hex = calloc(sb.st_size, sizeof(unsigned char))) == NULL)
|
|
err(1, "parsekeyfile: calloc");
|
|
if (read(fd, hex, sb.st_size) < sb.st_size)
|
|
err(1, "parsekeyfile: read");
|
|
close(fd);
|
|
ret = parsekey(hex, sb.st_size, auth);
|
|
free(hex);
|
|
return (ret);
|
|
}
|
|
|
|
int
|
|
get_id_type(char *string)
|
|
{
|
|
struct in6_addr ia;
|
|
|
|
if (string == NULL)
|
|
return (IKEV2_ID_NONE);
|
|
|
|
if (*string == '/')
|
|
return (IKEV2_ID_ASN1_DN);
|
|
else if (inet_pton(AF_INET, string, &ia) == 1)
|
|
return (IKEV2_ID_IPV4);
|
|
else if (inet_pton(AF_INET6, string, &ia) == 1)
|
|
return (IKEV2_ID_IPV6);
|
|
else if (strchr(string, '@'))
|
|
return (IKEV2_ID_UFQDN);
|
|
else
|
|
return (IKEV2_ID_FQDN);
|
|
}
|
|
|
|
struct ipsec_addr_wrap *
|
|
host(const char *s)
|
|
{
|
|
struct ipsec_addr_wrap *ipa = NULL;
|
|
int mask = -1;
|
|
char *p, *ps;
|
|
const char *errstr;
|
|
|
|
if ((ps = strdup(s)) == NULL)
|
|
err(1, "%s: strdup", __func__);
|
|
|
|
if ((p = strchr(ps, '/')) != NULL) {
|
|
mask = strtonum(p+1, 0, 128, &errstr);
|
|
if (errstr) {
|
|
fprintf(stderr, "netmask is %s: %s\n", errstr, p);
|
|
goto error;
|
|
}
|
|
p[0] = '\0';
|
|
}
|
|
|
|
if ((ipa = host_if(ps, mask)) == NULL &&
|
|
(ipa = host_ip(ps, mask)) == NULL &&
|
|
(ipa = host_dns(ps, mask)) == NULL)
|
|
fprintf(stderr, "no IP address found for %s\n", s);
|
|
|
|
error:
|
|
free(ps);
|
|
return (ipa);
|
|
}
|
|
|
|
struct ipsec_addr_wrap *
|
|
host_ip(const char *s, int mask)
|
|
{
|
|
struct ipsec_addr_wrap *ipa = NULL;
|
|
struct addrinfo hints, *res;
|
|
char hbuf[NI_MAXHOST];
|
|
|
|
bzero(&hints, sizeof(struct addrinfo));
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_socktype = SOCK_DGRAM; /*dummy*/
|
|
hints.ai_flags = AI_NUMERICHOST;
|
|
if (getaddrinfo(s, NULL, &hints, &res))
|
|
return (NULL);
|
|
if (res->ai_next)
|
|
err(1, "%s: %s expanded to multiple item", __func__, s);
|
|
|
|
ipa = calloc(1, sizeof(struct ipsec_addr_wrap));
|
|
if (ipa == NULL)
|
|
err(1, "%s", __func__);
|
|
ipa->af = res->ai_family;
|
|
copy_sockaddrtoipa(ipa, res->ai_addr);
|
|
ipa->next = NULL;
|
|
ipa->tail = ipa;
|
|
|
|
set_ipmask(ipa, mask);
|
|
if (getnameinfo(res->ai_addr, res->ai_addrlen,
|
|
hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST)) {
|
|
errx(1, "could not get a numeric hostname");
|
|
}
|
|
|
|
if (mask > -1) {
|
|
ipa->netaddress = 1;
|
|
if (asprintf(&ipa->name, "%s/%d", hbuf, mask) == -1)
|
|
err(1, "%s", __func__);
|
|
} else {
|
|
if ((ipa->name = strdup(hbuf)) == NULL)
|
|
err(1, "%s", __func__);
|
|
}
|
|
|
|
freeaddrinfo(res);
|
|
|
|
return (ipa);
|
|
}
|
|
|
|
struct ipsec_addr_wrap *
|
|
host_dns(const char *s, int mask)
|
|
{
|
|
struct ipsec_addr_wrap *ipa = NULL, *head = NULL;
|
|
struct addrinfo hints, *res0, *res;
|
|
int error;
|
|
char hbuf[NI_MAXHOST];
|
|
|
|
bzero(&hints, sizeof(struct addrinfo));
|
|
hints.ai_family = PF_UNSPEC;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_flags = AI_ADDRCONFIG;
|
|
error = getaddrinfo(s, NULL, &hints, &res0);
|
|
if (error)
|
|
return (NULL);
|
|
|
|
for (res = res0; res; res = res->ai_next) {
|
|
if (res->ai_family != AF_INET && res->ai_family != AF_INET6)
|
|
continue;
|
|
|
|
ipa = calloc(1, sizeof(struct ipsec_addr_wrap));
|
|
if (ipa == NULL)
|
|
err(1, "%s", __func__);
|
|
copy_sockaddrtoipa(ipa, res->ai_addr);
|
|
error = getnameinfo(res->ai_addr, res->ai_addrlen, hbuf,
|
|
sizeof(hbuf), NULL, 0, NI_NUMERICHOST);
|
|
if (error)
|
|
err(1, "host_dns: getnameinfo");
|
|
ipa->name = strdup(hbuf);
|
|
if (ipa->name == NULL)
|
|
err(1, "%s", __func__);
|
|
ipa->af = res->ai_family;
|
|
ipa->next = NULL;
|
|
ipa->tail = ipa;
|
|
if (head == NULL)
|
|
head = ipa;
|
|
else {
|
|
head->tail->next = ipa;
|
|
head->tail = ipa;
|
|
}
|
|
|
|
/*
|
|
* XXX for now, no netmask support for IPv6.
|
|
* but since there's no way to specify address family, once you
|
|
* have IPv6 address on a host, you cannot use dns/netmask
|
|
* syntax.
|
|
*/
|
|
if (ipa->af == AF_INET)
|
|
set_ipmask(ipa, mask == -1 ? 32 : mask);
|
|
else
|
|
if (mask != -1)
|
|
err(1, "host_dns: cannot apply netmask "
|
|
"on non-IPv4 address");
|
|
}
|
|
freeaddrinfo(res0);
|
|
|
|
return (head);
|
|
}
|
|
|
|
struct ipsec_addr_wrap *
|
|
host_if(const char *s, int mask)
|
|
{
|
|
struct ipsec_addr_wrap *ipa = NULL;
|
|
|
|
if (ifa_exists(s))
|
|
ipa = ifa_lookup(s);
|
|
|
|
return (ipa);
|
|
}
|
|
|
|
struct ipsec_addr_wrap *
|
|
host_any(void)
|
|
{
|
|
struct ipsec_addr_wrap *ipa;
|
|
|
|
ipa = calloc(1, sizeof(struct ipsec_addr_wrap));
|
|
if (ipa == NULL)
|
|
err(1, "%s", __func__);
|
|
ipa->af = AF_UNSPEC;
|
|
ipa->netaddress = 1;
|
|
ipa->tail = ipa;
|
|
ipa->type = IPSEC_ADDR_ANY;
|
|
return (ipa);
|
|
}
|
|
|
|
struct ipsec_addr_wrap *
|
|
host_dynamic(void)
|
|
{
|
|
struct ipsec_addr_wrap *ipa;
|
|
|
|
ipa = calloc(1, sizeof(struct ipsec_addr_wrap));
|
|
if (ipa == NULL)
|
|
err(1, "%s", __func__);
|
|
ipa->af = AF_UNSPEC;
|
|
ipa->tail = ipa;
|
|
ipa->type = IPSEC_ADDR_DYNAMIC;
|
|
return (ipa);
|
|
}
|
|
|
|
void
|
|
ifa_load(void)
|
|
{
|
|
struct ifaddrs *ifap, *ifa;
|
|
struct ipsec_addr_wrap *n = NULL, *h = NULL;
|
|
struct sockaddr_in *sa_in;
|
|
struct sockaddr_in6 *sa_in6;
|
|
|
|
if (getifaddrs(&ifap) == -1)
|
|
err(1, "ifa_load: getifaddrs");
|
|
|
|
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
|
|
if (ifa->ifa_addr == NULL ||
|
|
!(ifa->ifa_addr->sa_family == AF_INET ||
|
|
ifa->ifa_addr->sa_family == AF_INET6 ||
|
|
ifa->ifa_addr->sa_family == AF_LINK))
|
|
continue;
|
|
n = calloc(1, sizeof(struct ipsec_addr_wrap));
|
|
if (n == NULL)
|
|
err(1, "%s", __func__);
|
|
n->af = ifa->ifa_addr->sa_family;
|
|
if ((n->name = strdup(ifa->ifa_name)) == NULL)
|
|
err(1, "%s", __func__);
|
|
if (n->af == AF_INET) {
|
|
sa_in = (struct sockaddr_in *)ifa->ifa_addr;
|
|
memcpy(&n->address, sa_in, sizeof(*sa_in));
|
|
sa_in = (struct sockaddr_in *)ifa->ifa_netmask;
|
|
n->mask = mask2prefixlen((struct sockaddr *)sa_in);
|
|
} else if (n->af == AF_INET6) {
|
|
sa_in6 = (struct sockaddr_in6 *)ifa->ifa_addr;
|
|
memcpy(&n->address, sa_in6, sizeof(*sa_in6));
|
|
sa_in6 = (struct sockaddr_in6 *)ifa->ifa_netmask;
|
|
n->mask = mask2prefixlen6((struct sockaddr *)sa_in6);
|
|
}
|
|
n->next = NULL;
|
|
n->tail = n;
|
|
if (h == NULL)
|
|
h = n;
|
|
else {
|
|
h->tail->next = n;
|
|
h->tail = n;
|
|
}
|
|
}
|
|
|
|
iftab = h;
|
|
freeifaddrs(ifap);
|
|
}
|
|
|
|
int
|
|
ifa_exists(const char *ifa_name)
|
|
{
|
|
struct ipsec_addr_wrap *n;
|
|
struct ifgroupreq ifgr;
|
|
int s;
|
|
|
|
if (iftab == NULL)
|
|
ifa_load();
|
|
|
|
/* check wether this is a group */
|
|
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
|
|
err(1, "ifa_exists: socket");
|
|
bzero(&ifgr, sizeof(ifgr));
|
|
strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name));
|
|
if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == 0) {
|
|
close(s);
|
|
return (1);
|
|
}
|
|
close(s);
|
|
|
|
for (n = iftab; n; n = n->next) {
|
|
if (n->af == AF_LINK && !strncmp(n->name, ifa_name,
|
|
IFNAMSIZ))
|
|
return (1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
struct ipsec_addr_wrap *
|
|
ifa_grouplookup(const char *ifa_name)
|
|
{
|
|
struct ifg_req *ifg;
|
|
struct ifgroupreq ifgr;
|
|
int s;
|
|
size_t len;
|
|
struct ipsec_addr_wrap *n, *h = NULL, *hn;
|
|
|
|
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
|
|
err(1, "socket");
|
|
bzero(&ifgr, sizeof(ifgr));
|
|
strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name));
|
|
if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) {
|
|
close(s);
|
|
return (NULL);
|
|
}
|
|
|
|
len = ifgr.ifgr_len;
|
|
if ((ifgr.ifgr_groups = calloc(1, len)) == NULL)
|
|
err(1, "%s", __func__);
|
|
if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1)
|
|
err(1, "ioctl");
|
|
|
|
for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(struct ifg_req);
|
|
ifg++) {
|
|
len -= sizeof(struct ifg_req);
|
|
if ((n = ifa_lookup(ifg->ifgrq_member)) == NULL)
|
|
continue;
|
|
if (h == NULL)
|
|
h = n;
|
|
else {
|
|
for (hn = h; hn->next != NULL; hn = hn->next)
|
|
; /* nothing */
|
|
hn->next = n;
|
|
n->tail = hn;
|
|
}
|
|
}
|
|
free(ifgr.ifgr_groups);
|
|
close(s);
|
|
|
|
return (h);
|
|
}
|
|
|
|
struct ipsec_addr_wrap *
|
|
ifa_lookup(const char *ifa_name)
|
|
{
|
|
struct ipsec_addr_wrap *p = NULL, *h = NULL, *n = NULL;
|
|
struct sockaddr_in6 *in6;
|
|
uint8_t *s6;
|
|
|
|
if (iftab == NULL)
|
|
ifa_load();
|
|
|
|
if ((n = ifa_grouplookup(ifa_name)) != NULL)
|
|
return (n);
|
|
|
|
for (p = iftab; p; p = p->next) {
|
|
if (p->af != AF_INET && p->af != AF_INET6)
|
|
continue;
|
|
if (strncmp(p->name, ifa_name, IFNAMSIZ))
|
|
continue;
|
|
n = calloc(1, sizeof(struct ipsec_addr_wrap));
|
|
if (n == NULL)
|
|
err(1, "%s", __func__);
|
|
memcpy(n, p, sizeof(struct ipsec_addr_wrap));
|
|
if ((n->name = strdup(p->name)) == NULL)
|
|
err(1, "%s", __func__);
|
|
switch (n->af) {
|
|
case AF_INET:
|
|
set_ipmask(n, 32);
|
|
break;
|
|
case AF_INET6:
|
|
in6 = (struct sockaddr_in6 *)&n->address;
|
|
s6 = (uint8_t *)&in6->sin6_addr.s6_addr;
|
|
|
|
/* route/show.c and bgpd/util.c give KAME credit */
|
|
if (IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) {
|
|
uint16_t tmp16;
|
|
|
|
/* for now we can not handle link local,
|
|
* therefore bail for now
|
|
*/
|
|
free(n->name);
|
|
free(n);
|
|
continue;
|
|
|
|
memcpy(&tmp16, &s6[2], sizeof(tmp16));
|
|
/* use this when we support link-local
|
|
* n->??.scopeid = ntohs(tmp16);
|
|
*/
|
|
s6[2] = 0;
|
|
s6[3] = 0;
|
|
}
|
|
set_ipmask(n, 128);
|
|
break;
|
|
}
|
|
|
|
n->next = NULL;
|
|
n->tail = n;
|
|
if (h == NULL)
|
|
h = n;
|
|
else {
|
|
h->tail->next = n;
|
|
h->tail = n;
|
|
}
|
|
}
|
|
|
|
return (h);
|
|
}
|
|
|
|
void
|
|
set_ipmask(struct ipsec_addr_wrap *address, int b)
|
|
{
|
|
if (b == -1)
|
|
address->mask = address->af == AF_INET ? 32 : 128;
|
|
else
|
|
address->mask = b;
|
|
}
|
|
|
|
const struct ipsec_xf *
|
|
parse_xf(const char *name, unsigned int length, const struct ipsec_xf xfs[])
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; xfs[i].name != NULL; i++) {
|
|
if (strncmp(name, xfs[i].name, strlen(name)))
|
|
continue;
|
|
if (length == 0 || length == xfs[i].length)
|
|
return &xfs[i];
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
int
|
|
encxf_noauth(unsigned int id)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; ikeencxfs[i].name != NULL; i++)
|
|
if (ikeencxfs[i].id == id)
|
|
return ikeencxfs[i].noauth;
|
|
return (0);
|
|
}
|
|
|
|
size_t
|
|
keylength_xf(unsigned int saproto, unsigned int type, unsigned int id)
|
|
{
|
|
int i;
|
|
const struct ipsec_xf *xfs;
|
|
|
|
switch (type) {
|
|
case IKEV2_XFORMTYPE_ENCR:
|
|
if (saproto == IKEV2_SAPROTO_IKE)
|
|
xfs = ikeencxfs;
|
|
else
|
|
xfs = ipsecencxfs;
|
|
break;
|
|
case IKEV2_XFORMTYPE_INTEGR:
|
|
xfs = authxfs;
|
|
break;
|
|
default:
|
|
return (0);
|
|
}
|
|
|
|
for (i = 0; xfs[i].name != NULL; i++) {
|
|
if (xfs[i].id == id)
|
|
return (xfs[i].length * 8);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
size_t
|
|
noncelength_xf(unsigned int type, unsigned int id)
|
|
{
|
|
const struct ipsec_xf *xfs = ipsecencxfs;
|
|
int i;
|
|
|
|
if (type != IKEV2_XFORMTYPE_ENCR)
|
|
return (0);
|
|
|
|
for (i = 0; xfs[i].name != NULL; i++)
|
|
if (xfs[i].id == id)
|
|
return (xfs[i].nonce * 8);
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
copy_transforms(unsigned int type,
|
|
const struct ipsec_xf **xfs, unsigned int nxfs,
|
|
struct iked_transform **dst, unsigned int *ndst,
|
|
struct iked_transform *src, size_t nsrc)
|
|
{
|
|
unsigned int i;
|
|
struct iked_transform *a, *b;
|
|
const struct ipsec_xf *xf;
|
|
|
|
if (nxfs) {
|
|
for (i = 0; i < nxfs; i++) {
|
|
xf = xfs[i];
|
|
*dst = recallocarray(*dst, *ndst,
|
|
*ndst + 1, sizeof(struct iked_transform));
|
|
if (*dst == NULL)
|
|
err(1, "%s", __func__);
|
|
b = *dst + (*ndst)++;
|
|
|
|
b->xform_type = type;
|
|
b->xform_id = xf->id;
|
|
b->xform_keylength = xf->length * 8;
|
|
b->xform_length = xf->keylength * 8;
|
|
}
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < nsrc; i++) {
|
|
a = src + i;
|
|
if (a->xform_type != type)
|
|
continue;
|
|
*dst = recallocarray(*dst, *ndst,
|
|
*ndst + 1, sizeof(struct iked_transform));
|
|
if (*dst == NULL)
|
|
err(1, "%s", __func__);
|
|
b = *dst + (*ndst)++;
|
|
memcpy(b, a, sizeof(*b));
|
|
}
|
|
}
|
|
|
|
int
|
|
create_ike(char *name, int af, struct ipsec_addr_wrap *ipproto,
|
|
int rdomain, struct ipsec_hosts *hosts,
|
|
struct ipsec_hosts *peers, struct ipsec_mode *ike_sa,
|
|
struct ipsec_mode *ipsec_sa, uint8_t saproto,
|
|
uint8_t flags, char *srcid, char *dstid,
|
|
uint32_t ikelifetime, struct iked_lifetime *lt,
|
|
struct iked_auth *authtype, struct ipsec_filters *filter,
|
|
struct ipsec_addr_wrap *ikecfg, char *iface)
|
|
{
|
|
char idstr[IKED_ID_SIZE];
|
|
struct ipsec_addr_wrap *ipa, *ipb, *ipp;
|
|
struct iked_auth *ikeauth;
|
|
struct iked_policy pol;
|
|
struct iked_proposal *p, *ptmp;
|
|
struct iked_transform *xf;
|
|
unsigned int i, j, xfi, noauth, auth;
|
|
unsigned int ikepropid = 1, ipsecpropid = 1;
|
|
struct iked_flow *flow, *ftmp;
|
|
static unsigned int policy_id = 0;
|
|
struct iked_cfg *cfg;
|
|
int ret = -1;
|
|
|
|
bzero(&pol, sizeof(pol));
|
|
bzero(idstr, sizeof(idstr));
|
|
|
|
pol.pol_id = ++policy_id;
|
|
pol.pol_certreqtype = env->sc_certreqtype;
|
|
pol.pol_af = af;
|
|
pol.pol_saproto = saproto;
|
|
for (i = 0, ipp = ipproto; ipp; ipp = ipp->next, i++) {
|
|
if (i >= IKED_IPPROTO_MAX) {
|
|
yyerror("too many protocols");
|
|
return (-1);
|
|
}
|
|
pol.pol_ipproto[i] = ipp->type;
|
|
pol.pol_nipproto++;
|
|
}
|
|
|
|
pol.pol_flags = flags;
|
|
pol.pol_rdomain = rdomain;
|
|
memcpy(&pol.pol_auth, authtype, sizeof(struct iked_auth));
|
|
explicit_bzero(authtype, sizeof(*authtype));
|
|
|
|
if (name != NULL) {
|
|
if (strlcpy(pol.pol_name, name,
|
|
sizeof(pol.pol_name)) >= sizeof(pol.pol_name)) {
|
|
yyerror("name too long");
|
|
return (-1);
|
|
}
|
|
} else {
|
|
snprintf(pol.pol_name, sizeof(pol.pol_name),
|
|
"policy%d", policy_id);
|
|
}
|
|
|
|
if (iface != NULL) {
|
|
/* sec(4) */
|
|
if (strncmp("sec", iface, strlen("sec")) == 0)
|
|
pol.pol_flags |= IKED_POLICY_ROUTING;
|
|
|
|
pol.pol_iface = if_nametoindex(iface);
|
|
if (pol.pol_iface == 0) {
|
|
yyerror("invalid iface");
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
if (srcid) {
|
|
pol.pol_localid.id_type = get_id_type(srcid);
|
|
pol.pol_localid.id_length = strlen(srcid);
|
|
if (strlcpy((char *)pol.pol_localid.id_data,
|
|
srcid, IKED_ID_SIZE) >= IKED_ID_SIZE) {
|
|
yyerror("srcid too long");
|
|
return (-1);
|
|
}
|
|
}
|
|
if (dstid) {
|
|
pol.pol_peerid.id_type = get_id_type(dstid);
|
|
pol.pol_peerid.id_length = strlen(dstid);
|
|
if (strlcpy((char *)pol.pol_peerid.id_data,
|
|
dstid, IKED_ID_SIZE) >= IKED_ID_SIZE) {
|
|
yyerror("dstid too long");
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
if (filter != NULL) {
|
|
if (filter->tag)
|
|
strlcpy(pol.pol_tag, filter->tag, sizeof(pol.pol_tag));
|
|
pol.pol_tap = filter->tap;
|
|
}
|
|
|
|
if (peers == NULL) {
|
|
if (pol.pol_flags & IKED_POLICY_ACTIVE) {
|
|
yyerror("active mode requires peer specification");
|
|
return (-1);
|
|
}
|
|
pol.pol_flags |= IKED_POLICY_DEFAULT|IKED_POLICY_SKIP;
|
|
}
|
|
|
|
if (peers && peers->src && peers->dst &&
|
|
(peers->src->af != AF_UNSPEC) && (peers->dst->af != AF_UNSPEC) &&
|
|
(peers->src->af != peers->dst->af))
|
|
fatalx("create_ike: peer address family mismatch");
|
|
|
|
if (peers && (pol.pol_af != AF_UNSPEC) &&
|
|
((peers->src && (peers->src->af != AF_UNSPEC) &&
|
|
(peers->src->af != pol.pol_af)) ||
|
|
(peers->dst && (peers->dst->af != AF_UNSPEC) &&
|
|
(peers->dst->af != pol.pol_af))))
|
|
fatalx("create_ike: policy address family mismatch");
|
|
|
|
ipa = ipb = NULL;
|
|
if (peers) {
|
|
if (peers->src)
|
|
ipa = peers->src;
|
|
if (peers->dst)
|
|
ipb = peers->dst;
|
|
if (ipa == NULL && ipb == NULL) {
|
|
if (hosts->src && hosts->src->next == NULL)
|
|
ipa = hosts->src;
|
|
if (hosts->dst && hosts->dst->next == NULL)
|
|
ipb = hosts->dst;
|
|
}
|
|
}
|
|
if (ipa == NULL && ipb == NULL) {
|
|
yyerror("could not get local/peer specification");
|
|
return (-1);
|
|
}
|
|
if (pol.pol_flags & IKED_POLICY_ACTIVE) {
|
|
if (ipb == NULL || ipb->netaddress ||
|
|
(ipa != NULL && ipa->netaddress)) {
|
|
yyerror("active mode requires local/peer address");
|
|
return (-1);
|
|
}
|
|
}
|
|
if (ipa) {
|
|
memcpy(&pol.pol_local.addr, &ipa->address,
|
|
sizeof(ipa->address));
|
|
pol.pol_local.addr_af = ipa->af;
|
|
pol.pol_local.addr_mask = ipa->mask;
|
|
pol.pol_local.addr_net = ipa->netaddress;
|
|
if (pol.pol_af == AF_UNSPEC)
|
|
pol.pol_af = ipa->af;
|
|
}
|
|
if (ipb) {
|
|
memcpy(&pol.pol_peer.addr, &ipb->address,
|
|
sizeof(ipb->address));
|
|
pol.pol_peer.addr_af = ipb->af;
|
|
pol.pol_peer.addr_mask = ipb->mask;
|
|
pol.pol_peer.addr_net = ipb->netaddress;
|
|
if (pol.pol_af == AF_UNSPEC)
|
|
pol.pol_af = ipb->af;
|
|
}
|
|
|
|
if (ikelifetime)
|
|
pol.pol_rekey = ikelifetime;
|
|
|
|
if (lt)
|
|
pol.pol_lifetime = *lt;
|
|
else
|
|
pol.pol_lifetime = deflifetime;
|
|
|
|
TAILQ_INIT(&pol.pol_proposals);
|
|
RB_INIT(&pol.pol_flows);
|
|
|
|
if (ike_sa == NULL || ike_sa->nxfs == 0) {
|
|
/* AES-GCM proposal */
|
|
if ((p = calloc(1, sizeof(*p))) == NULL)
|
|
err(1, "%s", __func__);
|
|
p->prop_id = ikepropid++;
|
|
p->prop_protoid = IKEV2_SAPROTO_IKE;
|
|
p->prop_nxforms = ikev2_default_nike_transforms_noauth;
|
|
p->prop_xforms = ikev2_default_ike_transforms_noauth;
|
|
TAILQ_INSERT_TAIL(&pol.pol_proposals, p, prop_entry);
|
|
pol.pol_nproposals++;
|
|
|
|
/* Non GCM proposal */
|
|
if ((p = calloc(1, sizeof(*p))) == NULL)
|
|
err(1, "%s", __func__);
|
|
p->prop_id = ikepropid++;
|
|
p->prop_protoid = IKEV2_SAPROTO_IKE;
|
|
p->prop_nxforms = ikev2_default_nike_transforms;
|
|
p->prop_xforms = ikev2_default_ike_transforms;
|
|
TAILQ_INSERT_TAIL(&pol.pol_proposals, p, prop_entry);
|
|
pol.pol_nproposals++;
|
|
} else {
|
|
for (i = 0; i < ike_sa->nxfs; i++) {
|
|
noauth = auth = 0;
|
|
for (j = 0; j < ike_sa->xfs[i]->nencxf; j++) {
|
|
if (ike_sa->xfs[i]->encxf[j]->noauth)
|
|
noauth++;
|
|
else
|
|
auth++;
|
|
}
|
|
for (j = 0; j < ike_sa->xfs[i]->ngroupxf; j++) {
|
|
if (ike_sa->xfs[i]->groupxf[j]->id
|
|
== IKEV2_XFORMDH_NONE) {
|
|
yyerror("IKE group can not be \"none\".");
|
|
goto done;
|
|
}
|
|
}
|
|
if (ike_sa->xfs[i]->nauthxf)
|
|
auth++;
|
|
|
|
if (ike_sa->xfs[i]->nesnxf) {
|
|
yyerror("cannot use ESN with ikesa.");
|
|
goto done;
|
|
}
|
|
if (noauth && noauth != ike_sa->xfs[i]->nencxf) {
|
|
yyerror("cannot mix encryption transforms with "
|
|
"implicit and non-implicit authentication");
|
|
goto done;
|
|
}
|
|
if (noauth && ike_sa->xfs[i]->nauthxf) {
|
|
yyerror("authentication is implicit for given "
|
|
"encryption transforms");
|
|
goto done;
|
|
}
|
|
|
|
if (!auth) {
|
|
if ((p = calloc(1, sizeof(*p))) == NULL)
|
|
err(1, "%s", __func__);
|
|
|
|
xf = NULL;
|
|
xfi = 0;
|
|
copy_transforms(IKEV2_XFORMTYPE_ENCR,
|
|
ike_sa->xfs[i]->encxf,
|
|
ike_sa->xfs[i]->nencxf, &xf, &xfi,
|
|
ikev2_default_ike_transforms_noauth,
|
|
ikev2_default_nike_transforms_noauth);
|
|
copy_transforms(IKEV2_XFORMTYPE_DH,
|
|
ike_sa->xfs[i]->groupxf,
|
|
ike_sa->xfs[i]->ngroupxf, &xf, &xfi,
|
|
ikev2_default_ike_transforms_noauth,
|
|
ikev2_default_nike_transforms_noauth);
|
|
copy_transforms(IKEV2_XFORMTYPE_PRF,
|
|
ike_sa->xfs[i]->prfxf,
|
|
ike_sa->xfs[i]->nprfxf, &xf, &xfi,
|
|
ikev2_default_ike_transforms_noauth,
|
|
ikev2_default_nike_transforms_noauth);
|
|
|
|
p->prop_id = ikepropid++;
|
|
p->prop_protoid = IKEV2_SAPROTO_IKE;
|
|
p->prop_xforms = xf;
|
|
p->prop_nxforms = xfi;
|
|
TAILQ_INSERT_TAIL(&pol.pol_proposals, p, prop_entry);
|
|
pol.pol_nproposals++;
|
|
}
|
|
if (!noauth) {
|
|
if ((p = calloc(1, sizeof(*p))) == NULL)
|
|
err(1, "%s", __func__);
|
|
|
|
xf = NULL;
|
|
xfi = 0;
|
|
copy_transforms(IKEV2_XFORMTYPE_INTEGR,
|
|
ike_sa->xfs[i]->authxf,
|
|
ike_sa->xfs[i]->nauthxf, &xf, &xfi,
|
|
ikev2_default_ike_transforms,
|
|
ikev2_default_nike_transforms);
|
|
copy_transforms(IKEV2_XFORMTYPE_ENCR,
|
|
ike_sa->xfs[i]->encxf,
|
|
ike_sa->xfs[i]->nencxf, &xf, &xfi,
|
|
ikev2_default_ike_transforms,
|
|
ikev2_default_nike_transforms);
|
|
copy_transforms(IKEV2_XFORMTYPE_DH,
|
|
ike_sa->xfs[i]->groupxf,
|
|
ike_sa->xfs[i]->ngroupxf, &xf, &xfi,
|
|
ikev2_default_ike_transforms,
|
|
ikev2_default_nike_transforms);
|
|
copy_transforms(IKEV2_XFORMTYPE_PRF,
|
|
ike_sa->xfs[i]->prfxf,
|
|
ike_sa->xfs[i]->nprfxf, &xf, &xfi,
|
|
ikev2_default_ike_transforms,
|
|
ikev2_default_nike_transforms);
|
|
|
|
p->prop_id = ikepropid++;
|
|
p->prop_protoid = IKEV2_SAPROTO_IKE;
|
|
p->prop_xforms = xf;
|
|
p->prop_nxforms = xfi;
|
|
TAILQ_INSERT_TAIL(&pol.pol_proposals, p, prop_entry);
|
|
pol.pol_nproposals++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ipsec_sa == NULL || ipsec_sa->nxfs == 0) {
|
|
if ((p = calloc(1, sizeof(*p))) == NULL)
|
|
err(1, "%s", __func__);
|
|
p->prop_id = ipsecpropid++;
|
|
p->prop_protoid = saproto;
|
|
p->prop_nxforms = ikev2_default_nesp_transforms_noauth;
|
|
p->prop_xforms = ikev2_default_esp_transforms_noauth;
|
|
TAILQ_INSERT_TAIL(&pol.pol_proposals, p, prop_entry);
|
|
pol.pol_nproposals++;
|
|
|
|
if ((p = calloc(1, sizeof(*p))) == NULL)
|
|
err(1, "%s", __func__);
|
|
p->prop_id = ipsecpropid++;
|
|
p->prop_protoid = saproto;
|
|
p->prop_nxforms = ikev2_default_nesp_transforms;
|
|
p->prop_xforms = ikev2_default_esp_transforms;
|
|
TAILQ_INSERT_TAIL(&pol.pol_proposals, p, prop_entry);
|
|
pol.pol_nproposals++;
|
|
} else {
|
|
for (i = 0; i < ipsec_sa->nxfs; i++) {
|
|
noauth = auth = 0;
|
|
for (j = 0; j < ipsec_sa->xfs[i]->nencxf; j++) {
|
|
if (ipsec_sa->xfs[i]->encxf[j]->noauth)
|
|
noauth++;
|
|
else
|
|
auth++;
|
|
}
|
|
if (ipsec_sa->xfs[i]->nauthxf)
|
|
auth++;
|
|
|
|
if (noauth && noauth != ipsec_sa->xfs[i]->nencxf) {
|
|
yyerror("cannot mix encryption transforms with "
|
|
"implicit and non-implicit authentication");
|
|
goto done;
|
|
}
|
|
if (noauth && ipsec_sa->xfs[i]->nauthxf) {
|
|
yyerror("authentication is implicit for given "
|
|
"encryption transforms");
|
|
goto done;
|
|
}
|
|
|
|
if (!auth) {
|
|
if ((p = calloc(1, sizeof(*p))) == NULL)
|
|
err(1, "%s", __func__);
|
|
|
|
xf = NULL;
|
|
xfi = 0;
|
|
copy_transforms(IKEV2_XFORMTYPE_ENCR,
|
|
ipsec_sa->xfs[i]->encxf,
|
|
ipsec_sa->xfs[i]->nencxf, &xf, &xfi,
|
|
ikev2_default_esp_transforms_noauth,
|
|
ikev2_default_nesp_transforms_noauth);
|
|
copy_transforms(IKEV2_XFORMTYPE_DH,
|
|
ipsec_sa->xfs[i]->groupxf,
|
|
ipsec_sa->xfs[i]->ngroupxf, &xf, &xfi,
|
|
ikev2_default_esp_transforms_noauth,
|
|
ikev2_default_nesp_transforms_noauth);
|
|
copy_transforms(IKEV2_XFORMTYPE_ESN,
|
|
ipsec_sa->xfs[i]->esnxf,
|
|
ipsec_sa->xfs[i]->nesnxf, &xf, &xfi,
|
|
ikev2_default_esp_transforms_noauth,
|
|
ikev2_default_nesp_transforms_noauth);
|
|
|
|
p->prop_id = ipsecpropid++;
|
|
p->prop_protoid = saproto;
|
|
p->prop_xforms = xf;
|
|
p->prop_nxforms = xfi;
|
|
TAILQ_INSERT_TAIL(&pol.pol_proposals, p, prop_entry);
|
|
pol.pol_nproposals++;
|
|
}
|
|
if (!noauth) {
|
|
if ((p = calloc(1, sizeof(*p))) == NULL)
|
|
err(1, "%s", __func__);
|
|
|
|
xf = NULL;
|
|
xfi = 0;
|
|
copy_transforms(IKEV2_XFORMTYPE_INTEGR,
|
|
ipsec_sa->xfs[i]->authxf,
|
|
ipsec_sa->xfs[i]->nauthxf, &xf, &xfi,
|
|
ikev2_default_esp_transforms,
|
|
ikev2_default_nesp_transforms);
|
|
copy_transforms(IKEV2_XFORMTYPE_ENCR,
|
|
ipsec_sa->xfs[i]->encxf,
|
|
ipsec_sa->xfs[i]->nencxf, &xf, &xfi,
|
|
ikev2_default_esp_transforms,
|
|
ikev2_default_nesp_transforms);
|
|
copy_transforms(IKEV2_XFORMTYPE_DH,
|
|
ipsec_sa->xfs[i]->groupxf,
|
|
ipsec_sa->xfs[i]->ngroupxf, &xf, &xfi,
|
|
ikev2_default_esp_transforms,
|
|
ikev2_default_nesp_transforms);
|
|
copy_transforms(IKEV2_XFORMTYPE_ESN,
|
|
ipsec_sa->xfs[i]->esnxf,
|
|
ipsec_sa->xfs[i]->nesnxf, &xf, &xfi,
|
|
ikev2_default_esp_transforms,
|
|
ikev2_default_nesp_transforms);
|
|
|
|
p->prop_id = ipsecpropid++;
|
|
p->prop_protoid = saproto;
|
|
p->prop_xforms = xf;
|
|
p->prop_nxforms = xfi;
|
|
TAILQ_INSERT_TAIL(&pol.pol_proposals, p, prop_entry);
|
|
pol.pol_nproposals++;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (ipa = hosts->src, ipb = hosts->dst; ipa && ipb;
|
|
ipa = ipa->next, ipb = ipb->next) {
|
|
for (j = 0; j < pol.pol_nipproto; j++)
|
|
if (expand_flows(&pol, pol.pol_ipproto[j], ipa, ipb))
|
|
fatalx("create_ike: invalid flow");
|
|
if (pol.pol_nipproto == 0)
|
|
if (expand_flows(&pol, 0, ipa, ipb))
|
|
fatalx("create_ike: invalid flow");
|
|
}
|
|
|
|
for (j = 0, ipa = ikecfg; ipa; ipa = ipa->next, j++) {
|
|
if (j >= IKED_CFG_MAX)
|
|
break;
|
|
cfg = &pol.pol_cfg[j];
|
|
pol.pol_ncfg++;
|
|
|
|
cfg->cfg_action = ipa->action;
|
|
cfg->cfg_type = ipa->type;
|
|
memcpy(&cfg->cfg.address.addr, &ipa->address,
|
|
sizeof(ipa->address));
|
|
cfg->cfg.address.addr_mask = ipa->mask;
|
|
cfg->cfg.address.addr_net = ipa->netaddress;
|
|
cfg->cfg.address.addr_af = ipa->af;
|
|
}
|
|
|
|
if (dstid)
|
|
strlcpy(idstr, dstid, sizeof(idstr));
|
|
else if (!pol.pol_peer.addr_net)
|
|
strlcpy(idstr, print_addr(&pol.pol_peer.addr), sizeof(idstr));
|
|
|
|
ikeauth = &pol.pol_auth;
|
|
switch (ikeauth->auth_method) {
|
|
case IKEV2_AUTH_RSA_SIG:
|
|
pol.pol_certreqtype = IKEV2_CERT_RSA_KEY;
|
|
break;
|
|
case IKEV2_AUTH_ECDSA_256:
|
|
case IKEV2_AUTH_ECDSA_384:
|
|
case IKEV2_AUTH_ECDSA_521:
|
|
pol.pol_certreqtype = IKEV2_CERT_ECDSA;
|
|
break;
|
|
default:
|
|
pol.pol_certreqtype = IKEV2_CERT_NONE;
|
|
break;
|
|
}
|
|
|
|
log_debug("%s: using %s for peer %s", __func__,
|
|
print_xf(ikeauth->auth_method, 0, methodxfs), idstr);
|
|
|
|
config_setpolicy(env, &pol, PROC_IKEV2);
|
|
config_setflow(env, &pol, PROC_IKEV2);
|
|
|
|
rules++;
|
|
ret = 0;
|
|
|
|
done:
|
|
if (ike_sa) {
|
|
for (i = 0; i < ike_sa->nxfs; i++) {
|
|
free(ike_sa->xfs[i]->authxf);
|
|
free(ike_sa->xfs[i]->encxf);
|
|
free(ike_sa->xfs[i]->groupxf);
|
|
free(ike_sa->xfs[i]->prfxf);
|
|
free(ike_sa->xfs[i]);
|
|
}
|
|
free(ike_sa->xfs);
|
|
free(ike_sa);
|
|
}
|
|
if (ipsec_sa) {
|
|
for (i = 0; i < ipsec_sa->nxfs; i++) {
|
|
free(ipsec_sa->xfs[i]->authxf);
|
|
free(ipsec_sa->xfs[i]->encxf);
|
|
free(ipsec_sa->xfs[i]->groupxf);
|
|
free(ipsec_sa->xfs[i]->prfxf);
|
|
free(ipsec_sa->xfs[i]->esnxf);
|
|
free(ipsec_sa->xfs[i]);
|
|
}
|
|
free(ipsec_sa->xfs);
|
|
free(ipsec_sa);
|
|
}
|
|
TAILQ_FOREACH_SAFE(p, &pol.pol_proposals, prop_entry, ptmp) {
|
|
if (p->prop_xforms != ikev2_default_ike_transforms &&
|
|
p->prop_xforms != ikev2_default_ike_transforms_noauth &&
|
|
p->prop_xforms != ikev2_default_esp_transforms &&
|
|
p->prop_xforms != ikev2_default_esp_transforms_noauth)
|
|
free(p->prop_xforms);
|
|
free(p);
|
|
}
|
|
if (peers != NULL) {
|
|
iaw_free(peers->src);
|
|
iaw_free(peers->dst);
|
|
/* peers is static, cannot be freed */
|
|
}
|
|
if (hosts != NULL) {
|
|
iaw_free(hosts->src);
|
|
iaw_free(hosts->dst);
|
|
free(hosts);
|
|
}
|
|
iaw_free(ikecfg);
|
|
iaw_free(ipproto);
|
|
RB_FOREACH_SAFE(flow, iked_flows, &pol.pol_flows, ftmp) {
|
|
RB_REMOVE(iked_flows, &pol.pol_flows, flow);
|
|
free(flow);
|
|
}
|
|
free(name);
|
|
free(srcid);
|
|
free(dstid);
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
create_flow(struct iked_policy *pol, int proto, struct ipsec_addr_wrap *ipa,
|
|
struct ipsec_addr_wrap *ipb)
|
|
{
|
|
struct iked_flow *flow;
|
|
struct ipsec_addr_wrap *ippn;
|
|
|
|
if (ipa->af != ipb->af) {
|
|
yyerror("cannot mix different address families.");
|
|
return (-1);
|
|
}
|
|
|
|
if ((flow = calloc(1, sizeof(struct iked_flow))) == NULL)
|
|
fatalx("%s: failed to alloc flow.", __func__);
|
|
|
|
memcpy(&flow->flow_src.addr, &ipa->address,
|
|
sizeof(ipa->address));
|
|
flow->flow_src.addr_af = ipa->af;
|
|
flow->flow_src.addr_mask = ipa->mask;
|
|
flow->flow_src.addr_net = ipa->netaddress;
|
|
flow->flow_src.addr_port = ipa->port;
|
|
|
|
memcpy(&flow->flow_dst.addr, &ipb->address,
|
|
sizeof(ipb->address));
|
|
flow->flow_dst.addr_af = ipb->af;
|
|
flow->flow_dst.addr_mask = ipb->mask;
|
|
flow->flow_dst.addr_net = ipb->netaddress;
|
|
flow->flow_dst.addr_port = ipb->port;
|
|
|
|
ippn = ipa->srcnat;
|
|
if (ippn) {
|
|
memcpy(&flow->flow_prenat.addr, &ippn->address,
|
|
sizeof(ippn->address));
|
|
flow->flow_prenat.addr_af = ippn->af;
|
|
flow->flow_prenat.addr_mask = ippn->mask;
|
|
flow->flow_prenat.addr_net = ippn->netaddress;
|
|
} else {
|
|
flow->flow_prenat.addr_af = 0;
|
|
}
|
|
|
|
flow->flow_dir = IPSP_DIRECTION_OUT;
|
|
flow->flow_ipproto = proto;
|
|
flow->flow_saproto = pol->pol_saproto;
|
|
flow->flow_rdomain = pol->pol_rdomain;
|
|
|
|
if (RB_INSERT(iked_flows, &pol->pol_flows, flow) == NULL)
|
|
pol->pol_nflows++;
|
|
else {
|
|
warnx("create_ike: duplicate flow");
|
|
free(flow);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
expand_flows(struct iked_policy *pol, int proto, struct ipsec_addr_wrap *src,
|
|
struct ipsec_addr_wrap *dst)
|
|
{
|
|
struct ipsec_addr_wrap *ipa = NULL, *ipb = NULL;
|
|
int ret = -1;
|
|
int srcaf, dstaf;
|
|
|
|
srcaf = src->af;
|
|
dstaf = dst->af;
|
|
|
|
if (src->af == AF_UNSPEC &&
|
|
dst->af == AF_UNSPEC) {
|
|
/* Need both IPv4 and IPv6 flows */
|
|
src->af = dst->af = AF_INET;
|
|
ipa = expand_keyword(src);
|
|
ipb = expand_keyword(dst);
|
|
if (!ipa || !ipb)
|
|
goto done;
|
|
if (create_flow(pol, proto, ipa, ipb))
|
|
goto done;
|
|
|
|
iaw_free(ipa);
|
|
iaw_free(ipb);
|
|
src->af = dst->af = AF_INET6;
|
|
ipa = expand_keyword(src);
|
|
ipb = expand_keyword(dst);
|
|
if (!ipa || !ipb)
|
|
goto done;
|
|
if (create_flow(pol, proto, ipa, ipb))
|
|
goto done;
|
|
} else if (src->af == AF_UNSPEC) {
|
|
src->af = dst->af;
|
|
ipa = expand_keyword(src);
|
|
if (!ipa)
|
|
goto done;
|
|
if (create_flow(pol, proto, ipa, dst))
|
|
goto done;
|
|
} else if (dst->af == AF_UNSPEC) {
|
|
dst->af = src->af;
|
|
ipa = expand_keyword(dst);
|
|
if (!ipa)
|
|
goto done;
|
|
if (create_flow(pol, proto, src, ipa))
|
|
goto done;
|
|
} else if (create_flow(pol, proto, src, dst))
|
|
goto done;
|
|
ret = 0;
|
|
done:
|
|
src->af = srcaf;
|
|
dst->af = dstaf;
|
|
iaw_free(ipa);
|
|
iaw_free(ipb);
|
|
return (ret);
|
|
}
|
|
|
|
static struct ipsec_addr_wrap *
|
|
expand_keyword(struct ipsec_addr_wrap *ip)
|
|
{
|
|
switch(ip->af) {
|
|
case AF_INET:
|
|
switch(ip->type) {
|
|
case IPSEC_ADDR_ANY:
|
|
return (host("0.0.0.0/0"));
|
|
case IPSEC_ADDR_DYNAMIC:
|
|
return (host("0.0.0.0"));
|
|
}
|
|
break;
|
|
case AF_INET6:
|
|
switch(ip->type) {
|
|
case IPSEC_ADDR_ANY:
|
|
return (host("::/0"));
|
|
case IPSEC_ADDR_DYNAMIC:
|
|
return (host("::"));
|
|
}
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
int
|
|
create_user(const char *user, const char *pass)
|
|
{
|
|
struct iked_user usr;
|
|
|
|
bzero(&usr, sizeof(usr));
|
|
|
|
if (*user == '\0' || (strlcpy(usr.usr_name, user,
|
|
sizeof(usr.usr_name)) >= sizeof(usr.usr_name))) {
|
|
yyerror("invalid user name");
|
|
return (-1);
|
|
}
|
|
if (*pass == '\0' || (strlcpy(usr.usr_pass, pass,
|
|
sizeof(usr.usr_pass)) >= sizeof(usr.usr_pass))) {
|
|
yyerror("invalid password");
|
|
explicit_bzero(&usr, sizeof usr); /* zap partial password */
|
|
return (-1);
|
|
}
|
|
|
|
config_setuser(env, &usr, PROC_IKEV2);
|
|
|
|
rules++;
|
|
|
|
explicit_bzero(&usr, sizeof usr);
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
iaw_free(struct ipsec_addr_wrap *head)
|
|
{
|
|
struct ipsec_addr_wrap *n, *cur;
|
|
|
|
if (head == NULL)
|
|
return;
|
|
|
|
for (n = head; n != NULL; ) {
|
|
cur = n;
|
|
n = n->next;
|
|
if (cur->srcnat != NULL) {
|
|
free(cur->srcnat->name);
|
|
free(cur->srcnat);
|
|
}
|
|
free(cur->name);
|
|
free(cur);
|
|
}
|
|
}
|