/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2017 Jason King. * Copyright 2019 Joyent, Inc. */ /* * Manipulation and storage of IKEv2 Security Associations (SAs). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "defs.h" #include "ike.h" #include "ikev2_common.h" #include "ikev2_cookie.h" #include "ikev2_pkt.h" #include "ikev2_proto.h" #include "ikev2_sa.h" #include "pfkey.h" #include "pkcs11.h" #include "pkt.h" #include "util.h" #include "worker.h" /* * An arbitrary prime number pulled from the ether */ #define I2SA_NBUCKETS 73 static volatile uint_t half_open; /* # of larval/half open IKEv2 SAs */ static uint64_t remote_noise; /* random noise for rspi hash */ static uint64_t addr_noise; /* random noise for the addr hash */ /* protects all 3 hashes */ static rwlock_t i2sa_hash_lock = DEFAULTRWLOCK; static refhash_t *i2sa_lspi_refhash; static refhash_t *i2sa_rspi_refhash; static refhash_t *i2sa_addr_refhash; static umem_cache_t *i2sa_cache; static umem_cache_t *i2c_cache; static uint64_t i2sa_lspi_hash(const void *); static uint64_t i2sa_rspi_hash(const void *); static uint64_t i2sa_addr_hash(const void *); static int i2sa_lspi_cmp(const void *, const void *); static int i2sa_rspi_cmp(const void *, const void *); static int i2sa_addr_cmp(const void *, const void *); static void i2sa_unlink(ikev2_sa_t *); static void i2sa_p1_expire(void *); static int i2sa_ctor(void *, void *, int); static void i2sa_dtor(void *, void *); static void inc_half_open(void); static void dec_half_open(void); ikev2_sa_t * ikev2_sa_getbylspi(uint64_t spi, boolean_t initiator) { ikev2_sa_t *i2sa = NULL; ikev2_sa_t cmp_sa = { .flags = initiator ? I2SA_INITIATOR : 0, .i_spi = initiator ? spi : 0, .r_spi = initiator ? 0 : spi }; VERIFY0(rw_rdlock(&i2sa_hash_lock)); if ((i2sa = refhash_lookup(i2sa_lspi_refhash, &cmp_sa)) != NULL) I2SA_REFHOLD(i2sa); VERIFY0(rw_unlock(&i2sa_hash_lock)); if (i2sa != NULL) { (void) bunyan_key_add(log, BUNYAN_T_POINTER, LOG_KEY_I2SA, i2sa, BUNYAN_T_END); } return (i2sa); } ikev2_sa_t * ikev2_sa_getbyrspi(uint64_t spi, const struct sockaddr *restrict laddr, const struct sockaddr *restrict raddr, pkt_t *restrict init_pkt) { ikev2_sa_t *i2sa = NULL; pkt_payload_t *nonce = NULL; ikev2_sa_args_t cmp_sa_args = { 0 }; ikev2_sa_t cmp_sa = { .i_spi = spi, .sa_init_args = &cmp_sa_args, }; nonce = pkt_get_payload(init_pkt, IKEV2_PAYLOAD_NONCE, NULL); if (nonce == NULL) return (NULL); /* Inbound packet checks should prevent this */ VERIFY3U(nonce->pp_len, <=, sizeof (cmp_sa_args.i2a_nonce_i)); bcopy(nonce->pp_ptr, cmp_sa_args.i2a_nonce_i, nonce->pp_len); cmp_sa_args.i2a_nonce_i_len = nonce->pp_len; sockaddr_copy(laddr, &cmp_sa.laddr, B_FALSE); sockaddr_copy(raddr, &cmp_sa.raddr, B_FALSE); VERIFY0(rw_rdlock(&i2sa_hash_lock)); if ((i2sa = refhash_lookup(i2sa_rspi_refhash, &cmp_sa)) != NULL) I2SA_REFHOLD(i2sa); VERIFY0(rw_unlock(&i2sa_hash_lock)); if (i2sa != NULL) { (void) bunyan_key_add(log, BUNYAN_T_POINTER, LOG_KEY_I2SA, i2sa, BUNYAN_T_END); } return (i2sa); } ikev2_sa_t * ikev2_sa_getbyaddr(const struct sockaddr *restrict src, const struct sockaddr *restrict dst) { ikev2_sa_t *i2sa = NULL; ikev2_sa_t cmp_sa = { 0 }; sockaddr_copy(src, &cmp_sa.laddr, B_FALSE); sockaddr_copy(dst, &cmp_sa.raddr, B_FALSE); VERIFY0(rw_rdlock(&i2sa_hash_lock)); if ((i2sa = refhash_lookup(i2sa_addr_refhash, &cmp_sa)) != NULL) I2SA_REFHOLD(i2sa); VERIFY0(rw_unlock(&i2sa_hash_lock)); if (i2sa != NULL) { (void) bunyan_key_add(log, BUNYAN_T_POINTER, LOG_KEY_I2SA, i2sa, BUNYAN_T_END); } return (i2sa); } /* * Allocate a larval IKEv2 SA. * * Obtains a unique local SPI and assigns it to the SA and adds the SA to * the local SPI hash. If the packet used to trigger the creation of the SA * is given, take over management of it. Also create an SA expiration timer. * * If we initiated the SA creation, the remote SPI will not be known initially. * Once the protocol has proceeded enough to determine the remote SPI, * ikev2_sa_set_rspi() should be called. * * Parameters: * init_pkt The packet that trigged the creation of the SA or NULL * if we initiated. * laddr, * raddr The local and remote addresses of this SA. * * On successful create, the refheld larval IKEv2 SA is returned. In addition, * the IKEv2 SA queue is locked on return. * * On failure, NULL is returned. Caller maintains responsibility for * init_pkt in this instance. */ ikev2_sa_t * ikev2_sa_alloc(pkt_t *restrict init_pkt, const struct sockaddr *restrict laddr, const struct sockaddr *restrict raddr) { ikev2_sa_t *i2sa = NULL; hrtime_t expire = 0; boolean_t initiator = (init_pkt == NULL) ? B_TRUE : B_FALSE; (void) bunyan_trace(log, "Attempting to create new larval IKE SA", BUNYAN_T_BOOLEAN, LOG_KEY_INITIATOR, initiator, ss_bunyan(laddr), LOG_KEY_LADDR, ss_addr(laddr), ss_bunyan(raddr), LOG_KEY_RADDR, ss_addr(raddr), BUNYAN_T_END); expire = config->cfg_expire_timer; if ((i2sa = umem_cache_alloc(i2sa_cache, UMEM_DEFAULT)) == NULL) { STDERR(error, "No memory to create IKEv2 SA"); return (NULL); } if ((i2sa->sa_init_args = ikev2_sa_args_new(B_TRUE)) == NULL) { STDERR(error, "No memory to create IKEv2 SA"); umem_cache_free(i2sa_cache, i2sa); return (NULL); } i2sa->sa_init_args->i2a_i2sa = i2sa; /* Keep anyone else out while we initialize */ mutex_enter(&i2sa->i2sa_queue_lock); mutex_enter(&i2sa->i2sa_lock); i2sa->i2sa_tid = thr_self(); i2sa->flags |= initiator ? I2SA_INITIATOR : 0; sockaddr_copy(laddr, &i2sa->laddr, B_TRUE); sockaddr_copy(raddr, &i2sa->raddr, B_TRUE); /* * Use the port given to us if specified, otherwise use the default. * Take advantage of port being at the same offset for IPv4/v6 */ VERIFY(laddr->sa_family == AF_INET || laddr->sa_family == AF_INET6); if (ss_port(laddr) == 0) { ((struct sockaddr_in *)&i2sa->laddr)->sin_port = htons(IPPORT_IKE); } if (ss_port(raddr) == 0) { ((struct sockaddr_in *)&i2sa->raddr)->sin_port = htons(IPPORT_IKE); } /* * Generate a random local SPI and try to add it. Almost always this * will succeed on the first attempt. However if on the rare occasion * we generate a duplicate (or even far, far, rarer chance 0 is * returned), just retry until we pick a value that's not in use. */ for (;;) { uint64_t spi = 0; arc4random_buf(&spi, sizeof (spi)); /* * Incredibly unlikely we'll ever randomly generate 0, but * if we do, just try again. */ if (spi == 0) continue; if (initiator) i2sa->i_spi = spi; else i2sa->r_spi = spi; VERIFY0(rw_wrlock(&i2sa_hash_lock)); if (refhash_lookup(i2sa_lspi_refhash, i2sa) != NULL) { VERIFY0(rw_unlock(&i2sa_hash_lock)); continue; } refhash_insert(i2sa_lspi_refhash, i2sa); I2SA_REFHOLD(i2sa); refhash_insert(i2sa_addr_refhash, i2sa); I2SA_REFHOLD(i2sa); /* refhold for caller */ I2SA_REFHOLD(i2sa); VERIFY3U(i2sa->refcnt, ==, 3); VERIFY0(rw_unlock(&i2sa_hash_lock)); break; }; key_add_ike_spi(LOG_KEY_LSPI, I2SA_LOCAL_SPI(i2sa)); (void) bunyan_trace(log, "Allocated local SPI", BUNYAN_T_END); /* * If we're the initiator, we don't know the remote SPI until after * the remote peer responds. However if we are the responder, * we know what it is and can set it now. We also want to bump the * half open count to enable cookies if too many half-open inbound * connections are out there. */ if (!initiator) { inc_half_open(); ikev2_sa_set_remote_spi(i2sa, pkt_header(init_pkt)->initiator_spi); } mutex_exit(&i2sa->i2sa_queue_lock); if (!ikev2_sa_arm_timer(i2sa, expire, I2SA_EVT_P1_EXPIRE)) { STDERR(error, "Cannot create IKEv2 SA P1 expiration timer"); /* Gets rid of the refhash holds */ i2sa_unlink(i2sa); i2sa->i2sa_tid = 0; mutex_exit(&i2sa->i2sa_lock); /* Get rid of the caller hold (will also free it) */ I2SA_REFRELE(i2sa); (void) bunyan_debug(log, "Larval IKE SA creation failed", BUNYAN_T_END); return (NULL); } mutex_exit(&i2sa->i2sa_lock); mutex_enter(&i2sa->i2sa_queue_lock); mutex_enter(&i2sa->i2sa_lock); i2sa->i2sa_tid = 0; mutex_exit(&i2sa->i2sa_lock); /* * Leave i2sa_queue_lock held so caller has exclusive access to SA * upon return. */ (void) bunyan_debug(log, "New larval IKE SA created", BUNYAN_T_POINTER, LOG_KEY_I2SA, i2sa, BUNYAN_T_END); return (i2sa); } /* * Invoked when an SA has expired. REF from timer is passed to this * function. */ static void i2sa_p1_expire(void *data) { ikev2_sa_t *i2sa = data; I2SA_REFHOLD(i2sa); key_add_ike_spi(LOG_KEY_LSPI, I2SA_LOCAL_SPI(i2sa)); key_add_ike_spi(LOG_KEY_RSPI, I2SA_REMOTE_SPI(i2sa)); (void) bunyan_info(log, "Larval IKE SA (P1) timeout", BUNYAN_T_POINTER, LOG_KEY_I2SA, i2sa, BUNYAN_T_END); ikev2_sa_post_event(i2sa, I2SA_EVT_P1_EXPIRE); (void) bunyan_key_remove(log, LOG_KEY_LSPI); (void) bunyan_key_remove(log, LOG_KEY_RSPI); I2SA_REFRELE(i2sa); } void ikev2_sa_flush(void) { /* TODO: implement me */ } /* * Arm an IKEv2 SA timer to fire the given event reltime nanoseconds from now. * This is intended to be called during normal IKEv2 SA processing (hence * the VERIFY checks) with only the i2sa_lock held. Returns B_TRUE if * timer was successfully armed. */ boolean_t ikev2_sa_arm_timer(ikev2_sa_t *i2sa, hrtime_t reltime, i2sa_evt_t event, ...) { VERIFY(!MUTEX_HELD(&i2sa->i2sa_queue_lock)); VERIFY(MUTEX_HELD(&i2sa->i2sa_lock)); VERIFY3U(i2sa->i2sa_tid, ==, thr_self()); periodic_id_t *idp = NULL; periodic_func_t *cb = NULL; void *arg = i2sa; va_list ap; va_start(ap, event); switch (event) { case I2SA_EVT_NONE: INVALID(event); /*NOTREACHED*/ break; case I2SA_EVT_PKT_XMIT: { i2sa_req_t *i2r = va_arg(ap, i2sa_req_t *); idp = &i2r->i2r_timer; cb = ikev2_retransmit_cb; arg = i2r; break; } case I2SA_EVT_P1_EXPIRE: idp = &i2sa->i2sa_p1_timer; cb = i2sa_p1_expire; break; case I2SA_EVT_SOFT_EXPIRE: idp = &i2sa->i2sa_softlife_timer; INVALID("not yet"); break; case I2SA_EVT_HARD_EXPIRE: idp = &i2sa->i2sa_hardlife_timer; INVALID("not yet"); break; } va_end(ap); mutex_exit(&i2sa->i2sa_lock); mutex_enter(&i2sa->i2sa_queue_lock); mutex_enter(&i2sa->i2sa_lock); int rc = periodic_schedule(wk_periodic, reltime, PERIODIC_ONESHOT, cb, arg, idp); if (rc != 0) VERIFY3S(errno, ==, ENOMEM); mutex_exit(&i2sa->i2sa_queue_lock); return ((rc == 0) ? B_TRUE : B_FALSE); } /* * Disarm the given timer. If the particular timer happened to fire prior to * us disarming it (but before it's been processed), the event will also * be cleared. */ void ikev2_sa_disarm_timer(ikev2_sa_t *i2sa, i2sa_evt_t event, ...) { VERIFY(!MUTEX_HELD(&i2sa->i2sa_queue_lock)); VERIFY(MUTEX_HELD(&i2sa->i2sa_lock)); VERIFY3U(i2sa->i2sa_tid, ==, thr_self()); periodic_id_t *idp = NULL; i2sa_req_t *i2r = NULL; periodic_id_t id = 0; i2sa_evt_t pkt_events = 0; va_list ap; int rc; va_start(ap, event); switch (event) { case I2SA_EVT_NONE: INVALID(event); /*NOTREACHED*/ break; case I2SA_EVT_PKT_XMIT: i2r = va_arg(ap, i2sa_req_t *); idp = &i2r->i2r_timer; break; case I2SA_EVT_P1_EXPIRE: idp = &i2sa->i2sa_p1_timer; break; case I2SA_EVT_SOFT_EXPIRE: idp = &i2sa->i2sa_softlife_timer; break; case I2SA_EVT_HARD_EXPIRE: idp = &i2sa->i2sa_hardlife_timer; break; } va_end(ap); /* * If the timer callback is executing on another thread while * periodic_cancel() is called, the periodic_cancel() call will block * until the callback completes. Since every callback function needs * to acquire i2sa_queue_lock to post the event, we must call * periodic_cancel() without holding i2sa_queue_lock. * * Unfortunately, if the timer fires and is serviced on another thread * between the time we grab the id then drop the queue lock and then * attempt to cancel it, there is a very small and unavoidable chance * the id value we saved could be recycled causing us to cancel the * wrong event. * * As the the libperiodic id space can contain up to 2^32 - 1 ids, * and each IKEv2 SA consumes fewer than 10 ids, it seems highly * unlikely this can ever happen in practice. The only potential * solution would be to increate the side of the id space. */ mutex_exit(&i2sa->i2sa_lock); mutex_enter(&i2sa->i2sa_queue_lock); id = *idp; mutex_exit(&i2sa->i2sa_queue_lock); if (id != 0) { rc = periodic_cancel(wk_periodic, id); if (rc != 0) VERIFY3S(errno, ==, ENOENT); } mutex_enter(&i2sa->i2sa_queue_lock); /* * If the timer happened to fire while we were trying to clear it, * clear the indicator. */ if (i2r != NULL) { i2r->i2r_fired = B_FALSE; /* * XXX: Once WINDOW_SIZE support is finished, an IKEv2 SA will * have multiple i2sa_req_t's, so we will need something * along the lines of: * * foreach (i2sa_evt_t evt in i2sa) * pkt_events |= evt->i2r_fired; * * here (after clearing the i2r_fired for the request whose * timer we are cancelling). This will allow * i2sa->i2sa_events & I2SA_EVT_PKT_XMIT to indicate that * we need to check the i2r_fired of each in-progress * outbound request to see which one needs to be * retransmitted (we are unlikely to ever need to support * very large window sizes with the IKEv2 protocol as it * currently stands, so this shouldn't present any scaling * issues). */ } i2sa->i2sa_events &= (~event | pkt_events); *idp = 0; mutex_enter(&i2sa->i2sa_lock); mutex_exit(&i2sa->i2sa_queue_lock); } /* * Post that an event has fired. Should be called by a callback handler * without any IKE SA locks held. It will attempt to dispatch the event after * posting it (which may fail and the event merely queued if the IKEv2 SA has * already been pinned to a thread). */ void ikev2_sa_post_event(ikev2_sa_t *i2sa, i2sa_evt_t event) { VERIFY(!MUTEX_HELD(&i2sa->i2sa_lock)); mutex_enter(&i2sa->i2sa_queue_lock); i2sa->i2sa_events |= event; switch (event) { case I2SA_EVT_NONE: INVALID(event); /*NOTREACHED*/ break; case I2SA_EVT_PKT_XMIT: break; case I2SA_EVT_P1_EXPIRE: i2sa->i2sa_p1_timer = 0; break; case I2SA_EVT_SOFT_EXPIRE: i2sa->i2sa_softlife_timer = 0; break; case I2SA_EVT_HARD_EXPIRE: i2sa->i2sa_hardlife_timer = 0; break; } ikev2_dispatch(i2sa); mutex_exit(&i2sa->i2sa_queue_lock); } /* * Get the existing response for this packet if we have it, otherwise * return NULL */ pkt_t * ikev2_sa_get_response(ikev2_sa_t *restrict i2sa, const pkt_t *req) { VERIFY(MUTEX_HELD(&i2sa->i2sa_lock)); VERIFY(!I2P_RESPONSE(req)); /* * While msgid's are stored in network byte order, since we're just * checking equality, we don't need to bother to convert them to local * byte order. */ uint32_t req_id = pkt_header(req)->msgid; if (i2sa->last_resp_sent == NULL) return (NULL); if (pkt_header(i2sa->last_resp_sent)->msgid == req_id) return (i2sa->last_resp_sent); return (NULL); } /* * Returns B_TRUE if any requests are currently in progress (i.e. waiting * for a response). * * XXX: Currently there is at most 1 possible request in progress, but that * will change when SET_WINDOW_SIZE support is added. */ boolean_t ikev2_sa_has_requests(const ikev2_sa_t *i2sa) { VERIFY(MUTEX_HELD((mutex_t *)&i2sa->i2sa_lock)); return (I2REQ_ACTIVE(&i2sa->last_req)); } /* * To condemn an IKEv2 SA, the I2SA_CONDEMNED flag is set on the IKEv2 SA. * Once set, its is never unset for the remainder of the lifetime of the * IKEv2 SA. We cannot immediately teardown the IKEv2 SA once we wish to * condemn it unfortunately. For example, if we fail to authenticate the * responder, we cannot reply to a reply. Instead we must initiate a new * INFORMATIONAL exchange w/ the AUTHENTICATION_FAILED notification and * send it to the original responder. To deal with this, once the * I2SA_CONDEMNED flag is set, new requests (whether they are pf_key(7P) * messages or requests from our peer) are dropped. Once all of our * outstanding requests either complete or time out, ikev2_sa_condemn * is then called which starts the teardown process. * */ void ikev2_sa_condemn(ikev2_sa_t *i2sa) { VERIFY(i2sa->flags & I2SA_CONDEMNED); VERIFY(MUTEX_HELD(&i2sa->i2sa_queue_lock)); VERIFY(MUTEX_HELD(&i2sa->i2sa_lock)); VERIFY3U(i2sa->i2sa_tid, ==, thr_self()); (void) bunyan_info(log, "Condemning IKE SA", BUNYAN_T_END); i2sa_unlink(i2sa); /* Must drop the queue lock before disarming the timers */ mutex_exit(&i2sa->i2sa_queue_lock); /* Once these return, we know none of the callbacks are running */ (void) ikev2_sa_disarm_timer(i2sa, I2SA_EVT_PKT_XMIT, &i2sa->last_req); (void) ikev2_sa_disarm_timer(i2sa, I2SA_EVT_P1_EXPIRE); (void) ikev2_sa_disarm_timer(i2sa, I2SA_EVT_SOFT_EXPIRE); (void) ikev2_sa_disarm_timer(i2sa, I2SA_EVT_HARD_EXPIRE); ikev2_pkt_free(i2sa->last_resp_sent); i2sa->last_resp_sent = NULL; ikev2_pkt_free(i2sa->last_recvd); i2sa->last_recvd = NULL; if (i2sa->last_req.i2r_pkt != NULL) { ikev2_pkt_free(i2sa->last_req.i2r_pkt); i2sa->last_req.i2r_pkt = NULL; } ikev2_child_sa_t *i2c = refhash_first(i2sa->i2sa_child_sas); while (i2c != NULL) { ikev2_child_sa_t *i2c_next; i2c_next = refhash_next(i2sa->i2sa_child_sas, i2c); refhash_remove(i2sa->i2sa_child_sas, i2c); i2c = i2c_next; } mutex_exit(&i2sa->i2sa_lock); mutex_enter(&i2sa->i2sa_queue_lock); mutex_enter(&i2sa->i2sa_lock); for (size_t i = 0; i < I2SA_QUEUE_DEPTH; i++) { parsedmsg_t *pmsg = NULL; sadb_msg_t *samsg = NULL; switch (i2sa->i2sa_queue[i].i2m_type) { case I2SA_MSG_NONE: break; case I2SA_MSG_PKT: ikev2_pkt_free(i2sa->i2sa_queue[i].i2m_data); break; case I2SA_MSG_PFKEY: pmsg = i2sa->i2sa_queue[i].i2m_data; samsg = pmsg->pmsg_samsg; break; } i2sa->i2sa_queue[i].i2m_type = I2SA_MSG_NONE; i2sa->i2sa_queue[i].i2m_data = NULL; if (samsg != NULL && samsg->sadb_msg_pid == 0 && samsg->sadb_msg_type == SADB_ACQUIRE) { /* * The kernel currently only cares that errno != 0, * but this seems like the closest error code to * what's happening, just to be as informative as * possible. */ pfkey_send_error(samsg, ECANCELED); } parsedmsg_free(pmsg); } } /* * Should normally only be called as a result of I2SA_REFRELE() */ void ikev2_sa_free(ikev2_sa_t *i2sa) { if (i2sa == NULL) return; VERIFY3U(i2sa->refcnt, ==, 0); VERIFY3P(i2sa->last_resp_sent, ==, NULL); VERIFY3P(i2sa->last_recvd, ==, NULL); if (i2sa->i2sa_rule != NULL) RULE_REFRELE(i2sa->i2sa_rule); config_id_free(i2sa->local_id); config_id_free(i2sa->remote_id); /* All unauthenticated IKEv2 SAs are considered larval */ if ((i2sa->flags & (I2SA_AUTHENTICATED|I2SA_INITIATOR)) != (I2SA_AUTHENTICATED|I2SA_INITIATOR)) dec_half_open(); #define DESTROY(x, y) pkcs11_destroy_obj(#y, &(x)->y) DESTROY(i2sa, sk_d); DESTROY(i2sa, sk_ai); DESTROY(i2sa, sk_ar); DESTROY(i2sa, sk_ei); DESTROY(i2sa, sk_er); DESTROY(i2sa, sk_pi); DESTROY(i2sa, sk_pr); DESTROY(i2sa, psk); #undef DESTROY ikev2_sa_args_free(i2sa->sa_init_args); /* * This is likely redundant with the bzero of everything a few lines * down, but would rather be safe than sorry. */ explicit_bzero(i2sa->salt_i, sizeof (i2sa->salt_i)); explicit_bzero(i2sa->salt_r, sizeof (i2sa->salt_r)); /* Return it to it's initial constructed state */ refhash_t *refhash = i2sa->i2sa_child_sas; VERIFY0(mutex_destroy(&i2sa->i2sa_queue_lock)); VERIFY0(mutex_destroy(&i2sa->i2sa_lock)); bzero(i2sa, sizeof (*i2sa)); i2sa->i2sa_child_sas = refhash; VERIFY0(mutex_init(&i2sa->i2sa_queue_lock, USYNC_THREAD|LOCK_ERRORCHECK, NULL)); VERIFY0(mutex_init(&i2sa->i2sa_lock, USYNC_THREAD|LOCK_ERRORCHECK, NULL)); i2sa->msgwin = 1; umem_cache_free(i2sa_cache, i2sa); } /* * Set the remote SPI of an IKEv2 SA and add to the rhash */ void ikev2_sa_set_remote_spi(ikev2_sa_t *i2sa, uint64_t remote_spi) { VERIFY(IS_WORKER); VERIFY(MUTEX_HELD(&i2sa->i2sa_lock)); /* Never a valid SPI value */ VERIFY3U(remote_spi, !=, 0); /* * A bit confusing at times, but if we are the initiator of the * SA, the responder (ikev2_sa_t->remote_spi) is the remote spi, * otherwise we are the responder, so the remote spi is the * initiator (ikev2_sa_t->i_spi) */ if (i2sa->flags & I2SA_INITIATOR) { /* Should not be set already */ VERIFY3U(i2sa->r_spi, ==, 0); i2sa->r_spi = remote_spi; } else { /* Should not be set already */ VERIFY3U(i2sa->i_spi, ==, 0); i2sa->i_spi = remote_spi; } VERIFY0(rw_wrlock(&i2sa_hash_lock)); refhash_insert(i2sa_rspi_refhash, i2sa); VERIFY0(rw_unlock(&i2sa_hash_lock)); key_add_ike_spi(LOG_KEY_RSPI, remote_spi); (void) bunyan_trace(log, "Set remote SPI", BUNYAN_T_END); } static void i2sa_unlink(ikev2_sa_t *i2sa) { VERIFY(MUTEX_HELD(&i2sa->i2sa_lock)); VERIFY0(rw_wrlock(&i2sa_hash_lock)); refhash_remove(i2sa_lspi_refhash, i2sa); I2SA_REFRELE(i2sa); refhash_remove(i2sa_addr_refhash, i2sa); I2SA_REFRELE(i2sa); if (refhash_obj_valid(i2sa_rspi_refhash, i2sa)) { refhash_remove(i2sa_rspi_refhash, i2sa); I2SA_REFRELE(i2sa); } VERIFY0(rw_unlock(&i2sa_hash_lock)); } /* * Increase the count of larval SAs. If we reach our threshold for larval SAs, * enable the use of cookies. */ static void inc_half_open(void) { if (atomic_inc_uint_nv(&half_open) == ikev2_cookie_threshold) ikev2_cookie_enable(); } /* * Decrease the count of larval SAs. Disable cookies if the count falls * below the threshold */ static void dec_half_open(void) { /* * Instead of merely disabling cookies once we're below * ikev2_cookie_threshold half-open IKE SAs, we wait for * IKEV2_COOKIE_OFF_ADD additional half-open IKE SAs to * disappear to add a small amount of hysteresis and prevent * constantly flopping on and off once we're at the threshold. */ if (atomic_dec_uint_nv(&half_open) == ikev2_cookie_threshold - IKEV2_COOKIE_OFF_ADJ) ikev2_cookie_disable(); } /* * Add a message to the queue of the given IKEv2 SA. Must be called * with sa->i2sa_queue_lock held. Discards any non-replies for condemned * IKEv2 SAs as well as any requests when the queue is full. * Returns with i2sa_queue_lock released. * * Caller should treat function as if it consumes the message and should not * reference it after function returns. */ void ikev2_sa_queuemsg(ikev2_sa_t *sa, i2sa_msg_type_t type, void *data) { VERIFY(IS_WORKER); VERIFY(MUTEX_HELD(&sa->i2sa_queue_lock)); if (I2SA_QUEUE_FULL(sa)) { (void) bunyan_info(log, "Queue full; discarding packet", BUNYAN_T_END); goto discard; } if ((sa->flags & I2SA_CONDEMNED) && ((type != I2SA_MSG_PKT) || !I2P_RESPONSE((pkt_t *)data))) goto discard; i2sa_msg_t *msg = &sa->i2sa_queue[sa->i2sa_queue_start]; /* * XXX: A possible improvement might be to scan the queue and drop * duplicate messages */ msg->i2m_type = type; msg->i2m_data = data; sa->i2sa_queue_start++; sa->i2sa_queue_start %= I2SA_QUEUE_DEPTH; ikev2_dispatch(sa); mutex_exit(&sa->i2sa_queue_lock); return; discard: switch (type) { case I2SA_MSG_PKT: ikev2_pkt_free(data); break; case I2SA_MSG_PFKEY: /* XXX: Send a pf_key reply w/ error if this is an ACQUIRE? */ parsedmsg_free(data); break; case I2SA_MSG_NONE: INVALID(type); break; } mutex_exit(&sa->i2sa_queue_lock); } ikev2_child_sa_t * ikev2_child_sa_alloc(boolean_t inbound) { ikev2_child_sa_t *csa = NULL; if ((csa = umem_cache_alloc(i2c_cache, UMEM_DEFAULT)) == NULL) return (NULL); if (inbound) csa->i2c_flags |= I2CF_INBOUND; return (csa); } void ikev2_child_sa_free(ikev2_sa_t *restrict i2sa, ikev2_child_sa_t *restrict csa) { if (csa == NULL) return; if (i2sa != NULL) VERIFY(!refhash_obj_valid(i2sa->i2sa_child_sas, csa)); if (csa->i2c_pair != NULL) csa->i2c_pair->i2c_pair = NULL; csa->i2c_pair = NULL; bzero(csa, sizeof (*csa)); umem_cache_free(i2c_cache, csa); } ikev2_child_sa_t * ikev2_sa_get_child(ikev2_sa_t *i2sa, uint32_t spi, boolean_t inbound) { ikev2_child_sa_t cmp = { .i2c_flags = inbound ? I2CF_INBOUND : 0, .i2c_spi = spi }; return (refhash_lookup(i2sa->i2sa_child_sas, &cmp)); } void ikev2_sa_add_child(ikev2_sa_t *restrict i2sa, ikev2_child_sa_t *restrict i2c) { refhash_insert(i2sa->i2sa_child_sas, i2c); } void ikev2_sa_delete_child(ikev2_sa_t *restrict i2sa, ikev2_child_sa_t *restrict csa) { if (csa->i2c_pair != NULL) csa->i2c_pair->i2c_pair = NULL; csa->i2c_pair = NULL; refhash_remove(i2sa->i2sa_child_sas, csa); } void ikev2_sa_delete_children(ikev2_sa_t *restrict i2sa) { ikev2_child_sa_t *csa = NULL; struct sockaddr_storage src = { 0 }; struct sockaddr_storage dst = { 0 }; sockaddr_u_t srcu = { .sau_ss = &src }; sockaddr_u_t dstu = { .sau_ss = &dst }; /* * XXX: This is assuming we don't want to include the protocol or * port values in the src/dst addresses passed along in the * SADB_{DELETE,DELPAIR} message. Need to verify this. */ sockaddr_copy(SSTOSA(&i2sa->laddr), &src, B_FALSE); sockaddr_copy(SSTOSA(&i2sa->raddr), &dst, B_FALSE); csa = refhash_first(i2sa->i2sa_child_sas); while (csa != NULL) { ikev2_child_sa_t *next; uint8_t satype = ikev2_to_satype(csa->i2c_satype); (void) pfkey_delete(satype, csa->i2c_spi, I2C_INBOUND(csa) ? dstu : srcu, I2C_INBOUND(csa) ? srcu : dstu, (csa->i2c_pair != NULL) ? B_TRUE : B_FALSE); if (csa->i2c_pair != NULL) { refhash_remove(i2sa->i2sa_child_sas, csa->i2c_pair); csa->i2c_pair = NULL; } next = refhash_next(i2sa->i2sa_child_sas, csa); refhash_remove(i2sa->i2sa_child_sas, csa); csa = next; } } void ikev2_sa_clear_req(ikev2_sa_t *restrict i2sa, i2sa_req_t *restrict i2req) { pkt_t *pkt = i2req->i2r_pkt; (void) ikev2_sa_disarm_timer(i2sa, I2SA_EVT_PKT_XMIT, i2req); i2req->i2r_fired = B_FALSE; i2req->i2r_pkt = NULL; i2req->i2r_arg = NULL; ikev2_pkt_free(pkt); } static uint64_t i2c_hash(const void *arg) { const ikev2_child_sa_t *i2c = arg; uint64_t val = i2c->i2c_spi; if (I2C_INBOUND(i2c)) val |= (1ULL << 32); return (val); } static int i2c_cmp(const void *larg, const void *rarg) { const ikev2_child_sa_t *l = larg; const ikev2_child_sa_t *r = rarg; if (l->i2c_spi < r->i2c_spi) return (-1); if (l->i2c_spi > r->i2c_spi) return (1); if (I2C_INBOUND(l) && !I2C_INBOUND(r)) return (-1); if (!I2C_INBOUND(l) && I2C_INBOUND(r)) return (1); return (0); } static void i2c_refdtor(void *arg) { ikev2_child_sa_t *i2c = arg; bzero(i2c, sizeof (*i2c)); umem_cache_free(i2c_cache, i2c); } static int i2sa_ctor(void *buf, void *dummy __unused, int flags __unused) { NOTE(ARGUNUSED(dummy, flags)) ikev2_sa_t *i2sa = buf; bzero(i2sa, sizeof (*i2sa)); i2sa->msgwin = 1; VERIFY0(mutex_init(&i2sa->i2sa_lock, USYNC_THREAD|LOCK_ERRORCHECK, NULL)); VERIFY0(mutex_init(&i2sa->i2sa_queue_lock, USYNC_THREAD|LOCK_ERRORCHECK, NULL)); i2sa->i2sa_child_sas = refhash_create(I2SA_NBUCKETS, i2c_hash, i2c_cmp, i2c_refdtor, sizeof (ikev2_child_sa_t), offsetof(ikev2_child_sa_t, i2c_link), 0, UMEM_DEFAULT); return ((i2sa->i2sa_child_sas == NULL) ? 1 : 0); } static void i2sa_dtor(void *buf, void *dummy __unused) { NOTE(ARGUNUSED(dummy)) ikev2_sa_t *i2sa = (ikev2_sa_t *)buf; VERIFY0(mutex_destroy(&i2sa->i2sa_lock)); VERIFY0(mutex_destroy(&i2sa->i2sa_queue_lock)); refhash_destroy(i2sa->i2sa_child_sas); i2sa->i2sa_child_sas = NULL; } static int i2c_ctor(void *buf, void *dummy __unused, int flags __unused) { NOTE(ARGUNUSED(dummy, flags)) ikev2_child_sa_t *i2c = buf; bzero(i2c, sizeof (*i2c)); return (0); } const char * i2sa_msgtype_str(i2sa_msg_type_t type) { #define STR(x) case x: return (#x); switch (type) { STR(I2SA_MSG_NONE); STR(I2SA_MSG_PKT); STR(I2SA_MSG_PFKEY); } #undef STR INVALID(type); /*NOTREACHED*/ return (NULL); } static uint64_t i2sa_lspi_hash(const void *arg) { const ikev2_sa_t *i2sa = arg; return (I2SA_LOCAL_SPI(i2sa)); } static int i2sa_lspi_cmp(const void *larg, const void *rarg) { const ikev2_sa_t *l = larg; const ikev2_sa_t *r = rarg; uint64_t l_lspi = I2SA_LOCAL_SPI(l); uint64_t r_lspi = I2SA_LOCAL_SPI(r); if (l_lspi < r_lspi) return (-1); if (l_lspi > r_lspi) return (1); return (0); } /* * For hashing/lookup based on the remote spi, it is possible multiple peers * could choose the same SPI value. In addition, it is also possible peers * could be behind the same NAT address. To disambiguate, we follow * RFC7296 2.1 and look at the remote SPI, the addresses, and the init packet * to locate an IKEv2 SA. * * We only ever have to use this on a retransmit of the IKE_SA_INIT packet from * an remotely initiated IKE SA (due to packet loss, new KE group requested, * COOKIE request, etc) as that is the only instance where our local spi is * not included in the IKEv2 header -- we always prefer to lookup on the * local spi when present. * * Specifically we use the remote peer's nonce value as it should be randomly * generated, and at least 16 bytes long, so the chances of a collision * here are even smaller than with the remote SPI. It should be noted that * this is all done in an effort to minimize the amount of processing that * is done and discarded. If these fail, we'll just end up with some larval * ikev2_sa_t's (and some of the IKE_SA_INIT processing that goes with them) * that will eventually time out. */ static uint64_t i2sa_rspi_hash(const void *arg) { const ikev2_sa_t *i2sa = arg; const uint8_t *addrp[2] = { 0 }; uint64_t hash = remote_noise; uint8_t *hashp = (uint8_t *)&hash; uint8_t *ni = NULL; size_t addrlen = 0, hashidx = 0, ni_len = 0; hash ^= I2SA_REMOTE_SPI(i2sa); addrlen = ss_addrlen(SSTOSA(&i2sa->laddr)); addrp[0] = ss_addr(SSTOSA(&i2sa->laddr)); addrp[1] = ss_addr(SSTOSA(&i2sa->raddr)); for (size_t i = 0; i < 2; i++) { for (size_t j = 0; j < addrlen; j++) { hashp[hashidx++] ^= addrp[i][j]; hashidx %= sizeof (hash); } } ni = i2sa->sa_init_args->i2a_nonce_i; ni_len = i2sa->sa_init_args->i2a_nonce_i_len; for (size_t i = 0; i < ni_len; i++) { hashp[hashidx++] ^= ni[i]; hashidx %= sizeof (hash); } return (hash); } static int i2sa_rspi_cmp(const void *larg, const void *rarg) { const ikev2_sa_t *l = larg; const ikev2_sa_t *r = rarg; uint64_t l_rspi = I2SA_REMOTE_SPI(l); uint64_t r_rspi = I2SA_REMOTE_SPI(r); int cmp = 0; if (l_rspi < r_rspi) return (-1); if (l_rspi > r_rspi) return (1); if ((cmp = sockaddr_cmp(SSTOSA(&l->laddr), SSTOSA(&r->laddr))) != 0) return (cmp); if ((cmp = sockaddr_cmp(SSTOSA(&r->raddr), SSTOSA(&r->raddr))) != 0) return (cmp); if (l->sa_init_args == NULL && r->sa_init_args == NULL) return (0); if (l->sa_init_args == NULL) return (-1); if (r->sa_init_args == NULL) return (1); uint8_t *ni_l = l->sa_init_args->i2a_nonce_i; uint8_t *ni_r = r->sa_init_args->i2a_nonce_r; size_t ni_l_len = l->sa_init_args->i2a_nonce_i_len; size_t ni_r_len = r->sa_init_args->i2a_nonce_r_len; if (ni_l_len == 0 && ni_r_len == 0) return (0); if ((cmp = memcmp(ni_l, ni_r, MIN(ni_l_len, ni_r_len))) != 0) return (cmp); if (ni_l_len < ni_r_len) return (-1); if (ni_l_len > ni_r_len) return (1); return (0); } static uint64_t i2sa_addr_hash(const void *arg) { const ikev2_sa_t *i2sa = arg; const uint8_t *addrp[2] = { 0 }; uint64_t hash = addr_noise; uint8_t *hashp = (uint8_t *)&hash; size_t addrlen = 0, hashidx = 0; VERIFY3U(i2sa->laddr.ss_family, ==, i2sa->raddr.ss_family); addrlen = ss_addrlen(SSTOSA(&i2sa->laddr)); addrp[0] = ss_addr(SSTOSA(&i2sa->laddr)); addrp[1] = ss_addr(SSTOSA(&i2sa->raddr)); for (size_t i = 0; i < 2; i++) { for (size_t j = 0; j < addrlen; j++) { hashp[hashidx++] ^= addrp[i][j]; hashidx %= sizeof (hash); } } return (hash); } static int i2sa_addr_cmp(const void *larg, const void *rarg) { const ikev2_sa_t *l = larg; const ikev2_sa_t *r = rarg; int cmp; cmp = sockaddr_cmp(SSTOSA(&l->laddr), SSTOSA(&r->laddr)); if (cmp != 0) return (cmp); return (sockaddr_cmp(SSTOSA(&l->raddr), SSTOSA(&r->raddr))); } static void dummy_dtor(void *arg __unused) { NOTE(ARGUNUSED(arg)) } void ikev2_sa_init(void) { if ((i2sa_cache = umem_cache_create("IKEv2 SAs", sizeof (ikev2_sa_t), 0, i2sa_ctor, i2sa_dtor, NULL, NULL, NULL, 0)) == NULL) err(EXIT_FAILURE, "Unable to create IKEv2 SA cache"); if ((i2c_cache = umem_cache_create("IKEv2 Child SAs", sizeof (ikev2_child_sa_t), 0, i2c_ctor, NULL, NULL, NULL, NULL, 0)) == NULL) err(EXIT_FAILURE, "Unable to create IKEv2 Child SA cache"); arc4random_buf(&remote_noise, sizeof (remote_noise)); arc4random_buf(&addr_noise, sizeof (addr_noise)); i2sa_lspi_refhash = refhash_create(I2SA_NBUCKETS, i2sa_lspi_hash, i2sa_lspi_cmp, dummy_dtor, sizeof (ikev2_sa_t), offsetof(ikev2_sa_t, i2sa_lspi_link), 0, UMEM_NOFAIL); i2sa_rspi_refhash = refhash_create(I2SA_NBUCKETS, i2sa_rspi_hash, i2sa_rspi_cmp, dummy_dtor, sizeof (ikev2_sa_t), offsetof(ikev2_sa_t, i2sa_rspi_link), 0, UMEM_NOFAIL); i2sa_addr_refhash = refhash_create(I2SA_NBUCKETS, i2sa_addr_hash, i2sa_addr_cmp, dummy_dtor, sizeof (ikev2_sa_t), offsetof(ikev2_sa_t, i2sa_addr_link), 0, UMEM_NOFAIL); } void ikev2_sa_fini(void) { umem_cache_destroy(i2sa_cache); umem_cache_destroy(i2c_cache); refhash_destroy(i2sa_lspi_refhash); refhash_destroy(i2sa_rspi_refhash); refhash_destroy(i2sa_addr_refhash); }