summaryrefslogtreecommitdiff
path: root/sbin/iked/ikev2.c
diff options
context:
space:
mode:
authorMarkus Friedl <markus@cvs.openbsd.org>2014-02-17 15:53:47 +0000
committerMarkus Friedl <markus@cvs.openbsd.org>2014-02-17 15:53:47 +0000
commitfc4b6eee09214a73ae15dbec9a49caa1e08be760 (patch)
treefae67255daa6b902f6d20b6bcfa6616422d5cc7c /sbin/iked/ikev2.c
parent964fbe49b7ae0e11cfa7452b6b8c1a4c908ba546 (diff)
interpret 'config address net/prefix' as a pool of addresses and
randomly choose the address for CFG_REQUEST. this address will be used to replace 0.0.0.0/32 in the specified flow. e.g. > ikev2 passive esp from 192.168.1.0/24 to 0.0.0.0 \ > config address 192.168.10.200/24 will assign an address between 192.168.10.200 and 192.168.10.254 and replace 0.0.0.0 with this address. ok mikeb@ on older version of this diff.
Diffstat (limited to 'sbin/iked/ikev2.c')
-rw-r--r--sbin/iked/ikev2.c184
1 files changed, 181 insertions, 3 deletions
diff --git a/sbin/iked/ikev2.c b/sbin/iked/ikev2.c
index 95bb8f5a2c6..dbf8f1996a9 100644
--- a/sbin/iked/ikev2.c
+++ b/sbin/iked/ikev2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ikev2.c,v 1.93 2014/02/17 11:00:14 reyk Exp $ */
+/* $OpenBSD: ikev2.c,v 1.94 2014/02/17 15:53:46 markus Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -108,6 +108,10 @@ int ikev2_add_buf(struct ibuf *buf, struct ibuf *);
int ikev2_ipcomp_enable(struct iked_sa *);
+int ikev2_cp_setaddr(struct iked *, struct iked_sa *);
+int ikev2_cp_fixaddr(struct iked_sa *, struct iked_addr *,
+ struct iked_addr *);
+
static struct privsep_proc procs[] = {
{ "parent", PROC_PARENT, ikev2_dispatch_parent },
{ "ikev1", PROC_IKEV1, ikev2_dispatch_ikev1 },
@@ -1201,6 +1205,7 @@ ikev2_add_ts_payload(struct ibuf *buf, u_int type, struct iked_sa *sa)
struct ikev2_ts *ts;
struct iked_flow *flow;
struct iked_addr *addr;
+ struct iked_addr pooladdr;
u_int8_t *ptr;
size_t len = 0;
u_int32_t av[4], bv[4], mv[4];
@@ -1229,6 +1234,13 @@ ikev2_add_ts_payload(struct ibuf *buf, u_int type, struct iked_sa *sa)
} else
return (-1);
+ /* patch remote address (if configured to 0.0.0.0) */
+ if ((type == IKEV2_PAYLOAD_TSi && !sa->sa_hdr.sh_initiator) ||
+ (type == IKEV2_PAYLOAD_TSr && sa->sa_hdr.sh_initiator)) {
+ if (ikev2_cp_fixaddr(sa, addr, &pooladdr) != -1)
+ addr = &pooladdr;
+ }
+
ts->ts_protoid = flow->flow_ipproto;
if (addr->addr_port) {
@@ -1578,7 +1590,13 @@ ikev2_add_cp(struct iked *env, struct iked_sa *sa, struct ibuf *buf)
case IKEV2_CFG_INTERNAL_IP4_DHCP:
case IKEV2_CFG_INTERNAL_IP4_SERVER:
/* 4 bytes IPv4 address */
- in4 = (struct sockaddr_in *)&ikecfg->cfg.address.addr;
+ in4 = (ikecfg->cfg.address.addr_mask != 32 &&
+ (ikecfg->cfg_type ==
+ IKEV2_CFG_INTERNAL_IP4_ADDRESS) &&
+ sa->sa_addrpool &&
+ sa->sa_addrpool->addr_af == AF_INET) ?
+ (struct sockaddr_in *)&sa->sa_addrpool->addr :
+ (struct sockaddr_in *)&ikecfg->cfg.address.addr;
cfg->cfg_length = htobe16(4);
if (ibuf_add(buf, &in4->sin_addr.s_addr, 4) == -1)
return (-1);
@@ -1609,7 +1627,13 @@ ikev2_add_cp(struct iked *env, struct iked_sa *sa, struct ibuf *buf)
case IKEV2_CFG_INTERNAL_IP6_ADDRESS:
case IKEV2_CFG_INTERNAL_IP6_SUBNET:
/* 16 bytes IPv6 address + 1 byte prefix length */
- in6 = (struct sockaddr_in6 *)&ikecfg->cfg.address.addr;
+ in6 = (ikecfg->cfg.address.addr_mask != 128 &&
+ (ikecfg->cfg_type ==
+ IKEV2_CFG_INTERNAL_IP6_ADDRESS) &&
+ sa->sa_addrpool &&
+ sa->sa_addrpool->addr_af == AF_INET6) ?
+ (struct sockaddr_in6 *)&sa->sa_addrpool->addr :
+ (struct sockaddr_in6 *)&ikecfg->cfg.address.addr;
cfg->cfg_length = htobe16(17);
if (ibuf_add(buf, &in6->sin6_addr.s6_addr, 16) == -1)
return (-1);
@@ -2047,6 +2071,9 @@ ikev2_resp_ike_auth(struct iked *env, struct iked_sa *sa)
else if (!sa_stateok(sa, IKEV2_STATE_VALID))
return (0); /* ignore */
+ if (sa->sa_cp)
+ ikev2_cp_setaddr(env, sa);
+
if (ikev2_childsa_negotiate(env, sa, sa->sa_hdr.sh_initiator) == -1)
return (-1);
@@ -2727,6 +2754,14 @@ ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg)
timer_del(env, &sa->sa_timer);
timer_set(env, &sa->sa_timer, ikev2_ike_sa_timeout, sa);
timer_add(env, &sa->sa_timer, IKED_IKE_SA_REKEY_TIMEOUT);
+
+ if (sa->sa_addrpool) {
+ /* transfer sa_addrpool address */
+ RB_REMOVE(iked_addrpool, &env->sc_addrpool, sa);
+ nsa->sa_addrpool = sa->sa_addrpool;
+ sa->sa_addrpool = NULL;
+ RB_INSERT(iked_addrpool, &env->sc_addrpool, nsa);
+ }
} else
ret = ikev2_childsa_enable(env, sa);
@@ -3771,6 +3806,7 @@ ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator)
flowa->flow_local = &sa->sa_local;
flowa->flow_peer = &sa->sa_peer;
flowa->flow_ikesa = sa;
+ ikev2_cp_fixaddr(sa, &flow->flow_dst, &flowa->flow_dst);
if ((flowb = calloc(1, sizeof(*flowb))) == NULL) {
log_debug("%s: failed to get flow", __func__);
@@ -3785,6 +3821,7 @@ ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator)
sizeof(flow->flow_dst));
memcpy(&flowb->flow_dst, &flow->flow_src,
sizeof(flow->flow_src));
+ ikev2_cp_fixaddr(sa, &flow->flow_dst, &flowb->flow_src);
TAILQ_INSERT_TAIL(&sa->sa_flows, flowa, flow_entry);
TAILQ_INSERT_TAIL(&sa->sa_flows, flowb, flow_entry);
@@ -4385,3 +4422,144 @@ ikev2_print_id(struct iked_id *id, char *idstr, size_t idstrlen)
return (0);
}
+
+/*
+ * If we have an IKEV2_CP_REQUEST for IKEV2_CFG_INTERNAL_IP4_ADDRESS and
+ * if a network(pool) is configured, then select an address from that pool
+ * and remember it in the sa_addrpool attribute.
+ */
+int
+ikev2_cp_setaddr(struct iked *env, struct iked_sa *sa)
+{
+ struct iked_cfg *ikecfg = NULL;
+ struct iked_policy *pol = sa->sa_policy;
+ struct sockaddr_in *in4 = NULL, *cfg4 = NULL;
+ struct sockaddr_in6 *in6 = NULL, *cfg6 = NULL;
+ struct iked_sa key;
+ struct iked_addr addr;
+ u_int32_t mask, host, lower, upper, start;
+ size_t i;
+
+ if (sa->sa_addrpool || pol->pol_ncfg == 0)
+ return (0);
+ /* check for an address pool config (address w/ prefixlen != 32) */
+ bzero(&addr, sizeof(addr));
+ for (i = 0; i < pol->pol_ncfg; i++) {
+ ikecfg = &pol->pol_cfg[i];
+ if (ikecfg->cfg_type == IKEV2_CFG_INTERNAL_IP4_ADDRESS &&
+ ikecfg->cfg.address.addr_mask != 32) {
+ addr.addr_af = AF_INET;
+ break;
+ }
+ if (ikecfg->cfg_type == IKEV2_CFG_INTERNAL_IP6_ADDRESS &&
+ ikecfg->cfg.address.addr_mask != 128) {
+ addr.addr_af = AF_INET6;
+ break;
+ }
+ }
+ if (ikecfg == NULL)
+ return (0);
+
+ /*
+ * failure: pool configured, but not requested.
+ * If we continue, we might end up with flows where 0.0.0.0 is NOT
+ * replaced with an address from the pool with ikev2_cp_fixaddr().
+ */
+ if (sa->sa_cp != IKEV2_CP_REQUEST) {
+ log_debug("%s: pool configured, but IKEV2_CP_REQUEST missing",
+ __func__);
+ return (-1);
+ }
+
+ /* truncate prefixlen in the v6 case */
+ mask = prefixlen2mask(ikecfg->cfg.address.addr_mask);
+
+ switch (addr.addr_af) {
+ case AF_INET:
+ cfg4 = (struct sockaddr_in *)&ikecfg->cfg.address.addr;
+ in4 = (struct sockaddr_in *)&addr.addr;
+ in4->sin_family = AF_INET;
+ in4->sin_len = sizeof(*in4);
+ lower = ntohl(cfg4->sin_addr.s_addr & ~mask);
+ break;
+ case AF_INET6:
+ cfg6 = (struct sockaddr_in6 *)&ikecfg->cfg.address.addr;
+ in6 = (struct sockaddr_in6 *)&addr.addr;
+ in6->sin6_family = AF_INET6;
+ in6->sin6_len = sizeof(*in6);
+ lower = cfg6->sin6_addr.s6_addr[3];
+ break;
+ default:
+ return (-1);
+ }
+
+ if (lower == 0)
+ lower = 1;
+ /* Note that start, upper and host are in HOST byte order */
+ upper = ntohl(~mask);
+ /* Randomly select start from [lower, upper-1] */
+ start = arc4random_uniform(upper - lower) + lower;
+
+ key.sa_addrpool = &addr;
+
+ for (host = start;;) {
+ log_debug("%s: mask %x start %x lower %x host %x upper %x",
+ __func__, mask, start, lower, host, upper);
+ switch (addr.addr_af) {
+ case AF_INET:
+ in4->sin_addr.s_addr =
+ (cfg4->sin_addr.s_addr & mask) | htonl(host);
+ break;
+ case AF_INET6:
+ memcpy(in6, cfg6, sizeof(*in6));
+ in6->sin6_addr.s6_addr[3] = htonl(host);
+ break;
+ }
+ if (!RB_FIND(iked_addrpool, &env->sc_addrpool, &key))
+ break;
+ /* try next address */
+ host++;
+ /* but skip broadcast and network address */
+ if (host >= upper || host < lower)
+ host = lower;
+ if (host == start)
+ return (-1); /* exhausted */
+ }
+ if (!key.sa_addrpool)
+ return (-1); /* cannot happen? */
+ if ((sa->sa_addrpool = calloc(1, sizeof(addr))) == NULL)
+ return (-1);
+ memcpy(sa->sa_addrpool, &addr, sizeof(addr));
+ RB_INSERT(iked_addrpool, &env->sc_addrpool, sa);
+ return (0);
+}
+
+/*
+ * if 'addr' is 'UNSPECIFIED' replace it with sa_addrpool from
+ * the ip-pool and store the result in 'patched'.
+ */
+int
+ikev2_cp_fixaddr(struct iked_sa *sa, struct iked_addr *addr,
+ struct iked_addr *patched)
+{
+ struct sockaddr_in *in4;
+ struct sockaddr_in6 *in6;
+
+ if (sa->sa_addrpool == NULL ||
+ sa->sa_addrpool->addr_af != addr->addr_af)
+ return (-1);
+ switch (addr->addr_af) {
+ case AF_INET:
+ in4 = (struct sockaddr_in *)&addr->addr;
+ if (in4->sin_addr.s_addr)
+ return (-1);
+ break;
+ case AF_INET6:
+ in6 = (struct sockaddr_in6 *)&addr->addr;
+ if (IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr))
+ return (-1);
+ break;
+ }
+ memcpy(patched, sa->sa_addrpool, sizeof(*patched));
+ return (0);
+}