[
https://issues.apache.org/jira/browse/PROTON-2934?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Andrew Stitcher reassigned PROTON-2934:
---------------------------------------
Assignee: Clifford Jansen
> TLS 1.3 unsupported in Windows python-qpid-proton wheel
> -------------------------------------------------------
>
> Key: PROTON-2934
> URL: https://issues.apache.org/jira/browse/PROTON-2934
> Project: Qpid Proton
> Issue Type: Bug
> Components: proton-c, python-binding
> Environment: Windows 10 / 11 / Server 2019+, any Python
> `cp3x-win_amd64` wheel of `python-qpid-proton`
> Reporter: Clemens Vasters
> Assignee: Clifford Jansen
> Priority: Major
> Labels: python-binding, schannel, tls, windows
>
> The pre-built Windows wheel of `python-qpid-proton` (and, by extension, every
> `proton-c` build on Windows that uses the bundled SChannel TLS backend)
> cannot negotiate **TLS 1.3**. It tops out at TLS 1.2. This makes it
> impossible to connect from Windows to any AMQP 1.0 broker that enforces a
> TLS-1.3 minimum, e.g. an Azure Service Bus namespace with `minimumTlsVersion
> = 1.3`.
> `c/src/ssl/schannel.cpp` (the implementation selected at build time when
> `os.name == 'nt'` in `python/ext_build.py`) initialises its SChannel
> credentials with the **legacy** `SCHANNEL_CRED` structure:
> ```cpp
> // c/src/ssl/schannel.cpp ~ L237-256 (main branch, checked 2026-05-22)
> SCHANNEL_CRED descriptor;
> memset(&descriptor, 0, sizeof(descriptor));
> descriptor.dwVersion = SCHANNEL_CRED_VERSION;
> descriptor.dwFlags = SCH_CRED_NO_DEFAULT_CREDS |
> SCH_CRED_MANUAL_CRED_VALIDATION;
> ...
> *status = AcquireCredentialsHandle(NULL, UNISP_NAME, direction, NULL,
> &descriptor, NULL, NULL, &tmp_handle,
> &expiry);
> ```
> Microsoft's own SChannel documentation states that:
> 1. `SCHANNEL_CRED` is **deprecated**; "Applications should use the
> `SCH_CREDENTIALS` structure instead."
>
> https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-schannel_cred
> 2. TLS 1.3 was introduced in Windows 11 and Windows Server 2022, **but** the
> protocol can only be negotiated when an application uses the new
> `SCH_CREDENTIALS` structure together with `TLS_PARAMETERS`. Code that still
> calls `AcquireCredentialsHandle()` with `SCHANNEL_CRED` will at most
> negotiate TLS 1.2 regardless of what the underlying Windows version supports.
>
> https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-sch_credentials
> https://learn.microsoft.com/en-us/windows-server/security/tls/tls-schannel
> A grep across `c/src/ssl/schannel.cpp` on `main` (2026-05-22) confirms there
> is currently **no** code path using `SCH_CREDENTIALS`, no `TLS_PARAMETERS`
> block, and `grbitEnabledProtocols` is never set:
> ```
> SCH_CREDENTIALS : 0 occurrences
> grbitEnabledProtocols : 0 occurrences
> TLS_PARAMETERS : 0 occurrences
> SCHANNEL_CRED : 2 occurrences (declaration + version assignment)
> ```
> So the Windows wheel literally has no way to ask SChannel for TLS 1.3 today.
> ## Evidence on the released wheel
> Tested with `python-qpid-proton == 0.40.0`, wheel tag
> `cp311-cp311-win_amd64`, on Windows 11 x64, Python 3.11.9.
> 1. Binary string scan of the installed `cproton_ffi.pyd` (size 463 872 bytes):
> | Pattern | Matches |
> |-----------|---------|
> | `TLSv1.0` | 0 |
> | `TLSv1.1` | 1 |
> | `TLSv1.2` | 1 |
> | `TLSv1.3` | **0** |
> | `OpenSSL ` | 0 |
> (No OpenSSL banner strings present, consistent with the wheel using
> SChannel rather than OpenSSL.)
> 2. `proton.SSLDomain` exposes no API to pin the minimum/maximum TLS protocol
> version. Public methods on `SSLDomain` are limited to: `set_credentials`,
> `set_peer_authentication`, `set_trusted_ca_db`, `allow_unsecured_client`.
> There is no equivalent of OpenSSL's `SSL_CTX_set_min_proto_version` /
> `SSL_CTX_set_max_proto_version` reachable from the Python or C API.
> 3. Live reproduction against an Azure Service Bus namespace pinned to
> `minimumTlsVersion = 1.3`: the connection fails at the AMQP open handshake
> with:
> ```
> amqp:unauthorized-access "Invalid TLS version"
> ```
> The TCP/TLS handshake itself completes at TLS 1.2; the broker then closes
> the AMQP session because the negotiated TLS version is below the namespace
> policy. Repeating the same test from Linux (where the wheel uses OpenSSL)
> succeeds.
> ## Minimal reproducer
> Requires:
> - Windows 10 1809 or newer (TLS 1.3 SChannel needs Win 11 / Server 2022 for
> full client negotiation)
> - An Azure Service Bus namespace with `minimumTlsVersion = 1.3`. Equivalent:
> any AMQP 1.0 broker behind a TLS terminator (e.g. nginx / haproxy / Envoy)
> configured to require TLS 1.3.
> ```python
> # pip install python-qpid-proton==0.40.0 azure-identity
> import os
> from proton import Message
> from proton.utils import BlockingConnection
> from proton.reactor import Container
> # Replace with your namespace
> HOST = "<your-ns>.servicebus.windows.net"
> URL = f"amqps://\{HOST}:5671"
> conn = BlockingConnection(URL, sasl_enabled=True, allowed_mechs="ANONYMOUS",
> ssl_domain=None) # will fail before SASL on Win
> ```
> Expected (against a TLS-1.3-required broker): `proton.ConnectionException:
> amqp:unauthorized-access`. On Linux against the same broker the connection
> succeeds.
> ## Impact
> - All Python users on Windows are locked out of any AMQP 1.0 broker that
> requires TLS 1.3.
> - TLS 1.3 is increasingly a baseline corporate / regulatory requirement
> (PCI-DSS, FedRAMP High, several CSP "secure defaults" policies). For example,
> Azure Service Bus customers under the `CloudGov_ServBus_TLS` policy cannot
> lower `minimumTlsVersion` below 1.3.
> - The same code path is used by every C-binding language that links proton-c
> on Windows, so the bug surface is wider than just the Python wheel.
> - Workarounds today require either (a) switching to a non-proton AMQP stack
> on Windows, (b) running the Windows code under WSL/Linux, or (c) lowering the
> broker's TLS minimum — all of which are unacceptable for many users.
> ## Suggested fix
> Switch the SChannel credential acquisition path in `c/src/ssl/schannel.cpp`
> to use the modern `SCH_CREDENTIALS` structure, with a fallback to
> `SCHANNEL_CRED` only when running on a Windows release that pre-dates
> `SCH_CREDENTIALS` support (Windows 10 1809 / Server 2019).
> Outline (in `win_credential_cred_handle`):
> ```cpp
> #ifdef SCH_CREDENTIALS_VERSION // present in modern Windows 10 SDKs
> TLS_PARAMETERS tls_params = \{0};
> // leave grbitDisabledProtocols = 0 → let SChannel pick the strongest
> mutually-supported
> // version, including TLS 1.3 when the peer offers it.
> SCH_CREDENTIALS sch_creds = \{0};
> sch_creds.dwVersion = SCH_CREDENTIALS_VERSION;
> sch_creds.dwFlags = SCH_CRED_NO_DEFAULT_CREDS |
> SCH_CRED_MANUAL_CRED_VALIDATION;
> sch_creds.cTlsParameters = 1;
> sch_creds.pTlsParameters = &tls_params;
> if (cred->cert_context) {
> sch_creds.paCred = &cred->cert_context;
> sch_creds.cCreds = 1;
> }
> *status = AcquireCredentialsHandle(
> NULL, UNISP_NAME, direction, NULL,
> &sch_creds, NULL, NULL, &tmp_handle, &expiry);
> if (*status == SEC_E_UNSUPPORTED_FUNCTION) {
> // fallback path: old SCHANNEL_CRED for pre-1809 Windows
> ... existing legacy code ...
> }
> #else
> ... existing legacy code ...
> #endif
> ```
> ## References
> - Microsoft Learn — `SCHANNEL_CRED` (deprecated):
> https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-schannel_cred
> - Microsoft Learn — `SCH_CREDENTIALS` (replacement, supports TLS 1.3):
> https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-sch_credentials
> - Microsoft Learn — `TLS_PARAMETERS`:
> https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-tls_parameters
> - Microsoft Learn — TLS protocol support in SChannel (TLS 1.3 added in Win 11
> / Server 2022):
> https://learn.microsoft.com/en-us/windows-server/security/tls/tls-schannel
> - Related (already resolved) PROTON-1989 "TLS Configuration does not support
> TLSv1_3 in OpenSSL v1.1.1" — fixed the OpenSSL path only.
> - Related PROTON-2397 "Update default client TLS defaults for verifying
> outbound connections to AMQP servers" — also OpenSSL-only.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]