Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 153 additions & 0 deletions SPECS/openssl/CVE-2026-14457.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
From 1e8c398db67404babd3e5af999bb6bd86f720c76 Mon Sep 17 00:00:00 2001
From: Viktor Dukhovni <viktor@openssl.org>
Date: Sat, 27 Jun 2026 01:02:53 +1000
Subject: [PATCH] Handle signature_algorithms_cert extension in key-only
context

Servers or clients that configure only a private key in
expectation of always negotiating use of RFC7250 raw public keys
failed to handle the "signature_algorithms_cert" extension.

The issue is now resolved and the RPK tests now check that
key-only configurations are robust also when the extension
is sent by the peer.

Key-only configurations are quite uncommon. As a best practice,
RPK-capable servers and clients pair their private key with a
(possibly self-signed) certificate, enabling fallback to X.509
handshakes with non-RPK peers.

Fixes CVE-2026-14457

Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Norbert Pocs <norbertp@openssl.org>
Merge-date: Mon Aug 24 12:49:45 2026

Upstream Patch Reference: https://github.com/openssl/openssl/commit/1e8c398db67404babd3e5af999bb6bd86f720c76.patch
---
ssl/t1_lib.c | 14 ++++++++++
test/rpktest.c | 76 +++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 80 insertions(+), 10 deletions(-)

diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 93367804bbbc7..89faa0b642692 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -3634,6 +3634,20 @@ static int check_cert_usable(SSL_CONNECTION *s, const SIGALG_LOOKUP *sig,
if (supported <= 0)
return 0;

+ /*
+ * When RPK is negotiated there are no certificate signatures to
+ * constrain, and there may not even be a certificate configured.
+ */
+ if (TLSEXT_cert_type_rpk == (s->server ? s->ext.server_cert_type : s->ext.client_cert_type))
+ return 1;
+
+ /*
+ * RPK was enabled, adding candidate private-key-only slots, but was not
+ * negotiated, so the key-only slot is not usable.
+ */
+ if (x == NULL)
+ return 0;
+
/*
* The TLS 1.3 signature_algorithms_cert extension places restrictions
* on the sigalg with which the certificate was signed (by its issuer).
diff --git a/test/rpktest.c b/test/rpktest.c
index 4722d6dc3c702..a2505a4254e58 100644
--- a/test/rpktest.c
+++ b/test/rpktest.c
@@ -38,6 +38,37 @@ static OSSL_PROVIDER *defctxnull = NULL;
static const unsigned char cert_type_rpk[] = { TLSEXT_cert_type_rpk, TLSEXT_cert_type_x509 };
static const unsigned char SID_CTX[] = { 'r', 'p', 'k' };

+/*
+ * Wire form of a SignatureSchemeList that lists rsa_pkcs1_sha256
+ * and ed448 -- between them they cover the issuer signature on
+ * every cert this file loads from test/certs
+ * (sha256WithRSAEncryption for the RSA/ECDSA/Ed25519 leaves and
+ * ED448 for the Ed448 leaf), so the extension is harmless when
+ * the handshake is non-RPK and the server's check_cert_usable()
+ * has to walk the list against a real cert. When RPK is
+ * negotiated check_cert_usable() returns early without inspecting
+ * the list, and when the slot is an RPK-listed key-only slot but
+ * X509 was negotiated check_cert_usable() returns 0 on the x ==
+ * NULL path -- the inevitable outcome, now discovered earlier.
+ *
+ * Payload: length, rsa_pkcs1_sha256, ed448
+ */
+static const unsigned char sigalgs_cert_payload[] = {
+ 0x00, 0x04,
+ 0x04, 0x01,
+ 0x08, 0x08
+};
+
+static int sigalgs_cert_add_cb(SSL *s, unsigned int ext_type,
+ unsigned int context,
+ const unsigned char **out, size_t *outlen,
+ X509 *x, size_t chainidx, int *al, void *add_arg)
+{
+ *out = sigalgs_cert_payload;
+ *outlen = sizeof(sigalgs_cert_payload);
+ return 1;
+}
+
static int rpk_verify_client_cb(int ok, X509_STORE_CTX *ctx)
{
int err = X509_STORE_CTX_get_error(ctx);
@@ -255,18 +286,43 @@ static int test_rpk(int idx)
/* NEW */
SSL_CTX_set_verify(cctx, SSL_VERIFY_PEER, rpk_verify_client_cb);

- if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
- NULL, NULL)))
+ /*
+ * Send signature_algorithms_cert in every ClientHello, and in
+ * every TLS 1.3 CertificateRequest. The OpenSSL stack doesn't
+ * construct this extension by default in either direction, so
+ * register a custom add hook on both ends. This exercises the
+ * three distinct paths through check_cert_usable() on whichever
+ * side receives the extension:
+ * - RPK was negotiated for this side's cert -- early return 1,
+ * list contents ignored.
+ * - RPK was offered but X509 was negotiated and this side's
+ * slot holds only a private key -- x == NULL, return 0
+ * (any peer-sent signature_algorithms_cert against a key-only
+ * slot would otherwise trigger a crash).
+ * - X509 negotiated with a real cert -- walk the list, find
+ * a match against the issuer's signature algorithm.
+ * The server's registration only fires on TLS 1.3 connections
+ * where the server requests a client certificate (case 2, 9,
+ * 10 etc.); on TLS 1.2 the sigalgs travel inside the
+ * CertificateRequest body, not as a separate extension.
+ */
+ if (!TEST_true(SSL_CTX_add_custom_ext(cctx,
+ TLSEXT_TYPE_signature_algorithms_cert,
+ SSL_EXT_CLIENT_HELLO,
+ sigalgs_cert_add_cb, NULL, NULL,
+ NULL, NULL))
+ || !TEST_true(SSL_CTX_add_custom_ext(sctx,
+ TLSEXT_TYPE_signature_algorithms_cert,
+ SSL_EXT_TLS1_3_CERTIFICATE_REQUEST,
+ sigalgs_cert_add_cb, NULL, NULL,
+ NULL, NULL))
+ || !TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
+ NULL, NULL))
+ || !TEST_int_gt(SSL_dane_enable(serverssl, NULL), 0)
+ || !TEST_int_gt(SSL_dane_enable(clientssl, "example.com"), 0)
+ || !TEST_int_eq(SSL_use_PrivateKey_file(serverssl, privkey_file, SSL_FILETYPE_PEM), 1))
goto end;

