aglen > 16 + || EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, (int)ec->taglen, ec->tag) <= 0) { ERR_raise(ERR_LIB_CMS, CMS_R_CIPHER_AEAD_SET_TAG_ERROR); goto err; } + } else if (auth) { + ERR_raise(ERR_LIB_CMS, CMS_R_UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM); + goto err; } } len = EVP_CIPHER_CTX_get_key_length(ctx); diff --git a/crypto/openssl/crypto/cms/cms_env.c b/crypto/openssl/crypto/cms/cms_env.c index 0828d157fad6..70dd59c06169 100644 --- a/crypto/openssl/crypto/cms/cms_env.c +++ b/crypto/openssl/crypto/cms/cms_env.c @@ -619,13 +619,6 @@ static int cms_RecipientInfo_ktri_decrypt(CMS_ContentInfo *cms, if (!ossl_cms_env_asn1_ctrl(ri, 1)) goto err; - if (EVP_PKEY_is_a(pkey, "RSA")) - /* upper layer CMS code incorrectly assumes that a successful RSA - * decryption means that the key matches ciphertext (which never - * was the case, implicit rejection or not), so to make it work - * disable implicit rejection for RSA keys */ - EVP_PKEY_CTX_ctrl_str(ktri->pctx, "rsa_pkcs1_implicit_rejection", "0"); - if (evp_pkey_decrypt_alloc(ktri->pctx, &ek, &eklen, fixlen, ktri->encryptedKey->data, ktri->encryptedKey->length) diff --git a/crypto/openssl/crypto/cms/cms_pwri.c b/crypto/openssl/crypto/cms/cms_pwri.c index d62dbbde881b..faf6a164669b 100644 --- a/crypto/openssl/crypto/cms/cms_pwri.c +++ b/crypto/openssl/crypto/cms/cms_pwri.c @@ -200,18 +200,18 @@ static int kek_unwrap_key(unsigned char *out, size_t *outlen, const unsigned char *in, size_t inlen, EVP_CIPHER_CTX *ctx) { - size_t blocklen = EVP_CIPHER_CTX_get_block_size(ctx); + int blocklen = EVP_CIPHER_CTX_get_block_size(ctx); unsigned char *tmp; int outl, rv = 0; - if (blocklen == 0) + if (blocklen < 4) return 0; - if (inlen < 2 * blocklen) { + if (inlen < 2 * (size_t)blocklen) { /* too small */ return 0; } - if (inlen % blocklen) { + if (inlen > INT_MAX || inlen % blocklen) { /* Invalid size */ return 0; } @@ -367,6 +367,11 @@ int ossl_cms_RecipientInfo_pwri_crypt(const CMS_ContentInfo *cms, /* Finish password based key derivation to setup key in "ctx" */ + if (algtmp == NULL) { + ERR_raise_data(ERR_LIB_CMS, CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER, + "Missing KeyDerivationAlgorithm"); + goto err; + } if (!EVP_PBE_CipherInit_ex(algtmp->algorithm, (char *)pwri->pass, (int)pwri->passlen, algtmp->parameter, kekctx, en_de, diff --git a/crypto/openssl/crypto/crmf/crmf_lib.c b/crypto/openssl/crypto/crmf/crmf_lib.c index d5c8983b2fd4..34477d52662d 100644 --- a/crypto/openssl/crypto/crmf/crmf_lib.c +++ b/crypto/openssl/crypto/crmf/crmf_lib.c @@ -766,6 +766,7 @@ unsigned char *OSSL_CRMF_ENCRYPTEDVALUE_decrypt(const OSSL_CRMF_ENCRYPTEDVALUE * EVP_CIPHER *cipher = NULL; /* used cipher */ int cikeysize = 0; /* key size from cipher */ unsigned char *iv = NULL; /* initial vector for symmetric encryption */ + int iv_len; /* iv length */ unsigned char *out = NULL; /* decryption output buffer */ int n, ret = 0; EVP_PKEY_CTX *pkctx = NULL; /* private key context */ @@ -820,11 +821,12 @@ unsigned char *OSSL_CRMF_ENCRYPTEDVALUE_decrypt(const OSSL_CRMF_ENCRYPTEDVALUE * } else { goto end; } - if ((iv = OPENSSL_malloc(EVP_CIPHER_get_iv_length(cipher))) == NULL) + iv_len = EVP_CIPHER_get_iv_length(cipher); + if ((iv = OPENSSL_malloc(iv_len)) == NULL) goto end; - if (ASN1_TYPE_get_octetstring(enc->symmAlg->parameter, iv, - EVP_CIPHER_get_iv_length(cipher)) - != EVP_CIPHER_get_iv_length(cipher)) { + if (enc->symmAlg->parameter == NULL + || ASN1_TYPE_get_octetstring(enc->symmAlg->parameter, iv, iv_len) + != iv_len) { ERR_raise(ERR_LIB_CRMF, CRMF_R_MALFORMED_IV); goto end; } diff --git a/crypto/openssl/crypto/pkcs12/p12_mutl.c b/crypto/openssl/crypto/pkcs12/p12_mutl.c index 01956252df76..15072e12f26b 100644 --- a/crypto/openssl/crypto/pkcs12/p12_mutl.c +++ b/crypto/openssl/crypto/pkcs12/p12_mutl.c @@ -144,11 +144,13 @@ static int PBMAC1_PBKDF2_HMAC(OSSL_LIB_CTX *ctx, const char *propq, } pbkdf2_salt = pbkdf2_param->salt->value.octet_string; - /* RFC 9579 specifies missing key length as invalid */ + /* RFC 9879 specifies missing key length as invalid */ if (pbkdf2_param->keylength != NULL) keylen = ASN1_INTEGER_get(pbkdf2_param->keylength); - if (keylen <= 0 || keylen > EVP_MAX_MD_SIZE) { - ERR_raise(ERR_LIB_PKCS12, PKCS12_R_PARSE_ERROR); + /* RFC 9879 specifies too short key length as untrustworthy too */ + if (keylen < 20 || keylen > EVP_MAX_MD_SIZE) { + ERR_raise_data(ERR_LIB_PKCS12, PKCS12_R_PARSE_ERROR, + "Invalid Key length (%d is not in the range 20..64)", keylen); goto err; } diff --git a/crypto/openssl/crypto/pkcs7/pk7_doit.c b/crypto/openssl/crypto/pkcs7/pk7_doit.c index d6513cf3a379..1ec7895fc197 100644 --- a/crypto/openssl/crypto/pkcs7/pk7_doit.c +++ b/crypto/openssl/crypto/pkcs7/pk7_doit.c @@ -203,13 +203,6 @@ static int pkcs7_decrypt_rinfo(unsigned char **pek, int *peklen, if (EVP_PKEY_decrypt_init(pctx) <= 0) goto err; - if (EVP_PKEY_is_a(pkey, "RSA")) - /* upper layer pkcs7 code incorrectly assumes that a successful RSA - * decryption means that the key matches ciphertext (which never - * was the case, implicit rejection or not), so to make it work - * disable implicit rejection for RSA keys */ - EVP_PKEY_CTX_ctrl_str(pctx, "rsa_pkcs1_implicit_rejection", "0"); - ret = evp_pkey_decrypt_alloc(pctx, &ek, &eklen, fixlen, ri->enc_key->data, ri->enc_key->length); if (ret <= 0) diff --git a/crypto/openssl/crypto/pkcs7/pk7_smime.c b/crypto/openssl/crypto/pkcs7/pk7_smime.c index 97f20058979f..dc003ee2affd 100644 --- a/crypto/openssl/crypto/pkcs7/pk7_smime.c +++ b/crypto/openssl/crypto/pkcs7/pk7_smime.c @@ -222,6 +222,7 @@ int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store, int i, j = 0, k, ret = 0; BIO *p7bio = NULL; BIO *tmpout = NULL; + BIO *next = NULL; const PKCS7_CTX *p7_ctx; if (p7 == NULL) { @@ -352,9 +353,11 @@ err: BIO_free(tmpout); X509_STORE_CTX_free(cert_ctx); OPENSSL_free(buf); - if (indata != NULL) - BIO_pop(p7bio); - BIO_free_all(p7bio); + while (p7bio != NULL && p7bio != indata) { + next = BIO_pop(p7bio); + BIO_free(p7bio); + p7bio = next; + } sk_X509_free(signers); sk_X509_free(untrusted); return ret; diff --git a/crypto/openssl/doc/man3/CMS_decrypt.pod b/crypto/openssl/doc/man3/CMS_decrypt.pod index 121b74a30a10..66a94287b6f5 100644 --- a/crypto/openssl/doc/man3/CMS_decrypt.pod +++ b/crypto/openssl/doc/man3/CMS_decrypt.pod @@ -68,7 +68,7 @@ then the above behaviour is modified and an error B returned if no recipient encrypted key can be decrypted B generating a random content encryption key. Applications should use this flag with B especially in automated gateways as it can leave them -open to attack. +open to attack. See L for more details. It is possible to determine the correct recipient key by other means (for example looking them up in a database) and setting them in the CMS structure @@ -103,7 +103,7 @@ mentioned in CMS_verify() also applies to CMS_decrypt(). =head1 SEE ALSO -L, L +L, L, L =head1 HISTORY diff --git a/crypto/openssl/doc/man3/PKCS7_decrypt.pod b/crypto/openssl/doc/man3/PKCS7_decrypt.pod index aea15937ab86..cfb5b3f87376 100644 --- a/crypto/openssl/doc/man3/PKCS7_decrypt.pod +++ b/crypto/openssl/doc/man3/PKCS7_decrypt.pod @@ -22,6 +22,14 @@ B is an optional set of flags. Although the recipients certificate is not needed to decrypt the data it is needed to locate the appropriate (of possible several) recipients in the PKCS#7 structure. +When RSA PKCS#1 v1.5 Key Transport is in use, the invoked EVP_PKEY_decrypt() +will use implicit rejection mechanism. It always returns the result of RSA +decryption of the symmetric key to avoid Marvin attack. This result is +deterministic and can happen to match the symmetric cipher used for the content +encryption. In case when the certificate is not provided, the last +RecipientInfo producing the key looking valid will be used. It may cause +getting garbage content on decryption. + The following flags can be passed in the B parameter. If the B flag is set MIME headers for type B are deleted @@ -43,7 +51,7 @@ mentioned in PKCS7_sign() also applies to PKCS7_verify(). =head1 SEE ALSO -L, L +L, L, L =head1 COPYRIGHT diff --git a/crypto/openssl/include/internal/quic_cfq.h b/crypto/openssl/include/internal/quic_cfq.h index 0b2a3a4cb2d6..96c8d89eb600 100644 --- a/crypto/openssl/include/internal/quic_cfq.h +++ b/crypto/openssl/include/internal/quic_cfq.h @@ -149,6 +149,7 @@ QUIC_CFQ_ITEM *ossl_quic_cfq_get_priority_head(const QUIC_CFQ *cfq, QUIC_CFQ_ITEM *ossl_quic_cfq_item_get_priority_next(const QUIC_CFQ_ITEM *item, uint32_t pn_space); +int ossl_quic_cfq_discard_unreliable(QUIC_CFQ *cfq, QUIC_CFQ_ITEM *item); #endif #endif diff --git a/crypto/openssl/include/internal/quic_channel.h b/crypto/openssl/include/internal/quic_channel.h index b917b966abeb..cfaeab728178 100644 --- a/crypto/openssl/include/internal/quic_channel.h +++ b/crypto/openssl/include/internal/quic_channel.h @@ -468,6 +468,7 @@ int ossl_quic_bind_channel(QUIC_CHANNEL *ch, const BIO_ADDR *peer, const QUIC_CONN_ID *scid, const QUIC_CONN_ID *dcid, const QUIC_CONN_ID *odcid); +void ossl_ch_reset_rx_state(QUIC_CHANNEL *ch); #endif #endif diff --git a/crypto/openssl/include/internal/quic_fifd.h b/crypto/openssl/include/internal/quic_fifd.h index 4ea7a2e0d226..afa330cbc4a2 100644 --- a/crypto/openssl/include/internal/quic_fifd.h +++ b/crypto/openssl/include/internal/quic_fifd.h @@ -83,6 +83,7 @@ int ossl_quic_fifd_pkt_commit(QUIC_FIFD *fifd, QUIC_TXPIM_PKT *pkt); void ossl_quic_fifd_set_qlog_cb(QUIC_FIFD *fifd, QLOG *(*get_qlog_cb)(void *arg), void *arg); +void ossl_quic_fifd_pkt_discard_unreliable(QUIC_FIFD *fifd, QUIC_TXPIM_PKT *tpkt); #endif #endif diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c index d0b6ae4b070d..5bdc567b4bb1 100644 --- a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c @@ -58,6 +58,9 @@ static int aes_gcm_siv_initkey(void *vctx) memset(&data, 0, sizeof(data)); memcpy(&data.block[sizeof(data.counter)], ctx->nonce, NONCE_SIZE); + ctx->generated_tag = 0; + memset(ctx->tag, 0, TAG_SIZE); + /* msg_auth_key is always 16 bytes in size, regardless of AES128/AES256 */ /* counter is stored little-endian */ for (i = 0; i < BLOCK_SIZE; i += 8) { @@ -134,17 +137,6 @@ static int aes_gcm_siv_aad(PROV_AES_GCM_SIV_CTX *ctx, return 1; } -static int aes_gcm_siv_finish(PROV_AES_GCM_SIV_CTX *ctx) -{ - int ret = 0; - - if (ctx->enc) - return ctx->generated_tag; - ret = !CRYPTO_memcmp(ctx->tag, ctx->user_tag, sizeof(ctx->tag)); - ret &= ctx->have_user_tag; - return ret; -} - static int aes_gcm_siv_encrypt(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *in, unsigned char *out, size_t len) { @@ -271,6 +263,19 @@ static int aes_gcm_siv_decrypt(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *i return !error; } +static int aes_gcm_siv_finish(PROV_AES_GCM_SIV_CTX *ctx) +{ + int ret = 0; + + if (ctx->enc) + return ctx->generated_tag; + if (!ctx->generated_tag) + aes_gcm_siv_decrypt(ctx, NULL, NULL, 0); + ret = !CRYPTO_memcmp(ctx->tag, ctx->user_tag, sizeof(ctx->tag)); + ret &= ctx->have_user_tag; + return ret; +} + static int aes_gcm_siv_cipher(void *vctx, unsigned char *out, const unsigned char *in, size_t len) { diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c index b724c425e392..99254cb49a88 100644 --- a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c @@ -514,6 +514,19 @@ static int aes_ocb_cipher(void *vctx, unsigned char *out, size_t *outl, return 0; } + /* + * Mirror the streaming handler: refuse if the key has not been set, + * and push the buffered IV into the OCB context before any data is + * processed. Without this, CRYPTO_ocb128_encrypt/decrypt runs with + * Offset_0 = 0 regardless of the caller's IV -- catastrophic + * (key, nonce) reuse, and a subsequent EVP_*Final_ex() emits a tag + * that is a function of (key, iv) only. + */ + if (!ctx->key_set || !update_iv(ctx)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + if (!aes_generic_ocb_cipher(ctx, in, out, inl)) { ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); return 0; diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.c index 96f26757abe2..754e0757cda3 100644 --- a/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.c +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.c @@ -192,6 +192,7 @@ static int aes_siv_set_ctx_params(void *vctx, const OSSL_PARAM params[]) PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx; const OSSL_PARAM *p; unsigned int speed = 0; + SIV128_CONTEXT *sctx = &ctx->siv; if (ossl_param_is_empty(params)) return 1; @@ -226,6 +227,8 @@ static int aes_siv_set_ctx_params(void *vctx, const OSSL_PARAM params[]) if (keylen != ctx->keylen) return 0; } + sctx->final_ret = -1; + return 1; } diff --git a/crypto/openssl/providers/implementations/exchange/dh_exch.c b/crypto/openssl/providers/implementations/exchange/dh_exch.c index 94d4254ed5d2..2bfefc0aedf4 100644 --- a/crypto/openssl/providers/implementations/exchange/dh_exch.c +++ b/crypto/openssl/providers/implementations/exchange/dh_exch.c @@ -146,12 +146,15 @@ static int dh_init(void *vpdhctx, void *vdh, const OSSL_PARAM params[]) static int dh_match_params(DH *priv, DH *peer) { int ret; + int ignore_q = 1; FFC_PARAMS *dhparams_priv = ossl_dh_get0_params(priv); FFC_PARAMS *dhparams_peer = ossl_dh_get0_params(peer); + if (dhparams_priv != NULL && dhparams_priv->q != NULL) + ignore_q = 0; ret = dhparams_priv != NULL && dhparams_peer != NULL - && ossl_ffc_params_cmp(dhparams_priv, dhparams_peer, 1); + && ossl_ffc_params_cmp(dhparams_priv, dhparams_peer, ignore_q); if (!ret) ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS); return ret; diff --git a/crypto/openssl/ssl/quic/quic_cfq.c b/crypto/openssl/ssl/quic/quic_cfq.c index 3c59234ff0ff..16818e55f57d 100644 --- a/crypto/openssl/ssl/quic/quic_cfq.c +++ b/crypto/openssl/ssl/quic/quic_cfq.c @@ -7,6 +7,7 @@ * https://www.openssl.org/source/license.html */ +#include "internal/quic_channel.h" #include "internal/quic_cfq.h" #include "internal/numbers.h" @@ -307,6 +308,20 @@ void ossl_quic_cfq_mark_lost(QUIC_CFQ *cfq, QUIC_CFQ_ITEM *item, } } +int ossl_quic_cfq_discard_unreliable(QUIC_CFQ *cfq, QUIC_CFQ_ITEM *item) +{ + int discarded; + + if (ossl_quic_cfq_item_is_unreliable(item)) { + ossl_quic_cfq_release(cfq, item); + discarded = 1; + } else { + discarded = 0; + } + + return discarded; +} + /* * Releases a CFQ item. The item may be in either state (NEW or TX) prior to the * call. The QUIC_CFQ_ITEM pointer must not be used following this call. diff --git a/crypto/openssl/ssl/quic/quic_channel.c b/crypto/openssl/ssl/quic/quic_channel.c index 13692e5bd09e..5f81a8560d5f 100644 --- a/crypto/openssl/ssl/quic/quic_channel.c +++ b/crypto/openssl/ssl/quic/quic_channel.c @@ -2213,6 +2213,12 @@ static void ch_rx_check_forged_pkt_limit(QUIC_CHANNEL *ch) "forgery limit"); } +void ossl_ch_reset_rx_state(QUIC_CHANNEL *ch) +{ + ch->did_crypto_frame = 0; + ch->seen_path_challenge = 0; +} + /* Process queued incoming packets and handle frames, if any. */ static int ch_rx(QUIC_CHANNEL *ch, int channel_only, int *notify_other_threads) { diff --git a/crypto/openssl/ssl/quic/quic_channel_local.h b/crypto/openssl/ssl/quic/quic_channel_local.h index ae443fccca1e..e40b4901cbc7 100644 --- a/crypto/openssl/ssl/quic/quic_channel_local.h +++ b/crypto/openssl/ssl/quic/quic_channel_local.h @@ -12,6 +12,28 @@ #include "internal/quic_stream_map.h" #include "internal/quic_tls.h" +/* + * This is a part of PATH_CHALLENGE flood [1] mitigation. This limits the + * number of PATH_CHALLENGE frames QUIC stack is willing to process for + * connection. Local QUIC stack creates PATH_RESPONSE frame for PATH_CHALLENGE + * frame it receives from remote peer. The response frame is put Control Frame + * Queue waiting to be dispatched. The PATH_RESPONSE frame is removed from CFQ + * after it is dispatched. The QUIC_PATH_RESPONSE_QLEN limits the number of + * PATH_RESPONSE frames waiting to be dispatched. No new PATH_RESPONSE frames + * are inserted into CFQ if queue limit is exceeded. + * + * QUIC implementations use different limits for PATH_RESPONSE queue lengths: + * quic-go defines maxPathResponses as 256 + * quiche from cloadflare sets DEFAULT_MAX_PATH_CHALLENGE_RX_QUEUE_LEN to 3 + * t-quic from tencent chooses MAX_PATH_CHALS_RECV to be 8 + * + * OpenSSL here introduces QUIC_PATH_RESPONSE_QLEN as 32. + * + * [1] https://www.ietf.org/archive/id/draft-chen-quic-logical-vuln-mitigations-00.txt + * (section 4.2) + */ +#define QUIC_PATH_RESPONSE_QLEN 32 + /* * QUIC Channel Structure * ====================== @@ -457,6 +479,18 @@ struct quic_channel_st { /* Has qlog been requested? */ unsigned int is_tserver_ch : 1; + /* + * RFC 9000 Section 9.2.1 says: + * However, an endpoint SHOULD NOT send multiple + * PATH_CHALLENGE frames in a single packet. + * The counter here allows us to detect multiple presence + * of PATH_CHALLENGE frame in packet. We process only the + * first PATH_CHALLENGE frame found in packet. Remaining PATH_CHALLENGE + * frames are ignored. + * seen_path_challenge flag is always reset before + * ossl_quic_handle_frames() gets called. + */ + unsigned int seen_path_challenge : 1; /* Saved error stack in case permanent error was encountered */ ERR_STATE *err_state; @@ -467,6 +501,11 @@ struct quic_channel_st { /* Title for qlog purposes. We own this copy. */ char *qlog_title; + /* + * number of path responses waiting to be dispatched + * from control frame queue (CFQ) + */ + unsigned int path_response_limit; }; #endif diff --git a/crypto/openssl/ssl/quic/quic_fifd.c b/crypto/openssl/ssl/quic/quic_fifd.c index 03b8cebd3057..e80483b501d7 100644 --- a/crypto/openssl/ssl/quic/quic_fifd.c +++ b/crypto/openssl/ssl/quic/quic_fifd.c @@ -310,3 +310,46 @@ void ossl_quic_fifd_set_qlog_cb(QUIC_FIFD *fifd, QLOG *(*get_qlog_cb)(void *arg) fifd->get_qlog_cb = get_qlog_cb; fifd->get_qlog_cb_arg = get_qlog_cb_arg; } + +static void txpim_pkt_remove_cfq_item(QUIC_TXPIM_PKT *pkt, QUIC_CFQ_ITEM *cfq_item) +{ + QUIC_CFQ_ITEM *prev = cfq_item->pkt_prev; + + if (prev != NULL) { + prev->pkt_next = cfq_item->pkt_next; + } else { + pkt->retx_head = cfq_item->pkt_next; + } + + if (cfq_item->pkt_next != NULL) + cfq_item->pkt_next->pkt_prev = prev; + + cfq_item->pkt_prev = NULL; + cfq_item->pkt_next = NULL; +} + +void ossl_quic_fifd_pkt_discard_unreliable(QUIC_FIFD *fifd, QUIC_TXPIM_PKT *pkt) +{ + QUIC_CFQ_ITEM *cfq_item, *cfq_next; + + /* + * The packet has been written to network. We can discard frames we don't + * retransmit when loss is detected. + */ + cfq_item = pkt->retx_head; + while (cfq_item != NULL) { + /* + * Discarded items are moved to free list. If item + * got moved to free list we must also remove it from + * cfq list kept in pkt, so ACKM does not find it when + * receives an ACK for pkt. + */ + if (ossl_quic_cfq_discard_unreliable(fifd->cfq, cfq_item)) { + cfq_next = cfq_item->pkt_next; + txpim_pkt_remove_cfq_item(pkt, cfq_item); + cfq_item = cfq_next; + } else { + cfq_item = cfq_item->pkt_next; + } + } +} diff --git a/crypto/openssl/ssl/quic/quic_port.c b/crypto/openssl/ssl/quic/quic_port.c index 1e247e1ec624..dc79485b96a5 100644 --- a/crypto/openssl/ssl/quic/quic_port.c +++ b/crypto/openssl/ssl/quic/quic_port.c @@ -1666,8 +1666,10 @@ static void port_default_packet_handler(QUIC_URXE *e, void *arg, * forget qrx so channel can create a new one * with valid initial encryption level keys. */ - qrx_src = qrx; - qrx = NULL; + if (qrx != NULL) { + qrx_src = qrx; + qrx = NULL; + } } port_bind_channel(port, &e->peer, &scid, &hdr.dst_conn_id, diff --git a/crypto/openssl/ssl/quic/quic_rx_depack.c b/crypto/openssl/ssl/quic/quic_rx_depack.c index 786af9b4c221..1bdb43b7d639 100644 --- a/crypto/openssl/ssl/quic/quic_rx_depack.c +++ b/crypto/openssl/ssl/quic/quic_rx_depack.c @@ -931,6 +931,12 @@ static int depack_do_frame_retire_conn_id(PACKET *pkt, static void free_path_response(unsigned char *buf, size_t buf_len, void *arg) { + QUIC_CHANNEL *ch = (QUIC_CHANNEL *)arg; + + assert(ch->path_response_limit > 0); + + ch->path_response_limit--; + OPENSSL_free(buf); } @@ -951,33 +957,39 @@ static int depack_do_frame_path_challenge(PACKET *pkt, return 0; } - /* - * RFC 9000 s. 8.2.2: On receiving a PATH_CHALLENGE frame, an endpoint MUST - * respond by echoing the data contained in the PATH_CHALLENGE frame in a - * PATH_RESPONSE frame. - * - * TODO(QUIC FUTURE): We should try to avoid allocation here in the future. - */ - encoded_len = sizeof(uint64_t) + 1; - if ((encoded = OPENSSL_malloc(encoded_len)) == NULL) - goto err; + if (ch->seen_path_challenge == 0 + && ch->path_response_limit < QUIC_PATH_RESPONSE_QLEN) { + /* + * RFC 9000 s. 8.2.2: On receiving a PATH_CHALLENGE frame, an endpoint + * MUST respond by echoing the data contained in the PATH_CHALLENGE + * frame in a PATH_RESPONSE frame. + * + * TODO(QUIC FUTURE): We should try to avoid allocation here in the + * future. + */ + encoded_len = sizeof(uint64_t) + 1; + if ((encoded = OPENSSL_malloc(encoded_len)) == NULL) + goto err; - if (!WPACKET_init_static_len(&wpkt, encoded, encoded_len, 0)) - goto err; + if (!WPACKET_init_static_len(&wpkt, encoded, encoded_len, 0)) + goto err; - if (!ossl_quic_wire_encode_frame_path_response(&wpkt, frame_data)) { - WPACKET_cleanup(&wpkt); - goto err; - } + if (!ossl_quic_wire_encode_frame_path_response(&wpkt, frame_data)) { + WPACKET_cleanup(&wpkt); + goto err; + } - WPACKET_finish(&wpkt); + WPACKET_finish(&wpkt); - if (!ossl_quic_cfq_add_frame(ch->cfq, 0, QUIC_PN_SPACE_APP, - OSSL_QUIC_FRAME_TYPE_PATH_RESPONSE, - QUIC_CFQ_ITEM_FLAG_UNRELIABLE, - encoded, encoded_len, - free_path_response, NULL)) - goto err; + if (!ossl_quic_cfq_add_frame(ch->cfq, 0, QUIC_PN_SPACE_APP, + OSSL_QUIC_FRAME_TYPE_PATH_RESPONSE, + QUIC_CFQ_ITEM_FLAG_UNRELIABLE, + encoded, encoded_len, + free_path_response, ch)) + goto err; + ch->seen_path_challenge = 1; + ch->path_response_limit++; + } return 1; @@ -1432,7 +1444,7 @@ int ossl_quic_handle_frames(QUIC_CHANNEL *ch, OSSL_QRX_PKT *qpacket) if (ch == NULL) return 0; - ch->did_crypto_frame = 0; + ossl_ch_reset_rx_state(ch); /* Initialize |ackm_data| (and reinitialize |ok|)*/ memset(&ackm_data, 0, sizeof(ackm_data)); diff --git a/crypto/openssl/ssl/quic/quic_txp.c b/crypto/openssl/ssl/quic/quic_txp.c index 44aaad868d2f..b2565c1a9fee 100644 --- a/crypto/openssl/ssl/quic/quic_txp.c +++ b/crypto/openssl/ssl/quic/quic_txp.c @@ -3133,6 +3133,8 @@ static int txp_pkt_commit(OSSL_QUIC_TX_PACKETISER *txp, --probe_info->pto[pn_space]; } + ossl_quic_fifd_pkt_discard_unreliable(&txp->fifd, tpkt); + return rc; } diff --git a/crypto/openssl/test/cmsapitest.c b/crypto/openssl/test/cmsapitest.c index 0752d14df09c..d908bc6fc4c4 100644 --- a/crypto/openssl/test/cmsapitest.c +++ b/crypto/openssl/test/cmsapitest.c @@ -21,6 +21,7 @@ static X509 *cert = NULL; static EVP_PKEY *privkey = NULL; static char *derin = NULL; static char *too_long_iv_cms_in = NULL; +static char *pwri_kek_oob_der_in = NULL; static int test_encrypt_decrypt(const EVP_CIPHER *cipher) { @@ -512,7 +513,48 @@ end: return ret; } -OPT_TEST_DECLARE_USAGE("certfile privkeyfile derfile\n") +/* + * CMS EnvelopedData with a single PasswordRecipientInfo using + * id-alg-PWRI-KEK and an AES-128-CFB key encryption cipher + * (1-byte effective block size). The encryptedKey OCTET STRING is + * only two bytes long, so the wrapped key buffer is shorter than + * the seven octets read by the check-byte test in kek_unwrap_key(). + * Prior to CVE-2026-9076 this triggered an out-of-bounds heap read; + * CMS_decrypt() must now fail cleanly. + */ +static int test_pwri_kek_unwrap_short_encrypted_key(void) +{ + BIO *in = NULL; + CMS_ContentInfo *cms = NULL; + unsigned long err = 0; + int ret = 0; + + if (!TEST_ptr(in = BIO_new_file(pwri_kek_oob_der_in, "rb")) + || !TEST_ptr(cms = d2i_CMS_bio(in, NULL))) + goto end; + + /* + * The unwrap is attempted eagerly inside CMS_decrypt_set1_password(). + * It must fail cleanly (no OOB read) and report CMS_R_UNWRAP_FAILURE. + */ + if (!TEST_false(CMS_decrypt_set1_password(cms, + (unsigned char *)"password", -1))) + goto end; + + err = ERR_peek_last_error(); + if (!TEST_int_eq(ERR_GET_LIB(err), ERR_LIB_CMS) + || !TEST_int_eq(ERR_GET_REASON(err), CMS_R_UNWRAP_FAILURE)) + goto end; + + ERR_clear_error(); + ret = 1; +end: + CMS_ContentInfo_free(cms); + BIO_free(in); + return ret; +} + +OPT_TEST_DECLARE_USAGE("certfile privkeyfile derfile tooLongIVpem pwriKekOobDer\n") int setup_tests(void) { @@ -527,7 +569,8 @@ int setup_tests(void) if (!TEST_ptr(certin = test_get_argument(0)) || !TEST_ptr(privkeyin = test_get_argument(1)) || !TEST_ptr(derin = test_get_argument(2)) - || !TEST_ptr(too_long_iv_cms_in = test_get_argument(3))) + || !TEST_ptr(too_long_iv_cms_in = test_get_argument(3)) + || !TEST_ptr(pwri_kek_oob_der_in = test_get_argument(4))) *** 235 LINES SKIPPED ***