untrusted comment: verify with openbsd-78-base.pub RWS3/nvFmk4SWUeOPeXZ5J5Y3kfUqiId8CZy9Yh90d/70xy47zJWRBcekGCZakqGfe0pcHGejYJz6bqnkjJ8RJyttGcEFsMxdAA= OpenBSD 7.8 errata 063, September 30, 2026: A malicious IKEv2 peer could crash iked(8), or cause a certificate validation verdict to be applied to the wrong peer identity. Apply by doing: signify -Vep /etc/signify/openbsd-78-base.pub -x 063_iked.patch.sig \ -m - | (cd /usr/src && patch -p0) And then rebuild and install iked: cd /usr/src/sbin/iked make obj make make install Index: sbin/iked/ca.c =================================================================== RCS file: /cvs/src/sbin/iked/ca.c,v diff -u -p -r1.105.2.1 ca.c --- sbin/iked/ca.c 10 Aug 2026 11:32:59 -0000 1.105.2.1 +++ sbin/iked/ca.c 20 Sep 2026 21:39:31 -0000 @@ -612,8 +612,8 @@ ca_getcert(struct iked *env, struct imsg size_t len; struct iked_static_id id; unsigned int i; - struct iovec iov[3]; - int iovcnt = 3, cmd, ret = -1; + struct iovec iov[4]; + int iovcnt = 4, cmd, ret = -1; struct iked_id key; ptr = (uint8_t *)imsg->data; @@ -661,7 +661,7 @@ ca_getcert(struct iked *env, struct imsg untrusted, &issuer); if (ret == 0) { ret = ocsp_validate_cert(env, ptr, len, sh, - type, issuer); + type, issuer, &id); X509_free(issuer); if (ret == 0) { sk_X509_pop_free(untrusted, X509_free); @@ -699,8 +699,10 @@ ca_getcert(struct iked *env, struct imsg iov[0].iov_len = sizeof(sh); iov[1].iov_base = &type; iov[1].iov_len = sizeof(type); - iov[2].iov_base = ptr; - iov[2].iov_len = len; + iov[2].iov_base = &id; + iov[2].iov_len = sizeof(id); + iov[3].iov_base = ptr; + iov[3].iov_len = len; ret = proc_composev(&env->sc_ps, PROC_IKEV2, cmd, iov, iovcnt); ibuf_free(key.id_buf); Index: sbin/iked/iked.h =================================================================== RCS file: /cvs/src/sbin/iked/iked.h,v diff -u -p -r1.233 iked.h --- sbin/iked/iked.h 4 Nov 2024 02:44:28 -0000 1.233 +++ sbin/iked/iked.h 20 Sep 2026 21:39:31 -0000 @@ -1424,7 +1424,7 @@ __dead void fatalx(const char *, ...) int ocsp_connect(struct iked *, struct imsg *); int ocsp_receive_fd(struct iked *, struct imsg *); int ocsp_validate_cert(struct iked *, void *, size_t, struct iked_sahdr, - uint8_t, X509 *); + uint8_t, X509 *, struct iked_static_id *); /* parse.y */ int parse_config(const char *, struct iked *); Index: sbin/iked/ikev2.c =================================================================== RCS file: /cvs/src/sbin/iked/ikev2.c,v diff -u -p -r1.394.2.2 ikev2.c --- sbin/iked/ikev2.c 7 May 2026 17:36:35 -0000 1.394.2.2 +++ sbin/iked/ikev2.c 20 Sep 2026 21:39:32 -0000 @@ -329,6 +329,8 @@ ikev2_dispatch_cert(int fd, struct privs uint8_t *ptr; size_t len; struct iked_id *id = NULL; + struct iked_static_id peerid; + size_t peerlen; int ignore = 0; int i; @@ -358,6 +360,33 @@ ikev2_dispatch_cert(int fd, struct privs sa->sa_state < IKEV2_STATE_EAP) break; + if (sa->sa_state >= IKEV2_STATE_CLOSING) { + log_debug("%s: verdict for a closing SA, ignoring", + __func__); + break; + } + if (len < sizeof(peerid)) { + log_debug("%s: verdict without identity", __func__); + break; + } + memcpy(&peerid, ptr, sizeof(peerid)); + len -= sizeof(peerid); + ptr += sizeof(peerid); + + peerlen = ibuf_length(IKESA_DSTID(sa)->id_buf); + if (peerid.id_type != IKESA_DSTID(sa)->id_type || + peerid.id_length != peerlen || + peerlen > sizeof(peerid.id_data) || + memcmp(peerid.id_data, + ibuf_data(IKESA_DSTID(sa)->id_buf), peerlen) != 0) { + log_info("%s: verdict is for a different identity", + SPI_SA(sa, __func__)); + ikev2_ike_sa_setreason(sa, + "verdict for a different identity"); + sa_free(env, sa); + break; + } + if (sh.sh_initiator) id = &sa->sa_rcert; else @@ -967,6 +996,17 @@ ikev2_ike_auth_recv(struct iked *env, st else id = &sa->sa_iid; + /* peer's identity is fixed for the life of the SA */ + if (id->id_type && msg->msg_peerid.id_type && + (id->id_type != msg->msg_peerid.id_type || + ibuf_length(id->id_buf) != ibuf_length(msg->msg_peerid.id_buf) || + memcmp(ibuf_data(id->id_buf), ibuf_data(msg->msg_peerid.id_buf), + ibuf_length(id->id_buf)) != 0)) { + log_info("%s: peer changed its identity", SPI_SA(sa, __func__)); + ikev2_send_auth_failed(env, sa); + return (-1); + } + /* try to relookup the policy based on the peerid */ if (msg->msg_peerid.id_type && !sa->sa_hdr.sh_initiator) { old = sa->sa_policy; @@ -3119,6 +3159,11 @@ ikev2_handle_delete(struct iked *env, st goto done; } + if (msg->msg_del_buf == NULL) { + log_debug("%s: invalid delete payload", __func__); + goto done; + } + cnt = msg->msg_del_cnt; len = ibuf_length(msg->msg_del_buf); @@ -3823,6 +3868,12 @@ ikev2_resp_ike_eap_mschap(struct iked *e eap->eam_identity = NULL; return (eap_challenge_request(env, sa, eap->eam_id)); case EAP_STATE_MSCHAPV2_CHALLENGE: + if (sa->sa_eap.id_buf == NULL || + ibuf_size(sa->sa_eap.id_buf) != MSCHAPV2_CHALLENGE_SZ) { + log_info("%s: invalid EAP challenge", + SPI_SA(sa, __func__)); + return (-1); + } if (eap->eam_user) { name = eap->eam_user; } else if (sa->sa_eapid) { Index: sbin/iked/ikev2_pld.c =================================================================== RCS file: /cvs/src/sbin/iked/ikev2_pld.c,v diff -u -p -r1.136.4.1 ikev2_pld.c --- sbin/iked/ikev2_pld.c 1 Apr 2026 20:02:24 -0000 1.136.4.1 +++ sbin/iked/ikev2_pld.c 20 Sep 2026 21:39:32 -0000 @@ -1421,6 +1421,27 @@ ikev2_pld_delete(struct iked *env, struc cnt = betoh16(del.del_nspi); sz = del.del_spisize; + len = left - sizeof(del); + + switch (del.del_protoid) { + case IKEV2_SAPROTO_IKE: + if (sz != 0 || cnt != 0 || len != 0) { + log_debug("%s: malformed IKE delete", __func__); + return (-1); + } + break; + case IKEV2_SAPROTO_AH: + case IKEV2_SAPROTO_ESP: + if (sz != 4 || cnt == 0 || len != sz * cnt) { + log_debug("%s: malformed AH/ESP delete", __func__); + return (-1); + } + break; + default: + log_debug("%s: unsupported protoid %d for delete", __func__, + del.del_protoid); + return (-1); + } log_debug("%s: proto %s spisize %zu nspi %zu", __func__, print_map(del.del_protoid, ikev2_saproto_map), @@ -1435,16 +1456,10 @@ ikev2_pld_delete(struct iked *env, struc msg->msg_parent->msg_del_cnt = cnt; msg->msg_parent->msg_del_spisize = sz; - buf = msgbuf + offset + sizeof(del); - len = left - sizeof(del); - if (len == 0 || sz == 0 || cnt == 0) + if (del.del_protoid == IKEV2_SAPROTO_IKE) return (0); - if ((len / sz) != cnt) { - log_debug("%s: invalid payload length %zu/%zu != %zu", - __func__, len, sz, cnt); - return (-1); - } + buf = msgbuf + offset + sizeof(del); print_hex(buf, 0, len); Index: sbin/iked/ocsp.c =================================================================== RCS file: /cvs/src/sbin/iked/ocsp.c,v diff -u -p -r1.25 ocsp.c --- sbin/iked/ocsp.c 17 Jan 2024 08:25:02 -0000 1.25 +++ sbin/iked/ocsp.c 20 Sep 2026 21:39:32 -0000 @@ -43,6 +43,7 @@ struct iked_ocsp { struct iked *ocsp_env; /* back pointer to env */ struct iked_sahdr ocsp_sh; /* ike sa */ uint8_t ocsp_type; /* auth type */ + struct iked_static_id ocsp_peerid; /* ikeid under check */ struct iked_socket *ocsp_sock; /* socket to ocsp responder */ BIO *ocsp_cbio; /* matching OpenSSL obj */ OCSP_CERTID *ocsp_id; /* ocsp-id for cert */ @@ -263,7 +264,8 @@ ocsp_connect_finish(struct iked *env, in /* validate the certifcate stored in 'data' by querying the ocsp-responder */ int ocsp_validate_cert(struct iked *env, void *data, size_t len, - struct iked_sahdr sh, uint8_t type, X509 *issuer) + struct iked_sahdr sh, uint8_t type, X509 *issuer, + struct iked_static_id *peerid) { struct iovec iov[2]; STACK_OF(OPENSSL_STRING) *aia; /* Authority Information Access */ @@ -287,6 +289,8 @@ ocsp_validate_cert(struct iked *env, voi ocsp->ocsp_env = env; ocsp->ocsp_sh = sh; ocsp->ocsp_type = type; + if (peerid != NULL) + ocsp->ocsp_peerid = *peerid; if ((rawcert = BIO_new_mem_buf(data, len)) == NULL || (cert = d2i_X509_bio(rawcert, NULL)) == NULL || @@ -603,13 +607,15 @@ int ocsp_validate_finish(struct iked_ocsp *ocsp, int valid) { struct iked *env = ocsp->ocsp_env; - struct iovec iov[2]; - int iovcnt = 2, ret, cmd; + struct iovec iov[3]; + int iovcnt = 3, ret, cmd; iov[0].iov_base = &ocsp->ocsp_sh; iov[0].iov_len = sizeof(ocsp->ocsp_sh); iov[1].iov_base = &ocsp->ocsp_type; iov[1].iov_len = sizeof(ocsp->ocsp_type); + iov[2].iov_base = &ocsp->ocsp_peerid; + iov[2].iov_len = sizeof(ocsp->ocsp_peerid); cmd = valid ? IMSG_CERTVALID : IMSG_CERTINVALID; ret = proc_composev(&env->sc_ps, PROC_IKEV2, cmd, iov, iovcnt); Index: sbin/iked/radius.c =================================================================== RCS file: /cvs/src/sbin/iked/radius.c,v diff -u -p -r1.14 radius.c --- sbin/iked/radius.c 24 Jun 2025 00:05:42 -0000 1.14 +++ sbin/iked/radius.c 20 Sep 2026 21:39:32 -0000 @@ -79,16 +79,40 @@ iked_radius_request(struct iked *env, st } if (eap->eap_type == EAP_TYPE_IDENTITY) { + if (sa->sa_radreq != NULL) { + log_info("%s: duplicate EAP identity response", + SPI_SA(sa, __func__)); + return -1; + } + if (msg->msg_eap.eam_identity == NULL) { + log_info("%s: EAP identity already known", + SPI_SA(sa, __func__)); + return -1; + } if ((sa->sa_radreq = calloc(1, sizeof(struct iked_radserver_req))) == NULL) { log_debug( "%s: calloc failed for iked_radserver_req: %s", __func__, strerror(errno)); - return (-1); + return -1; } timer_set(env, &sa->sa_radreq->rr_timer, iked_radius_request_send, sa->sa_radreq); sa->sa_radreq->rr_user = strdup(msg->msg_eap.eam_identity); + if (sa->sa_radreq->rr_user == NULL) { + log_warn("%s: strdup failed", __func__); + iked_radius_request_free(env, sa->sa_radreq); + sa->sa_radreq = NULL; + return -1; + } + } else if (sa->sa_radreq == NULL) { + log_info("%s: EAP response received before identity", + SPI_SA(sa, __func__)); + return -1; + } else if (sa->sa_radreq->rr_user == NULL) { + log_info("%s: EAP response without pending RADIUS identity", + SPI_SA(sa, __func__)); + return -1; } if ((pkt = radius_new_request_packet(RADIUS_CODE_ACCESS_REQUEST))