Skip to content

Commit 5f6d2da

Browse files
committed
SNOW-1348650: implement ocsp validation (#2025)
1 parent 19db90d commit 5f6d2da

File tree

6 files changed

+1133
-2
lines changed

6 files changed

+1133
-2
lines changed

ci/test_fips.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ python -c "from cryptography.hazmat.backends.openssl import backend;print('Cryp
2121
pip freeze
2222

2323
cd $CONNECTOR_DIR
24-
pytest -vvv --cov=snowflake.connector --cov-report=xml:coverage.xml test
24+
pytest -vvv --cov=snowflake.connector --cov-report=xml:coverage.xml test --ignore=test/integ/aio --ignore=test/unit/aio
2525

2626
deactivate

src/snowflake/connector/aio/_network.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
SQLSTATE_CONNECTION_WAS_NOT_ESTABLISHED,
8282
)
8383
from ..time_util import TimeoutBackoffCtx, get_time_millis
84+
from ._ssl_connector import SnowflakeSSLConnector
8485

8586
if TYPE_CHECKING:
8687
from snowflake.connector.aio import SnowflakeConnection
@@ -816,7 +817,9 @@ async def _request_exec(
816817
raise err
817818

818819
def make_requests_session(self) -> aiohttp.ClientSession:
819-
s = aiohttp.ClientSession()
820+
s = aiohttp.ClientSession(
821+
connector=SnowflakeSSLConnector(snowflake_ocsp_mode=self._ocsp_mode)
822+
)
820823
# TODO: sync feature parity, proxy support
821824
# s.mount("http://", ProxySupportAdapter(max_retries=REQUESTS_RETRY))
822825
# s.mount("https://", ProxySupportAdapter(max_retries=REQUESTS_RETRY))
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#
2+
# Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3+
#
4+
5+
from __future__ import annotations
6+
7+
import ssl
8+
from collections import OrderedDict
9+
from logging import getLogger
10+
11+
from aiohttp.client_proto import ResponseHandler
12+
from asn1crypto.x509 import Certificate
13+
14+
from ..ocsp_asn1crypto import SnowflakeOCSPAsn1Crypto as SnowflakeOCSPAsn1CryptoSync
15+
from ._ocsp_snowflake import SnowflakeOCSP
16+
17+
logger = getLogger(__name__)
18+
19+
20+
class SnowflakeOCSPAsn1Crypto(SnowflakeOCSP, SnowflakeOCSPAsn1CryptoSync):
21+
22+
def extract_certificate_chain(self, connection: ResponseHandler):
23+
ssl_object = connection.transport.get_extra_info("ssl_object")
24+
if not ssl_object:
25+
raise RuntimeError(
26+
"Unable to get the SSL object from the asyncio transport to perform OCSP validation."
27+
"Please open an issue on the Snowflake Python Connector GitHub repository "
28+
"and provide your execution environment"
29+
" details: https://github.com/snowflakedb/snowflake-connector-python/issues/new/choose."
30+
"As a workaround, you can create the connection with `insecure_mode=True` to skip OCSP Validation."
31+
)
32+
33+
cert_map = OrderedDict()
34+
# in Python 3.10, get_unverified_chain was introduced as a
35+
# private method: https://github.com/python/cpython/pull/25467
36+
# which returns all the peer certs in the chain.
37+
# Python 3.13 will have the method get_unverified_chain publicly available on ssl.SSLSocket class
38+
# https://docs.python.org/pl/3.13/library/ssl.html#ssl.SSLSocket.get_unverified_chain
39+
unverified_chain = ssl_object._sslobj.get_unverified_chain()
40+
logger.debug("# of certificates: %s", len(unverified_chain))
41+
42+
for cert in unverified_chain:
43+
cert = Certificate.load(ssl.PEM_cert_to_DER_cert(cert.public_bytes()))
44+
logger.debug(
45+
"subject: %s, issuer: %s", cert.subject.native, cert.issuer.native
46+
)
47+
cert_map[cert.subject.sha256] = cert
48+
49+
return self.create_pair_issuer_subject(cert_map)

0 commit comments

Comments
 (0)