untrusted comment: verify with openbsd-79-base.pub RWTSdNN9A3yvWKcCX9dtoz4zcCWzGCNJ+ATB1NM7/rMz2NmQgHwkrRa/5azhf4qqHHtl4pi3H9cqUDU49dvba6GG8cfP4uG41wE= OpenBSD 7.9 errata 041, November 5, 2026: By sending crafted IKEv1 messages, an unauthenticated remote attacker could crash isakmpd(8) due to a NULL pointer dereference, out-of-bounds memory accesses or a double free. Apply by doing: signify -Vep /etc/signify/openbsd-79-base.pub -x 041_isakmpd.patch.sig \ -m - | (cd /usr/src && patch -p0) And then rebuild and install isakmpd: cd /usr/src/sbin/isakmpd make obj make make install Index: sbin/isakmpd/crypto.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/crypto.c,v diff -u -p -r1.35 crypto.c --- sbin/isakmpd/crypto.c 15 Jan 2018 09:54:48 -0000 1.35 +++ sbin/isakmpd/crypto.c 23 Oct 2026 21:52:10 -0000 @@ -188,8 +188,9 @@ cast1_decrypt(struct keystate *ks, u_int enum cryptoerr aes_init(struct keystate *ks, u_int8_t *key, u_int16_t len) { - AES_set_encrypt_key(key, len << 3, &ks->ks_aes[0]); - AES_set_decrypt_key(key, len << 3, &ks->ks_aes[1]); + if (AES_set_encrypt_key(key, len << 3, &ks->ks_aes[0]) != 0 || + AES_set_decrypt_key(key, len << 3, &ks->ks_aes[1]) != 0) + return EKEYLEN; return EOKAY; } @@ -252,9 +253,9 @@ crypto_init(struct crypto_xf *xf, u_int8 *err = xf->init(ks, key, len); if (*err != EOKAY) { - LOG_DBG((LOG_CRYPTO, 30, "crypto_init: weak key found for %s", - xf->name)); - free(ks); + LOG_DBG((LOG_CRYPTO, 30, "crypto_init: %s key setup failed " + "(key length %u, error %d)", xf->name, len, *err)); + freezero(ks, sizeof *ks); return 0; } return ks; Index: sbin/isakmpd/dpd.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/dpd.c,v diff -u -p -r1.20 dpd.c --- sbin/isakmpd/dpd.c 5 Dec 2017 20:31:45 -0000 1.20 +++ sbin/isakmpd/dpd.c 23 Oct 2026 21:52:11 -0000 @@ -139,7 +139,9 @@ dpd_start(struct sa *isakmp_sa) } /* - * All incoming DPD Notify messages enter here. Message has been validated. + * All incoming DPD Notify messages enter here. The generic Notify + * payload has been validated; authentication and the DPD payload + * format are checked here. */ void dpd_handle_notify(struct message *msg, struct payload *p) @@ -147,6 +149,23 @@ dpd_handle_notify(struct message *msg, s struct sa *isakmp_sa = msg->isakmp_sa; u_int16_t notify = GET_ISAKMP_NOTIFY_MSG_TYPE(p->p); u_int32_t p_seq; + + if (!isakmp_sa || (msg->flags & MSG_AUTHENTICATED) == 0) { + LOG_DBG((LOG_MESSAGE, 10, + "dpd_handle_notify: got unauthenticated %s", + constant_name(isakmp_notify_cst, notify))); + return; + } + + if (GET_ISAKMP_NOTIFY_PROTO(p->p) != ISAKMP_PROTO_ISAKMP || + GET_ISAKMP_NOTIFY_SPI_SZ(p->p) != ISAKMP_HDR_COOKIES_LEN || + GET_ISAKMP_GEN_LENGTH(p->p) < ISAKMP_NOTIFY_SPI_OFF + + ISAKMP_HDR_COOKIES_LEN + sizeof p_seq) { + LOG_DBG((LOG_MESSAGE, 10, + "dpd_handle_notify: malformed %s payload", + constant_name(isakmp_notify_cst, notify))); + return; + } /* Extract the sequence number. */ memcpy(&p_seq, p->p + ISAKMP_NOTIFY_SPI_OFF + ISAKMP_HDR_COOKIES_LEN, Index: sbin/isakmpd/exchange.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/exchange.c,v diff -u -p -r1.142.34.1 exchange.c --- sbin/isakmpd/exchange.c 3 Oct 2026 17:35:48 -0000 1.142.34.1 +++ sbin/isakmpd/exchange.c 23 Oct 2026 21:52:11 -0000 @@ -278,8 +278,14 @@ exchange_run(struct message *msg) */ if (exchange->initiator ^ (exchange->step % 2)) { done = 1; - if (exchange->step) + if (exchange->step) { msg = message_alloc_reply(msg); + if (!msg) { + log_error("exchange_run: " + "message_alloc_reply() failed"); + return; + } + } message_setup_header(msg, exchange->type, 0, exchange->message_id); if (handler(msg)) { @@ -936,6 +942,11 @@ exchange_establish_p2(struct sa *isakmp_ } } msg = message_alloc(isakmp_sa->transport, 0, ISAKMP_HDR_SZ); + if (!msg) { + log_error("exchange_establish_p2: message_alloc() failed"); + exchange_free(exchange); + return 0; /* exchange_free() runs finalize */ + } msg->isakmp_sa = isakmp_sa; sa_reference(isakmp_sa); @@ -1135,11 +1146,11 @@ exchange_free_aux(void *v_exch) if (exchange->in_transit && exchange->in_transit != exchange->last_sent) message_free(exchange->in_transit); - free(exchange->nonce_i); - free(exchange->nonce_r); + freezero(exchange->nonce_i, exchange->nonce_i_len); + freezero(exchange->nonce_r, exchange->nonce_r_len); free(exchange->id_i); free(exchange->id_r); - free(exchange->keystate); + freezero(exchange->keystate, sizeof(struct keystate)); if (exchange->data) { if (exchange->doi && exchange->doi->free_exchange_data) exchange->doi->free_exchange_data(exchange->data); @@ -1209,7 +1220,8 @@ exchange_upgrade_p1(struct message *msg) GET_ISAKMP_HDR_RCOOKIE(msg->iov[0].iov_base, exchange->cookies + ISAKMP_HDR_ICOOKIE_LEN); exchange_enter(exchange); - sa_isakmp_upgrade(msg); + if (!TAILQ_EMPTY(&exchange->sa_list)) + sa_isakmp_upgrade(msg); } static int @@ -1418,6 +1430,8 @@ exchange_finalize(struct message *msg) /* If we have nothing to retransmit we can safely remove ourselves. */ if (!exchange->last_sent) exchange_free(exchange); + else + exchange->flags |= EXCHANGE_FLAG_FINALIZED; } /* Stash a nonce into the exchange data. */ @@ -1445,13 +1459,14 @@ exchange_nonce(struct exchange *exchange nonce = initiator ? &exchange->nonce_i : &exchange->nonce_r; nonce_len = initiator ? &exchange->nonce_i_len : &exchange->nonce_r_len; - *nonce_len = nonce_sz; *nonce = malloc(nonce_sz); if (!*nonce) { + *nonce_len = 0; log_error("exchange_nonce: malloc (%lu) failed", (unsigned long)nonce_sz); return -1; } + *nonce_len = nonce_sz; memcpy(*nonce, buf, nonce_sz); snprintf(header, sizeof header, "exchange_nonce: NONCE_%c", initiator ? 'i' : 'r'); Index: sbin/isakmpd/exchange.h =================================================================== RCS file: /cvs/src/sbin/isakmpd/exchange.h,v diff -u -p -r1.37.34.1 exchange.h --- sbin/isakmpd/exchange.h 3 Oct 2026 17:35:48 -0000 1.37.34.1 +++ sbin/isakmpd/exchange.h 23 Oct 2026 21:52:11 -0000 @@ -224,6 +224,7 @@ struct exchange { #define EXCHANGE_FLAG_NAT_T_RFC 0x0080 /* Peer does RFC NAT-T. */ #define EXCHANGE_FLAG_NAT_T_DRAFT 0x0100 /* Peer does draft NAT-T.*/ #define EXCHANGE_FLAG_OPENBSD 0x0200 /* Peer is OpenBSD */ +#define EXCHANGE_FLAG_FINALIZED 0x0400 /* Finalize function called. */ extern int exchange_add_certs(struct message *); extern int exchange_add_certreqs(struct message *); Index: sbin/isakmpd/ike_auth.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/ike_auth.c,v diff -u -p -r1.118.24.1 ike_auth.c --- sbin/isakmpd/ike_auth.c 3 Oct 2026 17:35:48 -0000 1.118.24.1 +++ sbin/isakmpd/ike_auth.c 23 Oct 2026 21:52:11 -0000 @@ -402,12 +402,12 @@ pre_shared_gen_skeyid(struct exchange *e if (!exchange->recv_key) { log_error("pre_shared_gen_skeyid: malloc (%lu) failed", (unsigned long)keylen); - free(key); + freezero(key, keylen); return 0; } memcpy(exchange->recv_key, key, keylen); exchange->recv_certtype = ISAKMP_CERTENC_NONE; - free(key); + freezero(key, keylen); prf = prf_alloc(ie->prf_type, ie->hash->type, exchange->recv_key, keylen); @@ -454,7 +454,7 @@ sig_gen_skeyid(struct exchange *exchange prf = prf_alloc(ie->prf_type, ie->hash->type, key, exchange->nonce_i_len + exchange->nonce_r_len); - free(key); + freezero(key, exchange->nonce_i_len + exchange->nonce_r_len); if (!prf) return 0; @@ -524,9 +524,11 @@ pre_shared_decode_hash(struct message *m char header[80]; int initiator = exchange->initiator; u_int8_t **hash_p; + size_t *hash_sz_p; /* Choose the right fields to fill-in. */ hash_p = initiator ? &ie->hash_r : &ie->hash_i; + hash_sz_p = initiator ? &ie->hash_r_sz : &ie->hash_i_sz; payload = payload_first(msg, ISAKMP_PAYLOAD_HASH); if (!payload) { @@ -540,10 +542,12 @@ pre_shared_decode_hash(struct message *m /* XXX Need this hash be in the SA? */ *hash_p = malloc(hashsize); if (!*hash_p) { + *hash_sz_p = 0; log_error("pre_shared_decode_hash: malloc (%lu) failed", (unsigned long)hashsize); return -1; } + *hash_sz_p = hashsize; memcpy(*hash_p, payload->p + ISAKMP_HASH_DATA_OFF, hashsize); snprintf(header, sizeof header, "pre_shared_decode_hash: HASH_%c", initiator ? 'R' : 'I'); @@ -566,6 +570,7 @@ rsa_sig_decode_hash(struct message *msg) u_int32_t rawcertlen, *id_cert_len; RSA *key = 0; size_t hashsize = ie->hash->hashsize, id_len; + size_t *hash_sz_p; char header[80]; int len, initiator = exchange->initiator; int found = 0, n, i, id_found; @@ -576,6 +581,7 @@ rsa_sig_decode_hash(struct message *msg) /* Choose the right fields to fill-in. */ hash_p = initiator ? &ie->hash_r : &ie->hash_i; + hash_sz_p = initiator ? &ie->hash_r_sz : &ie->hash_i_sz; id = initiator ? exchange->id_r : exchange->id_i; id_len = initiator ? exchange->id_r_len : exchange->id_i_len; @@ -783,14 +789,19 @@ rsa_sig_decode_hash(struct message *msg) } *hash_p = malloc(len); if (!*hash_p) { + *hash_sz_p = 0; RSA_free(key); log_error("rsa_sig_decode_hash: malloc (%d) failed", len); return -1; } + *hash_sz_p = len; len = RSA_public_decrypt(len, p->p + ISAKMP_SIG_DATA_OFF, *hash_p, key, RSA_PKCS1_PADDING); if (len == -1) { RSA_free(key); + freezero(*hash_p, *hash_sz_p); + *hash_p = NULL; + *hash_sz_p = 0; log_print("rsa_sig_decode_hash: RSA_public_decrypt () failed"); return -1; } @@ -799,8 +810,9 @@ rsa_sig_decode_hash(struct message *msg) exchange->recv_keytype = ISAKMP_KEY_RSA; if (len != (int)hashsize) { - free(*hash_p); - *hash_p = 0; + freezero(*hash_p, *hash_sz_p); + *hash_p = NULL; + *hash_sz_p = 0; log_print("rsa_sig_decode_hash: len %lu != hashsize %lu", (unsigned long)len, (unsigned long)hashsize); return -1; @@ -957,6 +969,8 @@ skipcert: } sent_key = key_internalize(ISAKMP_KEY_RSA, ISAKMP_KEYTYPE_PRIVATE, data, datalen); + freezero(data, datalen); + data = NULL; if (!sent_key) { log_print("rsa_sig_encode_hash: bad RSA private key " "from dynamic SA acquisition subsystem"); Index: sbin/isakmpd/ike_phase_1.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/ike_phase_1.c,v diff -u -p -r1.78 ike_phase_1.c --- sbin/isakmpd/ike_phase_1.c 20 Sep 2018 11:49:55 -0000 1.78 +++ sbin/isakmpd/ike_phase_1.c 23 Oct 2026 21:52:11 -0000 @@ -738,7 +738,7 @@ ike_phase_1_post_exchange_KE_NONCE(struc exchange->keystate = crypto_init(exchange->crypto, key, exchange->key_length, &err); - free(key); + freezero(key, keylen); } else /* Setup our keystate using the raw skeyid_e. */ exchange->keystate = crypto_init(exchange->crypto, @@ -1070,7 +1070,9 @@ ike_phase_1_recv_ID(struct message *msg) } /* Compare expected/desired and received remote ID */ - if (memcmp(rid, payload->p + ISAKMP_ID_DATA_OFF, sz) != 0) { + if ((size_t)sz != GET_ISAKMP_GEN_LENGTH(payload->p) - + ISAKMP_ID_DATA_OFF || + memcmp(rid, payload->p + ISAKMP_ID_DATA_OFF, sz) != 0) { free(rid); log_print("ike_phase_1_recv_ID: " "received remote ID other than expected %s", p); @@ -1268,6 +1270,12 @@ attribute_unacceptable(u_int16_t type, u struct constant_map *map; struct attr_node *node; int rv, dur = 0; + + if (len < sizeof(u_int16_t)) { + log_print("attribute_unacceptable: too short attribute " + "(type %u, len %u)", type, len); + return 1; + } if (!tag) { log_print("attribute_unacceptable: " Index: sbin/isakmpd/ike_quick_mode.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/ike_quick_mode.c,v diff -u -p -r1.115.12.1 ike_quick_mode.c --- sbin/isakmpd/ike_quick_mode.c 3 Oct 2026 17:35:48 -0000 1.115.12.1 +++ sbin/isakmpd/ike_quick_mode.c 23 Oct 2026 21:52:12 -0000 @@ -1402,10 +1402,12 @@ post_quick_mode(struct message *msg) * blocksize as it is generated in chunks of * that blocksize. */ - iproto->keymat[i] - = malloc(((ie->keymat_len + prf->blocksize - 1) - / prf->blocksize) * prf->blocksize); + iproto->keymat_len[i] + = ((ie->keymat_len + prf->blocksize - 1) + / prf->blocksize) * prf->blocksize; + iproto->keymat[i] = malloc(iproto->keymat_len[i]); if (!iproto->keymat[i]) { + iproto->keymat_len[i] = 0; log_error("post_quick_mode: " "malloc (%lu) failed", (((unsigned long)ie->keymat_len + Index: sbin/isakmpd/ipsec.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/ipsec.c,v diff -u -p -r1.155.4.1 ipsec.c --- sbin/isakmpd/ipsec.c 3 Oct 2026 17:35:48 -0000 1.155.4.1 +++ sbin/isakmpd/ipsec.c 23 Oct 2026 21:52:12 -0000 @@ -779,13 +779,13 @@ ipsec_free_exchange_data(void *vie) free(ie->id_cr); free(ie->g_xi); free(ie->g_xr); - free(ie->g_xy); - free(ie->skeyid); - free(ie->skeyid_d); - free(ie->skeyid_a); - free(ie->skeyid_e); - free(ie->hash_i); - free(ie->hash_r); + freezero(ie->g_xy, ie->g_xy_len); + freezero(ie->skeyid, ie->skeyid_len); + freezero(ie->skeyid_d, ie->skeyid_len); + freezero(ie->skeyid_a, ie->skeyid_len); + freezero(ie->skeyid_e, ie->skeyid_len); + freezero(ie->hash_i, ie->hash_i_sz); + freezero(ie->hash_r, ie->hash_r_sz); if (ie->group) group_free(ie->group); for (attr = LIST_FIRST(&ie->attrs); attr; @@ -807,8 +807,8 @@ ipsec_free_sa_data(void *visa) free(isa->src_mask); free(isa->dst_net); free(isa->dst_mask); - free(isa->skeyid_a); - free(isa->skeyid_d); + freezero(isa->skeyid_a, isa->skeyid_len); + freezero(isa->skeyid_d, isa->skeyid_len); } /* Free the DOI-specific protocol data of an SA pointed to by VIPROTO. */ @@ -818,8 +818,12 @@ ipsec_free_proto_data(void *viproto) struct ipsec_proto *iproto = viproto; int i; - for (i = 0; i < 2; i++) - free(iproto->keymat[i]); + for (i = 0; i < 2; i++) { + if (iproto->keymat[i] == NULL) + continue; + freezero(iproto->keymat[i], iproto->keymat_len[i]); + iproto->keymat[i] = NULL; + } } /* Return exchange script based on TYPE. */ @@ -947,24 +951,34 @@ ipsec_validate_id_information(u_int8_t t switch (type) { case IPSEC_ID_IPV4_ADDR: + if (sz != sizeof(struct in_addr)) + return -1; LOG_DBG_BUF((LOG_MESSAGE, 40, "ipsec_validate_id_information: IPv4", buf, sizeof(struct in_addr))); break; case IPSEC_ID_IPV6_ADDR: + if (sz != sizeof(struct in6_addr)) + return -1; LOG_DBG_BUF((LOG_MESSAGE, 40, "ipsec_validate_id_information: IPv6", buf, sizeof(struct in6_addr))); break; case IPSEC_ID_IPV4_ADDR_SUBNET: + case IPSEC_ID_IPV4_RANGE: + if (sz != 2 * sizeof(struct in_addr)) + return -1; LOG_DBG_BUF((LOG_MESSAGE, 40, "ipsec_validate_id_information: IPv4 network/netmask", buf, 2 * sizeof(struct in_addr))); break; case IPSEC_ID_IPV6_ADDR_SUBNET: + case IPSEC_ID_IPV6_RANGE: + if (sz != 2 * sizeof(struct in6_addr)) + return -1; LOG_DBG_BUF((LOG_MESSAGE, 40, "ipsec_validate_id_information: IPv6 network/netmask", buf, 2 * sizeof(struct in6_addr))); @@ -1283,7 +1297,11 @@ ipsec_is_attribute_incompatible(u_int16_ void *vmsg) { struct message *msg = vmsg; - u_int16_t dv = decode_16(value); + u_int16_t dv; + + if (len < sizeof(dv)) + return 1; + dv = decode_16(value); if (msg->exchange->phase == 1) { switch (type) { @@ -1418,6 +1436,16 @@ ipsec_decode_attribute(u_int16_t type, u struct ipsec_exch *ie = exchange->data; static int lifetype = 0; + /* LIFE_DURATION attributes are validated below, so pass them on. */ + if (len < sizeof(u_int16_t) && + !(exchange->phase == 1 && type == IKE_ATTR_LIFE_DURATION) && + !(exchange->phase != 1 && type == IPSEC_ATTR_SA_LIFE_DURATION)) { + log_print("ipsec_decode_attribute: too short attribute " + "(type %u, len %u)", type, len); + lifetype = 0; + return 0; + } + if (exchange->phase == 1) { switch (type) { case IKE_ATTR_ENCRYPTION_ALGORITHM: @@ -1748,6 +1776,7 @@ ipsec_handle_leftover_payload(struct mes struct payload *payload) { u_int32_t spisz, nspis; + size_t len; struct sockaddr *dst; int reenter = 0; u_int8_t *spis, proto; @@ -1771,6 +1800,13 @@ ipsec_handle_leftover_payload(struct mes log_print("ipsec_handle_leftover_payload: invalid SPI " "size %d for proto %d in DELETE payload", spisz, proto); + return -1; + } + len = GET_ISAKMP_GEN_LENGTH(payload->p); + if (len < ISAKMP_DELETE_SPI_OFF || + (len - ISAKMP_DELETE_SPI_OFF) / spisz < nspis) { + log_print("ipsec_handle_leftover_payload: " + "SPI count %u exceeds payload length %zu", nspis, len); return -1; } spis = calloc(nspis, spisz); Index: sbin/isakmpd/ipsec.h =================================================================== RCS file: /cvs/src/sbin/isakmpd/ipsec.h,v diff -u -p -r1.27 ipsec.h --- sbin/isakmpd/ipsec.h 8 Nov 2017 13:33:49 -0000 1.27 +++ sbin/isakmpd/ipsec.h 23 Oct 2026 21:52:12 -0000 @@ -87,7 +87,9 @@ struct ipsec_exch { /* HASH_I & HASH_R. XXX Do these need to be saved here? */ u_int8_t *hash_i; + size_t hash_i_sz; u_int8_t *hash_r; + size_t hash_r_sz; /* KEYMAT */ size_t keymat_len; @@ -139,6 +141,7 @@ struct ipsec_proto { /* KEYMAT */ u_int8_t *keymat[2]; + size_t keymat_len[2]; }; extern u_int8_t *ipsec_add_hash_payload(struct message *, size_t); Index: sbin/isakmpd/isakmpd.8 =================================================================== RCS file: /cvs/src/sbin/isakmpd/isakmpd.8,v diff -u -p -r1.123.28.1 isakmpd.8 --- sbin/isakmpd/isakmpd.8 3 Oct 2026 17:35:48 -0000 1.123.28.1 +++ sbin/isakmpd/isakmpd.8 23 Oct 2026 21:52:12 -0000 @@ -669,7 +669,7 @@ The FIFO used to manually control .Xr isakmpd.policy 5 , .Xr iked 8 , .Xr sasyncd 8 , -.Xr ssl 8 , +.Xr ssl 8 .Sh STANDARDS .Rs .%A D. Piper Index: sbin/isakmpd/key.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/key.c,v diff -u -p -r1.27 key.c --- sbin/isakmpd/key.c 13 Oct 2021 16:57:43 -0000 1.27 +++ sbin/isakmpd/key.c 23 Oct 2026 21:52:12 -0000 @@ -164,7 +164,7 @@ key_from_printable(int type, int private } if (hex2raw(key, *data, datalen)) { log_error("key_from_printable: invalid hex key"); - free(*data); + freezero(*data, datalen); *data = NULL; *datalenp = 0; return; Index: sbin/isakmpd/log.h =================================================================== RCS file: /cvs/src/sbin/isakmpd/log.h,v diff -u -p -r1.25.70.1 log.h --- sbin/isakmpd/log.h 3 Oct 2026 17:35:48 -0000 1.25.70.1 +++ sbin/isakmpd/log.h 23 Oct 2026 21:52:12 -0000 @@ -78,7 +78,7 @@ extern void log_errorx(const char *,...) extern void log_fatal(const char *,...) __attribute__((__format__(__printf__, 1, 2))) __dead; extern void log_fatalx(const char *,...) - __attribute__((__format__(__printf__, 1, 2))); + __attribute__((__format__(__printf__, 1, 2))) __dead; extern void log_print(const char *,...) __attribute__((__format__(__printf__, 1, 2))); extern void log_verbose(const char *,...) Index: sbin/isakmpd/message.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/message.c,v diff -u -p -r1.129.40.1 message.c --- sbin/isakmpd/message.c 3 Oct 2026 17:35:48 -0000 1.129.40.1 +++ sbin/isakmpd/message.c 23 Oct 2026 21:52:12 -0000 @@ -159,6 +159,8 @@ message_alloc_reply(struct message *msg) struct message *reply; reply = message_alloc(msg->transport, 0, ISAKMP_HDR_SZ); + if (!reply) + return NULL; reply->exchange = msg->exchange; reply->isakmp_sa = msg->isakmp_sa; reply->flags = msg->flags; @@ -332,7 +334,7 @@ next_payload: * Parse a proposal payload found in message MSG. PAYLOAD is always * ISAKMP_PAYLOAD_PROPOSAL and ignored in here. It's needed as the API for * message_parse_payloads requires it. BUF points to the proposal's - * generic payload header. + * generic payload header. P is the outer SA payload. */ static int message_parse_proposal(struct message *msg, struct payload *p, @@ -340,6 +342,19 @@ message_parse_proposal(struct message *m { set payload_set; + /* Proposal must lie within outer SA payload. */ + if (buf + GET_ISAKMP_GEN_LENGTH(buf) > + p->p + GET_ISAKMP_GEN_LENGTH(p->p)) { + message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1); + return -1; + } + + if ((ISAKMP_PROP_SPI_OFF + GET_ISAKMP_PROP_SPI_SZ(buf) + + ISAKMP_GEN_SZ) > GET_ISAKMP_GEN_LENGTH(buf)) { + message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1); + return -1; + } + /* Put the proposal into the proposal bucket. */ if (message_index_payload(msg, p, payload, buf) == -1) return -1; @@ -360,6 +375,13 @@ static int message_parse_transform(struct message *msg, struct payload *p, u_int8_t payload, u_int8_t *buf) { + /* Transform must lie within outer proposal payload. */ + if (buf + GET_ISAKMP_GEN_LENGTH(buf) > + p->p + GET_ISAKMP_GEN_LENGTH(p->p)) { + message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1); + return -1; + } + /* Put the transform into the transform bucket. */ if (message_index_payload(msg, p, payload, buf) == -1) return -1; @@ -547,9 +569,10 @@ message_validate_delete(struct message * struct doi *doi; struct sa *sa, *isakmp_sa; struct sockaddr *dst, *dst_isa; + size_t spisz, len; u_int32_t nspis = GET_ISAKMP_DELETE_NSPIS(p->p); u_int8_t *spis = (u_int8_t *)p->p + ISAKMP_DELETE_SPI_OFF; - u_int32_t i; + u_int32_t i, spi; char *addr; /* Only accept authenticated DELETEs. */ @@ -593,6 +616,24 @@ message_validate_delete(struct message * message_free(msg); return -1; } + /* Enforce that SPI size matches the protocol. */ + spisz = (proto == ISAKMP_PROTO_ISAKMP) ? + ISAKMP_HDR_COOKIES_LEN : sizeof(u_int32_t); + if (GET_ISAKMP_DELETE_SPI_SZ(p->p) != spisz) { + log_print("message_validate_delete: invalid SPI size %u " + "for protocol %u", GET_ISAKMP_DELETE_SPI_SZ(p->p), proto); + message_free(msg); + return -1; + } + /* All SPIs must fit into the DELETE payload. */ + len = GET_ISAKMP_GEN_LENGTH(p->p); + if (len < ISAKMP_DELETE_SPI_OFF || + (len - ISAKMP_DELETE_SPI_OFF) / spisz < nspis) { + log_print("message_validate_delete: SPI count %u exceeds " + "payload length %zu", nspis, len); + message_free(msg); + return -1; + } /* Validate the SPIs. */ for (i = 0; i < nspis; i++) { /* Get ISAKMP SA protecting this message. */ @@ -612,9 +653,11 @@ message_validate_delete(struct message * if (proto == ISAKMP_PROTO_ISAKMP) sa = sa_lookup_isakmp_sa(dst, spis + i * ISAKMP_HDR_COOKIES_LEN); - else - sa = ipsec_sa_lookup(dst, ((u_int32_t *) spis)[i], - proto); + else { + /* Ensure correct alignment of SPI. */ + memcpy(&spi, spis + i * sizeof(spi), sizeof(spi)); + sa = ipsec_sa_lookup(dst, spi, proto); + } if (!sa) { LOG_DBG((LOG_MESSAGE, 50, "message_validate_delete: " "invalid spi (no valid SA found)")); @@ -862,6 +905,15 @@ message_validate_notify(struct message * return -1; } + if (GET_ISAKMP_GEN_LENGTH(p->p) < + ISAKMP_NOTIFY_SPI_OFF + GET_ISAKMP_NOTIFY_SPI_SZ(p->p)) { + LOG_DBG((LOG_MESSAGE, 50, "message_validate_notify: " + "payload too short for SPI size %u", + GET_ISAKMP_NOTIFY_SPI_SZ(p->p))); + message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1); + return -1; + } + /* Validate the SPI. XXX Just ISAKMP for now. */ if (proto == ISAKMP_PROTO_ISAKMP && GET_ISAKMP_NOTIFY_SPI_SZ(p->p) == ISAKMP_HDR_COOKIES_LEN && @@ -960,7 +1012,8 @@ message_validate_sa(struct message *msg, if (zero_test(pkt + ISAKMP_HDR_RCOOKIE_OFF, ISAKMP_HDR_RCOOKIE_LEN)) exchange = exchange_setup_p1(msg, doi_id); - else if (msg->isakmp_sa->flags & SA_FLAG_READY) + else if (msg->isakmp_sa && + (msg->isakmp_sa->flags & SA_FLAG_READY)) exchange = exchange_setup_p2(msg, doi_id); else { /* XXX What to do here? */ @@ -1367,10 +1420,21 @@ message_recv(struct message *msg) message_free(msg); return -1; } + /* + * Encrypted data must be a non-zero multiple of the + * block size. + */ + if (sz - ISAKMP_HDR_SZ == 0 || + (sz - ISAKMP_HDR_SZ) % ks->xf->blocksize != 0) { + log_print("message_recv: bad encrypted message length"); + message_free(msg); + freezero(ks, sizeof(*ks)); + return -1; + } msg->orig = malloc(sz); if (!msg->orig) { message_free(msg); - free(ks); + freezero(ks, sizeof(*ks)); return -1; } memcpy(msg->orig, buf, sz); @@ -1385,7 +1449,7 @@ message_recv(struct message *msg) */ if (GET_ISAKMP_HDR_NEXT_PAYLOAD(buf) != ISAKMP_PAYLOAD_NONE && message_sort_payloads(msg, GET_ISAKMP_HDR_NEXT_PAYLOAD(buf))) { - free(ks); + freezero(ks, sizeof(*ks)); return -1; } /* @@ -1395,7 +1459,7 @@ message_recv(struct message *msg) * XXX Should SAs and even transports be cleaned up then too? */ if (message_validate_payloads(msg)) { - free(ks); + freezero(ks, sizeof(*ks)); return -1; } /* @@ -1405,7 +1469,7 @@ message_recv(struct message *msg) if (!msg->exchange) { log_print("message_recv: no exchange"); message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1); - free(ks); + freezero(ks, sizeof(*ks)); return -1; } /* @@ -1430,7 +1494,7 @@ message_recv(struct message *msg) exch_type); message_drop(msg, ISAKMP_NOTIFY_INVALID_EXCHANGE_TYPE, 0, 1, 1); - free(ks); + freezero(ks, sizeof(*ks)); return -1; } /* Make sure the IV we used gets saved in the proper SA. */ @@ -1439,7 +1503,7 @@ message_recv(struct message *msg) msg->exchange->keystate = ks; msg->exchange->crypto = ks->xf; } else - free(ks); + freezero(ks, sizeof(*ks)); } /* Handle the flags. */ if (flags & ISAKMP_FLAGS_ENC) @@ -1949,6 +2013,17 @@ message_check_duplicate(struct message * message_free(msg); return -1; } + } + /* + * A finalized exchange only answers retransmits of the peer's + * last message. Anything else is not ours. + */ + if (exchange->flags & EXCHANGE_FLAG_FINALIZED) { + LOG_DBG((LOG_MESSAGE, 80, + "message_check_duplicate: ignoring message for finalized " + "exchange %p", exchange)); + message_free(msg); + return -1; } /* * As this new message is an indication that state is moving forward Index: sbin/isakmpd/monitor.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/monitor.c,v diff -u -p -r1.83.14.1 monitor.c --- sbin/isakmpd/monitor.c 3 Oct 2026 17:35:48 -0000 1.83.14.1 +++ sbin/isakmpd/monitor.c 23 Oct 2026 21:52:12 -0000 @@ -517,7 +517,7 @@ static void m_priv_bind(void) { int sock, v, err = 0; - struct sockaddr *name = 0; + struct sockaddr_storage name; socklen_t namelen; sock = mm_receive_fd(m_state.s); @@ -527,22 +527,18 @@ m_priv_bind(void) } must_read(&namelen, sizeof namelen); - name = malloc(namelen); - if (!name) { - log_print("m_priv_bind: malloc failed"); - close(sock); - return; - } - must_read((char *)name, namelen); + if (namelen < sizeof(struct sockaddr) || namelen > sizeof name) + log_fatalx("m_priv_bind: invalid address length"); + bzero(&name, sizeof name); + must_read(&name, namelen); - if (m_priv_check_bind(name, namelen) != 0) { + if (m_priv_check_bind((struct sockaddr *)&name, namelen) != 0) { err = EACCES; v = -1; } else { - v = bind(sock, name, namelen); + v = bind(sock, (struct sockaddr *)&name, namelen); if (v == -1) { - log_error("m_priv_bind: bind(%d,%p,%d) returned %d", - sock, name, namelen, v); + log_error("m_priv_bind: bind(%d)", sock); err = errno; } } @@ -552,9 +548,6 @@ m_priv_bind(void) must_write(&err, sizeof err); must_write(&v, sizeof v); - - free(name); - return; } /* Index: sbin/isakmpd/nat_traversal.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/nat_traversal.c,v diff -u -p -r1.25 nat_traversal.c --- sbin/isakmpd/nat_traversal.c 5 Dec 2017 20:31:45 -0000 1.25 +++ sbin/isakmpd/nat_traversal.c 23 Oct 2026 21:52:12 -0000 @@ -282,7 +282,7 @@ nat_t_add_nat_d(struct message *msg, str buf, buflen, 1); else ret = -1; - + if (ret) { free(buf); return -1; Index: sbin/isakmpd/pf_key_v2.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/pf_key_v2.c,v diff -u -p -r1.205.12.1 pf_key_v2.c --- sbin/isakmpd/pf_key_v2.c 3 Oct 2026 17:35:48 -0000 1.205.12.1 +++ sbin/isakmpd/pf_key_v2.c 23 Oct 2026 21:52:12 -0000 @@ -169,6 +169,10 @@ pf_key_v2_msg_free(struct pf_key_v2_msg { struct pf_key_v2_node *np; + /* Zero all segments. */ + TAILQ_FOREACH(np, msg, link) + explicit_bzero(np->seg, np->sz); + np = TAILQ_FIRST(msg); while (np) { TAILQ_REMOVE(msg, np, link); @@ -288,8 +292,11 @@ pf_key_v2_read(u_int32_t seq) (u_int8_t *) ext - (u_int8_t *) msg < msg->sadb_msg_len * PF_KEY_V2_CHUNK; ext = (struct sadb_ext *) ((u_int8_t *) ext + - ext->sadb_ext_len * PF_KEY_V2_CHUNK)) + ext->sadb_ext_len * PF_KEY_V2_CHUNK)) { + if (ext->sadb_ext_len == 0) + break; pf_key_v2_msg_add(ret, ext, 0); + } /* * If the message is not the one we are waiting for, queue it Index: sbin/isakmpd/policy.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/policy.c,v diff -u -p -r1.103.8.1 policy.c --- sbin/isakmpd/policy.c 3 Oct 2026 17:35:48 -0000 1.103.8.1 +++ sbin/isakmpd/policy.c 23 Oct 2026 21:52:12 -0000 @@ -374,12 +374,17 @@ policy_callback(char *name) GET_ISAKMP_GEN_LENGTH(proto->chosen->p)) return ""; + if (len < sizeof(u_int16_t)) + return ""; + switch (type) { case IPSEC_ATTR_SA_LIFE_TYPE: lifetype = decode_16(value); break; case IPSEC_ATTR_SA_LIFE_DURATION: + if (len != 2 && len != 4) + return ""; switch (proto->proto) { case IPSEC_PROTO_IPSEC_AH: if (lifetype == IPSEC_DURATION_SECONDS) { Index: sbin/isakmpd/prf.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/prf.c,v diff -u -p -r1.16 prf.c --- sbin/isakmpd/prf.c 21 Mar 2013 04:30:14 -0000 1.16 +++ sbin/isakmpd/prf.c 23 Oct 2026 21:52:12 -0000 @@ -114,7 +114,7 @@ prf_alloc(enum prfs type, int subtype, u if (!prfctx->ctx2) { log_error("prf_alloc: malloc (%d) failed", hash->ctxsize); - free(prfctx->ctx); + free(prfctx->ctx); /* not used yet, thus just free */ goto cleanprfctx; } prf->type = PRF_HMAC; @@ -150,8 +150,8 @@ prf_free(struct prf *prf) struct prf_hash_ctx *prfctx = prf->prfctx; if (prf->type == PRF_HMAC) { - free(prfctx->ctx2); - free(prfctx->ctx); + freezero(prfctx->ctx2, prfctx->hash->ctxsize); + freezero(prfctx->ctx, prfctx->hash->ctxsize); } free(prf->prfctx); free(prf); Index: sbin/isakmpd/sa.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/sa.c,v diff -u -p -r1.125.18.1 sa.c --- sbin/isakmpd/sa.c 3 Oct 2026 17:35:48 -0000 1.125.18.1 +++ sbin/isakmpd/sa.c 23 Oct 2026 21:52:12 -0000 @@ -45,6 +45,7 @@ #include "conf.h" #include "connection.h" #include "cookie.h" +#include "crypto.h" #include "doi.h" #include "dpd.h" #include "exchange.h" @@ -566,7 +567,7 @@ sa_release(struct sa *sa) if (sa->policy_id != -1) kn_close(sa->policy_id); free(sa->name); - free(sa->keystate); + freezero(sa->keystate, sizeof(struct keystate)); if (sa->nat_t_keepalive) timer_remove_event(sa->nat_t_keepalive); if (sa->dpd_event) Index: sbin/isakmpd/udp.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/udp.c,v diff -u -p -r1.95.72.1 udp.c --- sbin/isakmpd/udp.c 3 Oct 2026 17:35:48 -0000 1.95.72.1 +++ sbin/isakmpd/udp.c 23 Oct 2026 21:52:12 -0000 @@ -507,12 +507,12 @@ udp_decode_ids(struct transport *t) if (getnameinfo(src, SA_LEN(src), idsrc, sizeof idsrc, NULL, 0, NI_NUMERICHOST) != 0) { log_print("udp_decode_ids: getnameinfo () failed for 'src'"); - strlcpy(idsrc, "", 256); + strlcpy(idsrc, "", sizeof idsrc); } if (getnameinfo(dst, SA_LEN(dst), iddst, sizeof iddst, NULL, 0, NI_NUMERICHOST) != 0) { log_print("udp_decode_ids: getnameinfo () failed for 'dst'"); - strlcpy(iddst, "", 256); + strlcpy(iddst, "", sizeof iddst); } snprintf(result, sizeof result, "src: %s dst: %s", idsrc, iddst); Index: sbin/isakmpd/udp_encap.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/udp_encap.c,v diff -u -p -r1.24.18.1 udp_encap.c --- sbin/isakmpd/udp_encap.c 3 Oct 2026 17:35:48 -0000 1.24.18.1 +++ sbin/isakmpd/udp_encap.c 23 Oct 2026 21:52:12 -0000 @@ -322,6 +322,7 @@ static void udp_encap_handle_message(struct transport *t) { struct udp_transport *u = (struct udp_transport *)t; + struct transport *newt; struct sockaddr_storage from; struct message *msg; u_int32_t len = sizeof from; @@ -351,8 +352,8 @@ udp_encap_handle_message(struct transpor * Make a specialized UDP transport structure out of the incoming * transport and the address information we got from recvfrom(2). */ - t = t->virtual->vtbl->clone(t->virtual, (struct sockaddr *)&from); - if (!t) + newt = t->virtual->vtbl->clone(t->virtual, (struct sockaddr *)&from); + if (!newt) return; /* Check NULL-ESP marker. */ @@ -360,18 +361,22 @@ udp_encap_handle_message(struct transpor /* Should never happen. */ log_print("udp_encap_handle_message: " "Null-ESP marker not NULL or short message"); + newt->vtbl->remove(newt); return; } /* NAT-Keepalive messages should not be processed further. */ n -= sizeof(u_int32_t); - if (n == 1 && buf[sizeof(u_int32_t)] == 0xFF) + if (n == 1 && buf[sizeof(u_int32_t)] == 0xFF) { + newt->vtbl->remove(newt); return; + } - msg = message_alloc(t, buf + sizeof (u_int32_t), n); + msg = message_alloc(newt, buf + sizeof (u_int32_t), n); if (!msg) { log_error("failed to allocate message structure, dropping " "packet received on transport %p", u); + newt->vtbl->remove(newt); return; } Index: sbin/isakmpd/x509.c =================================================================== RCS file: /cvs/src/sbin/isakmpd/x509.c,v diff -u -p -r1.128 x509.c --- sbin/isakmpd/x509.c 5 Dec 2025 19:35:40 -0000 1.128 +++ sbin/isakmpd/x509.c 23 Oct 2026 21:52:12 -0000 @@ -422,7 +422,7 @@ x509_generate_kn(int id, X509 *cert) "failed to allocate memory for KeyNote credential"); goto fail; } - + free(ikey); ikey = NULL; free(skey); @@ -1279,12 +1279,15 @@ x509_cert_get_key(void *scert, void *key X509 *cert = scert; EVP_PKEY *key; - key = X509_get_pubkey(cert); + key = X509_get0_pubkey(cert); + if (key == NULL) { + log_print("x509_cert_get_key: failed to get public key"); + return 0; + } /* Check if we got the right key type. */ if (EVP_PKEY_id(key) != EVP_PKEY_RSA) { log_print("x509_cert_get_key: public key is not a RSA key"); - X509_free(cert); return 0; } *(RSA **)keyp = RSAPublicKey_dup(EVP_PKEY_get0_RSA(key));