- if (!TEST_int_gt(SSL_dane_enable(serverssl, NULL), 0))
- goto end;
- if (!TEST_int_gt(SSL_dane_enable(clientssl, "example.com"), 0))
- goto end;
-
- /* Set private key and certificate */
- if (!TEST_int_eq(SSL_use_PrivateKey_file(serverssl, privkey_file, SSL_FILETYPE_PEM), 1))
- goto end;
/* Only a private key */
if (idx == 1) {
if (idx_server_server_rpk == 0 || idx_client_server_rpk == 0) {
91 changes: 91 additions & 0 deletions SPECS/openssl/CVE-2026-54874.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
From e8d55072e55dd5096de11e2e6c149f5422b9aa30 Mon Sep 17 00:00:00 2001
From: Matt Caswell <matt@openssl.foundation>
Date: Tue, 23 Jun 2026 11:53:17 +0100
Subject: [PATCH] Avoid full read buffer allocation when buffering DTLS
next-epoch records

dtls_rlayer_buffer_record() buffers records that arrive early for the
next epoch while a handshake is in progress. It did this by taking
ownership of the entire live read buffer (sized for the largest
possible record, ~16.7KB) and allocating a brand new one to carry on
reading, regardless of how small the buffered record actually was.
With the queue capped at 100 entries, a peer could send around 100
tiny bogus next-epoch records (~14 bytes each on the wire) and force
around 1.7MB of heap allocation per connection.

Instead, copy only the record's own on-wire bytes (header and
ciphertext) into the queue entry, and leave the live read buffer
untouched. Memory use is now proportional to what the peer actually
sends.

Fixes CVE-2026-54874

Assisted-by: Claude:claude-sonnet-4-6
Reviewed-by: Milan Broz <mbroz@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Andrew Dinh <andrewd@openssl.org>
Merge-date: Mon Aug 24 15:39:09 2026
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: https://github.com/openssl/openssl/commit/7110cb2f75806d0bf809eb2f90790d477900be40.patch
---
ssl/record/methods/dtls_meth.c | 29 +++++++++++++----------------
1 file changed, 13 insertions(+), 16 deletions(-)

diff --git a/ssl/record/methods/dtls_meth.c b/ssl/record/methods/dtls_meth.c
index b13945a..9e7126c 100644
--- a/ssl/record/methods/dtls_meth.c
+++ b/ssl/record/methods/dtls_meth.c
@@ -299,29 +299,26 @@ static int dtls_rlayer_buffer_record(OSSL_RECORD_LAYER *rl, struct pqueue_st *qu
return -1;
}

- rdata->packet = rl->packet;
+ /*
+ * Take a copy of just this record's on-wire bytes (header + ciphertext)
+ * rather than the whole (much larger) read buffer. The live rl->rbuf is
+ * left untouched and continues to be used for subsequent reads.
+ */
rdata->packet_length = rl->packet_length;
- memcpy(&(rdata->rbuf), &rl->rbuf, sizeof(TLS_BUFFER));
- memcpy(&(rdata->rrec), &rl->rrec[0], sizeof(TLS_RL_RECORD));
-
- item->data = rdata;
-
- rl->packet = NULL;
- rl->packet_length = 0;
- memset(&rl->rbuf, 0, sizeof(TLS_BUFFER));
- memset(&rl->rrec[0], 0, sizeof(rl->rrec[0]));
-
- if (!tls_setup_read_buffer(rl)) {
- /* RLAYERfatal() already called */
- OPENSSL_free(rdata->rbuf.buf);
+ rdata->packet = OPENSSL_memdup(rl->packet, rl->packet_length);
+ if (rdata->packet == NULL) {
OPENSSL_free(rdata);
pitem_free(item);
+ RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_CRYPTO_LIB);
return -1;
}
+ memcpy(&(rdata->rrec), &rl->rrec[0], sizeof(TLS_RL_RECORD));
+
+ item->data = rdata;

if (pqueue_insert(queue, item) == NULL) {
/* Must be a duplicate so ignore it */
- OPENSSL_free(rdata->rbuf.buf);
+ OPENSSL_free(rdata->packet);
OPENSSL_free(rdata);
pitem_free(item);
}
@@ -607,7 +604,7 @@ static int dtls_free(OSSL_RECORD_LAYER *rl)
/* Push to the next record layer */
ret &= BIO_write_ex(rl->next, rdata->packet, rdata->packet_length,
&written);
- OPENSSL_free(rdata->rbuf.buf);
+ OPENSSL_free(rdata->packet);
OPENSSL_free(item->data);
pitem_free(item);
}
--
2.45.4

