Skip to content

Commit 817bcc8

Browse files
Narrow exceptions and add helpers
1 parent 69c8755 commit 817bcc8

File tree

2 files changed

+56
-71
lines changed

2 files changed

+56
-71
lines changed

DESCRIPTION.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Source code is also available at: https://github.com/snowflakedb/snowflake-conne
99
# Release Notes
1010
- v3.18.0(TBD)
1111
- Added the `workload_identity_impersonation_path` parameter to support service account impersonation for Workload Identity Federation on GCP workloads only
12+
- Added support for intermediate cetificates as roots when they are stored in the trust store
1213

1314
- v3.17.3(September 02,2025)
1415
- Enhanced configuration file permission warning messages.

src/snowflake/connector/ssl_wrap_socket.py

Lines changed: 55 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#
99
# and added OCSP validator on the top.
1010
import logging
11+
import os
1112
import ssl
1213
import time
1314
import weakref
@@ -39,6 +40,53 @@
3940
log = logging.getLogger(__name__)
4041

4142

43+
# Helper utilities (private)
44+
def _resolve_cafile(kwargs: dict[str, Any]) -> str | None:
45+
"""Resolve CA bundle path from kwargs or standard environment variables.
46+
47+
Precedence:
48+
1) kwargs['ca_certs'] if provided by caller
49+
2) REQUESTS_CA_BUNDLE
50+
3) SSL_CERT_FILE
51+
"""
52+
caf = kwargs.get("ca_certs")
53+
if caf:
54+
return caf
55+
return os.environ.get("REQUESTS_CA_BUNDLE") or os.environ.get("SSL_CERT_FILE")
56+
57+
58+
def _ensure_partial_chain_on_context(ctx: PyOpenSSLContext, cafile: str | None) -> None:
59+
"""Load CA bundle (when provided) and enable OpenSSL partial-chain support on ctx."""
60+
if cafile:
61+
try:
62+
ctx.load_verify_locations(cafile=cafile, capath=None)
63+
except (ssl.SSLError, OSError, ValueError):
64+
# Leave context unchanged; handshake/validation surfaces failures
65+
pass
66+
try:
67+
store = ctx._ctx.get_cert_store()
68+
from OpenSSL import crypto as _crypto
69+
70+
if hasattr(_crypto, "X509StoreFlags") and hasattr(
71+
_crypto.X509StoreFlags, "PARTIAL_CHAIN"
72+
):
73+
store.set_flags(_crypto.X509StoreFlags.PARTIAL_CHAIN)
74+
except (AttributeError, ImportError, OpenSSL.SSL.Error, OSError, ValueError):
75+
# Best-effort; if not available, default chain building applies
76+
pass
77+
78+
79+
def _build_context_with_partial_chain(cafile: str | None) -> PyOpenSSLContext:
80+
"""Create PyOpenSSL context configured for CERT_REQUIRED and partial-chain trust."""
81+
ctx = PyOpenSSLContext(ssl_.PROTOCOL_TLS_CLIENT)
82+
try:
83+
ctx.verify_mode = ssl.CERT_REQUIRED
84+
except Exception:
85+
pass
86+
_ensure_partial_chain_on_context(ctx, cafile)
87+
return ctx
88+
89+
4290
# Store a *weak* reference so that the context variable doesn’t prolong the
4391
# lifetime of the SessionManager. Once all owning connections are GC-ed the
4492
# weakref goes dead and OCSP will fall back to its local manager (but most
@@ -90,35 +138,6 @@ def reset_current_session_manager(token) -> None:
90138
pass
91139

92140

93-
def _build_pyopenssl_context_with_ca_and_partial_chain(
94-
cafile: str | None,
95-
) -> PyOpenSSLContext:
96-
ctx = PyOpenSSLContext(ssl_.PROTOCOL_TLS_CLIENT)
97-
try:
98-
# Ensure certificate verification is enabled
99-
ctx.verify_mode = ssl.CERT_REQUIRED
100-
except Exception:
101-
pass
102-
try:
103-
if cafile:
104-
ctx.load_verify_locations(cafile=cafile, capath=None)
105-
except Exception:
106-
pass
107-
# Enable partial-chain verification so intermediates in trust store can
108-
# terminate chains
109-
try:
110-
store = ctx._ctx.get_cert_store()
111-
from OpenSSL import crypto as _crypto
112-
113-
if hasattr(_crypto, "X509StoreFlags") and hasattr(
114-
_crypto.X509StoreFlags, "PARTIAL_CHAIN"
115-
):
116-
store.set_flags(_crypto.X509StoreFlags.PARTIAL_CHAIN)
117-
except Exception:
118-
pass
119-
return ctx
120-
121-
122141
def inject_into_urllib3() -> None:
123142
"""Monkey-patch urllib3 with PyOpenSSL-backed SSL-support and OCSP."""
124143
log.debug("Injecting ssl_wrap_socket_with_ocsp")
@@ -155,51 +174,16 @@ def ssl_wrap_socket_with_ocsp(*args: Any, **kwargs: Any) -> WrappedSocket:
155174
kwargs["ca_certs"] = certifi.where()
156175

157176
# Ensure PyOpenSSL context with partial-chain is used if no suitable context provided
158-
try:
159-
provided_ctx = kwargs.get("ssl_context", None)
160-
if not isinstance(provided_ctx, PyOpenSSLContext):
161-
cafile_for_ctx = kwargs.get("ca_certs")
162-
if not cafile_for_ctx:
163-
import os as _os
164-
165-
cafile_for_ctx = _os.environ.get(
166-
"REQUESTS_CA_BUNDLE"
167-
) or _os.environ.get("SSL_CERT_FILE")
168-
kwargs["ssl_context"] = _build_pyopenssl_context_with_ca_and_partial_chain(
169-
cafile_for_ctx
170-
)
171-
except Exception:
172-
pass
177+
provided_ctx = kwargs.get("ssl_context", None)
178+
if not isinstance(provided_ctx, PyOpenSSLContext):
179+
cafile_for_ctx = _resolve_cafile(kwargs)
180+
kwargs["ssl_context"] = _build_context_with_partial_chain(cafile_for_ctx)
173181

174182
# If a PyOpenSSLContext is provided, ensure it trusts the provided CA and
175183
# partial-chain is enabled
176-
try:
177-
provided_ctx = kwargs.get("ssl_context", None)
178-
if isinstance(provided_ctx, PyOpenSSLContext):
179-
caf = kwargs.get("ca_certs")
180-
if not caf:
181-
import os as _os
182-
183-
caf = _os.environ.get("REQUESTS_CA_BUNDLE") or _os.environ.get(
184-
"SSL_CERT_FILE"
185-
)
186-
try:
187-
if caf:
188-
provided_ctx.load_verify_locations(cafile=caf, capath=None)
189-
except Exception:
190-
pass
191-
try:
192-
store = provided_ctx._ctx.get_cert_store()
193-
from OpenSSL import crypto as _crypto
194-
195-
if hasattr(_crypto, "X509StoreFlags") and hasattr(
196-
_crypto.X509StoreFlags, "PARTIAL_CHAIN"
197-
):
198-
store.set_flags(_crypto.X509StoreFlags.PARTIAL_CHAIN)
199-
except Exception:
200-
pass
201-
except Exception:
202-
pass
184+
provided_ctx = kwargs.get("ssl_context", None)
185+
if isinstance(provided_ctx, PyOpenSSLContext):
186+
_ensure_partial_chain_on_context(provided_ctx, _resolve_cafile(kwargs))
203187

204188
ret = ssl_.ssl_wrap_socket(*args, **kwargs)
205189

0 commit comments

Comments
 (0)