Skip to content

Commit 413039c

Browse files
sfc-gh-mmishchenkosfc-gh-pczajka
authored andcommitted
SNOW-2067577 OCSP: stop certificates chain traversal as soon as a trusted one met (#2299)
1 parent 57bc117 commit 413039c

File tree

4 files changed

+73
-24
lines changed

4 files changed

+73
-24
lines changed

DESCRIPTION.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,30 @@ Source code is also available at: https://github.com/snowflakedb/snowflake-conne
88

99
# Release Notes
1010
- v3.13.3(TBD)
11+
- v3.15(TBD)
12+
- Bumped up min boto and botocore version to 1.24
13+
- OCSP: terminate certificates chain traversal if a trusted certificate already reached
14+
15+
- v3.14.1(April 21, 2025)
16+
- Added support for Python 3.13.
17+
- NOTE: Windows 64 support is still experimental and should not yet be used for production environments.
18+
- Dropped support for Python 3.8.
19+
- Added basic decimal floating-point type support.
20+
- Added experimental authentication methods.
21+
- Added support of GCS regional endpoints.
22+
- Added support of GCS virtual urls. See more: https://cloud.google.com/storage/docs/request-endpoints#xml-api
23+
- Added `client_fetch_threads` experimental parameter to better utilize threads for fetching query results.
24+
- Added `check_arrow_conversion_error_on_every_column` connection property that can be set to `False` to restore previous behaviour in which driver will ignore errors until it occurs in the last column. This flag's purpose is to unblock workflows that may be impacted by the bugfix and will be removed in later releases.
25+
- Lowered log levels from info to debug for some of the messages to make the output easier to follow.
26+
- Allowed the connector to inherit a UUID4 generated upstream, provided in statement parameters (field: `requestId`), rather than automatically generate a UUID4 to use for the HTTP Request ID.
27+
- Improved logging in urllib3, boto3, botocore - assured data masking even after migration to the external owned library in the future.
28+
- Improved error message for client-side query cancellations due to timeouts.
29+
- Improved security and robustness for the temporary credentials cache storage.
30+
- Fixed a bug that caused driver to fail silently on `TO_DATE` arrow to python conversion when invalid date was followed by the correct one.
31+
- Fixed expired S3 credentials update and increment retry when expired credentials are found.
32+
- Deprecated `insecure_mode` connection property and replaced it with `disable_ocsp_checks` with the same behavior as the former property.
33+
34+
- v3.14.0(March 03, 2025)
1135
- Bumped pyOpenSSL dependency upper boundary from <25.0.0 to <26.0.0.
1236
- Removed the workaround for a Python 2.7 bug.
1337
- Added a <19.0.0 pin to pyarrow as a workaround to a bug affecting Azure Batch.

src/snowflake/connector/ocsp_asn1crypto.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -398,15 +398,22 @@ def extract_certificate_chain(
398398
from OpenSSL.crypto import FILETYPE_ASN1, dump_certificate
399399

400400
cert_map = OrderedDict()
401-
logger.debug("# of certificates: %s", len(connection.get_peer_cert_chain()))
402-
403-
for cert_openssl in connection.get_peer_cert_chain():
401+
cert_chain = connection.get_peer_cert_chain()
402+
logger.debug("# of certificates: %s", len(cert_chain))
403+
self._lazy_read_ca_bundle()
404+
for cert_openssl in cert_chain:
404405
cert_der = dump_certificate(FILETYPE_ASN1, cert_openssl)
405406
cert = Certificate.load(cert_der)
406407
logger.debug(
407408
"subject: %s, issuer: %s", cert.subject.native, cert.issuer.native
408409
)
409410
cert_map[cert.subject.sha256] = cert
411+
if cert.issuer.sha256 in SnowflakeOCSP.ROOT_CERTIFICATES_DICT:
412+
logger.debug(
413+
"A trusted root certificate found: %s, stopping chain traversal here",
414+
cert.subject.native,
415+
)
416+
break
410417

411418
return self.create_pair_issuer_subject(cert_map)
412419

src/snowflake/connector/tool/dump_ocsp_response.py

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,38 +5,55 @@
55

66
from __future__ import annotations
77

8+
import logging
9+
import sys
810
import time
9-
from os import path
11+
from argparse import ArgumentParser, Namespace
1012
from time import gmtime, strftime
1113

1214
from asn1crypto import ocsp as asn1crypto_ocsp
1315

1416
from snowflake.connector.compat import urlsplit
1517
from snowflake.connector.ocsp_asn1crypto import SnowflakeOCSPAsn1Crypto as SFOCSP
18+
from snowflake.connector.ocsp_snowflake import OCSPTelemetryData
1619
from snowflake.connector.ssl_wrap_socket import _openssl_connect
1720

1821

22+
def _parse_args() -> Namespace:
23+
parser = ArgumentParser(
24+
prog="dump_ocsp_response",
25+
description="Dump OCSP Response for the URLs (an internal tool).",
26+
)
27+
parser.add_argument(
28+
"-o",
29+
"--output-file",
30+
required=False,
31+
help="Dump output file",
32+
type=str,
33+
default=None,
34+
)
35+
parser.add_argument(
36+
"--log-level",
37+
required=False,
38+
help="Log level",
39+
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
40+
)
41+
parser.add_argument("--log-file", required=False, help="Log file", default=None)
42+
parser.add_argument("urls", nargs="+", help="URLs to dump OCSP Response for")
43+
return parser.parse_args()
44+
45+
1946
def main() -> None:
2047
"""Internal Tool: OCSP response dumper."""
21-
22-
def help() -> None:
23-
print("Dump OCSP Response for the URL. ")
24-
print(
25-
"""
26-
Usage: {} <url> [<url> ...]
27-
""".format(
28-
path.basename(sys.argv[0])
48+
args = _parse_args()
49+
if args.log_level:
50+
if args.log_file:
51+
logging.basicConfig(
52+
filename=args.log_file, level=getattr(logging, args.log_level.upper())
2953
)
30-
)
31-
sys.exit(2)
32-
33-
import sys
34-
35-
if len(sys.argv) < 2:
36-
help()
37-
38-
urls = sys.argv[1:]
39-
dump_ocsp_response(urls, output_filename=None)
54+
else:
55+
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
56+
dump_ocsp_response(args.urls, output_filename=args.output_file)
4057

4158

4259
def dump_good_status(current_time, single_response) -> None:
@@ -91,7 +108,7 @@ def dump_ocsp_response(urls, output_filename):
91108
for issuer, subject in cert_data:
92109
_, _ = ocsp.create_ocsp_request(issuer, subject)
93110
_, _, _, cert_id, ocsp_response_der = ocsp.validate_by_direct_connection(
94-
issuer, subject
111+
issuer, subject, OCSPTelemetryData()
95112
)
96113
ocsp_response = asn1crypto_ocsp.OCSPResponse.load(ocsp_response_der)
97114
print("------------------------------------------------------------")
@@ -119,7 +136,7 @@ def dump_ocsp_response(urls, output_filename):
119136

120137
if output_filename:
121138
SFOCSP.OCSP_CACHE.write_ocsp_response_cache_file(ocsp, output_filename)
122-
return SFOCSP.OCSP_CACHE.CACHE
139+
return SFOCSP.OCSP_CACHE
123140

124141

125142
if __name__ == "__main__":

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ deps =
8484
pytest-timeout
8585
pytest-xdist
8686
mock
87+
certifi<2025.4.26
8788
skip_install = True
8889
setenv = {[testenv]setenv}
8990
passenv = {[testenv]passenv}

0 commit comments

Comments
 (0)