51 changes: 51 additions & 0 deletions SPECS/openssl/CVE-2026-63072.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
From c8948e1c0eaaec49987b641d5076b89d302ad2c6 Mon Sep 17 00:00:00 2001
From: Daniel Kubec <kubec@openssl.foundation>
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 <mbroz@openssl.org>
Reviewed-by: Andrew Dinh <andrewd@openssl.org>
Merge-date: Mon Aug 24 14:44:05 2026
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: https://github.com/openssl/openssl/commit/9ec2f6d2ae2bcad907cf7ee38584855bafe4979a.patch
---
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 79697cd..74d5841 100644
--- a/crypto/cms/cms_kari.c
+++ b/crypto/cms/cms_kari.c
@@ -217,6 +217,7 @@ static int cms_kek_cipher(unsigned char **pout, size_t *poutlen,
int rv = 0;
unsigned char *out = NULL;
int outlen;
+ size_t outsize;

keklen = EVP_CIPHER_CTX_get_key_length(kari->ctx);
if (keklen > EVP_MAX_KEY_LENGTH)
@@ -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))
--
2.45.4

42 changes: 42 additions & 0 deletions SPECS/openssl/CVE-2026-63073.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
From 7d84f4435382b0f6e7ec2e602064dc2f9d3664cf Mon Sep 17 00:00:00 2001
From: Norbert Pocs <norbertp@openssl.org>
Date: Mon, 20 Jul 2026 14:10:47 +0200
Subject: [PATCH] CMP unexpected sender DN used as format string in
ERR_raise_data()

