diff options
author | Markus Friedl <markus@cvs.openbsd.org> | 2014-02-21 20:52:39 +0000 |
---|---|---|
committer | Markus Friedl <markus@cvs.openbsd.org> | 2014-02-21 20:52:39 +0000 |
commit | 2b50af7c9de53fbb89f291fcfccebead5bf4c675 (patch) | |
tree | 289df4b5265a356dee7671bc438c69b1e62b409a /sbin/iked/ikev2.c | |
parent | 91b2958e49611dab4d66c35180640ecce1176320 (diff) |
support rekeying for IPCOMP; ok mikeb@
Diffstat (limited to 'sbin/iked/ikev2.c')
-rw-r--r-- | sbin/iked/ikev2.c | 148 |
1 files changed, 128 insertions, 20 deletions
diff --git a/sbin/iked/ikev2.c b/sbin/iked/ikev2.c index 8ae7f8832ad..e534a2e7cee 100644 --- a/sbin/iked/ikev2.c +++ b/sbin/iked/ikev2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ikev2.c,v 1.95 2014/02/18 13:10:48 markus Exp $ */ +/* $OpenBSD: ikev2.c,v 1.96 2014/02/21 20:52:38 markus Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org> @@ -106,7 +106,8 @@ ssize_t ikev2_add_ts_payload(struct ibuf *, u_int, struct iked_sa *); int ikev2_add_data(struct ibuf *, void *, size_t); int ikev2_add_buf(struct ibuf *buf, struct ibuf *); -int ikev2_ipcomp_enable(struct iked_sa *); +int ikev2_ipcomp_enable(struct iked *, struct iked_sa *); +void ikev2_ipcomp_csa_free(struct iked *, struct iked_childsa *); int ikev2_cp_setaddr(struct iked *, struct iked_sa *); int ikev2_cp_fixaddr(struct iked_sa *, struct iked_addr *, @@ -1397,8 +1398,9 @@ ikev2_add_ipcompnotify(struct iked *env, struct ibuf *e, if (spi == 0) return (len); cpi = htobe16((u_int16_t)spi); - if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_NOTIFY) == -1) - return (-1); + if (*pld) + if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_NOTIFY) == -1) + return (-1); if ((*pld = ikev2_add_payload(e)) == NULL) return (-1); len = sizeof(*n) + sizeof(cpi) + sizeof(transform); @@ -2324,9 +2326,10 @@ ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa, { struct iked_childsa *csa = NULL, *csb = NULL; struct ikev2_notify *n; - struct ikev2_payload *pld; + struct ikev2_payload *pld = NULL; struct ibuf *e = NULL, *nonce = NULL; u_int8_t *ptr; + u_int8_t firstpayload; u_int32_t spi; ssize_t len = 0; int initiator, ret = -1; @@ -2359,6 +2362,18 @@ ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa, if ((e = ibuf_static()) == NULL) goto done; + /* compression */ + if ((sa->sa_policy->pol_flags & IKED_POLICY_IPCOMP) && + (len = ikev2_add_ipcompnotify(env, e, &pld, 0, sa)) == -1) + goto done; + + if (pld) { + firstpayload = IKEV2_PAYLOAD_NOTIFY; + if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_SA) == -1) + goto done; + } else + firstpayload = IKEV2_PAYLOAD_SA; + /* SA payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; @@ -2403,7 +2418,7 @@ ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa, goto done; ret = ikev2_msg_send_encrypt(env, sa, &e, - IKEV2_EXCHANGE_CREATE_CHILD_SA, IKEV2_PAYLOAD_SA, 0); + IKEV2_EXCHANGE_CREATE_CHILD_SA, firstpayload, 0); if (ret == 0) { if (rekey) { csa->csa_rekey = 1; @@ -2516,11 +2531,12 @@ ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg) struct iked_spi *spi, *rekey = &msg->msg_rekey; struct ikev2_keyexchange *ke; struct ikev2_notify *n; - struct ikev2_payload *pld; + struct ikev2_payload *pld = NULL; struct ibuf *buf = NULL, *e = NULL, *nonce = NULL; struct group *group; u_int64_t spi64; u_int32_t spi32; + u_int8_t firstpayload; ssize_t len = 0; int initiator, protoid, rekeying = 1; int ret = -1; @@ -2661,6 +2677,18 @@ ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg) if ((e = ibuf_static()) == NULL) goto done; + /* compression (unless IKE rekeying) */ + if (!nsa && sa->sa_ipcomp && + (len = ikev2_add_ipcompnotify(env, e, &pld, 0, sa)) == -1) + goto done; + + if (pld) { + firstpayload = IKEV2_PAYLOAD_NOTIFY; + if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_SA) == -1) + goto done; + } else + firstpayload = IKEV2_PAYLOAD_SA; + /* SA payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; @@ -2706,7 +2734,7 @@ ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg) goto done; if ((ret = ikev2_msg_send_encrypt(env, sa, &e, - IKEV2_EXCHANGE_CREATE_CHILD_SA, IKEV2_PAYLOAD_SA, 1)) == -1) + IKEV2_EXCHANGE_CREATE_CHILD_SA, firstpayload, 1)) == -1) goto done; if (protoid == IKEV2_SAPROTO_IKE) { @@ -3787,8 +3815,11 @@ ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator) &flow->flow_src) && IKED_ADDR_EQ(&saflow->flow_dst, &flow->flow_dst) && - saflow->flow_saproto == prop->prop_protoid) + saflow->flow_saproto == + prop->prop_protoid) { skip = 1; + break; + } } if (skip) continue; @@ -3936,11 +3967,30 @@ ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, int initiator) return (ret); } +/* free a replaced IPCOMP SA */ +void +ikev2_ipcomp_csa_free(struct iked *env, struct iked_childsa *csa) +{ + if (csa->csa_children) + fatalx("ikev2_ipcomp_csa_free: has children"); + if (csa->csa_ikesa) + TAILQ_REMOVE(&csa->csa_ikesa->sa_childsas, csa, + csa_entry); + if (csa->csa_loaded) { + log_debug("%s: csa %p loaded: calling pfkey_sa_delete", + __func__, csa); + pfkey_sa_delete(env->sc_pfkey, csa); + RB_REMOVE(iked_activesas, &env->sc_activesas, csa); + } + childsa_free(csa); +} + int -ikev2_ipcomp_enable(struct iked_sa *sa) +ikev2_ipcomp_enable(struct iked *env, struct iked_sa *sa) { - struct iked_childsa *other, *csa = NULL, *csb = NULL; + struct iked_childsa *other, *nother, *csa = NULL, *csb = NULL; struct iked_flow *flow, *flowa = NULL, *flowb = NULL; + struct iked_flow *nflow, *oflow; if ((csa = calloc(1, sizeof(*csa))) == NULL || (csb = calloc(1, sizeof(*csb))) == NULL || @@ -3954,10 +4004,19 @@ ikev2_ipcomp_enable(struct iked_sa *sa) } /* switch ESP SAs to transport mode */ - TAILQ_FOREACH(other, &sa->sa_childsas, csa_entry) + TAILQ_FOREACH(other, &sa->sa_childsas, csa_entry) { if (!other->csa_rekey && !other->csa_loaded && - other->csa_saproto == IKEV2_SAPROTO_ESP) + other->csa_saproto == IKEV2_SAPROTO_ESP) { other->csa_transport = 1; + if (other->csa_dir == IPSP_DIRECTION_OUT) { + other->csa_parent = csa; + csa->csa_children++; + } else { + other->csa_parent = csb; + csb->csa_children++; + } + } + } /* install IPCOMP SAs */ csa->csa_ikesa = sa; @@ -3975,7 +4034,7 @@ ikev2_ipcomp_enable(struct iked_sa *sa) csa->csa_dir = IPSP_DIRECTION_OUT; csa->csa_local = &sa->sa_local; csa->csa_peer = &sa->sa_peer; - csb->csa_allocated = 0; + csa->csa_persistent = 1; memcpy(csb, csa, sizeof(*csb)); csb->csa_spi.spi = csa->csa_peerspi; @@ -3985,6 +4044,26 @@ ikev2_ipcomp_enable(struct iked_sa *sa) csb->csa_peer = csa->csa_local; csb->csa_allocated = 1; + /* remove old replaced IPCOMP SAs */ + TAILQ_FOREACH_SAFE(other, &sa->sa_childsas, csa_entry, nother) { + if (other->csa_saproto != IKEV2_SAPROTO_IPCOMP || + other->csa_children != 0) + continue; + if (other->csa_dir == csa->csa_dir && + IKED_ADDR_EQ(other->csa_local, csa->csa_local) && + IKED_ADDR_EQ(other->csa_peer, csa->csa_peer)) { + log_debug("%s: csa %p replaces %p", + __func__, csa, other); + ikev2_ipcomp_csa_free(env, other); + } else if (other->csa_dir == csb->csa_dir && + IKED_ADDR_EQ(other->csa_local, csb->csa_local) && + IKED_ADDR_EQ(other->csa_peer, csb->csa_peer)) { + log_debug("%s: csa %p replaces %p", + __func__, csb, other); + ikev2_ipcomp_csa_free(env, other); + } + } + TAILQ_INSERT_TAIL(&sa->sa_childsas, csa, csa_entry); TAILQ_INSERT_TAIL(&sa->sa_childsas, csb, csa_entry); @@ -3992,13 +4071,27 @@ ikev2_ipcomp_enable(struct iked_sa *sa) csb->csa_peersa = csa; /* redirect flows to IPCOMP */ - TAILQ_FOREACH(flow, &sa->sa_flows, flow_entry) { + /* XXX expensive? should be merged into ikev2_childsa_negotiate() */ + TAILQ_FOREACH_SAFE(flow, &sa->sa_flows, flow_entry, nflow) { if (flow->flow_loaded || flow->flow_saproto != IKEV2_SAPROTO_ESP) continue; - log_debug("%s: flow %p saproto %d -> %d", __func__, - flow, flow->flow_saproto, IKEV2_SAPROTO_IPCOMP); - flow->flow_saproto = IKEV2_SAPROTO_IPCOMP; + TAILQ_FOREACH(oflow, &sa->sa_flows, flow_entry) + if (IKED_ADDR_EQ(&oflow->flow_src, &flow->flow_src) && + IKED_ADDR_EQ(&oflow->flow_dst, &flow->flow_dst) && + oflow->flow_dir == flow->flow_dir && + oflow->flow_saproto == IKEV2_SAPROTO_IPCOMP) + break; + if (oflow != NULL) { + log_debug("%s: keeping oflow %p, indentical to flow %p", + __func__, oflow, flow); + TAILQ_REMOVE(&sa->sa_flows, flow, flow_entry); + flow_free(flow); + } else { + log_debug("%s: flow %p saproto %d -> %d", __func__, + flow, flow->flow_saproto, IKEV2_SAPROTO_IPCOMP); + flow->flow_saproto = IKEV2_SAPROTO_IPCOMP; + } } /* setup ESP flows for gateways */ @@ -4015,11 +4108,25 @@ ikev2_ipcomp_enable(struct iked_sa *sa) flowa->flow_peer = &sa->sa_peer; memcpy(&flowa->flow_src, &sa->sa_local, sizeof(sa->sa_local)); memcpy(&flowa->flow_dst, &sa->sa_peer, sizeof(sa->sa_peer)); + socket_setport((struct sockaddr *)&flowa->flow_src.addr, 0); + socket_setport((struct sockaddr *)&flowa->flow_dst.addr, 0); + flowa->flow_src.addr_port = flowa->flow_dst.addr_port = 0; flowa->flow_src.addr_mask = flowa->flow_dst.addr_mask = (sa->sa_local.addr_af == AF_INET) ? 32 : 128; - flowa->flow_src.addr_port = flowa->flow_dst.addr_port = 0; flowa->flow_ikesa = sa; + /* skip if flow already exists */ + TAILQ_FOREACH(flow, &sa->sa_flows, flow_entry) { + if (IKED_ADDR_EQ(&flow->flow_src, &flowa->flow_src) && + IKED_ADDR_EQ(&flow->flow_dst, &flowa->flow_dst) && + flow->flow_dir == flowa->flow_dir && + flow->flow_saproto == flowa->flow_saproto) { + free(flowa); + free(flowb); + goto done; + } + } + memcpy(flowb, flowa, sizeof(*flowb)); flowb->flow_dir = IPSP_DIRECTION_IN; memcpy(&flowb->flow_dst, &flowa->flow_src, sizeof(flowa->flow_src)); @@ -4028,6 +4135,7 @@ ikev2_ipcomp_enable(struct iked_sa *sa) TAILQ_INSERT_TAIL(&sa->sa_flows, flowa, flow_entry); TAILQ_INSERT_TAIL(&sa->sa_flows, flowb, flow_entry); + done: /* make sure IPCOMP CPIs are not reused */ sa->sa_ipcomp = 0; sa->sa_cpi_in = sa->sa_cpi_out = 0; @@ -4042,7 +4150,7 @@ ikev2_childsa_enable(struct iked *env, struct iked_sa *sa) struct iked_flow *flow, *oflow; if (sa->sa_ipcomp && sa->sa_cpi_in && sa->sa_cpi_out && - ikev2_ipcomp_enable(sa) == -1) + ikev2_ipcomp_enable(env, sa) == -1) return (-1); TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) { |