Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected]
Control: affects -1 + src:qpid-proton
User: [email protected]
Usertags: pu

Hi,

I'd like to upload qpid-proton/0.37.0-6.

[ Reason ]
qpid-proton 0.37.0 (and every other release up to and including
0.40.0) is affected by four CVEs, reported by Salvatore Bonaccorso in
#1152779 and all fixed upstream in 0.41.1:

  * CVE-2026-105031: the OpenSSL TLS implementation added to rather
    than replaced the default system trust anchors, potentially
    trusting unexpected peer certificates.
  * CVE-2026-105042: a deeply nested message body decoded by
    pn_message_body() could overflow the stack and cause a crash.
  * CVE-2026-114230: when dispatching SASL frames, Proton also
    dispatched AMQP frames claiming to be SASL frames, allowing
    authentication bypass.
  * CVE-2026-115942: a misconfigured wildcard TLS certificate causes a
    stack read overflow.

These are long-standing upstream issues, not a regression introduced
during the trixie cycle.

[ Impact ]
Users of the Proton C/C++ libraries and of the Python bindings
(python3-qpid-proton) remain exposed to remote crash (CVE-2026-105042),
TLS peer impersonation against applications relying on
pn_ssl_domain_set_trusted_ca_db() (CVE-2026-105031), authentication
bypass against SASL endpoints (CVE-2026-114230) and an out-of-bounds
read triggered by a misconfigured wildcard certificate
(CVE-2026-115942).

[ Tests ]
Most backported patches carry regression tests, ran at package build.

[ Risks ]
CVE-2026-105031 and CVE-2026-114230 are direct cherry-picks of the
upstream 0.41.1 fixes (PROTON-2976, PROTON-2970); the former
intentionally changes pn_ssl_domain_set_trusted_ca_db() to replace the
default trust store instead of adding to it, which is the documented
upstream behaviour and the point of the fix. CVE-2026-105042 and
CVE-2026-115942 are fixed with minimal bounds instead of the large
upstream 0.41.1 reworks (iterative decoder, RFC 9525 host name
verification), which are not practical to backport to 0.37.x: the
decoder refuses to descend past 1000 levels, and wildcard patterns that
cannot match without reading out of bounds are rejected. Legitimate
AMQP messages nest far below these limits. The changes are small,
self-contained and reviewed; the alternative (upgrading stable to
0.41.1) was rejected as too large a delta.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

[ Changes ]
The patches, plus python3-setuptools in Build-Depends:.

[ Other info ]
Upstream 0.41.1 (already in unstable as 0.41.1-1) fixes the same four
CVEs with larger reworks of the decoder and of the TLS host name
verification; it was deliberately not considered for trixie. The
debdiff is attached: qpid-proton_0.37.0-6+deb13u1.debdiff

Package is already uploaded.

Cheers,

