From 7ca64ab386c83371675682ae44ab55c90e20d1b4 Mon Sep 17 00:00:00 2001 From: Gordon Tetlow Date: Mon, 26 Jan 2026 10:42:20 -0800 Subject: [PATCH] openssl: Fix multiple vulnerabilities This is a rollup commit from upstream to fix: Stack buffer overflow in CMS AuthEnvelopedData parsing (CVE-2025-15467) Heap out-of-bounds write in BIO_f_linebuffer on short writes (CVE-2025-68160) Unauthenticated/unencrypted trailing bytes with low-level OCB function calls (CVE-2025-69418) Out of bounds write in PKCS12_get_friendlyname() UTF-8 conversion (CVE-2025-69419) Missing ASN1_TYPE validation in TS_RESP_verify_response() function (CVE-2025-69420) NULL Pointer Dereference in PKCS12_item_decrypt_d2i_ex function (CVE-2025-69421) Missing ASN1_TYPE validation in PKCS#12 parsing (CVE-2026-22795) ASN1_TYPE Type Confusion in the PKCS7_digest_from_attributes() function (CVE-2026-22796) See https://openssl-library.org/news/secadv/ for additional details. Approved by: so Obtained from: OpenSSL Security: FreeBSD-SA-26:01.openssl Security: CVE-2025-15467 Security: CVE-2025-68160 Security: CVE-2025-69418 Security: CVE-2025-69419 Security: CVE-2025-69420 Security: CVE-2025-69421 Security: CVE-2026-22795 Security: CVE-2026-22796 --- crypto/openssl/apps/s_client.c | 3 ++- crypto/openssl/crypto/asn1/a_strex.c | 6 +++-- crypto/openssl/crypto/asn1/evp_asn1.c | 20 +++++++++++++++ crypto/openssl/crypto/bio/bf_lbuf.c | 32 +++++++++++++++++++----- crypto/openssl/crypto/evp/evp_lib.c | 5 ++-- crypto/openssl/crypto/modes/ocb128.c | 10 ++++++-- crypto/openssl/crypto/pkcs12/p12_decr.c | 5 ++++ crypto/openssl/crypto/pkcs12/p12_kiss.c | 10 ++++++-- crypto/openssl/crypto/pkcs12/p12_utl.c | 5 ++++ crypto/openssl/crypto/pkcs7/pk7_doit.c | 2 ++ crypto/openssl/crypto/ts/ts_rsp_verify.c | 4 +-- 11 files changed, 84 insertions(+), 18 deletions(-) diff --git a/crypto/openssl/apps/s_client.c b/crypto/openssl/apps/s_client.c index efa2879ca0e..e450eee8cfb 100644 --- a/crypto/openssl/apps/s_client.c +++ b/crypto/openssl/apps/s_client.c @@ -2650,8 +2650,9 @@ int s_client_main(int argc, char **argv) goto end; } atyp = ASN1_generate_nconf(genstr, cnf); - if (atyp == NULL) { + if (atyp == NULL || atyp->type != V_ASN1_SEQUENCE) { NCONF_free(cnf); + ASN1_TYPE_free(atyp); BIO_printf(bio_err, "ASN1_generate_nconf failed\n"); goto end; } diff --git a/crypto/openssl/crypto/asn1/a_strex.c b/crypto/openssl/crypto/asn1/a_strex.c index a6049f7dd2e..a490dfe1245 100644 --- a/crypto/openssl/crypto/asn1/a_strex.c +++ b/crypto/openssl/crypto/asn1/a_strex.c @@ -204,8 +204,10 @@ static int do_buf(unsigned char *buf, int buflen, orflags = CHARTYPE_LAST_ESC_2253; if (type & BUF_TYPE_CONVUTF8) { unsigned char utfbuf[6]; - int utflen; - utflen = UTF8_putc(utfbuf, sizeof(utfbuf), c); + int utflen = UTF8_putc(utfbuf, sizeof(utfbuf), c); + + if (utflen < 0) + return -1; /* error happened with UTF8 */ for (i = 0; i < utflen; i++) { /* * We don't need to worry about setting orflags correctly diff --git a/crypto/openssl/crypto/asn1/evp_asn1.c b/crypto/openssl/crypto/asn1/evp_asn1.c index 13d8ed3893a..6aca0117bc1 100644 --- a/crypto/openssl/crypto/asn1/evp_asn1.c +++ b/crypto/openssl/crypto/asn1/evp_asn1.c @@ -60,6 +60,12 @@ static ossl_inline void asn1_type_init_oct(ASN1_OCTET_STRING *oct, oct->flags = 0; } +/* + * This function copies 'anum' to 'num' and the data of 'oct' to 'data'. + * If the length of 'data' > 'max_len', copies only the first 'max_len' + * bytes, but returns the full length of 'oct'; this allows distinguishing + * whether all the data was copied. + */ static int asn1_type_get_int_oct(ASN1_OCTET_STRING *oct, int32_t anum, long *num, unsigned char *data, int max_len) { @@ -106,6 +112,13 @@ int ASN1_TYPE_set_int_octetstring(ASN1_TYPE *a, long num, unsigned char *data, return 0; } +/* + * This function decodes an int-octet sequence and copies the integer to 'num' + * and the data of octet to 'data'. + * If the length of 'data' > 'max_len', copies only the first 'max_len' + * bytes, but returns the full length of 'oct'; this allows distinguishing + * whether all the data was copied. + */ int ASN1_TYPE_get_int_octetstring(const ASN1_TYPE *a, long *num, unsigned char *data, int max_len) { @@ -162,6 +175,13 @@ int ossl_asn1_type_set_octetstring_int(ASN1_TYPE *a, long num, return 0; } +/* + * This function decodes an octet-int sequence and copies the data of octet + * to 'data' and the integer to 'num'. + * If the length of 'data' > 'max_len', copies only the first 'max_len' + * bytes, but returns the full length of 'oct'; this allows distinguishing + * whether all the data was copied. + */ int ossl_asn1_type_get_octetstring_int(const ASN1_TYPE *a, long *num, unsigned char *data, int max_len) { diff --git a/crypto/openssl/crypto/bio/bf_lbuf.c b/crypto/openssl/crypto/bio/bf_lbuf.c index 6908e64d365..2eab35af672 100644 --- a/crypto/openssl/crypto/bio/bf_lbuf.c +++ b/crypto/openssl/crypto/bio/bf_lbuf.c @@ -189,14 +189,34 @@ static int linebuffer_write(BIO *b, const char *in, int inl) while (foundnl && inl > 0); /* * We've written as much as we can. The rest of the input buffer, if - * any, is text that doesn't and with a NL and therefore needs to be - * saved for the next trip. + * any, is text that doesn't end with a NL and therefore we need to try + * free up some space in our obuf so we can make forward progress. */ - if (inl > 0) { - memcpy(&(ctx->obuf[ctx->obuf_len]), in, inl); - ctx->obuf_len += inl; - num += inl; + while (inl > 0) { + size_t avail = (size_t)ctx->obuf_size - (size_t)ctx->obuf_len; + size_t to_copy; + + if (avail == 0) { + /* Flush buffered data to make room */ + i = BIO_write(b->next_bio, ctx->obuf, ctx->obuf_len); + if (i <= 0) { + BIO_copy_next_retry(b); + return num > 0 ? num : i; + } + if (i < ctx->obuf_len) + memmove(ctx->obuf, ctx->obuf + i, ctx->obuf_len - i); + ctx->obuf_len -= i; + continue; + } + + to_copy = inl > (int)avail ? avail : (size_t)inl; + memcpy(&(ctx->obuf[ctx->obuf_len]), in, to_copy); + ctx->obuf_len += (int)to_copy; + in += to_copy; + inl -= (int)to_copy; + num += (int)to_copy; } + return num; } diff --git a/crypto/openssl/crypto/evp/evp_lib.c b/crypto/openssl/crypto/evp/evp_lib.c index 4f3d901eba5..55cc9ca36df 100644 --- a/crypto/openssl/crypto/evp/evp_lib.c +++ b/crypto/openssl/crypto/evp/evp_lib.c @@ -249,10 +249,9 @@ int evp_cipher_get_asn1_aead_params(EVP_CIPHER_CTX *c, ASN1_TYPE *type, if (type == NULL || asn1_params == NULL) return 0; - i = ossl_asn1_type_get_octetstring_int(type, &tl, NULL, EVP_MAX_IV_LENGTH); - if (i <= 0) + i = ossl_asn1_type_get_octetstring_int(type, &tl, iv, EVP_MAX_IV_LENGTH); + if (i <= 0 || i > EVP_MAX_IV_LENGTH) return -1; - ossl_asn1_type_get_octetstring_int(type, &tl, iv, i); memcpy(asn1_params->iv, iv, i); asn1_params->iv_len = i; diff --git a/crypto/openssl/crypto/modes/ocb128.c b/crypto/openssl/crypto/modes/ocb128.c index b5202ba5bd5..95601dadf18 100644 --- a/crypto/openssl/crypto/modes/ocb128.c +++ b/crypto/openssl/crypto/modes/ocb128.c @@ -342,7 +342,7 @@ int CRYPTO_ocb128_encrypt(OCB128_CONTEXT *ctx, if (num_blocks && all_num_blocks == (size_t)all_num_blocks && ctx->stream != NULL) { - size_t max_idx = 0, top = (size_t)all_num_blocks; + size_t max_idx = 0, top = (size_t)all_num_blocks, processed_bytes = 0; /* * See how many L_{i} entries we need to process data at hand @@ -356,6 +356,9 @@ int CRYPTO_ocb128_encrypt(OCB128_CONTEXT *ctx, ctx->stream(in, out, num_blocks, ctx->keyenc, (size_t)ctx->sess.blocks_processed + 1, ctx->sess.offset.c, (const unsigned char (*)[16])ctx->l, ctx->sess.checksum.c); + processed_bytes = num_blocks * 16; + in += processed_bytes; + out += processed_bytes; } else { /* Loop through all full blocks to be encrypted */ for (i = ctx->sess.blocks_processed + 1; i <= all_num_blocks; i++) { @@ -434,7 +437,7 @@ int CRYPTO_ocb128_decrypt(OCB128_CONTEXT *ctx, if (num_blocks && all_num_blocks == (size_t)all_num_blocks && ctx->stream != NULL) { - size_t max_idx = 0, top = (size_t)all_num_blocks; + size_t max_idx = 0, top = (size_t)all_num_blocks, processed_bytes = 0; /* * See how many L_{i} entries we need to process data at hand @@ -448,6 +451,9 @@ int CRYPTO_ocb128_decrypt(OCB128_CONTEXT *ctx, ctx->stream(in, out, num_blocks, ctx->keydec, (size_t)ctx->sess.blocks_processed + 1, ctx->sess.offset.c, (const unsigned char (*)[16])ctx->l, ctx->sess.checksum.c); + processed_bytes = num_blocks * 16; + in += processed_bytes; + out += processed_bytes; } else { OCB_BLOCK tmp; diff --git a/crypto/openssl/crypto/pkcs12/p12_decr.c b/crypto/openssl/crypto/pkcs12/p12_decr.c index a5adafa954a..2e14a49efa0 100644 --- a/crypto/openssl/crypto/pkcs12/p12_decr.c +++ b/crypto/openssl/crypto/pkcs12/p12_decr.c @@ -137,6 +137,11 @@ void *PKCS12_item_decrypt_d2i_ex(const X509_ALGOR *algor, const ASN1_ITEM *it, void *ret; int outlen = 0; + if (oct == NULL) { + ERR_raise(ERR_LIB_PKCS12, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + if (!PKCS12_pbe_crypt_ex(algor, pass, passlen, oct->data, oct->length, &out, &outlen, 0, libctx, propq)) return NULL; diff --git a/crypto/openssl/crypto/pkcs12/p12_kiss.c b/crypto/openssl/crypto/pkcs12/p12_kiss.c index 229b34cf642..d7e5f2ce46a 100644 --- a/crypto/openssl/crypto/pkcs12/p12_kiss.c +++ b/crypto/openssl/crypto/pkcs12/p12_kiss.c @@ -190,11 +190,17 @@ static int parse_bag(PKCS12_SAFEBAG *bag, const char *pass, int passlen, ASN1_BMPSTRING *fname = NULL; ASN1_OCTET_STRING *lkid = NULL; - if ((attrib = PKCS12_SAFEBAG_get0_attr(bag, NID_friendlyName))) + if ((attrib = PKCS12_SAFEBAG_get0_attr(bag, NID_friendlyName))) { + if (attrib->type != V_ASN1_BMPSTRING) + return 0; fname = attrib->value.bmpstring; + } - if ((attrib = PKCS12_SAFEBAG_get0_attr(bag, NID_localKeyID))) + if ((attrib = PKCS12_SAFEBAG_get0_attr(bag, NID_localKeyID))) { + if (attrib->type != V_ASN1_OCTET_STRING) + return 0; lkid = attrib->value.octet_string; + } switch (PKCS12_SAFEBAG_get_nid(bag)) { case NID_keyBag: diff --git a/crypto/openssl/crypto/pkcs12/p12_utl.c b/crypto/openssl/crypto/pkcs12/p12_utl.c index 3afc8b2f13c..dfd88298869 100644 --- a/crypto/openssl/crypto/pkcs12/p12_utl.c +++ b/crypto/openssl/crypto/pkcs12/p12_utl.c @@ -212,6 +212,11 @@ char *OPENSSL_uni2utf8(const unsigned char *uni, int unilen) /* re-run the loop emitting UTF-8 string */ for (asclen = 0, i = 0; i < unilen; ) { j = bmp_to_utf8(asctmp+asclen, uni+i, unilen-i); + /* when UTF8_putc fails */ + if (j < 0) { + OPENSSL_free(asctmp); + return NULL; + } if (j == 4) i += 4; else i += 2; asclen += j; diff --git a/crypto/openssl/crypto/pkcs7/pk7_doit.c b/crypto/openssl/crypto/pkcs7/pk7_doit.c index e9de097da18..50b25799065 100644 --- a/crypto/openssl/crypto/pkcs7/pk7_doit.c +++ b/crypto/openssl/crypto/pkcs7/pk7_doit.c @@ -1182,6 +1182,8 @@ ASN1_OCTET_STRING *PKCS7_digest_from_attributes(STACK_OF(X509_ATTRIBUTE) *sk) ASN1_TYPE *astype; if ((astype = get_attribute(sk, NID_pkcs9_messageDigest)) == NULL) return NULL; + if (astype->type != V_ASN1_OCTET_STRING) + return NULL; return astype->value.octet_string; } diff --git a/crypto/openssl/crypto/ts/ts_rsp_verify.c b/crypto/openssl/crypto/ts/ts_rsp_verify.c index 792a27ce572..d940c49c6b4 100644 --- a/crypto/openssl/crypto/ts/ts_rsp_verify.c +++ b/crypto/openssl/crypto/ts/ts_rsp_verify.c @@ -209,7 +209,7 @@ static ESS_SIGNING_CERT *ossl_ess_get_signing_cert(const PKCS7_SIGNER_INFO *si) const unsigned char *p; attr = PKCS7_get_signed_attribute(si, NID_id_smime_aa_signingCertificate); - if (attr == NULL) + if (attr == NULL || attr->type != V_ASN1_SEQUENCE) return NULL; p = attr->value.sequence->data; return d2i_ESS_SIGNING_CERT(NULL, &p, attr->value.sequence->length); @@ -222,7 +222,7 @@ ESS_SIGNING_CERT_V2 *ossl_ess_get_signing_cert_v2(const PKCS7_SIGNER_INFO *si) const unsigned char *p; attr = PKCS7_get_signed_attribute(si, NID_id_smime_aa_signingCertificateV2); - if (attr == NULL) + if (attr == NULL || attr->type != V_ASN1_SEQUENCE) return NULL; p = attr->value.sequence->data; return d2i_ESS_SIGNING_CERT_V2(NULL, &p, attr->value.sequence->length);