ossl_cmp_msg_check_update() converts an unexpected CMP response sender DN with
X509_NAME_oneline() and passes that peer-controlled string directly as the
format argument to ERR_raise_data(). Printable percent characters survive the
DN conversion, so a sender such as CN=%s%n reaches vsnprintf() as active format
syntax without matching varargs.

Fixes: CVE-2026-63073

Original patch by: Filipe Casal of Trail of Bits in collaboration with OpenAI

Signed-off-by: Norbert Pocs <norbertp@openssl.org>
Reviewed-by: Milan Broz <mbroz@openssl.org>
Reviewed-by: Igor Ustinov <igus@openssl.foundation>
Merge-date: Mon Aug 24 13:02:29 2026
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: https://github.com/openssl/openssl/commit/a7e5a6eea8fd3ccca6b6fbba031a5fbf8a3d93b4.patch
---
crypto/cmp/cmp_vfy.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crypto/cmp/cmp_vfy.c b/crypto/cmp/cmp_vfy.c
index d6d18d7..f89a65f 100644
--- a/crypto/cmp/cmp_vfy.c
+++ b/crypto/cmp/cmp_vfy.c
@@ -730,7 +730,7 @@ int ossl_cmp_msg_check_update(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg,
"expected sender", expected_sender)) {
str = X509_NAME_oneline(actual_sender, NULL, 0);
ERR_raise_data(ERR_LIB_CMP, CMP_R_UNEXPECTED_SENDER,
- str != NULL ? str : "<unknown>");
+ "%s", str != NULL ? str : "<unknown>");
OPENSSL_free(str);
return 0;
}
--
2.45.4

53 changes: 53 additions & 0 deletions SPECS/openssl/CVE-2026-63074.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
From 2e579bc487e3ba8f7161796eaa554cc239a66d21 Mon Sep 17 00:00:00 2001
From: Neil Horman <nhorman@openssl.org>
Date: Tue, 30 Jun 2026 15:09:01 -0400
Subject: [PATCH] Fix unbounded cert cache growth in cmp

If a remote user sends cmp messages to a server with a list of
extraCerts and the message is rejected, the extraCerts from the message
remain in the server contexts untrusted certificate stack. This exposes
servers with long lived ctx objects to denial of service attacks in
which an attacker sends messages intending to be rejected with a large
list of additional cerificated repeatedly, forcing the server to store
them indefinately.

Fix it by rolling back the added extra certs if the message is rejected,
using the same method we do when the context is configured to not do
caching at all.

Fixes openssl/srt#224

Fixes CVE-2026-63074

Reviewed-by: Milan Broz <mbroz@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Merge-date: Mon Aug 24 12:42:57 2026
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: https://github.com/openssl/openssl/commit/f636f9ca0fa1bae5b42f9e787f025c96fb09c43a.patch
---
crypto/cmp/cmp_vfy.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/crypto/cmp/cmp_vfy.c b/crypto/cmp/cmp_vfy.c
index f89a65f..b6d69ff 100644
--- a/crypto/cmp/cmp_vfy.c
+++ b/crypto/cmp/cmp_vfy.c
@@ -774,8 +774,13 @@ int ossl_cmp_msg_check_update(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg,
res = 1; /* support more aggressive fuzzing by letting invalid msg pass */
#endif

- /* remove extraCerts again if not caching */
- if (ctx->noCacheExtraCerts)
+ /*
+ * remove extraCerts again if not caching
+ * or if we failed validation above, lest a remote user
+ * starts sending us lots of certificates in invalid messages
+ * leading to a DOS from unbounded certificate stack growth
+ */
+ if (ctx->noCacheExtraCerts || res != 1)
while (num_added-- > 0)
X509_free(sk_X509_shift(ctx->untrusted));

--
2.45.4

Loading
Loading