Thomas Goirand (zigo)
diff -Nru qpid-proton-0.37.0/debian/changelog 
qpid-proton-0.37.0/debian/changelog
--- qpid-proton-0.37.0/debian/changelog 2025-02-26 14:19:48.000000000 +0100
+++ qpid-proton-0.37.0/debian/changelog 2026-11-10 16:08:41.000000000 +0100
@@ -1,3 +1,32 @@
+qpid-proton (0.37.0-6+deb13u1) trixie; urgency=medium
+
+  * CVE-2026-105031: the OpenSSL TLS implementation added to rather
+    than replaced the default system trust anchors, potentially
+    trusting unexpected peer certificates. Applied upstream patch
+    "PROTON-2976: Make pn_ssl_domain_set_trusted_ca_db replace the
+    default CAs":
+    - CVE-2026-105031_Make-pn_ssl_domain_set_trusted_ca_db-replace-the-.patch
+  * CVE-2026-105042: a deeply nested message body decoded by
+    pn_message_body() could overflow the stack and crash the
+    application. Applied a minimal fix bounding the decoder nesting
+    depth (upstream reworked the decoder in 0.41.1):
+    - CVE-2026-105042_Limit-the-AMQP-decoder-nesting-depth.patch
+  * CVE-2026-114230: SASL frame dispatch also dispatched AMQP frames
+    claiming to be SASL frames, allowing authentication bypass.
+    Applied upstream patch "PROTON-2970: Validate the SASL state
+    before handling a frame":
+    - CVE-2026-114230_Validate-the-SASL-state-before-handling-a-fra....patch
+  * CVE-2026-115942: a misconfigured wildcard TLS certificate causes a
+    stack read overflow. Applied a minimal fix rejecting wildcard
+    patterns that cannot match without reading out of bounds (upstream
+    enforces RFC 9525 through a full rework in 0.41.1):
+    - CVE-2026-115942_Bound-wildcard-pattern-match-in-match_dns_pattern.patch
+    (Closes: #1152779)
+  * debian/control: Add python3-setuptools to Build-Depends, for the
+    distutils shim needed by the CMake Python detection with Python 3.13.
+
+ -- Thomas Goirand <[email protected]>  Tue, 10 Nov 2026 16:08:41 +0100
+
 qpid-proton (0.37.0-6) unstable; urgency=medium
 
   * Rebuild to remove python 3.12 support.
diff -Nru qpid-proton-0.37.0/debian/control qpid-proton-0.37.0/debian/control
--- qpid-proton-0.37.0/debian/control   2025-02-26 14:19:48.000000000 +0100
+++ qpid-proton-0.37.0/debian/control   2026-11-10 16:08:41.000000000 +0100
@@ -14,6 +14,7 @@
  pkgconf,
  python3-all,
  python3-all-dev,
+ python3-setuptools,
  python3-sphinx,
  swig,
  uuid-dev,
diff -Nru 
qpid-proton-0.37.0/debian/patches/CVE-2026-105031_Make-pn_ssl_domain_set_trusted_ca_db-replace-the-.patch
 
qpid-proton-0.37.0/debian/patches/CVE-2026-105031_Make-pn_ssl_domain_set_trusted_ca_db-replace-the-.patch
--- 
qpid-proton-0.37.0/debian/patches/CVE-2026-105031_Make-pn_ssl_domain_set_trusted_ca_db-replace-the-.patch
   1970-01-01 01:00:00.000000000 +0100
+++ 
qpid-proton-0.37.0/debian/patches/CVE-2026-105031_Make-pn_ssl_domain_set_trusted_ca_db-replace-the-.patch
   2026-11-10 16:08:41.000000000 +0100
@@ -0,0 +1,225 @@
+Description: Make pn_ssl_domain_set_trusted_ca_db replace the default CAs
+ When using the OpenSSL TLS implementation, both the
+ pn_ssl_domain_set_trusted_ca_db() & pn_tls_config_set_trusted_certs()
+ APIs added to rather than replaced the default system trust anchors,
+ leading to potentially trusting unexpected peer certificates: the
+ context now gets a fresh certificate store instead of loading into
+ the one holding the system defaults.
+ .
+ Fixes CVE-2026-105031.
+Author: Andrew Stitcher <[email protected]>
+Date: Mon, 28 Sep 2026 21:20:01 -0400
+Origin: upstream, 
https://github.com/apache/qpid-proton/commit/d793f97e4a2d0f960eda32724a2e867236468337
+Last-Update: 2026-11-10
+
+Index: qpid-proton/c/include/proton/ssl.h
+===================================================================
+--- qpid-proton.orig/c/include/proton/ssl.h
++++ qpid-proton/c/include/proton/ssl.h
+@@ -174,6 +174,11 @@ PN_EXTERN int  pn_ssl_domain_set_credent
+  * will depend on how the OS is set up. When using the Windows SChannel 
implementation the default
+  * will be the users default trusted certificate store.
+  *
++ * @note The database given here replaces that default rather than adding to 
it, so after
++ * this call the certificates named here are the only trust anchors the 
domain will accept.
++ * Repeating the call replaces the database again; to trust more than one CA, 
name a
++ * directory or a file holding all of them.
++ *
+  * @param[in] domain the ssl domain that will use the database.
+  * @param[in] certificate_db database of trusted CAs, used to authenticate 
the peer.
+  * @return 0 on success
+Index: qpid-proton/c/include/proton/tls.h
+===================================================================
+--- qpid-proton.orig/c/include/proton/tls.h
++++ qpid-proton/c/include/proton/tls.h
+@@ -170,6 +170,11 @@ PN_TLS_EXTERN int  pn_tls_config_set_cre
+  * will depend on how the OS is set up. When using the Windows SChannel 
implementation the default
+  * will be the users default trusted certificate store.
+  *
++ * @note The database given here replaces that default rather than adding to 
it, so after
++ * this call the certificates named here are the only trust anchors the 
domain will accept.
++ * Repeating the call replaces the database again; to trust more than one CA, 
name a
++ * directory or a file holding all of them.
++ *
+  * @param[in] domain the tls domain that will use the database.
+  * @param[in] certificate_db database of trusted CAs, used to authenticate 
the peer.
+  * @return 0 on success
+Index: qpid-proton/c/src/ssl/openssl.c
+===================================================================
+--- qpid-proton.orig/c/src/ssl/openssl.c
++++ qpid-proton/c/src/ssl/openssl.c
+@@ -716,11 +716,28 @@ int pn_ssl_domain_set_trusted_ca_db(pn_s
+     file = certificate_db;
+   }
+ 
+-  if (SSL_CTX_load_verify_locations( domain->ctx, file, dir ) != 1) {
+-    ssl_log_error("SSL_CTX_load_verify_locations( %s ) failed", 
certificate_db);
++  /* Replace the context's store rather than loading into it: the store 
already holds the
++     system default certificates, installed when the domain was created, and 
the documented
++     contract is that naming a database overrides that default rather than 
adding to it.
++     Loading into the existing store would leave every system CA a trust 
anchor, so a peer
++     holding any publicly issued certificate would verify against a domain 
configured to
++     trust one private CA. SSL_CTX_set_cert_store() takes ownership and frees 
the old
++     store. */
++  X509_STORE *store = X509_STORE_new();
++  if (!store) {
++    ssl_log_error("Unable to allocate certificate store");
+     return -1;
+   }
++  // Support intermediate/subordinate CAs as trust anchors, as the replaced 
store did.
++  X509_STORE_set_flags(store, X509_V_FLAG_PARTIAL_CHAIN);
+ 
++  if (X509_STORE_load_locations( store, file, dir ) != 1) {
++    ssl_log_error("X509_STORE_load_locations( %s ) failed", certificate_db);
++    X509_STORE_free(store);
++    return -1;
++  }
++
++  SSL_CTX_set_cert_store( domain->ctx, store );
+   return 0;
+ }
+ 
+Index: qpid-proton/c/src/tls/openssl.c
+===================================================================
+--- qpid-proton.orig/c/src/tls/openssl.c
++++ qpid-proton/c/src/tls/openssl.c
+@@ -971,11 +971,28 @@ int pn_tls_config_set_trusted_certs(pn_t
+     file = certificate_db;
+   }
+ 
+-  if (SSL_CTX_load_verify_locations( domain->ctx, file, dir ) != 1) {
+-    ssl_log_error("SSL_CTX_load_verify_locations( %s ) failed", 
certificate_db);
++  /* Replace the context's store rather than loading into it: the store 
already holds the
++     system default certificates, installed when the domain was created, and 
the documented
++     contract is that naming a database overrides that default rather than 
adding to it.
++     Loading into the existing store would leave every system CA a trust 
anchor, so a peer
++     holding any publicly issued certificate would verify against a domain 
configured to
++     trust one private CA. SSL_CTX_set_cert_store() takes ownership and frees 
the old
++     store. */
++  X509_STORE *store = X509_STORE_new();
++  if (!store) {
++    ssl_log_error("Unable to allocate certificate store");
+     return -1;
+   }
++  // Support intermediate/subordinate CAs as trust anchors, as the replaced 
store did.
++  X509_STORE_set_flags(store, X509_V_FLAG_PARTIAL_CHAIN);
+ 
++  if (X509_STORE_load_locations( store, file, dir ) != 1) {
++    ssl_log_error("X509_STORE_load_locations( %s ) failed", certificate_db);
++    X509_STORE_free(store);
++    return -1;
++  }
++
++  SSL_CTX_set_cert_store( domain->ctx, store );
+   return 0;
+ }
+ 
+Index: qpid-proton/python/tests/proton_tests/ssl.py
+===================================================================
+--- qpid-proton.orig/python/tests/proton_tests/ssl.py
++++ qpid-proton/python/tests/proton_tests/ssl.py
+@@ -231,6 +231,104 @@ class SslTest(common.Test):
+         server.connection.close()
+         self._pump(client, server)
+ 
++    def _system_ca_domains(self, system_ca):
++        """ Build a fresh client/server domain pair with the system default 
trust store
++        pointed at system_ca.
++
++        OpenSSL reads SSL_CERT_FILE when the domain installs the system 
defaults, so the
++        variable has to be in place before the domains are constructed - 
hence the fresh
++        pair rather than the ones setUp() made.  SChannel takes its default 
from the
++        Windows certificate store and ignores the variable.
++        """
++        old = os.environ.get("SSL_CERT_FILE")
++        os.environ["SSL_CERT_FILE"] = system_ca
++        try:
++            return (SSLDomain(SSLDomain.MODE_SERVER), 
SSLDomain(SSLDomain.MODE_CLIENT))
++        finally:
++            if old is None:
++                del os.environ["SSL_CERT_FILE"]
++            else:
++                os.environ["SSL_CERT_FILE"] = old
++
++    def test_trusted_ca_db_replaces_system_default(self):
++        """ A configured CA database overrides the system default rather than 
adding to it.
++
++        Without this the system store stays in the trust set, so a 
certificate issued by
++        any CA the machine happens to trust verifies against a domain 
configured to trust
++        one private CA.  bad-server is self-signed, so treating it as a 
system root makes
++        it exactly such a certificate.
++        """
++        if os.name == "nt":
++            raise Skipped("Windows SChannel does not use SSL_CERT_FILE.")
++        server_domain, client_domain = self._system_ca_domains(
++            self._testpath("bad-server-certificate.pem"))
++
++        
server_domain.set_credentials(self._testpath("bad-server-certificate.pem"),
++                                      
self._testpath("bad-server-private-key.pem"),
++                                      "server-password")
++        client_domain.set_trusted_ca_db(self._testpath("ca-certificate.pem"))
++        client_domain.set_peer_authentication(SSLDomain.VERIFY_PEER)
++
++        server = SslTest.SslTestConnection(server_domain, 
mode=Transport.SERVER)
++        client = SslTest.SslTestConnection(client_domain)
++
++        client.connection.open()
++        server.connection.open()
++        self._pump(client, server)
++
++        assert client.transport.closed
++        assert server.transport.closed
++        assert client.connection.state & Endpoint.REMOTE_UNINIT
++        assert server.connection.state & Endpoint.REMOTE_UNINIT
++
++    def test_system_default_ca_db_used_when_unconfigured(self):
++        """ With no CA database configured the system default is still the 
trust set.
++        """
++        if os.name == "nt":
++            raise Skipped("Windows SChannel does not use SSL_CERT_FILE.")
++        server_domain, client_domain = self._system_ca_domains(
++            self._testpath("bad-server-certificate.pem"))
++
++        
server_domain.set_credentials(self._testpath("bad-server-certificate.pem"),
++                                      
self._testpath("bad-server-private-key.pem"),
++                                      "server-password")
++        client_domain.set_peer_authentication(SSLDomain.VERIFY_PEER)
++
++        server = SslTest.SslTestConnection(server_domain, 
mode=Transport.SERVER)
++        client = SslTest.SslTestConnection(client_domain)
++
++        client.connection.open()
++        server.connection.open()
++        self._pump(client, server)
++        assert client.ssl.protocol_name() is not None
++        client.connection.close()
++        server.connection.close()
++        self._pump(client, server)
++
++    def test_intermediate_ca(self):
++        """ Ensure an intermediate/subordinate certificate can be used as a 
CA for validation.
++        """
++        if os.name == "nt":
++            raise Skipped("Test of OpenSSL X509_V_FLAG_PARTIAL_CHAIN flag.")
++        
self.server_domain.set_credentials(self._testpath("server-certificate-ca2.pem"),
++                                           
self._testpath("server-private-key-ca2.pem"),
++                                           "server-password")
++
++        
self.client_domain.set_trusted_ca_db(self._testpath("subca-certificate.pem"))
++        self.client_domain.set_peer_authentication(SSLDomain.VERIFY_PEER)
++
++        server = SslTest.SslTestConnection(self.server_domain, 
mode=Transport.SERVER)
++        client = SslTest.SslTestConnection(self.client_domain)
++
++        client.connection.open()
++        server.connection.open()
++        self._pump(client, server)
++        assert client.ssl.get_cert_subject() is not None
++        assert client.transport.condition is None
++        client.connection.close()
++        server.connection.close()
++        self._pump(client, server)
++
+     def test_certificate_fingerprint_and_subfields(self):
+         if os.name == "nt":
+             raise Skipped("Windows support for certificate fingerprint and 
subfield not implemented yet")
diff -Nru 
qpid-proton-0.37.0/debian/patches/CVE-2026-105042_Limit-the-AMQP-decoder-nesting-depth.patch
 
qpid-proton-0.37.0/debian/patches/CVE-2026-105042_Limit-the-AMQP-decoder-nesting-depth.patch
--- 
qpid-proton-0.37.0/debian/patches/CVE-2026-105042_Limit-the-AMQP-decoder-nesting-depth.patch
        1970-01-01 01:00:00.000000000 +0100
+++ 
qpid-proton-0.37.0/debian/patches/CVE-2026-105042_Limit-the-AMQP-decoder-nesting-depth.patch
        2026-11-10 16:08:41.000000000 +0100
@@ -0,0 +1,150 @@
+Description: Limit the AMQP decoder nesting depth
+ A message body that is deeply nested and decoded by pn_message_body()
+ could overflow the stack and cause a crash: every container or
+ described level of the AMQP encoding consumes a C stack frame, so a
+ crafted message nested a few thousands of times, encoded with a few
+ bytes per level, is enough. The decoder now refuses to descend past
+ PN_DECODER_MAX_DEPTH (1000) levels and reports PN_ARG_ERR instead.
+ .
+ Fixes CVE-2026-105042.
+ .
+ Upstream reworked the decoder to be iterative rather than recursive
+ in 0.41.1 (PROTON-2967), which is not practical to backport to the
+ 0.37.x branch; this is the minimal equivalent fix.
+Author: Thomas Goirand <[email protected]>
+Date: Mon, 10 Nov 2026 17:00:00 +0100
+Forwarded: no
+Assisted-By: Infomaniak Euria-Code
+Last-Update: 2026-11-10
+
+Index: qpid-proton/c/src/core/decoder.c
+===================================================================
+--- qpid-proton.orig/c/src/core/decoder.c
++++ qpid-proton/c/src/core/decoder.c
+@@ -39,6 +39,7 @@ void pn_decoder_initialize(pn_decoder_t
+   decoder->size = 0;
+   decoder->position = NULL;
+   decoder->error = NULL;
++  decoder->depth = 0;
+ }
+ 
+ void pn_decoder_finalize(pn_decoder_t *decoder)
+@@ -179,10 +180,14 @@ static inline pn_type_t pn_code2type(uin
+ 
+ static int pni_decoder_decode_type(pn_decoder_t *decoder, pn_data_t *data, 
uint8_t *code);
+ static int pni_decoder_single_described(pn_decoder_t *decoder, pn_data_t 
*data);
++static int pni_decoder_decode_value(pn_decoder_t *decoder, pn_data_t *data, 
uint8_t code);
+ static int pni_decoder_single(pn_decoder_t *decoder, pn_data_t *data);
++static int pni_decoder_decode_value_inner(pn_decoder_t *decoder, pn_data_t 
*data, uint8_t code);
++static int pni_decoder_single_described_inner(pn_decoder_t *decoder, 
pn_data_t *data);
++static int pni_decoder_single_inner(pn_decoder_t *decoder, pn_data_t *data);
+ void pni_data_set_array_type(pn_data_t *data, pn_type_t type);
+ 
+-static int pni_decoder_decode_value(pn_decoder_t *decoder, pn_data_t *data, 
uint8_t code)
++static int pni_decoder_decode_value_inner(pn_decoder_t *decoder, pn_data_t 
*data, uint8_t code)
+ {
+   int err;
+   conv_t conv;
+@@ -485,7 +490,7 @@ static inline bool pni_allowed_descripto
+     code != PNE_MAP8 && code != PNE_MAP32;
+ }
+ 
+-int pni_decoder_single_described(pn_decoder_t *decoder, pn_data_t *data)
++static int pni_decoder_single_described_inner(pn_decoder_t *decoder, 
pn_data_t *data)
+ {
+   if (!pn_decoder_remaining(decoder)) {
+     return PN_UNDERFLOW;
+@@ -506,7 +511,7 @@ int pni_decoder_single_described(pn_deco
+   return 0;
+ }
+ 
+-int pni_decoder_single(pn_decoder_t *decoder, pn_data_t *data)
++static int pni_decoder_single_inner(pn_decoder_t *decoder, pn_data_t *data)
+ {
+   uint8_t code;
+   int err = pni_decoder_decode_type(decoder, data, &code);
+@@ -524,12 +529,61 @@ ssize_t pn_decoder_decode(pn_decoder_t *
+   decoder->input = src;
+   decoder->size = size;
+   decoder->position = src;
++  decoder->depth = 0;
+ 
+   int err = pni_decoder_single(decoder, dst);
+ 
+-  if (err == PN_UNDERFLOW) 
++  if (err == PN_UNDERFLOW)
+       return pn_error_format(pn_data_error(dst), PN_UNDERFLOW, "not enough 
data to decode");
+   if (err) return err;
+ 
+   return decoder->position - decoder->input;
+ }
++
++/* Recursion guards: an AMQP value nests containers and described values,
++ * and every level of nesting consumes a C stack frame. A crafted message
++ * can nest thousands of levels from a few bytes each, overflowing the
++ * stack (CVE-2026-105042). Every recursive entry goes through one of
++ * these wrappers, which refuse to descend past PN_DECODER_MAX_DEPTH.
++ * Upstream reworked the decoder to be iterative in 0.41.1 (PROTON-2967);
++ * this is the minimal fix for the 0.37.x branch.
++ */
++
++static int pni_decoder_decode_value(pn_decoder_t *decoder, pn_data_t *data, 
uint8_t code)
++{
++  if (decoder->depth >= PN_DECODER_MAX_DEPTH) {
++    return pn_error_format(pn_data_error(data), PN_ARG_ERR,
++                           "AMQP value nesting too deep (limit: %d)",
++                           PN_DECODER_MAX_DEPTH);
++  }
++  decoder->depth++;
++  int err = pni_decoder_decode_value_inner(decoder, data, code);
++  decoder->depth--;
++  return err;
++}
++
++static int pni_decoder_single_described(pn_decoder_t *decoder, pn_data_t 
*data)
++{
++  if (decoder->depth >= PN_DECODER_MAX_DEPTH) {
++    return pn_error_format(pn_data_error(data), PN_ARG_ERR,
++                           "AMQP value nesting too deep (limit: %d)",
++                           PN_DECODER_MAX_DEPTH);
++  }
++  decoder->depth++;
++  int err = pni_decoder_single_described_inner(decoder, data);
++  decoder->depth--;
++  return err;
++}
++
++int pni_decoder_single(pn_decoder_t *decoder, pn_data_t *data)
++{
++  if (decoder->depth >= PN_DECODER_MAX_DEPTH) {
++    return pn_error_format(pn_data_error(data), PN_ARG_ERR,
++                           "AMQP value nesting too deep (limit: %d)",
++                           PN_DECODER_MAX_DEPTH);
++  }
++  decoder->depth++;
++  int err = pni_decoder_single_inner(decoder, data);
++  decoder->depth--;
++  return err;
++}
+Index: qpid-proton/c/src/core/decoder.h
+===================================================================
+--- qpid-proton.orig/c/src/core/decoder.h
++++ qpid-proton/c/src/core/decoder.h
+@@ -22,11 +22,17 @@
+  *
+  */
+ 
++/* Maximum AMQP nesting depth accepted by the decoder: every container or
++   described level recurses, so a deep enough message would overflow the
++   C stack (CVE-2026-105042). */
++#define PN_DECODER_MAX_DEPTH 1000
++
+ typedef struct pn_decoder_t {
+   const char *input;
+   size_t size;
+   const char *position;
+   pn_error_t *error;
++  int depth;
+ } pn_decoder_t;
+ 
+ void pn_decoder_initialize(pn_decoder_t *decoder);
diff -Nru 
qpid-proton-0.37.0/debian/patches/CVE-2026-114230_Validate-the-SASL-state-before-handling-a-frame.patch
 
qpid-proton-0.37.0/debian/patches/CVE-2026-114230_Validate-the-SASL-state-before-handling-a-frame.patch
--- 
qpid-proton-0.37.0/debian/patches/CVE-2026-114230_Validate-the-SASL-state-before-handling-a-frame.patch
     1970-01-01 01:00:00.000000000 +0100
+++ 
qpid-proton-0.37.0/debian/patches/CVE-2026-114230_Validate-the-SASL-state-before-handling-a-frame.patch
     2026-11-10 16:08:41.000000000 +0100
@@ -0,0 +1,59 @@
+Description: Validate the SASL state before handling a frame
+ When dispatching SASL frames to authenticate the peer, Proton would
+ also dispatch AMQP frames if they claimed to be SASL frames. This
+ allows authentication bypass: the frames are now rejected unless the
+ SASL layer is in the state expecting them.
+ .
+ Fixes CVE-2026-114230.
+Author: Andrew Stitcher <[email protected]>
+Date: Mon, 24 Aug 2026 21:43:15 -0400
+Origin: upstream, 
https://github.com/apache/qpid-proton/commit/0ec4d39707285d12608e6597671747c541ba245d
+Last-Update: 2026-11-10
+
+diff --git a/c/src/sasl/sasl.c b/c/src/sasl/sasl.c
+index 1fc16a9d..5467781f 100644
+--- a/c/src/sasl/sasl.c
++++ b/c/src/sasl/sasl.c
+@@ -898,6 +898,10 @@ int pn_do_init(pn_transport_t *transport, uint8_t 
frame_type, uint16_t channel,
+ 
+   // We should only receive this if we are a sasl server
+   if (sasl->client) return PN_ERR;
++  // Check the protocol state using desired_state not last_state: a 
pipelining peer can
++  // legitimately send its next frame before we've actually written out our 
own, so
++  // last_state can still be lagging behind the frame we've already committed 
to sending.
++  if (sasl->desired_state != SASL_POSTED_MECHANISMS) return PN_ERR;
+ 
+   pn_bytes_t mech;
+   pn_bytes_t recv;
+@@ -930,6 +934,7 @@ int pn_do_mechanisms(pn_transport_t *transport, uint8_t 
frame_type, uint16_t cha
+ 
+   // We should only receive this if we are a sasl client
+   if (!sasl->client) return PN_ERR;
++  if (sasl->desired_state != SASL_NONE) return PN_ERR;
+ 
+   pn_string_t *mechs = pn_string("");
+ 
+@@ -998,6 +1003,7 @@ int pn_do_challenge(pn_transport_t *transport, uint8_t 
frame_type, uint16_t chan
+ 
+   // We should only receive this if we are a sasl client
+   if (!sasl->client) return PN_ERR;
++  if (sasl->desired_state != SASL_POSTED_INIT && sasl->desired_state != 
SASL_POSTED_RESPONSE) return PN_ERR;
+ 
+   pn_bytes_t recv;
+ 
+@@ -1019,6 +1025,7 @@ int pn_do_response(pn_transport_t *transport, uint8_t 
frame_type, uint16_t chann
+ 
+   // We should only receive this if we are a sasl server
+   if (sasl->client) return PN_ERR;
++  if (sasl->desired_state != SASL_POSTED_CHALLENGE) return PN_ERR;
+ 
+   pn_bytes_t recv;
+ 
+@@ -1040,6 +1047,7 @@ int pn_do_outcome(pn_transport_t *transport, uint8_t 
frame_type, uint16_t channe
+ 
+   // We should only receive this if we are a sasl client
+   if (!sasl->client) return PN_ERR;
++  if (sasl->desired_state != SASL_POSTED_INIT && sasl->desired_state != 
SASL_POSTED_RESPONSE) return PN_ERR;
+ 
+   uint8_t outcome;
+   pn_bytes_t recv;
diff -Nru 
qpid-proton-0.37.0/debian/patches/CVE-2026-115942_Bound-wildcard-pattern-match-in-match_dns_pattern.patch
 
qpid-proton-0.37.0/debian/patches/CVE-2026-115942_Bound-wildcard-pattern-match-in-match_dns_pattern.patch
--- 
qpid-proton-0.37.0/debian/patches/CVE-2026-115942_Bound-wildcard-pattern-match-in-match_dns_pattern.patch
   1970-01-01 01:00:00.000000000 +0100
+++ 
qpid-proton-0.37.0/debian/patches/CVE-2026-115942_Bound-wildcard-pattern-match-in-match_dns_pattern.patch
   2026-11-10 16:08:41.000000000 +0100
@@ -0,0 +1,34 @@
+Description: Bound the wildcard pattern match against the subject label
+ A misconfigured wildcard TLS certificate causes a stack read overflow:
+ when the fixed prefix and suffix of a wildcard DNS pattern are longer
+ than the label of the host name being verified, the suffix comparison
+ reads before the start of the subject label buffer. Such a pattern can
+ never match, so it is now rejected before any out of bounds read.
+ .
+ Fixes CVE-2026-115942.
+ .
+ Upstream 0.41.1 reworked the whole host name verification on top of
+ X509_VERIFY_PARAM, enforcing RFC 9525 (PROTON-2974), which is not
+ practical to backport to the 0.37.x branch; this is the minimal
+ equivalent fix for the stack read overflow.
+Author: Thomas Goirand <[email protected]>
+Date: Mon, 10 Nov 2026 17:00:00 +0100
+Forwarded: no
+Assisted-By: Infomaniak Euria-Code
+Last-Update: 2026-11-10
+
+Index: qpid-proton/c/src/ssl/openssl.c
+===================================================================
+--- qpid-proton.orig/c/src/ssl/openssl.c
++++ qpid-proton/c/src/ssl/openssl.c
+@@ -260,6 +260,10 @@ static bool match_dns_pattern( const cha
+       char *suffix = star + 1;
+       int suffix_len = strlen(suffix);
+       if (prefix_len && pn_strncasecmp( prefix, slabel, prefix_len )) return 
false;
++      /* A wildcard pattern whose fixed parts cannot fit in the subject
++         label can never match, and trying to would read outside of the
++         subject label buffer (CVE-2026-115942). */
++      if (prefix_len + suffix_len > (int) strlen(slabel)) return false;
+       if (suffix_len && pn_strncasecmp( suffix,
+                                         slabel + (strlen(slabel) - 
suffix_len),
+                                         suffix_len )) return false;
diff -Nru qpid-proton-0.37.0/debian/patches/series 
qpid-proton-0.37.0/debian/patches/series
--- qpid-proton-0.37.0/debian/patches/series    2025-02-26 14:19:48.000000000 
+0100
+++ qpid-proton-0.37.0/debian/patches/series    2026-11-10 16:08:41.000000000 
+0100
@@ -1,3 +1,7 @@
 remove-cgi
 continue-building-with-deprecated-declarations.patch
 0002-Drop-old-FindPython.cmake.patch
+CVE-2026-105031_Make-pn_ssl_domain_set_trusted_ca_db-replace-the-.patch
+CVE-2026-105042_Limit-the-AMQP-decoder-nesting-depth.patch
+CVE-2026-114230_Validate-the-SASL-state-before-handling-a-frame.patch
+CVE-2026-115942_Bound-wildcard-pattern-match-in-match_dns_pattern.patch

Reply via email to