From nobody Tue Jun 9 19:17:54 2026 X-Original-To: dev-commits-src-branches@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4gZdwL4G46z6gTyV for ; Tue, 09 Jun 2026 19:17:54 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R13" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4gZdwL1hWcz3LyH for ; Tue, 09 Jun 2026 19:17:54 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781032674; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Y9fXkiGYScb6EJYTur9kyzRngpFcy6OOMIXUsUi30NE=; b=qFvIHEi9K6zvTjCfP/aen5s8ARUTv4JRM8QgOyiyt4gOkIBSoMEhZm/CL3nCPjF96miKtz 2b5Sy8HMfiu+px06Powu70mHMV9igSiVuShXv3IQJDY12lRK/d3bukUjuSQi5W4R5O9s8c 0HLsq7dBH69FdhRu2XHN+T06bPLkp8YO2NkiKLKU93YnDas3L9J0zPvkZby+JUR7nik5dy Eb+0vfcQOi0bWfRGF7Omjd7if92r1glA+18LYkQa3f20rDxgKdHxYAjmyCTUbqruxEpH4L zTrSASdMSGw9rk/9lGc+pTak68V6sm/j+SMLnuB1wPfl0idEvGCZKui1d8ZppQ== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1781032674; a=rsa-sha256; cv=none; b=iQtw8AbqKjTjq/2K+EFPYKVyHXHRaOKic7cCpH+MtH8tEc0vtMEkALosuprCmQSZClKKPD FxarM8EN0FbRBP1ZbArgYocutGB4QHjKc4lJ9OUZ0SxopXfOyRGETLY6WQvcRrI+cD8srN MPDyw6hyrhEz+uOKPlSTJd2VI4KKHLLsb8htrC5drms7g1C0xu8XSV2YqVvMcH3NqqjJWO 4kXCAe3WdVfk+Ho5mgrZv6KuwB57s9Zxu8Pele/rIJt78Q3LRR/DsQG7hImuAIrA5UxdiO Mzbtgw8PBEjObcTzO8BzjI6ZKpWA4LTygjPQHzB0Vw33B/BzMfHtof+mxrrKOA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781032674; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Y9fXkiGYScb6EJYTur9kyzRngpFcy6OOMIXUsUi30NE=; b=Lo3zacPuldPBTWX98iDvetVke84WiirBRXic+3x1Sd7lLSq1g9Rx36S6jQ3RdCElBZ3BMD Ch2b3RD6+XeJLw2F7nX9Nj7pgdu9+pVZ8SBu2t9KePA4XtDb7fqL1VSabxWJMwkZP/kGq9 5awE4Kk8JsdNv7khIHrgIpCTLbNk+Dqk4Jf2FJkzGuPuaPCqAVE9dchoOZPpRxCjKiTNvQ I1si0tbMTdTD5DHzoxtCdTbvVdLWbwBxHn3v2bMkXkCtbhxOuHXZ7O0parretU3SmaBg7T 33AlFdaovZQx4D81ktwOHDVLuW4pi+YcNDPNtVcmFjrklQMx7LqnAeAYFSs1hQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gZdwL1FfpznFq for ; Tue, 09 Jun 2026 19:17:54 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3e5ae by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 09 Jun 2026 19:17:54 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org Cc: Gordon Tetlow From: Mark Johnston Subject: git: ec6bfa889b83 - stable/14 - openssl: Fix multiple vulnerabilities List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-branches@freebsd.org Sender: owner-dev-commits-src-branches@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: markj X-Git-Repository: src X-Git-Refname: refs/heads/stable/14 X-Git-Reftype: branch X-Git-Commit: ec6bfa889b839645961113344186b85ed8477f48 Auto-Submitted: auto-generated Date: Tue, 09 Jun 2026 19:17:54 +0000 Message-Id: <6a2866e2.3e5ae.3e394f0d@gitrepo.freebsd.org> The branch stable/14 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=ec6bfa889b839645961113344186b85ed8477f48 commit ec6bfa889b839645961113344186b85ed8477f48 Author: Gordon Tetlow AuthorDate: 2026-06-07 03:24:17 +0000 Commit: Mark Johnston CommitDate: 2026-06-09 19:15:22 +0000 openssl: Fix multiple vulnerabilities This is a rollup commit from upstream to fix: Reject oversized inputs in ASN1_mbstring_ncopy() cms: kek_unwrap_key: Fix out-of-bounds read in check-byte validation cms: kek_unwrap_key: test for fix out-of-bounds read in check-byte validation Avoid length truncation in ASN1_STRING_set Reject potentially forged encrypted CMS AuthEnvelopedData messages Fix potential NULL dereference processing CMS PasswordRecipientInfo Match the local q DHX parameter against the peer's q Apply the buffered IV on the AES-OCB EVP_Cipher() path Fix handling of empty-ciphertext messages in AES-GCM-SIV and AES-SIV Fix possible use-after-free in OpenSSL PKCS7_verify() Approved by: so Obtained from: OpenSSL Security: FreeBSD-SA-26:35.openssl Security: CVE-2026-7383 Security: CVE-2026-9076 Security: CVE-2026-34180 Security: CVE-2026-34182 Security: CVE-2026-42766 Security: CVE-2026-42770 Security: CVE-2026-45445 Security: CVE-2026-45446 Security: CVE-2026-45447 --- crypto/openssl/crypto/asn1/a_mbstr.c | 31 ++++++++++- crypto/openssl/crypto/asn1/tasn_dec.c | 24 +++++--- crypto/openssl/crypto/cms/cms_enc.c | 18 ++++-- crypto/openssl/crypto/cms/cms_env.c | 6 +- crypto/openssl/crypto/cms/cms_err.c | 4 +- crypto/openssl/crypto/cms/cms_local.h | 2 +- crypto/openssl/crypto/cms/cms_pwri.c | 15 ++++- crypto/openssl/crypto/err/openssl.txt | 3 +- crypto/openssl/crypto/pkcs7/pk7_smime.c | 9 +-- crypto/openssl/include/crypto/cmserr.h | 2 +- crypto/openssl/include/openssl/cmserr.h | 4 +- .../implementations/ciphers/cipher_aes_ocb.c | 13 +++++ .../implementations/ciphers/cipher_aes_siv.c | 3 + .../providers/implementations/exchange/dh_exch.c | 5 +- .../cms-msg/enveloped-content-type-for-aes-gcm.pem | 7 +++ crypto/openssl/test/cmsapitest.c | 54 +++++++++++++++++- crypto/openssl/test/evp_extra_test.c | 61 +++++++++++++++++++++ crypto/openssl/test/recipes/80-test_cms.t | 12 +++- crypto/openssl/test/recipes/80-test_cmsapi.t | 3 +- .../80-test_cmsapi_data/cms_pwri_kek_oob.der | Bin 0 -> 193 bytes 20 files changed, 239 insertions(+), 37 deletions(-) diff --git a/crypto/openssl/crypto/asn1/a_mbstr.c b/crypto/openssl/crypto/asn1/a_mbstr.c index 531e01d33257..9329e5795a9e 100644 --- a/crypto/openssl/crypto/asn1/a_mbstr.c +++ b/crypto/openssl/crypto/asn1/a_mbstr.c @@ -174,11 +174,27 @@ int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len, break; case MBSTRING_BMP: + if (nchar > INT_MAX / 2) { + ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG); + if (free_out) { + ASN1_STRING_free(dest); + *out = NULL; + } + return -1; + } outlen = nchar << 1; cpyfunc = cpy_bmp; break; case MBSTRING_UNIV: + if (nchar > INT_MAX / 4) { + ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG); + if (free_out) { + ASN1_STRING_free(dest); + *out = NULL; + } + return -1; + } outlen = nchar << 2; cpyfunc = cpy_univ; break; @@ -186,8 +202,11 @@ int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len, case MBSTRING_UTF8: outlen = 0; ret = traverse_string(in, len, inform, out_utf8, &outlen); - if (ret < 0) { - ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING); + if (ret < 0) { /* error already raised in out_utf8() */ + if (free_out) { + ASN1_STRING_free(dest); + *out = NULL; + } return -1; } cpyfunc = cpy_utf8; @@ -271,9 +290,15 @@ static int out_utf8(unsigned long value, void *arg) int *outlen, len; len = UTF8_putc(NULL, -1, value); - if (len <= 0) + if (len <= 0) { + ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING); return len; + } outlen = arg; + if (*outlen > INT_MAX - len) { + ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG); + return -1; + } *outlen += len; return 1; } diff --git a/crypto/openssl/crypto/asn1/tasn_dec.c b/crypto/openssl/crypto/asn1/tasn_dec.c index 9138ad381f7d..4cbc6275cc35 100644 --- a/crypto/openssl/crypto/asn1/tasn_dec.c +++ b/crypto/openssl/crypto/asn1/tasn_dec.c @@ -54,7 +54,7 @@ static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, const ASN1_ITEM *it, int tag, int aclass, char opt, ASN1_TLC *ctx); -static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, +static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, long len, int utype, char *free_cont, const ASN1_ITEM *it); /* Table to convert tags to bit values, used for MSTRING type */ @@ -855,19 +855,24 @@ err: /* Translate ASN1 content octets into a structure */ -static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, +static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, long len, int utype, char *free_cont, const ASN1_ITEM *it) { ASN1_VALUE **opval = NULL; ASN1_STRING *stmp; ASN1_TYPE *typ = NULL; int ret = 0; + int ilen = (int)len; const ASN1_PRIMITIVE_FUNCS *pf; ASN1_INTEGER **tint; pf = it->funcs; - if (pf && pf->prim_c2i) - return pf->prim_c2i(pval, cont, len, utype, free_cont, it); + if (pf && pf->prim_c2i) { + if (len == (long)ilen) + return pf->prim_c2i(pval, cont, ilen, utype, free_cont, it); + ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_LONG); + return 0; + } /* If ANY type clear type and set pointer to internal value */ if (it->utype == V_ASN1_ANY) { if (*pval == NULL) { @@ -885,7 +890,8 @@ static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, } switch (utype) { case V_ASN1_OBJECT: - if (!ossl_c2i_ASN1_OBJECT((ASN1_OBJECT **)pval, &cont, len)) + if (len != (long)ilen + || !ossl_c2i_ASN1_OBJECT((ASN1_OBJECT **)pval, &cont, ilen)) goto err; break; @@ -940,6 +946,10 @@ static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, case V_ASN1_SET: case V_ASN1_SEQUENCE: default: + if (len != (long)ilen) { + ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_LONG); + goto err; + } if (utype == V_ASN1_BMPSTRING && (len & 1)) { ERR_raise(ERR_LIB_ASN1, ASN1_R_BMPSTRING_IS_WRONG_LENGTH); goto err; @@ -964,10 +974,10 @@ static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, if (*free_cont) { OPENSSL_free(stmp->data); stmp->data = (unsigned char *)cont; /* UGLY CAST! RL */ - stmp->length = len; + stmp->length = ilen; *free_cont = 0; } else { - if (!ASN1_STRING_set(stmp, cont, len)) { + if (!ASN1_STRING_set(stmp, cont, ilen)) { ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); ASN1_STRING_free(stmp); *pval = NULL; diff --git a/crypto/openssl/crypto/cms/cms_enc.c b/crypto/openssl/crypto/cms/cms_enc.c index 8c1a15aeda71..367617576175 100644 --- a/crypto/openssl/crypto/cms/cms_enc.c +++ b/crypto/openssl/crypto/cms/cms_enc.c @@ -23,7 +23,7 @@ /* Return BIO based on EncryptedContentInfo and key */ BIO *ossl_cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec, - const CMS_CTX *cms_ctx) + const CMS_CTX *cms_ctx, int auth) { BIO *b; EVP_CIPHER_CTX *ctx; @@ -104,14 +104,20 @@ BIO *ossl_cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec, goto err; } if ((EVP_CIPHER_get_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER)) { + if (!auth) { + ERR_raise(ERR_LIB_CMS, CMS_R_CIPHER_AEAD_IN_ENVELOPED_DATA); + goto err; + } piv = aparams.iv; - if (ec->taglen > 0 - && EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, - ec->taglen, ec->tag) - <= 0) { + + if (ec->taglen < 4 || ec->taglen > 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); @@ -263,5 +269,5 @@ BIO *ossl_cms_EncryptedData_init_bio(const CMS_ContentInfo *cms) if (enc->encryptedContentInfo->cipher && enc->unprotectedAttrs) enc->version = 2; return ossl_cms_EncryptedContent_init_bio(enc->encryptedContentInfo, - ossl_cms_get0_cmsctx(cms)); + ossl_cms_get0_cmsctx(cms), 0); } diff --git a/crypto/openssl/crypto/cms/cms_env.c b/crypto/openssl/crypto/cms/cms_env.c index 2326253b6743..d2eace4fef5d 100644 --- a/crypto/openssl/crypto/cms/cms_env.c +++ b/crypto/openssl/crypto/cms/cms_env.c @@ -1099,7 +1099,7 @@ static BIO *cms_EnvelopedData_Decryption_init_bio(CMS_ContentInfo *cms) { CMS_EncryptedContentInfo *ec = cms->d.envelopedData->encryptedContentInfo; BIO *contentBio = ossl_cms_EncryptedContent_init_bio(ec, - ossl_cms_get0_cmsctx(cms)); + ossl_cms_get0_cmsctx(cms), 0); EVP_CIPHER_CTX *ctx = NULL; if (contentBio == NULL) @@ -1137,7 +1137,7 @@ static BIO *cms_EnvelopedData_Encryption_init_bio(CMS_ContentInfo *cms) /* Get BIO first to set up key */ ec = env->encryptedContentInfo; - ret = ossl_cms_EncryptedContent_init_bio(ec, ossl_cms_get0_cmsctx(cms)); + ret = ossl_cms_EncryptedContent_init_bio(ec, ossl_cms_get0_cmsctx(cms), 0); /* If error end of processing */ if (!ret) @@ -1189,7 +1189,7 @@ BIO *ossl_cms_AuthEnvelopedData_init_bio(CMS_ContentInfo *cms) ec->tag = aenv->mac->data; ec->taglen = aenv->mac->length; } - ret = ossl_cms_EncryptedContent_init_bio(ec, ossl_cms_get0_cmsctx(cms)); + ret = ossl_cms_EncryptedContent_init_bio(ec, ossl_cms_get0_cmsctx(cms), 1); /* If error or no cipher end of processing */ if (ret == NULL || ec->cipher == NULL) diff --git a/crypto/openssl/crypto/cms/cms_err.c b/crypto/openssl/crypto/cms/cms_err.c index 37e52963e16d..bc922d0ee03d 100644 --- a/crypto/openssl/crypto/cms/cms_err.c +++ b/crypto/openssl/crypto/cms/cms_err.c @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -25,6 +25,8 @@ static const ERR_STRING_DATA CMS_str_reasons[] = { "certificate has no keyid" }, { ERR_PACK(ERR_LIB_CMS, 0, CMS_R_CERTIFICATE_VERIFY_ERROR), "certificate verify error" }, + { ERR_PACK(ERR_LIB_CMS, 0, CMS_R_CIPHER_AEAD_IN_ENVELOPED_DATA), + "cipher aead in enveloped data" }, { ERR_PACK(ERR_LIB_CMS, 0, CMS_R_CIPHER_AEAD_SET_TAG_ERROR), "cipher aead set tag error" }, { ERR_PACK(ERR_LIB_CMS, 0, CMS_R_CIPHER_GET_TAG), "cipher get tag" }, diff --git a/crypto/openssl/crypto/cms/cms_local.h b/crypto/openssl/crypto/cms/cms_local.h index a92a67fa8b24..d80689f64d68 100644 --- a/crypto/openssl/crypto/cms/cms_local.h +++ b/crypto/openssl/crypto/cms/cms_local.h @@ -429,7 +429,7 @@ int ossl_cms_set1_ias(CMS_IssuerAndSerialNumber **pias, X509 *cert); int ossl_cms_set1_keyid(ASN1_OCTET_STRING **pkeyid, X509 *cert); BIO *ossl_cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec, - const CMS_CTX *ctx); + const CMS_CTX *ctx, int auth); BIO *ossl_cms_EncryptedData_init_bio(const CMS_ContentInfo *cms); int ossl_cms_EncryptedContent_init(CMS_EncryptedContentInfo *ec, const EVP_CIPHER *cipher, diff --git a/crypto/openssl/crypto/cms/cms_pwri.c b/crypto/openssl/crypto/cms/cms_pwri.c index 46313f2bfe2c..8b0ea233fd6d 100644 --- a/crypto/openssl/crypto/cms/cms_pwri.c +++ b/crypto/openssl/crypto/cms/cms_pwri.c @@ -189,14 +189,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 (inlen < 2 * blocklen) { + + if (blocklen < 4) + return 0; + + if (inlen < 2 * (size_t)blocklen) { /* too small */ return 0; } - if (inlen % blocklen) { + if (inlen > INT_MAX || inlen % blocklen) { /* Invalid size */ return 0; } @@ -350,6 +354,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/err/openssl.txt b/crypto/openssl/crypto/err/openssl.txt index 756fafdfa24a..dc4e3f310f08 100644 --- a/crypto/openssl/crypto/err/openssl.txt +++ b/crypto/openssl/crypto/err/openssl.txt @@ -1,4 +1,4 @@ -# Copyright 1999-2025 The OpenSSL Project Authors. All Rights Reserved. +# Copyright 1999-2026 The OpenSSL Project Authors. All Rights Reserved. # # Licensed under the Apache License 2.0 (the "License"). You may not use # this file except in compliance with the License. You can obtain a copy @@ -284,6 +284,7 @@ CMS_R_ATTRIBUTE_ERROR:161:attribute error CMS_R_CERTIFICATE_ALREADY_PRESENT:175:certificate already present CMS_R_CERTIFICATE_HAS_NO_KEYID:160:certificate has no keyid CMS_R_CERTIFICATE_VERIFY_ERROR:100:certificate verify error +CMS_R_CIPHER_AEAD_IN_ENVELOPED_DATA:182:cipher aead in enveloped data CMS_R_CIPHER_AEAD_SET_TAG_ERROR:184:cipher aead set tag error CMS_R_CIPHER_GET_TAG:185:cipher get tag CMS_R_CIPHER_INITIALISATION_ERROR:101:cipher initialisation error diff --git a/crypto/openssl/crypto/pkcs7/pk7_smime.c b/crypto/openssl/crypto/pkcs7/pk7_smime.c index 001b96d31183..fadf543e9db7 100644 --- a/crypto/openssl/crypto/pkcs7/pk7_smime.c +++ b/crypto/openssl/crypto/pkcs7/pk7_smime.c @@ -218,6 +218,7 @@ int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store, int i, j = 0, k, ret = 0; BIO *p7bio = NULL; BIO *tmpin = NULL, *tmpout = NULL; + BIO *next = NULL; const PKCS7_CTX *p7_ctx; if (p7 == NULL) { @@ -366,11 +367,11 @@ err: BIO_free(tmpout); X509_STORE_CTX_free(cert_ctx); OPENSSL_free(buf); - if (tmpin == indata) { - if (indata) - BIO_pop(p7bio); + while (p7bio != NULL && p7bio != indata) { + next = BIO_pop(p7bio); + BIO_free(p7bio); + p7bio = next; } - BIO_free_all(p7bio); sk_X509_free(signers); return ret; } diff --git a/crypto/openssl/include/crypto/cmserr.h b/crypto/openssl/include/crypto/cmserr.h index f9fd933682e5..8b896822d091 100644 --- a/crypto/openssl/include/crypto/cmserr.h +++ b/crypto/openssl/include/crypto/cmserr.h @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2020-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/crypto/openssl/include/openssl/cmserr.h b/crypto/openssl/include/openssl/cmserr.h index c584b90574e2..6c0baf2362fc 100644 --- a/crypto/openssl/include/openssl/cmserr.h +++ b/crypto/openssl/include/openssl/cmserr.h @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -17,7 +17,6 @@ #include #ifndef OPENSSL_NO_CMS - /* * CMS reason codes. */ @@ -26,6 +25,7 @@ #define CMS_R_CERTIFICATE_ALREADY_PRESENT 175 #define CMS_R_CERTIFICATE_HAS_NO_KEYID 160 #define CMS_R_CERTIFICATE_VERIFY_ERROR 100 +#define CMS_R_CIPHER_AEAD_IN_ENVELOPED_DATA 182 #define CMS_R_CIPHER_AEAD_SET_TAG_ERROR 184 #define CMS_R_CIPHER_GET_TAG 185 #define CMS_R_CIPHER_INITIALISATION_ERROR 101 diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c index 8a8eddf36eec..70c0703ddb7c 100644 --- a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c @@ -516,6 +516,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 510c1581b593..c3facacfbcf4 100644 --- a/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.c +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.c @@ -203,6 +203,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 (params == NULL) return 1; @@ -237,6 +238,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 bb2d355c8143..668602a5e523 100644 --- a/crypto/openssl/providers/implementations/exchange/dh_exch.c +++ b/crypto/openssl/providers/implementations/exchange/dh_exch.c @@ -113,12 +113,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/test/cms-msg/enveloped-content-type-for-aes-gcm.pem b/crypto/openssl/test/cms-msg/enveloped-content-type-for-aes-gcm.pem new file mode 100644 index 000000000000..b0610a7ec8a2 --- /dev/null +++ b/crypto/openssl/test/cms-msg/enveloped-content-type-for-aes-gcm.pem @@ -0,0 +1,7 @@ +-----BEGIN PKCS7----- +MIAGCSqGSIb3DQEHA6CAMIACAQIxNqI0AgEEMAgEBkMwRkVFMDALBglghkgBZQME +AQUEGPN0q9rM3neSiY7HIADpnqWym33mRZC4JDCABgkqhkiG9w0BBwEwHgYJYIZI +AWUDBAEGMBEEDIExQGiHZFSYa0ZBqQIBEKCABGNap+JL1B21Mq7ojKPzVuxtRkg3 +LWt8khnK1EzfmV7e64l5KnTdjq9+gfbwOfbuhTavfBI7VK/ZtpH3HII4fCOe37kV +mju8/YnYeRq2KcxESmJBySV/veMwxqmHGAw71JyHpg4AAAAAAAAAAAAA +-----END PKCS7----- diff --git a/crypto/openssl/test/cmsapitest.c b/crypto/openssl/test/cmsapitest.c index 7e74c5daf221..0a7e536bbe75 100644 --- a/crypto/openssl/test/cmsapitest.c +++ b/crypto/openssl/test/cmsapitest.c @@ -20,6 +20,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) { @@ -45,6 +46,12 @@ static int test_encrypt_decrypt(const EVP_CIPHER *cipher) CMS_TEXT))) goto end; + if (!(EVP_CIPHER_get_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER) + && !TEST_ptr(contentbio = CMS_EnvelopedData_decrypt(content->d.envelopedData, + NULL, privkey, cert, NULL, + CMS_TEXT, NULL, NULL))) + goto end; + /* Check we got the message we first started with */ if (!TEST_int_eq(BIO_gets(outmsgbio, buf, sizeof(buf)), strlen(msg)) || !TEST_int_eq(strcmp(buf, msg), 0)) @@ -484,7 +491,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) { @@ -499,7 +547,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))) return 0; certbio = BIO_new_file(certin, "r"); @@ -535,6 +584,7 @@ int setup_tests(void) ADD_TEST(test_encrypted_data_aead); ADD_ALL_TESTS(test_d2i_CMS_decode, 2); ADD_TEST(test_cms_aesgcm_iv_too_long); + ADD_TEST(test_pwri_kek_unwrap_short_encrypted_key); return 1; } diff --git a/crypto/openssl/test/evp_extra_test.c b/crypto/openssl/test/evp_extra_test.c index 1b0f0711cb57..7bd41db115ca 100644 --- a/crypto/openssl/test/evp_extra_test.c +++ b/crypto/openssl/test/evp_extra_test.c @@ -5414,6 +5414,64 @@ static int test_aes_rc4_keylen_change_cve_2023_5363(void) } #endif +/* + * AES-SIV reuse-without-rekey: + * msg1: legit non-empty CT, tag verifies, final_ret=0 + * msg2: no reinit (or reinit with key=NULL), set forged tag, + * AAD only, DecryptFinal -> does stale final_ret leak through? + */ +static int test_aes_siv_ctx_reuse(void) +{ + unsigned char key[32] = { 7 }; /* AES-128-SIV => 2*16 */ + unsigned char pt[9] = "payload!"; + unsigned char ct[9], tagbuf[16], out[16], zero16[16] = { 0 }; + unsigned char aad[14] = "forged header"; + int outl, ret = 0; + EVP_CIPHER_CTX *e = NULL, *d = NULL; + EVP_CIPHER *c = EVP_CIPHER_fetch(NULL, "AES-128-SIV", NULL); + + if (c == NULL) { + return TEST_skip("AES-128-SIV cipher is not available"); + } + + /* produce a valid (ct,tag) for msg1 */ + e = EVP_CIPHER_CTX_new(); + if (!TEST_ptr(e) + || !TEST_true(EVP_EncryptInit_ex2(e, c, key, NULL, NULL)) + || !TEST_true(EVP_EncryptUpdate(e, NULL, &outl, (unsigned char *)"hdr1", 4)) + || !TEST_true(EVP_EncryptUpdate(e, ct, &outl, pt, sizeof(pt))) + || !TEST_true(EVP_EncryptFinal_ex(e, out, &outl)) + || !TEST_true(EVP_CIPHER_CTX_ctrl(e, EVP_CTRL_AEAD_GET_TAG, 16, tagbuf))) { + EVP_CIPHER_CTX_free(e); + goto err; + } + EVP_CIPHER_CTX_free(e); + + /* msg1 decrypt */ + d = EVP_CIPHER_CTX_new(); + if (!TEST_ptr(d) + || !TEST_true(EVP_DecryptInit_ex2(d, c, key, NULL, NULL)) + || !TEST_true(EVP_CIPHER_CTX_ctrl(d, EVP_CTRL_AEAD_SET_TAG, 16, tagbuf)) + || !TEST_true(EVP_DecryptUpdate(d, NULL, &outl, (unsigned char *)"hdr1", 4)) + || !TEST_true(EVP_DecryptUpdate(d, out, &outl, ct, sizeof(ct))) + || !TEST_true(EVP_DecryptFinal_ex(d, out, &outl))) + goto err; + + /* msg2 on SAME ctx, reinit with key=NULL => initkey skipped, final_ret should be reset */ + if (!TEST_true(EVP_DecryptInit_ex2(d, NULL, NULL, NULL, NULL)) + || !TEST_true(EVP_CIPHER_CTX_ctrl(d, EVP_CTRL_AEAD_SET_TAG, 16, zero16)) + || !TEST_true(EVP_DecryptUpdate(d, NULL, &outl, aad, sizeof(aad))) /* forged AAD */ + || !TEST_false(EVP_DecryptFinal_ex(d, out, &outl))) + goto err; + + ret = 1; + +err: + EVP_CIPHER_CTX_free(d); + EVP_CIPHER_free(c); + return ret; +} + static int test_invalid_ctx_for_digest(void) { int ret; @@ -5637,6 +5695,9 @@ int setup_tests(void) ADD_TEST(test_aes_rc4_keylen_change_cve_2023_5363); #endif + /* Test case for CVE-2026-45446 */ + ADD_TEST(test_aes_siv_ctx_reuse); + ADD_TEST(test_invalid_ctx_for_digest); ADD_TEST(test_evp_cipher_negative_length); diff --git a/crypto/openssl/test/recipes/80-test_cms.t b/crypto/openssl/test/recipes/80-test_cms.t index b6ee61464409..f573651e26bc 100644 --- a/crypto/openssl/test/recipes/80-test_cms.t +++ b/crypto/openssl/test/recipes/80-test_cms.t @@ -51,7 +51,7 @@ my ($no_des, $no_dh, $no_dsa, $no_ec, $no_ec2m, $no_rc2, $no_zlib) $no_rc2 = 1 if disabled("legacy"); -plan tests => 23; +plan tests => 24; ok(run(test(["pkcs7_test"])), "test pkcs7"); @@ -1054,6 +1054,16 @@ ok(!run(app(['openssl', 'cms', '-verify', ])), "issue#19643"); +# Check that users get error when using incorrect envelope type for AEAD algorithms +ok(!run(app(['openssl', 'cms', '-decrypt', + '-inform', 'PEM', '-stream', + '-secretkey', '000102030405060708090A0B0C0D0E0F', + '-secretkeyid', 'C0FEE0', + '-in', srctop_file("test/cms-msg", + "enveloped-content-type-for-aes-gcm.pem") + ])), + "Error AES-GCM in enveloped content type"); + # Check that kari encryption with originator does not segfault with({ exit_checker => sub { return shift == 3; } }, sub { diff --git a/crypto/openssl/test/recipes/80-test_cmsapi.t b/crypto/openssl/test/recipes/80-test_cmsapi.t index 8d9371e005c0..3d1dae846464 100644 --- a/crypto/openssl/test/recipes/80-test_cmsapi.t +++ b/crypto/openssl/test/recipes/80-test_cmsapi.t @@ -19,5 +19,6 @@ plan tests => 1; ok(run(test(["cmsapitest", srctop_file("test", "certs", "servercert.pem"), srctop_file("test", "certs", "serverkey.pem"), srctop_file("test", "recipes", "80-test_cmsapi_data", "encryptedData.der"), - srctop_file("test", "recipes", "80-test_cmsapi_data", "encDataWithTooLongIV.pem")])), + srctop_file("test", "recipes", "80-test_cmsapi_data", "encDataWithTooLongIV.pem"), + srctop_file("test", "recipes", "80-test_cmsapi_data", "cms_pwri_kek_oob.der")])), "running cmsapitest"); diff --git a/crypto/openssl/test/recipes/80-test_cmsapi_data/cms_pwri_kek_oob.der b/crypto/openssl/test/recipes/80-test_cmsapi_data/cms_pwri_kek_oob.der new file mode 100644 index 000000000000..c3ef3abd10e6 Binary files /dev/null and b/crypto/openssl/test/recipes/80-test_cmsapi_data/cms_pwri_kek_oob.der differ