diff --git a/openssl-3.0-CVE-2026-54874.patch b/openssl-3.0-CVE-2026-54874.patch new file mode 100644 index 0000000000000000000000000000000000000000..932781252ab146b75650cd859d15767e97f33eb7 --- /dev/null +++ b/openssl-3.0-CVE-2026-54874.patch @@ -0,0 +1,264 @@ +From 1ca5a124b48f1ffe6980ad33724b452fbd62da1e Mon Sep 17 00:00:00 2001 +From: Matt Caswell +Date: Tue, 23 Jun 2026 17:21:48 +0100 +Subject: [PATCH] Avoid full read buffer allocation when buffering DTLS records + +dtls1_buffer_record() is used to hold onto a DTLS record across three +different scenarios: a record arriving early for the next epoch while +a handshake is in progress, re-queuing a record once the epoch catches +up and it has been decrypted, and application data arriving between +ChangeCipherSpec and Finished. In all three cases it took ownership of +the entire live read buffer (sized for the largest possible record, +~16.7KB) and allocated a brand new one to carry on reading, regardless +of how small the buffered record actually was. With each of the three +queues capped at 100 entries, a peer could send a stream of tiny +bogus records and force a disproportionate amount of heap allocation +per connection. + +Instead, copy only the record's own on-wire bytes (header plus +ciphertext, or header plus plaintext for an already-decrypted record) +into the queue entry, and leave the live read buffer untouched. On +retrieval, copy those bytes back into the live buffer and rebase any +SSL3_RECORD data/input pointers that pointed into the original packet, +rather than handing out a pointer to a standalone allocation. Memory +use is now proportional to what the peer actually sends. + +Fixes CVE-2026-54874 + +Assisted-by: Claude:claude-sonnet-4-6 +Assisted-by: Claude:claude-opus-5 +Reviewed-by: Milan Broz +Reviewed-by: Andrew Dinh +Merge-date: Mon Aug 24 15:40:45 2026 + + +--- + ssl/record/rec_layer_d1.c | 110 ++++++++++++++++++++++++++++++++++----------- + ssl/record/record.h | 1 + ssl/record/ssl3_record.c | 7 ++- + 3 files changed, 87 insertions(+), 31 deletions(-) + +diff --git a/ssl/record/rec_layer_d1.c b/ssl/record/rec_layer_d1.c +index 79de021..025684b 100644 +--- a/ssl/record/rec_layer_d1.c ++++ b/ssl/record/rec_layer_d1.c +@@ -70,7 +70,7 @@ void DTLS_RECORD_LAYER_clear(RECORD_LAYER *rl) + + while ((item = pqueue_pop(d->unprocessed_rcds.q)) != NULL) { + rdata = (DTLS1_RECORD_DATA *)item->data; +- OPENSSL_free(rdata->rbuf.buf); ++ OPENSSL_free(rdata->packet); + OPENSSL_free(item->data); + pitem_free(item); + } +@@ -78,8 +78,8 @@ void DTLS_RECORD_LAYER_clear(RECORD_LAYER *rl) + while ((item = pqueue_pop(d->processed_rcds.q)) != NULL) { + rdata = (DTLS1_RECORD_DATA *)item->data; + if (rl->s->options & SSL_OP_CLEANSE_PLAINTEXT) +- OPENSSL_cleanse(rdata->rbuf.buf, rdata->rbuf.len); +- OPENSSL_free(rdata->rbuf.buf); ++ OPENSSL_cleanse(rdata->packet, rdata->packet_length); ++ OPENSSL_free(rdata->packet); + OPENSSL_free(item->data); + pitem_free(item); + } +@@ -87,8 +87,8 @@ void DTLS_RECORD_LAYER_clear(RECORD_LAYER *rl) + while ((item = pqueue_pop(d->buffered_app_data.q)) != NULL) { + rdata = (DTLS1_RECORD_DATA *)item->data; + if (rl->s->options & SSL_OP_CLEANSE_PLAINTEXT) +- OPENSSL_cleanse(rdata->rbuf.buf, rdata->rbuf.len); +- OPENSSL_free(rdata->rbuf.buf); ++ OPENSSL_cleanse(rdata->packet, rdata->packet_length); ++ OPENSSL_free(rdata->packet); + OPENSSL_free(item->data); + pitem_free(item); + } +@@ -130,15 +130,48 @@ static int dtls1_copy_record(SSL *s, pitem *item) + + rdata = (DTLS1_RECORD_DATA *)item->data; + +- SSL3_BUFFER_release(&s->rlayer.rbuf); ++ /* ++ * The record was read into a read buffer at least this size, so it must fit ++ * - see the length checks in ssl3_read_n(). Verify it rather than risk ++ * overrunning the buffer if that ever ceases to hold. ++ */ ++ if (!ossl_assert(rdata->packet_length <= s->rlayer.rbuf.len)) { ++ OPENSSL_free(rdata->packet); ++ return 0; ++ } + +- s->rlayer.packet = rdata->packet; ++ /* ++ * rdata->packet is a standalone copy of this record's on-wire bytes (see ++ * dtls1_buffer_record()). Copy it into the live read buffer so that ++ * s->rlayer.packet continues to point inside s->rlayer.rbuf.buf, as it ++ * does for every other record, then free our standalone copy. ++ */ ++ memcpy(s->rlayer.rbuf.buf, rdata->packet, rdata->packet_length); ++ s->rlayer.rbuf.offset = 0; ++ s->rlayer.rbuf.left = 0; ++ s->rlayer.packet = s->rlayer.rbuf.buf; + s->rlayer.packet_length = rdata->packet_length; +- memcpy(&s->rlayer.rbuf, &(rdata->rbuf), sizeof(SSL3_BUFFER)); + memcpy(&s->rlayer.rrec, &(rdata->rrec), sizeof(SSL3_RECORD)); + ++ /* ++ * dtls1_buffer_record() rebased rrec.data/input onto rdata->packet if they ++ * pointed into this record's own bytes, so translate them again onto the ++ * record's new location in the read buffer. Anything still pointing ++ * outside rdata->packet is either a separate allocation (rr->comp) or a ++ * leftover from a previously processed record that will be overwritten ++ * before use, so leave it alone. ++ */ ++ if (rdata->rrec.data >= rdata->packet ++ && rdata->rrec.data < rdata->packet + rdata->packet_length) ++ s->rlayer.rrec[0].data = s->rlayer.packet + (rdata->rrec.data - rdata->packet); ++ if (rdata->rrec.input >= rdata->packet ++ && rdata->rrec.input < rdata->packet + rdata->packet_length) ++ s->rlayer.rrec[0].input = s->rlayer.packet + (rdata->rrec.input - rdata->packet); ++ ++ OPENSSL_free(rdata->packet); ++ + /* Set proper sequence number for mac calculation */ +- memcpy(&(s->rlayer.read_sequence[2]), &(rdata->packet[5]), 6); ++ memcpy(&(s->rlayer.read_sequence[2]), &(s->rlayer.packet[5]), 6); + + return 1; + } +@@ -161,11 +194,37 @@ int dtls1_buffer_record(SSL *s, record_pqueue *queue, unsigned char *priority) + return -1; + } + +- rdata->packet = s->rlayer.packet; ++ /* ++ * Take a copy of just this record's own on-wire bytes - the header plus the ++ * record body, which for an already-processed record holds the plaintext ++ * decrypted in place - rather than the whole (much larger) read buffer. The ++ * live s->rlayer.rbuf is left untouched and continues to be used for ++ * subsequent reads. ++ */ + rdata->packet_length = s->rlayer.packet_length; +- memcpy(&(rdata->rbuf), &s->rlayer.rbuf, sizeof(SSL3_BUFFER)); ++ rdata->packet = OPENSSL_memdup(s->rlayer.packet, s->rlayer.packet_length); ++ if (rdata->packet == NULL) { ++ OPENSSL_free(rdata); ++ pitem_free(item); ++ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE); ++ return -1; ++ } + memcpy(&(rdata->rrec), &s->rlayer.rrec, sizeof(SSL3_RECORD)); + ++ /* ++ * The copied rrec.data/input still point into the live read buffer. Rebase ++ * any that point within this record's own bytes onto our standalone copy, ++ * so that dtls1_copy_record() can translate them again on retrieval. ++ * Pointers elsewhere (e.g. into rr->comp, or left over from a previously ++ * processed record) are not ours to move and are left alone. ++ */ ++ if (rdata->rrec.data >= s->rlayer.packet ++ && rdata->rrec.data < s->rlayer.packet + s->rlayer.packet_length) ++ rdata->rrec.data = rdata->packet + (rdata->rrec.data - s->rlayer.packet); ++ if (rdata->rrec.input >= s->rlayer.packet ++ && rdata->rrec.input < s->rlayer.packet + s->rlayer.packet_length) ++ rdata->rrec.input = rdata->packet + (rdata->rrec.input - s->rlayer.packet); ++ + item->data = rdata; + + #ifndef OPENSSL_NO_SCTP +@@ -178,22 +237,9 @@ int dtls1_buffer_record(SSL *s, record_pqueue *queue, unsigned char *priority) + } + #endif + +- s->rlayer.packet = NULL; +- s->rlayer.packet_length = 0; +- memset(&s->rlayer.rbuf, 0, sizeof(s->rlayer.rbuf)); +- memset(&s->rlayer.rrec, 0, sizeof(s->rlayer.rrec)); +- +- if (!ssl3_setup_buffers(s)) { +- /* SSLfatal() already called */ +- OPENSSL_free(rdata->rbuf.buf); +- OPENSSL_free(rdata); +- pitem_free(item); +- return -1; +- } +- + if (pqueue_insert(queue->q, item) == NULL) { + /* Must be a duplicate so ignore it */ +- OPENSSL_free(rdata->rbuf.buf); ++ OPENSSL_free(rdata->packet); + OPENSSL_free(rdata); + pitem_free(item); + } +@@ -204,15 +250,16 @@ int dtls1_buffer_record(SSL *s, record_pqueue *queue, unsigned char *priority) + int dtls1_retrieve_buffered_record(SSL *s, record_pqueue *queue) + { + pitem *item; ++ int ret; + + item = pqueue_pop(queue->q); + if (item) { +- dtls1_copy_record(s, item); ++ ret = dtls1_copy_record(s, item); + + OPENSSL_free(item->data); + pitem_free(item); + +- return 1; ++ return ret; + } + + return 0; +@@ -257,7 +304,13 @@ int dtls1_process_buffered_records(SSL *s) + + /* Process all the records. */ + while (pqueue_peek(s->rlayer.d->unprocessed_rcds.q)) { +- dtls1_get_unprocessed_record(s); ++ if (!dtls1_get_unprocessed_record(s)) { ++ /* ++ * Should not happen. The record has been dropped, so move on ++ * to the next one. ++ */ ++ continue; ++ } + bitmap = dtls1_get_bitmap(s, rr, &is_next_epoch); + if (bitmap == NULL) { + /* +@@ -401,6 +454,7 @@ int dtls1_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf, + } + #endif + ++ /* On failure the record is simply dropped */ + dtls1_copy_record(s, item); + + OPENSSL_free(item->data); +diff --git a/ssl/record/record.h b/ssl/record/record.h +index 234656b..0211480 100644 +--- a/ssl/record/record.h ++++ b/ssl/record/record.h +@@ -85,7 +85,6 @@ typedef struct record_pqueue_st { + typedef struct dtls1_record_data_st { + unsigned char *packet; + size_t packet_length; +- SSL3_BUFFER rbuf; + SSL3_RECORD rrec; + #ifndef OPENSSL_NO_SCTP + struct bio_dgram_sctp_rcvinfo recordinfo; +diff --git a/ssl/record/ssl3_record.c b/ssl/record/ssl3_record.c +index 3c0b132..dd659d0 100644 +--- a/ssl/record/ssl3_record.c ++++ b/ssl/record/ssl3_record.c +@@ -1674,8 +1674,11 @@ int dtls1_process_record(SSL *s, DTLS1_BITMAP *bitmap) + * after use :-). + */ + +- /* we have pulled in a full packet so zero things */ +- RECORD_LAYER_reset_packet_length(&s->rlayer); ++ /* ++ * Leave s->rlayer.packet_length alone: ssl3_read_n() starts each new record ++ * by resetting it, and it must still describe this record's on-wire bytes ++ * for dtls1_buffer_record() should this record end up being buffered. ++ */ + + /* Mark receipt of record. */ + dtls1_record_bitmap_update(s, bitmap); diff --git a/openssl-3.0-CVE-2026-63072.patch b/openssl-3.0-CVE-2026-63072.patch new file mode 100644 index 0000000000000000000000000000000000000000..90d53c5616370255fb70cafd3f13c4e447399968 --- /dev/null +++ b/openssl-3.0-CVE-2026-63072.patch @@ -0,0 +1,49 @@ +From a0c8ec557d9cac078f032d76cdf684fe743eb382 Mon Sep 17 00:00:00 2001 +From: Daniel Kubec +Date: Sun, 2 Aug 2026 00:23:39 +0000 +Subject: [PATCH] Fix heap buffer overflow (8-byte OOB write) in AES-WRAP-PAD + unwrap + +On its integrity-failure paths that primitive writes and cleanses up to inlen +bytes of the output buffer. Size the buffer for that worst case so a failed +unwrap cannot write past the allocation. + +Fixes CVE-2026-63072 + +Reviewed-by: Milan Broz +Reviewed-by: Andrew Dinh +Merge-date: Mon Aug 24 14:44:55 2026 + +Adapted-by: PkgAgent/deepseek-v4 (modified to adapt to opencloudos-stream) + +--- + crypto/cms/cms_kari.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/crypto/cms/cms_kari.c b/crypto/cms/cms_kari.c +index a2f422a..302e7ac 100644 +--- a/crypto/cms/cms_kari.c ++++ b/crypto/cms/cms_kari.c +@@ -216,6 +216,7 @@ static int cms_kek_cipher(unsigned char **pout, size_t *poutlen, + size_t keklen; + int rv = 0; + unsigned char *out = NULL; ++ size_t outsize; + int outlen; + + keklen = EVP_CIPHER_CTX_get_key_length(kari->ctx); +@@ -230,7 +231,13 @@ static int cms_kek_cipher(unsigned char **pout, size_t *poutlen, + /* obtain output length of ciphered key */ + if (!EVP_CipherUpdate(kari->ctx, NULL, &outlen, in, inlen)) + goto err; +- out = OPENSSL_malloc(outlen); ++ /* ++ * On its integrity-failure paths that primitive writes and cleanses up to ++ * inlen bytes of the output buffer. Size the buffer for that worst case so ++ * a failed unwrap cannot write past the allocation. ++ */ ++ outsize = (size_t)outlen < inlen ? inlen : (size_t)outlen; ++ out = OPENSSL_malloc(outsize); + if (out == NULL) + goto err; + if (!EVP_CipherUpdate(kari->ctx, out, &outlen, in, inlen)) diff --git a/openssl-3.0-CVE-2026-63076.patch b/openssl-3.0-CVE-2026-63076.patch new file mode 100644 index 0000000000000000000000000000000000000000..ae48e26f2b833b0d1159ead45fee8e879f606af8 --- /dev/null +++ b/openssl-3.0-CVE-2026-63076.patch @@ -0,0 +1,39 @@ +From 37882aa2e0256e1072442a8f62f7db45b995c45b Mon Sep 17 00:00:00 2001 +From: Daniel Kubec +Date: Tue, 21 Jul 2026 11:19:29 +0200 +Subject: [PATCH] Fix Remote NULL deref in ossl_cmp_calc_protection() via + crafted protectionAlg + +ossl_cmp_calc_protection() only checked whether the protectionAlg parameter +(ppval) was NULL before treating it as a PBMParameter ASN1_STRING. + +X509_ALGOR_get0() does not validate the ASN.1 type of the parameter against what +the caller expects. For id-PasswordBasedMAC, a crafted message can encode the +parameter as a BOOLEAN instead of the expected PBMParameter SEQUENCE. Because +the ASN1_TYPE value union overlays the boolean int on the pointer field, ppval +comes back as a bogus non-NULL pointer (e.g. 0xff). + +Fixes CVE-2026-63076 + +Reviewed-by: Milan Broz +Reviewed-by: Norbert Pocs +Merge-date: Sat Aug 22 06:12:04 2026 + + +--- + crypto/cmp/cmp_protect.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/crypto/cmp/cmp_protect.c b/crypto/cmp/cmp_protect.c +index 539f653..846ba6b 100644 +--- a/crypto/cmp/cmp_protect.c ++++ b/crypto/cmp/cmp_protect.c +@@ -63,7 +63,7 @@ ASN1_BIT_STRING *ossl_cmp_calc_protection(const OSSL_CMP_CTX *ctx, + ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_PBM_SECRET); + return NULL; + } +- if (ppval == NULL) { ++ if (pptype != V_ASN1_SEQUENCE || ppval == NULL) { + ERR_raise(ERR_LIB_CMP, CMP_R_ERROR_CALCULATING_PROTECTION); + return NULL; + } diff --git a/openssl.spec b/openssl.spec index d9d5ca4fc4b186d3ef3757fd14b7e2731ae7c3a3..be4c6ee90b62e6f5e68cca3e5cf5e0089022ae7a 100644 --- a/openssl.spec +++ b/openssl.spec @@ -21,7 +21,7 @@ print(string.sub(hash, 0, 16)) Summary: Utilities from the general purpose cryptography library with TLS implementation Name: openssl Version: 3.0.12 -Release: 27%{?dist} +Release: 28%{?dist} License: ASL 2.0 URL: http://www.openssl.org/ Source0: https://www.openssl.org/source/openssl-%{version}.tar.gz @@ -146,6 +146,12 @@ Patch0040: openssl-3.0-CVE-2026-22795-CVE-2026-22796.patch Patch0041: openssl-3.0-CVE-2026-45447.patch # https://github.com/openssl/openssl/commit/03c1f4d45fb963aee7d5833390c507cd290182bc Patch0042: openssl-3.0-CVE-2026-34182.patch +# https://github.com/openssl/openssl/commit/1ca5a124b48f1ffe6980ad33724b452fbd62da1e +Patch0043: openssl-3.0-CVE-2026-54874.patch +# https://github.com/openssl/openssl/commit/a0c8ec557d9cac078f032d76cdf684fe743eb382 +Patch0044: openssl-3.0-CVE-2026-63072.patch +# https://github.com/openssl/openssl/commit/37882aa2e0256e1072442a8f62f7db45b995c45b +Patch0045: openssl-3.0-CVE-2026-63076.patch Patch5000: set-Availablein-default-in-evppkey_rsa_common.patch Patch5001: openssl-3.0.12-support-tlcp.patch @@ -467,6 +473,10 @@ install -m644 %{SOURCE5} \ %changelog +* Wed Sep 23 2026 PkgAgent Robot - 3.0.12-28 +- [Type] security +- [DESC] Fix CVE-2026-54874, CVE-2026-63072, CVE-2026-63076 + * Mon Jun 15 2026 Feng Weiyao - 3.0.12-27 - [Type] security - [DESC] Fix CVE-2026-34182, CVE-2026-45447