From 7c708c122d32947f9f4517a94f3846ea86e3e9ad Mon Sep 17 00:00:00 2001 From: lingsheng <860373352@qq.com> Date: Mon, 17 Nov 2025 16:56:06 +0800 Subject: [PATCH] fix CVE-2024-35195 (cherry picked from commit dc24d45c2c65aca405e3d6e6668710746cc474f4) --- backport-0001-CVE-2024-35195.patch | 133 ++++ backport-0002-CVE-2024-35195.patch | 1043 ++++++++++++++++++++++++++++ backport-0003-CVE-2024-35195.patch | 98 +++ backport-0004-CVE-2024-35195.patch | 66 ++ backport-0005-CVE-2024-35195.patch | 116 ++++ backport-0006-CVE-2024-35195.patch | 49 ++ backport-0007-CVE-2024-35195.patch | 63 ++ backport-0008-CVE-2024-35195.patch | 109 +++ python-requests.spec | 13 +- 9 files changed, 1689 insertions(+), 1 deletion(-) create mode 100644 backport-0001-CVE-2024-35195.patch create mode 100644 backport-0002-CVE-2024-35195.patch create mode 100644 backport-0003-CVE-2024-35195.patch create mode 100644 backport-0004-CVE-2024-35195.patch create mode 100644 backport-0005-CVE-2024-35195.patch create mode 100644 backport-0006-CVE-2024-35195.patch create mode 100644 backport-0007-CVE-2024-35195.patch create mode 100644 backport-0008-CVE-2024-35195.patch diff --git a/backport-0001-CVE-2024-35195.patch b/backport-0001-CVE-2024-35195.patch new file mode 100644 index 0000000..90d8567 --- /dev/null +++ b/backport-0001-CVE-2024-35195.patch @@ -0,0 +1,133 @@ +From c0813a2d910ea6b4f8438b91d315b8d181302356 Mon Sep 17 00:00:00 2001 +From: Ian Stapleton Cordasco +Date: Sun, 3 Mar 2024 07:00:49 -0600 +Subject: [PATCH] Use TLS settings in selecting connection pool + +Previously, if someone made a request with `verify=False` then made a +request where they expected verification to be enabled to the same host, +they would potentially reuse a connection where TLS had not been +verified. + +This fixes that issue. + +Reference:https://github.com/psf/requests/commit/c0813a2d910ea6b4f8438b91d315b8d181302356 +Conflict:Adapt context,remove tox.ini +--- + requests/adapters.py | 58 +++++++++++++++++++++++++++++++++++++++++- + tests/test_requests.py | 7 +++++ + 2 files changed, 64 insertions(+), 1 deletion(-) + +diff --git a/requests/adapters.py b/requests/adapters.py +index 78e3bb6..03cd069 100644 +--- a/requests/adapters.py ++++ b/requests/adapters.py +@@ -8,6 +8,7 @@ and maintain connections. + + import os.path + import socket # noqa: F401 ++import typing + + from urllib3.exceptions import ClosedPoolError, ConnectTimeoutError + from urllib3.exceptions import HTTPError as _HTTPError +@@ -61,12 +62,38 @@ except ImportError: + raise InvalidSchema("Missing dependencies for SOCKS support.") + + ++if typing.TYPE_CHECKING: ++ from .models import PreparedRequest ++ ++ + DEFAULT_POOLBLOCK = False + DEFAULT_POOLSIZE = 10 + DEFAULT_RETRIES = 0 + DEFAULT_POOL_TIMEOUT = None + + ++def _urllib3_request_context( ++ request: "PreparedRequest", verify: "bool | str | None" ++) -> "(typing.Dict[str, typing.Any], typing.Dict[str, typing.Any])": ++ host_params = {} ++ pool_kwargs = {} ++ parsed_request_url = urlparse(request.url) ++ scheme = parsed_request_url.scheme.lower() ++ port = parsed_request_url.port ++ cert_reqs = "CERT_REQUIRED" ++ if verify is False: ++ cert_reqs = "CERT_NONE" ++ if isinstance(verify, str): ++ pool_kwargs["ca_certs"] = verify ++ pool_kwargs["cert_reqs"] = cert_reqs ++ host_params = { ++ "scheme": scheme, ++ "host": parsed_request_url.hostname, ++ "port": port, ++ } ++ return host_params, pool_kwargs ++ ++ + class BaseAdapter: + """The Base Transport Adapter""" + +@@ -328,6 +355,35 @@ class HTTPAdapter(BaseAdapter): + + return response + ++ def _get_connection(self, request, verify, proxies=None): ++ # Replace the existing get_connection without breaking things and ++ # ensure that TLS settings are considered when we interact with ++ # urllib3 HTTP Pools ++ proxy = select_proxy(request.url, proxies) ++ try: ++ host_params, pool_kwargs = _urllib3_request_context(request, verify) ++ except ValueError as e: ++ raise InvalidURL(e, request=request) ++ if proxy: ++ proxy = prepend_scheme_if_needed(proxy, "http") ++ proxy_url = parse_url(proxy) ++ if not proxy_url.host: ++ raise InvalidProxyURL( ++ "Please check proxy URL. It is malformed " ++ "and could be missing the host." ++ ) ++ proxy_manager = self.proxy_manager_for(proxy) ++ conn = proxy_manager.connection_from_host( ++ **host_params, pool_kwargs=pool_kwargs ++ ) ++ else: ++ # Only scheme should be lower case ++ conn = self.poolmanager.connection_from_host( ++ **host_params, pool_kwargs=pool_kwargs ++ ) ++ ++ return conn ++ + def get_connection(self, url, proxies=None): + """Returns a urllib3 connection for the given URL. This should not be + called from user code, and is only exposed for use when subclassing the +@@ -451,7 +507,7 @@ class HTTPAdapter(BaseAdapter): + """ + + try: +- conn = self.get_connection(request.url, proxies) ++ conn = self._get_connection(request, verify, proxies) + except LocationValueError as e: + raise InvalidURL(e, request=request) + +diff --git a/tests/test_requests.py b/tests/test_requests.py +index b420c44..1263088 100644 +--- a/tests/test_requests.py ++++ b/tests/test_requests.py +@@ -2808,3 +2808,10 @@ class TestPreparingURLs: + with pytest.raises(requests.exceptions.JSONDecodeError) as excinfo: + r.json() + assert excinfo.value.doc == r.text ++ ++ def test_different_connection_pool_for_tls_settings(self): ++ s = requests.Session() ++ r1 = s.get("https://invalid.badssl.com", verify=False) ++ assert r1.status_code == 421 ++ with pytest.raises(requests.exceptions.SSLError): ++ s.get("https://invalid.badssl.com") +-- +2.33.0 + diff --git a/backport-0002-CVE-2024-35195.patch b/backport-0002-CVE-2024-35195.patch new file mode 100644 index 0000000..015d0f0 --- /dev/null +++ b/backport-0002-CVE-2024-35195.patch @@ -0,0 +1,1043 @@ +From a94e9b5308ffcc3d2913ab873e9810a6601a67da Mon Sep 17 00:00:00 2001 +From: Ian Stapleton Cordasco +Date: Wed, 13 Mar 2024 15:58:45 -0500 +Subject: [PATCH] Add local TLS server + +This also adds certificates for testing purposes and files to make it +easy to generate/regenerate them. + +This also replaces an existing test of how we utilize our pool manager +such that we don't connect to badssl.com + +Finally, this adds additional context parameters for our pool manager to +account for mTLS certificates used by clients to authenticate to a +server. + +Reference:https://github.com/psf/requests/commit/a94e9b5308ffcc3d2913ab873e9810a6601a67da +Conflict:Adapt context +--- + requests/adapters.py | 18 +++- + tests/certs/README.md | 10 ++ + tests/certs/expired/Makefile | 13 +++ + tests/certs/expired/README.md | 11 ++ + tests/certs/expired/ca/Makefile | 13 +++ + tests/certs/expired/ca/ca-private.key | 28 ++++++ + tests/certs/expired/ca/ca.cnf | 12 +++ + tests/certs/expired/ca/ca.crt | 20 ++++ + tests/certs/expired/ca/ca.srl | 1 + + tests/certs/expired/server/Makefile | 16 +++ + tests/certs/expired/server/cert.cnf | 24 +++++ + tests/certs/expired/server/server.csr | 19 ++++ + tests/certs/expired/server/server.key | 28 ++++++ + tests/certs/expired/server/server.pem | 41 ++++++++ + tests/certs/mtls/Makefile | 7 ++ + tests/certs/mtls/README.md | 4 + + tests/certs/mtls/client/Makefile | 16 +++ + tests/certs/mtls/client/ca | 1 + + tests/certs/mtls/client/cert.cnf | 26 +++++ + tests/certs/mtls/client/client.csr | 24 +++++ + tests/certs/mtls/client/client.key | 28 ++++++ + tests/certs/mtls/client/client.pem | 41 ++++++++ + tests/certs/valid/ca | 1 + + tests/certs/valid/server/Makefile | 16 +++ + tests/certs/valid/server/cert.cnf | 31 ++++++ + tests/certs/valid/server/server.csr | 19 ++++ + tests/certs/valid/server/server.key | 28 ++++++ + tests/certs/valid/server/server.pem | 47 +++++++++ + tests/test_requests.py | 140 +++++++++++++++++++++++++- + tests/testserver/server.py | 42 ++++++++ + 30 files changed, 716 insertions(+), 9 deletions(-) + create mode 100644 tests/certs/README.md + create mode 100644 tests/certs/expired/Makefile + create mode 100644 tests/certs/expired/README.md + create mode 100644 tests/certs/expired/ca/Makefile + create mode 100644 tests/certs/expired/ca/ca-private.key + create mode 100644 tests/certs/expired/ca/ca.cnf + create mode 100644 tests/certs/expired/ca/ca.crt + create mode 100644 tests/certs/expired/ca/ca.srl + create mode 100644 tests/certs/expired/server/Makefile + create mode 100644 tests/certs/expired/server/cert.cnf + create mode 100644 tests/certs/expired/server/server.csr + create mode 100644 tests/certs/expired/server/server.key + create mode 100644 tests/certs/expired/server/server.pem + create mode 100644 tests/certs/mtls/Makefile + create mode 100644 tests/certs/mtls/README.md + create mode 100644 tests/certs/mtls/client/Makefile + create mode 120000 tests/certs/mtls/client/ca + create mode 100644 tests/certs/mtls/client/cert.cnf + create mode 100644 tests/certs/mtls/client/client.csr + create mode 100644 tests/certs/mtls/client/client.key + create mode 100644 tests/certs/mtls/client/client.pem + create mode 120000 tests/certs/valid/ca + create mode 100644 tests/certs/valid/server/Makefile + create mode 100644 tests/certs/valid/server/cert.cnf + create mode 100644 tests/certs/valid/server/server.csr + create mode 100644 tests/certs/valid/server/server.key + create mode 100644 tests/certs/valid/server/server.pem + +diff --git a/requests/adapters.py b/requests/adapters.py +index 03cd069..227c4d6 100644 +--- a/requests/adapters.py ++++ b/requests/adapters.py +@@ -73,7 +73,9 @@ DEFAULT_POOL_TIMEOUT = None + + + def _urllib3_request_context( +- request: "PreparedRequest", verify: "bool | str | None" ++ request: "PreparedRequest", ++ verify: "bool | str | None", ++ client_cert: "typing.Tuple[str, str] | str | None", + ) -> "(typing.Dict[str, typing.Any], typing.Dict[str, typing.Any])": + host_params = {} + pool_kwargs = {} +@@ -86,6 +88,14 @@ def _urllib3_request_context( + if isinstance(verify, str): + pool_kwargs["ca_certs"] = verify + pool_kwargs["cert_reqs"] = cert_reqs ++ if client_cert is not None: ++ if isinstance(client_cert, tuple) and len(client_cert) == 2: ++ pool_kwargs["cert_file"] = client_cert[0] ++ pool_kwargs["key_file"] = client_cert[1] ++ else: ++ # According to our docs, we allow users to specify just the client ++ # cert path ++ pool_kwargs["cert_file"] = client_cert + host_params = { + "scheme": scheme, + "host": parsed_request_url.hostname, +@@ -355,13 +365,13 @@ class HTTPAdapter(BaseAdapter): + + return response + +- def _get_connection(self, request, verify, proxies=None): ++ def _get_connection(self, request, verify, proxies=None, cert=None): + # Replace the existing get_connection without breaking things and + # ensure that TLS settings are considered when we interact with + # urllib3 HTTP Pools + proxy = select_proxy(request.url, proxies) + try: +- host_params, pool_kwargs = _urllib3_request_context(request, verify) ++ host_params, pool_kwargs = _urllib3_request_context(request, verify, cert) + except ValueError as e: + raise InvalidURL(e, request=request) + if proxy: +@@ -507,7 +517,7 @@ class HTTPAdapter(BaseAdapter): + """ + + try: +- conn = self._get_connection(request, verify, proxies) ++ conn = self._get_connection(request, verify, proxies=proxies, cert=cert) + except LocationValueError as e: + raise InvalidURL(e, request=request) + +diff --git a/tests/certs/README.md b/tests/certs/README.md +new file mode 100644 +index 0000000..4bf7002 +--- /dev/null ++++ b/tests/certs/README.md +@@ -0,0 +1,10 @@ ++# Testing Certificates ++ ++This is a collection of certificates useful for testing aspects of Requests' ++behaviour. ++ ++The certificates include: ++ ++* [expired](./expired) server certificate with a valid certificate authority ++* [mtls](./mtls) provides a valid client certificate with a 2 year validity ++* [valid](./valid) has a valid server certificate +diff --git a/tests/certs/expired/Makefile b/tests/certs/expired/Makefile +new file mode 100644 +index 0000000..d5a51da +--- /dev/null ++++ b/tests/certs/expired/Makefile +@@ -0,0 +1,13 @@ ++.PHONY: all clean ca server ++ ++ca: ++ make -C $@ all ++ ++server: ++ make -C $@ all ++ ++all: ca server ++ ++clean: ++ make -C ca clean ++ make -C server clean +diff --git a/tests/certs/expired/README.md b/tests/certs/expired/README.md +new file mode 100644 +index 0000000..f7234f8 +--- /dev/null ++++ b/tests/certs/expired/README.md +@@ -0,0 +1,11 @@ ++# Expired Certificates and Configuration for Testing ++ ++This has a valid certificate authority in [ca](./ca) and an invalid server ++certificate in [server](./server). ++ ++This can all be regenerated with: ++ ++``` ++make clean ++make all ++``` +diff --git a/tests/certs/expired/ca/Makefile b/tests/certs/expired/ca/Makefile +new file mode 100644 +index 0000000..098193f +--- /dev/null ++++ b/tests/certs/expired/ca/Makefile +@@ -0,0 +1,13 @@ ++.PHONY: all clean ++ ++root_files = ca-private.key ca.crt ++ ++ca-private.key: ++ openssl genrsa -out ca-private.key 2048 ++ ++all: ca-private.key ++ openssl req -x509 -sha256 -days 7300 -key ca-private.key -out ca.crt -config ca.cnf ++ ln -s ca.crt cacert.pem ++ ++clean: ++ rm -f cacert.pem ca.crt ca-private.key *.csr +diff --git a/tests/certs/expired/ca/ca-private.key b/tests/certs/expired/ca/ca-private.key +new file mode 100644 +index 0000000..507b1f5 +--- /dev/null ++++ b/tests/certs/expired/ca/ca-private.key +@@ -0,0 +1,28 @@ ++-----BEGIN PRIVATE KEY----- ++MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHlIhe7GLCeSk8 ++RZOKdtmyKns6KdZgGw/LcxPkYvQlu1g0zV8X0DqVr2LdMumWUTNCc9sPdSlAG+He ++mQp2TMoWUMumMuwDtit9RT0Sb6Eh9svWgjY9ferovPJRfCWUTsA2Ug8uoh0wyEXK ++na7X6fHt5E3B9vj0+b9a4vDibdBXV11FheLT02/uEmAEJDdP/zeBgvVbhcVyumO6 ++fAGMIWzR2ukhe8z/ma5H9zoi4gZA8nsK6reZUD8+6affnPe+jIt/AdzggtV9jkWm ++zSpr+RHeZ0y+q4eik2ZNUGg4XcF6JsJ9yu/AqLBXxd38uLdFfgyhP2y6K628yzgy ++e6lzFyWnAgMBAAECggEAFwzHhzcD3PQDWCus85PwZoxTeQ817BmUBGpBBOKM0gLG ++GCsT7XsmGP2NjICBy9OK+QTKawmb/wR5XK0OMUWDHXqtWn+NFIyojyo8+HEeCf8n ++4ZleTFHLnJ+d2N1etbc2qc9mY3tjpaurq8/0Tol9YH06ock1TY2+lO+a5HvMURnY ++hcWs70CamL+5B/6n67DhjzMtIW3dIXuEEceM1BW/jW8SKq0JHpQ3t+OJwID7zFaJ ++bLyOwAVheMzVGvN3yphf8tll3tMA65bNjdOzgOfZSjAy7EGjW3DyAolDw9jKLRyu ++E0gw/exNGe618oMIeUDv0KParlL4RjdiUP8l0xYOwQKBgQD3eYj9rWeqZquI9vKP ++gaSv6urb2UJLngShZUpEZRNJgBO+Ewiof0w8tpQdsnuMvWudxMLbzgiUNA+NyC/K ++CpzIXFkWnWx+A/pxs8ZO8moOfajVRayJgeOLsQZb7c4fXGsVGApbN4+cPNhTNG6d ++ucErv6tae/SzAzcLc5Vkw/ELxwKBgQDOdJ5Wl5JeKAvU/3kF6+MYWCrXxZqMjoHS ++y1BtyMX5RbdaWTCfDUu1aV3qJOJjjWQ9DJdJQcEsrTjOpD4bVdZx4w/XEG0JXAa3 ++jRypVHGdeG/TjhUGJA8U+KX3a1DkcdqM9pqFYRw5Ie95Wz9YRroI+YkixqpK8d7W ++C+5BodxXIQKBgCk8Lv9V7XgPM3XW8APJbk+BrTCEuu8unUbnQcCztssAdEmvkjnB ++PErBgVyRaNTCmzPmnTFS20sWgaD2QkBAFG+uM4n5ISK+NvTLJ7fv3IwdlAw1V9Jx ++uiCElrKqpTXEiHMzVkZss5ks6j6y9duCIBXSEhM5pERPvNRDphjsLTXxAoGARSNC ++nyb1Kjjo9XR0V+pNy6pC9q1C+00B5tCVZ55zxe114Hi70pfGQcM+YxnlAoeoCNW9 ++mBfAFDESNAlGjyrovIzYkiH7EcZSrYdBEOepgJ2DfWo4Wi0bK9+03K2AknAaS1iO ++GJqTtAJMSuymwu40gKroJNA42Q40nKO0LyCARGECgYEAiFRHkblBtStv22SpZxNC ++jim9yuM0ikh7Ij1lEHysc/GWb2RQNxQVk54BU2kQ0d9xwMZQTKvpF3VE9t7uGdwt ++AasWPr/tWYt35Ud0D4bNlagJJ4Xdslf8n1nkq3qqqDQbd7kkQRgwGzVr0uVg7ZfS ++26qSPQ0/aF9nagb5eHX3AuU= ++-----END PRIVATE KEY----- +diff --git a/tests/certs/expired/ca/ca.cnf b/tests/certs/expired/ca/ca.cnf +new file mode 100644 +index 0000000..8c4b823 +--- /dev/null ++++ b/tests/certs/expired/ca/ca.cnf +@@ -0,0 +1,12 @@ ++[req] ++default_bits = 2048 ++prompt = no ++default_md = sha256 ++encrypt_key = no ++distinguished_name = dn ++ ++[dn] ++C = US # country code ++O = Python Software Foundation # organization ++OU = python-requests # organization unit/department ++CN = Self-Signed Root CA # common name / your cert name +diff --git a/tests/certs/expired/ca/ca.crt b/tests/certs/expired/ca/ca.crt +new file mode 100644 +index 0000000..c332b7c +--- /dev/null ++++ b/tests/certs/expired/ca/ca.crt +@@ -0,0 +1,20 @@ ++-----BEGIN CERTIFICATE----- ++MIIDWzCCAkMCFA9wdtNh/V99DRwYp8vXjPxSjJnWMA0GCSqGSIb3DQEBCwUAMGox ++CzAJBgNVBAYTAlVTMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlv ++bjEYMBYGA1UECwwPcHl0aG9uLXJlcXVlc3RzMRwwGgYDVQQDDBNTZWxmLVNpZ25l ++ZCBSb290IENBMB4XDTI0MDMxMjIxMDQwM1oXDTQ0MDMwNzIxMDQwM1owajELMAkG ++A1UEBhMCVVMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMRgw ++FgYDVQQLDA9weXRob24tcmVxdWVzdHMxHDAaBgNVBAMME1NlbGYtU2lnbmVkIFJv ++b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHlIhe7GLCeSk8 ++RZOKdtmyKns6KdZgGw/LcxPkYvQlu1g0zV8X0DqVr2LdMumWUTNCc9sPdSlAG+He ++mQp2TMoWUMumMuwDtit9RT0Sb6Eh9svWgjY9ferovPJRfCWUTsA2Ug8uoh0wyEXK ++na7X6fHt5E3B9vj0+b9a4vDibdBXV11FheLT02/uEmAEJDdP/zeBgvVbhcVyumO6 ++fAGMIWzR2ukhe8z/ma5H9zoi4gZA8nsK6reZUD8+6affnPe+jIt/AdzggtV9jkWm ++zSpr+RHeZ0y+q4eik2ZNUGg4XcF6JsJ9yu/AqLBXxd38uLdFfgyhP2y6K628yzgy ++e6lzFyWnAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGymNVTsKSAq8Ju6zV+AWAyV ++GcUNBmLpgzDA0e7pkVYhHTdWKlGH4GnrRcp0nvnSbr6iq1Ob/8yEUUoRzK55Flws ++Kt1OLwnZyhfRoSUesoEqpP68vzWEgiYv0QuIWvzNt0YfAAvEgGoc3iri44MelKLn ++9ZMT8m91nVamA35R8ZjfeAkNp2xcz0a67V0ww6o4wSXrG7o5ZRXyjqZ/9K7SfwUJ ++rV9RciccsjH/MzKbfrx73QwsbPWiFmjzHopdasIO0lDlmgm/r9gKfkbzfKoGCgLZ ++6an6FlmLftLSXijf/QwtqeSP9fODeE3dzBmnTM3jdoVS53ZegUDWNl14o25v2Kg= ++-----END CERTIFICATE----- +diff --git a/tests/certs/expired/ca/ca.srl b/tests/certs/expired/ca/ca.srl +new file mode 100644 +index 0000000..fab6840 +--- /dev/null ++++ b/tests/certs/expired/ca/ca.srl +@@ -0,0 +1 @@ ++4F36C3A7E075BA6452D10EEB81E7F189FF489B74 +diff --git a/tests/certs/expired/server/Makefile b/tests/certs/expired/server/Makefile +new file mode 100644 +index 0000000..79914ee +--- /dev/null ++++ b/tests/certs/expired/server/Makefile +@@ -0,0 +1,16 @@ ++.PHONY: all clean ++ ++server.key: ++ openssl genrsa -out $@ 2048 ++ ++server.csr: server.key ++ openssl req -key $< -new -out $@ -config cert.cnf ++ ++server.pem: server.csr ++ openssl x509 -req -CA ../ca/ca.crt -CAkey ../ca/ca-private.key -in server.csr -outform PEM -out server.pem -days 0 -CAcreateserial ++ openssl x509 -in ../ca/ca.crt -outform PEM >> $@ ++ ++all: server.pem ++ ++clean: ++ rm -f server.* +diff --git a/tests/certs/expired/server/cert.cnf b/tests/certs/expired/server/cert.cnf +new file mode 100644 +index 0000000..a773fc6 +--- /dev/null ++++ b/tests/certs/expired/server/cert.cnf +@@ -0,0 +1,24 @@ ++[req] ++req_extensions = v3_req ++distinguished_name = req_distinguished_name ++prompt=no ++ ++[req_distinguished_name] ++C = US ++ST = DE ++O = Python Software Foundation ++OU = python-requests ++CN = localhost ++ ++[v3_req] ++# Extensions to add to a certificate request ++basicConstraints = CA:FALSE ++keyUsage = digitalSignature, keyEncipherment ++extendedKeyUsage = serverAuth ++subjectAltName = @alt_names ++ ++[alt_names] ++DNS.1 = *.localhost ++DNS.1 = localhost ++IP.1 = 127.0.0.1 ++IP.2 = ::1 +diff --git a/tests/certs/expired/server/server.csr b/tests/certs/expired/server/server.csr +new file mode 100644 +index 0000000..5e3c177 +--- /dev/null ++++ b/tests/certs/expired/server/server.csr +@@ -0,0 +1,19 @@ ++-----BEGIN CERTIFICATE REQUEST----- ++MIIDHjCCAgYCAQAwbTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkRFMSMwIQYDVQQK ++DBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlvbjEYMBYGA1UECwwPcHl0aG9uLXJl ++cXVlc3RzMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IB ++DwAwggEKAoIBAQCKulIMpo633iCgbkKv1UoiLC4sQt5xWpgguujywu3hLYwmPFp9 ++kvPt//imqtl8FhuhKqJ8FCGrVl2YIGj1RJIB3GW7MSPNCuIBFL/gwNi35LxDPtoA ++IPyXytIR7VH9+ch9DFInJaoA/BekMuKvbXk54VW9whpHbwkXSG2lBS2vKL0XemYh ++9VjvtuRDji2iOZpznlVE2PEN80bojArp6oYKakv2kYzgzgxAJiI/NZGvC7mbSI4e ++ja7ad3R9G0kB1FzNj36jrNO5WtxHO/mrRiXSpDeyUbitYvt0HKoM0vhTnOR+BspP ++IltfwOQh8qq2Q2AaMHNcVjMH3gHCZADfhk/zAgMBAAGgbDBqBgkqhkiG9w0BCQ4x ++XTBbMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMB ++MCwGA1UdEQQlMCOCCWxvY2FsaG9zdIcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATAN ++BgkqhkiG9w0BAQsFAAOCAQEAfAhEhrulsZae71YFqgvzwJHm/hzXh47hErtgDXVJ ++mFqAxgF6XrnzYujlt3XQXUx/8vdrU7jH+Pe8WO1rDvFwRPMDGoBF3RX29SzyX/2F ++e102egnoRR+Hlf0Ixqu0CuTjEVnD+g4mRgXhV7LPKP4W6qGwzcVbaJ3c/zRcfqNR ++g9gN6Q6Qt4fXDc7wlx2T3nOszBLQ2XCsIyzVtOJ2sSuadqKH9Aj+mrkrLBdzVFHD ++FHnTMJ0t0+anZwd+AWDNsCr5lIwBGL634zw7/yJepMHuPFd2X24S3u8EaWPkfVQn ++lV6rLQMGjXYTe2xuYzlUCUYnKvkyPTMjSXDkxWa+WSNwyQ== ++-----END CERTIFICATE REQUEST----- +diff --git a/tests/certs/expired/server/server.key b/tests/certs/expired/server/server.key +new file mode 100644 +index 0000000..27ddafd +--- /dev/null ++++ b/tests/certs/expired/server/server.key +@@ -0,0 +1,28 @@ ++-----BEGIN PRIVATE KEY----- ++MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCKulIMpo633iCg ++bkKv1UoiLC4sQt5xWpgguujywu3hLYwmPFp9kvPt//imqtl8FhuhKqJ8FCGrVl2Y ++IGj1RJIB3GW7MSPNCuIBFL/gwNi35LxDPtoAIPyXytIR7VH9+ch9DFInJaoA/Bek ++MuKvbXk54VW9whpHbwkXSG2lBS2vKL0XemYh9VjvtuRDji2iOZpznlVE2PEN80bo ++jArp6oYKakv2kYzgzgxAJiI/NZGvC7mbSI4eja7ad3R9G0kB1FzNj36jrNO5WtxH ++O/mrRiXSpDeyUbitYvt0HKoM0vhTnOR+BspPIltfwOQh8qq2Q2AaMHNcVjMH3gHC ++ZADfhk/zAgMBAAECggEAFSF9RvUFzyb0BEvXN44+/QaKv+4tkMmSW4Xs3rFnZ4G3 ++E8nkpLUCF9ICD2z9tKNvcPScDFdKq5z7o6ToJ9faf5MRIdrBz8UlGLIO6g6l1Bjw ++vjNwJE3h+8MGjXl/IDbwXW/HgbQAeabsePPRSJRdvz2+ACn1M8VLdrLvFJA93ayW +++n3Bk0bXdsrzqBGdoDiNzmIHI3WqdONiR9TymuJe41NJtMKxQDF+c6Y1n/X1OtBk ++s9L+u9Xr+R3H72xSYrf1KH1mFZJfTnIPoOmdEU2tVZnZj03rZhT7p8R1fVNX6OHu ++NX1Dy9VA6J7dbcqdPvTI743ByQeb+hNnqI/3hmV5eQKBgQC++1Wn3v/dxtczjA+I ++tN4a7zyjhazpB25lde55HVfCQPxmYxIYct+j6S0JkMaoLrjiEDb4pnu4Gt4MDqZa ++r0Xm8t3wD1YKUUbhpBEGvsMhAEZEIsBOcwkTiEwsoF0mKFa2mTyqAImgIQa8uFt8 ++Y/oTj55XFe1x6pZKEJRg+K+QSwKBgQC59ONVkMSBirLGS+G+b2kqiBdwZB/3s3wr ++feS1xTa+deL3AChnKT9+MsVqOkxdE2TRj/mAeF+5Woa5bPMvgr9Kl7u8bulTH80l ++YA/N6FneO11/ncnkgK9wN54kd5TiOtGsGB5S5t/nEAIMUIwWrM/cRau72xNEWOhT ++Tvw7TOSF+QKBgQCa/texeiYmE24sA4vH4yIuseKAw8hlBwbtiRyVZt8GZD9zyQuy ++k+g02tUWYk0XyXN65LX4bwURkZyMJIeWKZGNsaW1YnzturDQB5tZ4g/zBIoCWkHA ++aVQAaimIPk3a3foiD5NQVUdckfEp0GVPOsSGg5R6EO23+i8mxPXnDW1OqQKBgGvf ++lelTO8tyLFdAOcqBUt6rZ/1499p3snaAZ6bSqvk95dYnr0h48y5AQaln/FiaIYg4 ++HyLZsZ4S18jFXSWYkWOyNeQP6yafciBWY5StT0TN52VaoX3+8McGXKUHAcVjHbLZ ++ou2wpP6jmKyQJVQaF9LOT9uAMOMbOFrrnQLBjmfxAoGAQAnUhMFG5mwi9Otxt6Mz ++g+Gr+3JTlzwC3L7UwGdlFc3G2vSdGx/yOrfzpxPImfIBS95mibDfdvEBMer26pvw ++a/ycqybyX9d/5nPDIaJ1lc4M4cbHC/cB52JI6avr/1g8OMK7lR7b/FsPVHS1w8kl ++n6uwEjVt2+gP2o9DFTGs158= ++-----END PRIVATE KEY----- +diff --git a/tests/certs/expired/server/server.pem b/tests/certs/expired/server/server.pem +new file mode 100644 +index 0000000..05a2a4d +--- /dev/null ++++ b/tests/certs/expired/server/server.pem +@@ -0,0 +1,41 @@ ++-----BEGIN CERTIFICATE----- ++MIIDXjCCAkYCFE82w6fgdbpkUtEO64Hn8Yn/SJt0MA0GCSqGSIb3DQEBCwUAMGox ++CzAJBgNVBAYTAlVTMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlv ++bjEYMBYGA1UECwwPcHl0aG9uLXJlcXVlc3RzMRwwGgYDVQQDDBNTZWxmLVNpZ25l ++ZCBSb290IENBMB4XDTI0MDMxMzIxMTQ0NVoXDTI0MDMxMzIxMTQ0NVowbTELMAkG ++A1UEBhMCVVMxCzAJBgNVBAgMAkRFMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUg ++Rm91bmRhdGlvbjEYMBYGA1UECwwPcHl0aG9uLXJlcXVlc3RzMRIwEAYDVQQDDAls ++b2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCKulIMpo63 ++3iCgbkKv1UoiLC4sQt5xWpgguujywu3hLYwmPFp9kvPt//imqtl8FhuhKqJ8FCGr ++Vl2YIGj1RJIB3GW7MSPNCuIBFL/gwNi35LxDPtoAIPyXytIR7VH9+ch9DFInJaoA ++/BekMuKvbXk54VW9whpHbwkXSG2lBS2vKL0XemYh9VjvtuRDji2iOZpznlVE2PEN ++80bojArp6oYKakv2kYzgzgxAJiI/NZGvC7mbSI4eja7ad3R9G0kB1FzNj36jrNO5 ++WtxHO/mrRiXSpDeyUbitYvt0HKoM0vhTnOR+BspPIltfwOQh8qq2Q2AaMHNcVjMH ++3gHCZADfhk/zAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGeQdB4+iDbJ78eKhCMV ++49Cm8nyYi9215rRRJ24Bw6BtVw1ECwymxLVOEB0gHCu8kKdsFnniFBtChts/ilFg ++blIyPKTsb3+kQW9YV9QwVdFdC4mTIljujCSQ4HNUC/Vjfnz85SDKf9/3PMKRr36+ ++GtSLIozudPvkNmCv68jy3RRXyCwWHc43BLMSZKPD/W+DEuXShI9OIpIlSLBx16Hz ++4ce3/1pGuITWcsw6UcRqW31oPR31QmNs5fsq5ZCojDNFzEFCA1t9LiR6UOftFUKy ++yOZWfZeAGGdK75U+XDqS9Xkr5/ic5jE0I5rT7e7r3lpvQdgIj8lSx493fczLOGHr ++YA0= ++-----END CERTIFICATE----- ++-----BEGIN CERTIFICATE----- ++MIIDWzCCAkMCFA9wdtNh/V99DRwYp8vXjPxSjJnWMA0GCSqGSIb3DQEBCwUAMGox ++CzAJBgNVBAYTAlVTMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlv ++bjEYMBYGA1UECwwPcHl0aG9uLXJlcXVlc3RzMRwwGgYDVQQDDBNTZWxmLVNpZ25l ++ZCBSb290IENBMB4XDTI0MDMxMjIxMDQwM1oXDTQ0MDMwNzIxMDQwM1owajELMAkG ++A1UEBhMCVVMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMRgw ++FgYDVQQLDA9weXRob24tcmVxdWVzdHMxHDAaBgNVBAMME1NlbGYtU2lnbmVkIFJv ++b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHlIhe7GLCeSk8 ++RZOKdtmyKns6KdZgGw/LcxPkYvQlu1g0zV8X0DqVr2LdMumWUTNCc9sPdSlAG+He ++mQp2TMoWUMumMuwDtit9RT0Sb6Eh9svWgjY9ferovPJRfCWUTsA2Ug8uoh0wyEXK ++na7X6fHt5E3B9vj0+b9a4vDibdBXV11FheLT02/uEmAEJDdP/zeBgvVbhcVyumO6 ++fAGMIWzR2ukhe8z/ma5H9zoi4gZA8nsK6reZUD8+6affnPe+jIt/AdzggtV9jkWm ++zSpr+RHeZ0y+q4eik2ZNUGg4XcF6JsJ9yu/AqLBXxd38uLdFfgyhP2y6K628yzgy ++e6lzFyWnAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGymNVTsKSAq8Ju6zV+AWAyV ++GcUNBmLpgzDA0e7pkVYhHTdWKlGH4GnrRcp0nvnSbr6iq1Ob/8yEUUoRzK55Flws ++Kt1OLwnZyhfRoSUesoEqpP68vzWEgiYv0QuIWvzNt0YfAAvEgGoc3iri44MelKLn ++9ZMT8m91nVamA35R8ZjfeAkNp2xcz0a67V0ww6o4wSXrG7o5ZRXyjqZ/9K7SfwUJ ++rV9RciccsjH/MzKbfrx73QwsbPWiFmjzHopdasIO0lDlmgm/r9gKfkbzfKoGCgLZ ++6an6FlmLftLSXijf/QwtqeSP9fODeE3dzBmnTM3jdoVS53ZegUDWNl14o25v2Kg= ++-----END CERTIFICATE----- +diff --git a/tests/certs/mtls/Makefile b/tests/certs/mtls/Makefile +new file mode 100644 +index 0000000..399a906 +--- /dev/null ++++ b/tests/certs/mtls/Makefile +@@ -0,0 +1,7 @@ ++.PHONY: all clean ++ ++all: ++ make -C client all ++ ++clean: ++ make -C client clean +diff --git a/tests/certs/mtls/README.md b/tests/certs/mtls/README.md +new file mode 100644 +index 0000000..9a3df46 +--- /dev/null ++++ b/tests/certs/mtls/README.md +@@ -0,0 +1,4 @@ ++# Certificate Examples for mTLS ++ ++This has some generated certificates for mTLS utilization. The idea is to be ++able to have testing around how Requests handles client certificates. +diff --git a/tests/certs/mtls/client/Makefile b/tests/certs/mtls/client/Makefile +new file mode 100644 +index 0000000..9c6c388 +--- /dev/null ++++ b/tests/certs/mtls/client/Makefile +@@ -0,0 +1,16 @@ ++.PHONY: all clean ++ ++client.key: ++ openssl genrsa -out $@ 2048 ++ ++client.csr: client.key ++ openssl req -key $< -new -out $@ -config cert.cnf ++ ++client.pem: client.csr ++ openssl x509 -req -CA ./ca/ca.crt -CAkey ./ca/ca-private.key -in client.csr -outform PEM -out client.pem -days 730 -CAcreateserial ++ openssl x509 -in ./ca/ca.crt -outform PEM >> $@ ++ ++all: client.pem ++ ++clean: ++ rm -f client.* +diff --git a/tests/certs/mtls/client/ca b/tests/certs/mtls/client/ca +new file mode 120000 +index 0000000..85c8e8f +--- /dev/null ++++ b/tests/certs/mtls/client/ca +@@ -0,0 +1 @@ ++../../expired/ca/ +\ No newline at end of file +diff --git a/tests/certs/mtls/client/cert.cnf b/tests/certs/mtls/client/cert.cnf +new file mode 100644 +index 0000000..338e252 +--- /dev/null ++++ b/tests/certs/mtls/client/cert.cnf +@@ -0,0 +1,26 @@ ++[req] ++req_extensions = v3_req ++distinguished_name = req_distinguished_name ++prompt=no ++ ++[req_distinguished_name] ++C = US ++ST = DE ++O = Python Software Foundation ++OU = python-requests ++CN = requests ++ ++[v3_req] ++# Extensions to add to a certificate request ++basicConstraints = CA:FALSE ++keyUsage = digitalSignature, keyEncipherment ++extendedKeyUsage = clientAuth ++subjectAltName = @alt_names ++ ++[alt_names] ++DNS.1 = *.localhost ++IP.1 = 127.0.0.1 ++IP.2 = ::1 ++URI.1 = spiffe://trust.python.org/v0/maintainer/sigmavirus24/project/requests/org/psf ++URI.2 = spiffe://trust.python.org/v1/maintainer:sigmavirus24/project:requests/org:psf ++URI.3 = spiffe://trust.python.org/v1/maintainer=sigmavirus24/project=requests/org=psf +diff --git a/tests/certs/mtls/client/client.csr b/tests/certs/mtls/client/client.csr +new file mode 100644 +index 0000000..9a5713d +--- /dev/null ++++ b/tests/certs/mtls/client/client.csr +@@ -0,0 +1,24 @@ ++-----BEGIN CERTIFICATE REQUEST----- ++MIIEGjCCAwICAQAwbDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkRFMSMwIQYDVQQK ++DBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlvbjEYMBYGA1UECwwPcHl0aG9uLXJl ++cXVlc3RzMREwDwYDVQQDDAhyZXF1ZXN0czCCASIwDQYJKoZIhvcNAQEBBQADggEP ++ADCCAQoCggEBAMn3iQycTjUzpKJChRNkcm33UB282cUwpxeqKN4ahHxBpS09HRhk ++cQYO7yErEUQwzQnBQEcIpzzeIMZIqHuCkgnySjeEJd95AIzNzGyoLLkS51TcJwgR ++v83AvT8ljA88s9h38qGTy4/TCxJgf76pfHIuC1qoKVQh3AuHj9nOxIZLUsrdDbWF ++WoLqKSVyTby+RXvSAppAR+cuBCaWStQ6xFORn48RHfc6t30ggD4rDAjyU6Vz6oR8 ++ot3XmGdK0h42UdqidUWkRJajEbpkCnQSXS21IvfXKxF5sFqAXJrj9iVbUfpNPpaa ++W8IrHByngyV8amazGZrASstUVRFtWrnrcWECAwEAAaCCAWcwggFjBgkqhkiG9w0B ++CQ4xggFUMIIBUDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggr ++BgEFBQcDAjCCAR8GA1UdEQSCARYwggESggsqLmxvY2FsaG9zdIcEfwAAAYcQAAAA ++AAAAAAAAAAAAAAAAAYZNc3BpZmZlOi8vdHJ1c3QucHl0aG9uLm9yZy92MC9tYWlu ++dGFpbmVyL3NpZ21hdmlydXMyNC9wcm9qZWN0L3JlcXVlc3RzL29yZy9wc2aGTXNw ++aWZmZTovL3RydXN0LnB5dGhvbi5vcmcvdjEvbWFpbnRhaW5lcjpzaWdtYXZpcnVz ++MjQvcHJvamVjdDpyZXF1ZXN0cy9vcmc6cHNmhk1zcGlmZmU6Ly90cnVzdC5weXRo ++b24ub3JnL3YxL21haW50YWluZXI9c2lnbWF2aXJ1czI0L3Byb2plY3Q9cmVxdWVz ++dHMvb3JnPXBzZjANBgkqhkiG9w0BAQsFAAOCAQEAwP1KJ+Evddn2RV1FM6BFkoDK ++MPDO9qwb8ea3j57SIJXZlpw168DljmuGzxJw9oys2O6FYcspbHIocAkfFwiYgVAr ++NEog6xlCdPxNBJgC3YFIKwnmBjMPG6ZCWiJn940qTbaJ/j6ZviN17uW4K7Sl+THp ++IkMv29uQTWkfg+GbZ9q1hm2m2GHhYLGLAUdJdtv7JI+yq5uxdsWaCANpH6kc8SnK ++2rik6D3iItDhHCmToHBpdEnP8J+KDzf5pJrv/g3WH8XVrl4ZzBsOhmciWF4C3Hbf ++9eu8eAsp1AsIrZOEGTfClBd7vFCES5DmI0/iRs4czQooqZPnHjOw3Azp/LujrA== ++-----END CERTIFICATE REQUEST----- +diff --git a/tests/certs/mtls/client/client.key b/tests/certs/mtls/client/client.key +new file mode 100644 +index 0000000..8107125 +--- /dev/null ++++ b/tests/certs/mtls/client/client.key +@@ -0,0 +1,28 @@ ++-----BEGIN PRIVATE KEY----- ++MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDJ94kMnE41M6Si ++QoUTZHJt91AdvNnFMKcXqijeGoR8QaUtPR0YZHEGDu8hKxFEMM0JwUBHCKc83iDG ++SKh7gpIJ8ko3hCXfeQCMzcxsqCy5EudU3CcIEb/NwL0/JYwPPLPYd/Khk8uP0wsS ++YH++qXxyLgtaqClUIdwLh4/ZzsSGS1LK3Q21hVqC6iklck28vkV70gKaQEfnLgQm ++lkrUOsRTkZ+PER33Ord9IIA+KwwI8lOlc+qEfKLd15hnStIeNlHaonVFpESWoxG6 ++ZAp0El0ttSL31ysRebBagFya4/YlW1H6TT6WmlvCKxwcp4MlfGpmsxmawErLVFUR ++bVq563FhAgMBAAECggEABhWX97JJxN6JFNOjhgGzqiPA3R8lrFlv3zhNbODS9u9U ++q404xYBZIKaYhkucLzgNJUBrevhZbsL+V8WJQIH0JlU57nw5ATIjAHA+uqiXraen ++zRhTcLHK28b1AeRUA4LU+YN7jWnnawN075kf9WgjtfOJ0gcDimOkE7uCFjyyvPJA ++LG9bG+8enGjvUleKXNgmwP4Sq/GlEdGz9Qy+8ga3mtfAULUWe8haFNZXK8CN3xPp ++wmVqy7QzgH2TGN1p6Dyxib9ksSN/lOg0dShL8zgu+QXDNx2VwmVrI8Vr02vmB//0 ++bYxCo5pfICPIFLjLl5yo30dvrUfYqF29PperStHGlQKBgQD/TdemlLjJNP0fvSs7 ++KEVJj/22YuHK+wurNr2ZFbSdcF3v9sfiwysllmEyGr5cNYA56uUbfG+8VSw7kDll ++G+6BKK2UdlPH++6RahqWLqo4k6rsNrkq7elj8xG4gIjR5qzu2uLpjNwp2BGmIoUI ++eb1NcLfTlMcNCooV8RHjm1Z5WwKBgQDKhHkUPDcJm2/9Ltq2NZQMrCS7o4LV2uAI ++GhGpISfY+SfHkQQNZ9Fvbe6hrFeZs31nAvlTDpPEg/LGSVKA5I2EZT9gwzAQU1TD ++Cyol4xqqWFWlwze7w+RLYqX5LtXf7NJg2m5p+ZOoOzzqvTVpodDxqTlCNp2/6ICP ++vAIvWhbA8wKBgAYlr62ZIyHlHrsm6OWRwKlWyDseAmXKyasjtEj9Vs37qKdgf8ub +++2v6RPjZ3/+EYkQCveV9h4s3WctNW7Rtib6eZh+PAdFs5X+m2GEJWpvmIlVxs9+u ++vtHjRmf04FZ9gWh26MPK2no/c51Wc3GSzNYSgrqbeHd963k/xrh+QwTFAoGAZZjb ++3UjwG4O9RPjyhCKQ6WKa8v9urbamWaoqXfziLrmgOUAJFmiU6x/tbXI2aEdhjAIz ++7nULsLS5YLx8BWmjjV3106dYP3hut4KsXGF4iSjTnts25J27tA4DUeUrKrF2QVyT ++s9qfNvCw+Np/J0Uku3e33/3iWdpcVL9vIS5C5/0CgYBEuxb3dffNRqEiNkpOUrCD ++mQTqbO3X+hin9zT3GrxQE+7KpfCfdDIqdK6c5UWHirR3HUjUPZmIFLSx8msfLl3k ++hgQw37NMV+asg0Wy3P908qbtnEA2P6aDOMQeHJoC7qEHIDOcOQ1KP3FMvOrdscwS ++f0IIDygTH6fYr329s0iXjg== ++-----END PRIVATE KEY----- +diff --git a/tests/certs/mtls/client/client.pem b/tests/certs/mtls/client/client.pem +new file mode 100644 +index 0000000..0a11d4d +--- /dev/null ++++ b/tests/certs/mtls/client/client.pem +@@ -0,0 +1,41 @@ ++-----BEGIN CERTIFICATE----- ++MIIDXTCCAkUCFE82w6fgdbpkUtEO64Hn8Yn/SJtzMA0GCSqGSIb3DQEBCwUAMGox ++CzAJBgNVBAYTAlVTMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlv ++bjEYMBYGA1UECwwPcHl0aG9uLXJlcXVlc3RzMRwwGgYDVQQDDBNTZWxmLVNpZ25l ++ZCBSb290IENBMB4XDTI0MDMxMzE4MzUwNFoXDTI2MDMxMzE4MzUwNFowbDELMAkG ++A1UEBhMCVVMxCzAJBgNVBAgMAkRFMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUg ++Rm91bmRhdGlvbjEYMBYGA1UECwwPcHl0aG9uLXJlcXVlc3RzMREwDwYDVQQDDAhy ++ZXF1ZXN0czCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMn3iQycTjUz ++pKJChRNkcm33UB282cUwpxeqKN4ahHxBpS09HRhkcQYO7yErEUQwzQnBQEcIpzze ++IMZIqHuCkgnySjeEJd95AIzNzGyoLLkS51TcJwgRv83AvT8ljA88s9h38qGTy4/T ++CxJgf76pfHIuC1qoKVQh3AuHj9nOxIZLUsrdDbWFWoLqKSVyTby+RXvSAppAR+cu ++BCaWStQ6xFORn48RHfc6t30ggD4rDAjyU6Vz6oR8ot3XmGdK0h42UdqidUWkRJaj ++EbpkCnQSXS21IvfXKxF5sFqAXJrj9iVbUfpNPpaaW8IrHByngyV8amazGZrASstU ++VRFtWrnrcWECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAHHgMckLDRV72p1FEVmCh ++AAPZjCswiPZFrwGPN57JqSWjoRB9ilKvo87aPosEO7vfa05OD/qkM/T9Qykuhati ++I1T1T7qX4Ymb5kTJIBouuflAO3uKVaq+ga2Q/HLlU5w/VoMU4RuK7+RaiRUEE3xL ++iPSMBvZpoMj695LnzcGrT5oLkFI0bTIlpQt1SFjDpHFtOj/ZdwgSbZYLoTCBXQK3 ++7Y29qAj/XwEiCH63n8tJKvZcD8/ssMIMIdWhNmu+0jOWica/3WSih9Geoy6Ydtxi ++I5t9vRjC4LIipMUAF86AJIfvHJyI6aCNT420LaR6NRW0FQn5CPTHPAsKg3JkAywn ++Ew== ++-----END CERTIFICATE----- ++-----BEGIN CERTIFICATE----- ++MIIDWzCCAkMCFA9wdtNh/V99DRwYp8vXjPxSjJnWMA0GCSqGSIb3DQEBCwUAMGox ++CzAJBgNVBAYTAlVTMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlv ++bjEYMBYGA1UECwwPcHl0aG9uLXJlcXVlc3RzMRwwGgYDVQQDDBNTZWxmLVNpZ25l ++ZCBSb290IENBMB4XDTI0MDMxMjIxMDQwM1oXDTQ0MDMwNzIxMDQwM1owajELMAkG ++A1UEBhMCVVMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMRgw ++FgYDVQQLDA9weXRob24tcmVxdWVzdHMxHDAaBgNVBAMME1NlbGYtU2lnbmVkIFJv ++b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHlIhe7GLCeSk8 ++RZOKdtmyKns6KdZgGw/LcxPkYvQlu1g0zV8X0DqVr2LdMumWUTNCc9sPdSlAG+He ++mQp2TMoWUMumMuwDtit9RT0Sb6Eh9svWgjY9ferovPJRfCWUTsA2Ug8uoh0wyEXK ++na7X6fHt5E3B9vj0+b9a4vDibdBXV11FheLT02/uEmAEJDdP/zeBgvVbhcVyumO6 ++fAGMIWzR2ukhe8z/ma5H9zoi4gZA8nsK6reZUD8+6affnPe+jIt/AdzggtV9jkWm ++zSpr+RHeZ0y+q4eik2ZNUGg4XcF6JsJ9yu/AqLBXxd38uLdFfgyhP2y6K628yzgy ++e6lzFyWnAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGymNVTsKSAq8Ju6zV+AWAyV ++GcUNBmLpgzDA0e7pkVYhHTdWKlGH4GnrRcp0nvnSbr6iq1Ob/8yEUUoRzK55Flws ++Kt1OLwnZyhfRoSUesoEqpP68vzWEgiYv0QuIWvzNt0YfAAvEgGoc3iri44MelKLn ++9ZMT8m91nVamA35R8ZjfeAkNp2xcz0a67V0ww6o4wSXrG7o5ZRXyjqZ/9K7SfwUJ ++rV9RciccsjH/MzKbfrx73QwsbPWiFmjzHopdasIO0lDlmgm/r9gKfkbzfKoGCgLZ ++6an6FlmLftLSXijf/QwtqeSP9fODeE3dzBmnTM3jdoVS53ZegUDWNl14o25v2Kg= ++-----END CERTIFICATE----- +diff --git a/tests/certs/valid/ca b/tests/certs/valid/ca +new file mode 120000 +index 0000000..46f26c3 +--- /dev/null ++++ b/tests/certs/valid/ca +@@ -0,0 +1 @@ ++../expired/ca +\ No newline at end of file +diff --git a/tests/certs/valid/server/Makefile b/tests/certs/valid/server/Makefile +new file mode 100644 +index 0000000..9ce6778 +--- /dev/null ++++ b/tests/certs/valid/server/Makefile +@@ -0,0 +1,16 @@ ++.PHONY: all clean ++ ++server.key: ++ openssl genrsa -out $@ 2048 ++ ++server.csr: server.key ++ openssl req -key $< -config cert.cnf -new -out $@ ++ ++server.pem: server.csr ++ openssl x509 -req -CA ../ca/ca.crt -CAkey ../ca/ca-private.key -in server.csr -outform PEM -out server.pem -extfile cert.cnf -extensions v3_ca -days 7200 -CAcreateserial ++ openssl x509 -in ../ca/ca.crt -outform PEM >> $@ ++ ++all: server.pem ++ ++clean: ++ rm -f server.* +diff --git a/tests/certs/valid/server/cert.cnf b/tests/certs/valid/server/cert.cnf +new file mode 100644 +index 0000000..f9a01cd +--- /dev/null ++++ b/tests/certs/valid/server/cert.cnf +@@ -0,0 +1,31 @@ ++[req] ++req_extensions = v3_req ++distinguished_name = req_distinguished_name ++prompt=no ++ ++[req_distinguished_name] ++C = US ++ST = DE ++O = Python Software Foundation ++OU = python-requests ++CN = localhost ++ ++[v3_req] ++# Extensions to add to a certificate request ++basicConstraints = critical, CA:FALSE ++keyUsage = critical, digitalSignature, keyEncipherment ++extendedKeyUsage = critical, serverAuth ++subjectAltName = critical, @alt_names ++ ++[v3_ca] ++# Extensions to add to a certificate request ++basicConstraints = critical, CA:FALSE ++keyUsage = critical, digitalSignature, keyEncipherment ++extendedKeyUsage = critical, serverAuth ++subjectAltName = critical, @alt_names ++ ++[alt_names] ++DNS.1 = *.localhost ++DNS.1 = localhost ++IP.1 = 127.0.0.1 ++IP.2 = ::1 +diff --git a/tests/certs/valid/server/server.csr b/tests/certs/valid/server/server.csr +new file mode 100644 +index 0000000..000d1fa +--- /dev/null ++++ b/tests/certs/valid/server/server.csr +@@ -0,0 +1,19 @@ ++-----BEGIN CERTIFICATE REQUEST----- ++MIIDKjCCAhICAQAwbTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkRFMSMwIQYDVQQK ++DBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlvbjEYMBYGA1UECwwPcHl0aG9uLXJl ++cXVlc3RzMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IB ++DwAwggEKAoIBAQChEKOx377ymuDg23By5Re1DHi2RiBKSHr85/ZTZuwP/69lHN7q ++TQEO//EMEFZ9+ZwezeJJsejjP2HO5lQZbcsWok3hbM0wVT+vApkogPvJ8WNFFWFe ++ZBnGLi/1WM9cSZpUsDJ0XCsG0RTtO27wfgZQlKQMZxTkfi971oPYxNVSjTm2JcLT ++kvwYIwxjJXPDTOgRo9TEAY3cWkCrBJN4w74GWBTM5KDDA230T7WwLuv81XD2LvYj ++YYdMBGcxPr5tYTIlp3LncbcrDRNk3pbYQk0bRJgkw2vUkteiRGjkt+dgVnLc6+MI ++W+VLXEpj+zsOZ5/R4d1pofqj9sDyDPhtNr1JAgMBAAGgeDB2BgkqhkiG9w0BCQ4x ++aTBnMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgWgMBYGA1UdJQEB/wQMMAoG ++CCsGAQUFBwMBMC8GA1UdEQEB/wQlMCOCCWxvY2FsaG9zdIcEfwAAAYcQAAAAAAAA ++AAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAFTlFTn5Mn8JXtqB5bGjuiChe ++ClA6Y32Co4l7N0CtAlf+bExwLdpLOleTX3WnryIPALl9uBUI/67dy/STn/J1Yn86 ++jWPEFwpmYNSKgQljYWcwtBdYLWfIsJO11kKdaAkOUHBEN5DKrXJ46Vs4918bD1/Q ++6ztqdrThiKc646u9xB58Hg7F0IyMWbHfs0x16ZpcN9otrIkbqOE2wzTmc65O1t1i ++HDljcSk7OnNy3a9wtLEnyPiyMqHf2k/bTlmiDRVe3cSy9xieoqmzHTnOCSASe1y9 ++7lcEBQild18Jo4nACV4vCYOUwrMi/58LWW+lD6OmMnPiWUqOvMbgMffMNDpWPA== ++-----END CERTIFICATE REQUEST----- +diff --git a/tests/certs/valid/server/server.key b/tests/certs/valid/server/server.key +new file mode 100644 +index 0000000..d6afaf5 +--- /dev/null ++++ b/tests/certs/valid/server/server.key +@@ -0,0 +1,28 @@ ++-----BEGIN PRIVATE KEY----- ++MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQChEKOx377ymuDg ++23By5Re1DHi2RiBKSHr85/ZTZuwP/69lHN7qTQEO//EMEFZ9+ZwezeJJsejjP2HO ++5lQZbcsWok3hbM0wVT+vApkogPvJ8WNFFWFeZBnGLi/1WM9cSZpUsDJ0XCsG0RTt ++O27wfgZQlKQMZxTkfi971oPYxNVSjTm2JcLTkvwYIwxjJXPDTOgRo9TEAY3cWkCr ++BJN4w74GWBTM5KDDA230T7WwLuv81XD2LvYjYYdMBGcxPr5tYTIlp3LncbcrDRNk ++3pbYQk0bRJgkw2vUkteiRGjkt+dgVnLc6+MIW+VLXEpj+zsOZ5/R4d1pofqj9sDy ++DPhtNr1JAgMBAAECggEAIuLzBfXgCvXzlBjL2kMXd7p4EgkN+PEKnKmUr/t40b1Q ++zR6sBQWBX3GeET4fseElSQHQzCQaPNCve4xltm1S4jftFREHP7sTVHHEYWLQxuy/ ++Uwkewj5927CI6ERgg82YfVP91bjaA/u5I+pt7O7rKLyNbPdN7fEMEW+FNuhpiVvg ++JMrcK1BCFL6pmIT21LyTwkacMKZSPko58pWE24MA9aSCHk6cXdwQWQK0AfQT3XGT ++C4I0hRed7LgqMH+gMuhpakiO13t8yTwxt2iQC9+aa4oSHD3BOi/CwIWfe1mHwmlr ++cj4Kof1JSnK4SVTD16T++PlnWZkF6oaLUNg+/c2C9QKBgQDOFSYIY7+HzinT2hbI ++yTIJCHpp+Iee+WVvvxjdZIPMDINrlIiHcMfXb0itUdcUO6tz0KYDMDLRC9CSP0ar ++6mBWUTHfAKF2S4JpI9JYI4PNtIpOP1NiYuyJlnh5+ytU1yIeIvl39hmLcRwI9mgz ++njy/D7yEoDCrG1dhcltubKpNXQKBgQDIFAVg0A7MNcxBZDLlk1NAME2JKOSszX8E ++VNucvZD+9l+L9V9BmwwPQdzYifv/dNp3nYn+lxRPPgze3ZWu4+PeDuGudxu0I6ll ++beFdbIcp1wbeQguzHYLjBYJqsMb4Pao5HPInjPu/HWfZlg9oZpJbKVucQwbonJLX ++lgca9KaE3QKBgA+OUx+g/+0tZ8ThGoUvgsJhzHPBWeNrKfgEcckMdFJrw2PUg3XN ++0pf1g4PpwJV7Z5bHcjCda8iR3r2bXydM+tapLF2L+6QlUQPEu3UBwUo+zY3Yg9/S ++Xc6I+DEk/4FY9+9UboZaolT/RcF7cCQtVqKJeo58VRAlcTQe4L32H+jVAoGALXX3 ++Ht9HbXkP1w/YTLej4+LVy0OCag0rPiW13LBqALSkUx3GrhZ3sAPMFVuM6ad4eFNQ ++ZouXbsXvkLgSabGYNf11o/mmTtEHjWdhHKQrNgOIqPmixOkAs2quDmXqX79LLTz5 ++fKkZDny0+wiQqa0cth/4k9HbAQGKj/ej16kdKPUCgYAz08Y39NnJYxRNz3tu/7C6 ++jKyXKxhuZCZCt3cSWto5Tg0mVVB+2Jk2GhG1hCfZoRCP25R3FFBR1HOJgOc59T7C ++LL67FdO0+7mj/WNzHj3+9gyOYQyQgPVDaTmsJLbuzT2S+GpR94ZNliwL2NEa5baG ++B/Nb2ruRNj0GgZVw48N4XQ== ++-----END PRIVATE KEY----- +diff --git a/tests/certs/valid/server/server.pem b/tests/certs/valid/server/server.pem +new file mode 100644 +index 0000000..0168cd3 +--- /dev/null ++++ b/tests/certs/valid/server/server.pem +@@ -0,0 +1,47 @@ ++-----BEGIN CERTIFICATE----- ++MIIEhTCCA22gAwIBAgIUTzbDp+B1umRS0Q7rgefxif9Im3wwDQYJKoZIhvcNAQEL ++BQAwajELMAkGA1UEBhMCVVMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3Vu ++ZGF0aW9uMRgwFgYDVQQLDA9weXRob24tcmVxdWVzdHMxHDAaBgNVBAMME1NlbGYt ++U2lnbmVkIFJvb3QgQ0EwHhcNMjQwMzE0MDAxMDAzWhcNNDMxMTMwMDAxMDAzWjBt ++MQswCQYDVQQGEwJVUzELMAkGA1UECAwCREUxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0 ++d2FyZSBGb3VuZGF0aW9uMRgwFgYDVQQLDA9weXRob24tcmVxdWVzdHMxEjAQBgNV ++BAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKEQ ++o7HfvvKa4ODbcHLlF7UMeLZGIEpIevzn9lNm7A//r2Uc3upNAQ7/8QwQVn35nB7N ++4kmx6OM/Yc7mVBltyxaiTeFszTBVP68CmSiA+8nxY0UVYV5kGcYuL/VYz1xJmlSw ++MnRcKwbRFO07bvB+BlCUpAxnFOR+L3vWg9jE1VKNObYlwtOS/BgjDGMlc8NM6BGj ++1MQBjdxaQKsEk3jDvgZYFMzkoMMDbfRPtbAu6/zVcPYu9iNhh0wEZzE+vm1hMiWn ++cudxtysNE2TelthCTRtEmCTDa9SS16JEaOS352BWctzr4whb5UtcSmP7Ow5nn9Hh ++3Wmh+qP2wPIM+G02vUkCAwEAAaOCAR4wggEaMAwGA1UdEwEB/wQCMAAwDgYDVR0P ++AQH/BAQDAgWgMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMBMC8GA1UdEQEB/wQlMCOC ++CWxvY2FsaG9zdIcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATAdBgNVHQ4EFgQUJ90a ++UnXKPP13yDprLhG39fUrnu8wgZEGA1UdIwSBiTCBhqFupGwwajELMAkGA1UEBhMC ++VVMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMRgwFgYDVQQL ++DA9weXRob24tcmVxdWVzdHMxHDAaBgNVBAMME1NlbGYtU2lnbmVkIFJvb3QgQ0GC ++FA9wdtNh/V99DRwYp8vXjPxSjJnWMA0GCSqGSIb3DQEBCwUAA4IBAQCVh4hiraRv ++JzYbS/TombP//xfVEWHXDBEYsT5GgWf7GPJ/QtSvv6uJFsK7heqLzf9f+r4Z5xMh ++YAkb0oe/Ge0T30Mo1YaBEqkKuQL9lOMcP69S9uFz2VT6I/76I8qqAu2AFhu74p8f ++qudwmQyRYo1Ryg4R/SgRhSJKF/ST/2wOusNWSsBe1s8S2PmtOb4dr3cMBGihrUzS ++DmCQpWjuiuE23HXnnYDc/EUAnEEPkLDgCsE9iLq37FPUHcHjqdYIAhmImPBpv2EL ++ftXeRWfxN2hRHpS5Fn3QuAOwfJw5tUcVXojJCJfSpL+Ac97iSjxNaDIPlyomauKw ++1rgbUkSw+9JQ ++-----END CERTIFICATE----- ++-----BEGIN CERTIFICATE----- ++MIIDWzCCAkMCFA9wdtNh/V99DRwYp8vXjPxSjJnWMA0GCSqGSIb3DQEBCwUAMGox ++CzAJBgNVBAYTAlVTMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlv ++bjEYMBYGA1UECwwPcHl0aG9uLXJlcXVlc3RzMRwwGgYDVQQDDBNTZWxmLVNpZ25l ++ZCBSb290IENBMB4XDTI0MDMxMjIxMDQwM1oXDTQ0MDMwNzIxMDQwM1owajELMAkG ++A1UEBhMCVVMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMRgw ++FgYDVQQLDA9weXRob24tcmVxdWVzdHMxHDAaBgNVBAMME1NlbGYtU2lnbmVkIFJv ++b3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHlIhe7GLCeSk8 ++RZOKdtmyKns6KdZgGw/LcxPkYvQlu1g0zV8X0DqVr2LdMumWUTNCc9sPdSlAG+He ++mQp2TMoWUMumMuwDtit9RT0Sb6Eh9svWgjY9ferovPJRfCWUTsA2Ug8uoh0wyEXK ++na7X6fHt5E3B9vj0+b9a4vDibdBXV11FheLT02/uEmAEJDdP/zeBgvVbhcVyumO6 ++fAGMIWzR2ukhe8z/ma5H9zoi4gZA8nsK6reZUD8+6affnPe+jIt/AdzggtV9jkWm ++zSpr+RHeZ0y+q4eik2ZNUGg4XcF6JsJ9yu/AqLBXxd38uLdFfgyhP2y6K628yzgy ++e6lzFyWnAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGymNVTsKSAq8Ju6zV+AWAyV ++GcUNBmLpgzDA0e7pkVYhHTdWKlGH4GnrRcp0nvnSbr6iq1Ob/8yEUUoRzK55Flws ++Kt1OLwnZyhfRoSUesoEqpP68vzWEgiYv0QuIWvzNt0YfAAvEgGoc3iri44MelKLn ++9ZMT8m91nVamA35R8ZjfeAkNp2xcz0a67V0ww6o4wSXrG7o5ZRXyjqZ/9K7SfwUJ ++rV9RciccsjH/MzKbfrx73QwsbPWiFmjzHopdasIO0lDlmgm/r9gKfkbzfKoGCgLZ ++6an6FlmLftLSXijf/QwtqeSP9fODeE3dzBmnTM3jdoVS53ZegUDWNl14o25v2Kg= ++-----END CERTIFICATE----- +diff --git a/tests/test_requests.py b/tests/test_requests.py +index 1263088..9c924fe 100644 +--- a/tests/test_requests.py ++++ b/tests/test_requests.py +@@ -7,6 +7,7 @@ import json + import os + import pickle + import re ++import threading + import warnings + + import pytest +@@ -50,6 +51,7 @@ from requests.structures import CaseInsensitiveDict + + from . import SNIMissingWarning + from .compat import StringIO ++from .testserver.server import TLSServer, consume_socket_content + from .utils import override_environ + + # Requests to this URL should always fail with a connection timeout (nothing +@@ -2809,9 +2811,137 @@ class TestPreparingURLs: + r.json() + assert excinfo.value.doc == r.text + +- def test_different_connection_pool_for_tls_settings(self): ++ def test_different_connection_pool_for_tls_settings_verify_True(self): ++ def response_handler(sock): ++ consume_socket_content(sock, timeout=0.5) ++ sock.send( ++ b"HTTP/1.1 200 OK\r\n" ++ b"Content-Length: 18\r\n\r\n" ++ b'\xff\xfe{\x00"\x00K0"\x00=\x00"\x00\xab0"\x00\r\n' ++ ) ++ ++ s = requests.Session() ++ close_server = threading.Event() ++ server = TLSServer( ++ handler=response_handler, ++ wait_to_close_event=close_server, ++ requests_to_handle=3, ++ cert_chain="tests/certs/expired/server/server.pem", ++ keyfile="tests/certs/expired/server/server.key", ++ ) ++ ++ with server as (host, port): ++ url = f"https://{host}:{port}" ++ r1 = s.get(url, verify=False) ++ assert r1.status_code == 200 ++ ++ # Cannot verify self-signed certificate ++ with pytest.raises(requests.exceptions.SSLError): ++ s.get(url) ++ ++ close_server.set() ++ assert 2 == len(s.adapters["https://"].poolmanager.pools) ++ ++ def test_different_connection_pool_for_tls_settings_verify_bundle_expired_cert( ++ self, ++ ): ++ def response_handler(sock): ++ consume_socket_content(sock, timeout=0.5) ++ sock.send( ++ b"HTTP/1.1 200 OK\r\n" ++ b"Content-Length: 18\r\n\r\n" ++ b'\xff\xfe{\x00"\x00K0"\x00=\x00"\x00\xab0"\x00\r\n' ++ ) ++ ++ s = requests.Session() ++ close_server = threading.Event() ++ server = TLSServer( ++ handler=response_handler, ++ wait_to_close_event=close_server, ++ requests_to_handle=3, ++ cert_chain="tests/certs/expired/server/server.pem", ++ keyfile="tests/certs/expired/server/server.key", ++ ) ++ ++ with server as (host, port): ++ url = f"https://{host}:{port}" ++ r1 = s.get(url, verify=False) ++ assert r1.status_code == 200 ++ ++ # Has right trust bundle, but certificate expired ++ with pytest.raises(requests.exceptions.SSLError): ++ s.get(url, verify="tests/certs/expired/ca/ca.crt") ++ ++ close_server.set() ++ assert 2 == len(s.adapters["https://"].poolmanager.pools) ++ ++ def test_different_connection_pool_for_tls_settings_verify_bundle_unexpired_cert( ++ self, ++ ): ++ def response_handler(sock): ++ consume_socket_content(sock, timeout=0.5) ++ sock.send( ++ b"HTTP/1.1 200 OK\r\n" ++ b"Content-Length: 18\r\n\r\n" ++ b'\xff\xfe{\x00"\x00K0"\x00=\x00"\x00\xab0"\x00\r\n' ++ ) ++ ++ s = requests.Session() ++ close_server = threading.Event() ++ server = TLSServer( ++ handler=response_handler, ++ wait_to_close_event=close_server, ++ requests_to_handle=3, ++ cert_chain="tests/certs/valid/server/server.pem", ++ keyfile="tests/certs/valid/server/server.key", ++ ) ++ ++ with server as (host, port): ++ url = f"https://{host}:{port}" ++ r1 = s.get(url, verify=False) ++ assert r1.status_code == 200 ++ ++ r2 = s.get(url, verify="tests/certs/valid/ca/ca.crt") ++ assert r2.status_code == 200 ++ ++ close_server.set() ++ assert 2 == len(s.adapters["https://"].poolmanager.pools) ++ ++ def test_different_connection_pool_for_mtls_settings(self): ++ client_cert = None ++ ++ def response_handler(sock): ++ nonlocal client_cert ++ client_cert = sock.getpeercert() ++ consume_socket_content(sock, timeout=0.5) ++ sock.send( ++ b"HTTP/1.1 200 OK\r\n" ++ b"Content-Length: 18\r\n\r\n" ++ b'\xff\xfe{\x00"\x00K0"\x00=\x00"\x00\xab0"\x00\r\n' ++ ) ++ + s = requests.Session() +- r1 = s.get("https://invalid.badssl.com", verify=False) +- assert r1.status_code == 421 +- with pytest.raises(requests.exceptions.SSLError): +- s.get("https://invalid.badssl.com") ++ close_server = threading.Event() ++ server = TLSServer( ++ handler=response_handler, ++ wait_to_close_event=close_server, ++ requests_to_handle=2, ++ cert_chain="tests/certs/expired/server/server.pem", ++ keyfile="tests/certs/expired/server/server.key", ++ mutual_tls=True, ++ cacert="tests/certs/expired/ca/ca.crt", ++ ) ++ ++ cert = ( ++ "tests/certs/mtls/client/client.pem", ++ "tests/certs/mtls/client/client.key", ++ ) ++ with server as (host, port): ++ url = f"https://{host}:{port}" ++ r1 = s.get(url, verify=False, cert=cert) ++ assert r1.status_code == 200 ++ with pytest.raises(requests.exceptions.SSLError): ++ s.get(url, cert=cert) ++ close_server.set() ++ ++ assert client_cert is not None +diff --git a/tests/testserver/server.py b/tests/testserver/server.py +index 5936abd..da1b656 100644 +--- a/tests/testserver/server.py ++++ b/tests/testserver/server.py +@@ -1,5 +1,6 @@ + import select + import socket ++import ssl + import threading + + +@@ -132,3 +133,44 @@ class Server(threading.Thread): + self._close_server_sock_ignore_errors() + self.join() + return False # allow exceptions to propagate ++ ++ ++class TLSServer(Server): ++ def __init__( ++ self, ++ *, ++ handler=None, ++ host="localhost", ++ port=0, ++ requests_to_handle=1, ++ wait_to_close_event=None, ++ cert_chain=None, ++ keyfile=None, ++ mutual_tls=False, ++ cacert=None, ++ ): ++ super().__init__( ++ handler=handler, ++ host=host, ++ port=port, ++ requests_to_handle=requests_to_handle, ++ wait_to_close_event=wait_to_close_event, ++ ) ++ self.cert_chain = cert_chain ++ self.keyfile = keyfile ++ self.ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) ++ self.ssl_context.load_cert_chain(self.cert_chain, keyfile=self.keyfile) ++ self.mutual_tls = mutual_tls ++ self.cacert = cacert ++ if mutual_tls: ++ # For simplicity, we're going to assume that the client cert is ++ # issued by the same CA as our Server certificate ++ self.ssl_context.verify_mode = ssl.CERT_OPTIONAL ++ self.ssl_context.load_verify_locations(self.cacert) ++ ++ def _create_socket_and_bind(self): ++ sock = socket.socket() ++ sock = self.ssl_context.wrap_socket(sock, server_side=True) ++ sock.bind((self.host, self.port)) ++ sock.listen() ++ return sock +-- +2.33.0 + diff --git a/backport-0003-CVE-2024-35195.patch b/backport-0003-CVE-2024-35195.patch new file mode 100644 index 0000000..4c4f142 --- /dev/null +++ b/backport-0003-CVE-2024-35195.patch @@ -0,0 +1,98 @@ +From 9a40d1277807f0a4f26c9a37eea8ec90faa8aadc Mon Sep 17 00:00:00 2001 +From: agubelu +Date: Wed, 15 May 2024 22:07:26 +0200 +Subject: [PATCH] Avoid reloading root certificates to improve concurrent + performance (#6667) + +Reference:https://github.com/psf/requests/commit/9a40d1277807f0a4f26c9a37eea8ec90faa8aadc +Conflict:Adapt context +--- + requests/adapters.py | 46 +++++++++++++++++++++++++++----------------- + 1 file changed, 28 insertions(+), 18 deletions(-) + +diff --git a/requests/adapters.py b/requests/adapters.py +index 227c4d6..dbc6dc2 100644 +--- a/requests/adapters.py ++++ b/requests/adapters.py +@@ -26,6 +26,7 @@ from urllib3.poolmanager import PoolManager, proxy_from_url + from urllib3.util import Timeout as TimeoutSauce + from urllib3.util import parse_url + from urllib3.util.retry import Retry ++from urllib3.util.ssl_ import create_urllib3_context + + from .auth import _basic_auth_str + from .compat import basestring, urlparse +@@ -71,6 +72,11 @@ DEFAULT_POOLSIZE = 10 + DEFAULT_RETRIES = 0 + DEFAULT_POOL_TIMEOUT = None + ++_preloaded_ssl_context = create_urllib3_context() ++_preloaded_ssl_context.load_verify_locations( ++ extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH) ++) ++ + + def _urllib3_request_context( + request: "PreparedRequest", +@@ -85,8 +91,13 @@ def _urllib3_request_context( + cert_reqs = "CERT_REQUIRED" + if verify is False: + cert_reqs = "CERT_NONE" +- if isinstance(verify, str): +- pool_kwargs["ca_certs"] = verify ++ elif verify is True: ++ pool_kwargs["ssl_context"] = _preloaded_ssl_context ++ elif isinstance(verify, str): ++ if not os.path.isdir(verify): ++ pool_kwargs["ca_certs"] = verify ++ else: ++ pool_kwargs["ca_cert_dir"] = verify + pool_kwargs["cert_reqs"] = cert_reqs + if client_cert is not None: + if isinstance(client_cert, tuple) and len(client_cert) == 2: +@@ -285,27 +296,26 @@ class HTTPAdapter(BaseAdapter): + """ + if url.lower().startswith("https") and verify: + +- cert_loc = None ++ conn.cert_reqs = "CERT_REQUIRED" + +- # Allow self-specified cert location. ++ # Only load the CA certificates if 'verify' is a string indicating the CA bundle to use. ++ # Otherwise, if verify is a boolean, we don't load anything since ++ # the connection will be using a context with the default certificates already loaded, ++ # and this avoids a call to the slow load_verify_locations() + if verify is not True: ++ # `verify` must be a str with a path then + cert_loc = verify + +- if not cert_loc: +- cert_loc = extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH) +- +- if not cert_loc or not os.path.exists(cert_loc): +- raise OSError( +- f"Could not find a suitable TLS CA certificate bundle, " +- f"invalid path: {cert_loc}" +- ) ++ if not os.path.exists(cert_loc): ++ raise OSError( ++ f"Could not find a suitable TLS CA certificate bundle, " ++ f"invalid path: {cert_loc}" ++ ) + +- conn.cert_reqs = "CERT_REQUIRED" +- +- if not os.path.isdir(cert_loc): +- conn.ca_certs = cert_loc +- else: +- conn.ca_cert_dir = cert_loc ++ if not os.path.isdir(cert_loc): ++ conn.ca_certs = cert_loc ++ else: ++ conn.ca_cert_dir = cert_loc + else: + conn.cert_reqs = "CERT_NONE" + conn.ca_certs = None +-- +2.33.0 + diff --git a/backport-0004-CVE-2024-35195.patch b/backport-0004-CVE-2024-35195.patch new file mode 100644 index 0000000..8ed97a4 --- /dev/null +++ b/backport-0004-CVE-2024-35195.patch @@ -0,0 +1,66 @@ +From aa1461b68aa73e2f6ec0e78c8853b635c76fd099 Mon Sep 17 00:00:00 2001 +From: Nate Prewitt +Date: Tue, 21 May 2024 05:40:52 -0700 +Subject: [PATCH] Move _get_connection to get_connection_with_tls_context + +Reference:https://github.com/psf/requests/commit/aa1461b68aa73e2f6ec0e78c8853b635c76fd099 +Conflict:no +--- + requests/adapters.py | 27 +++++++++++++++++++++------ + 1 file changed, 21 insertions(+), 6 deletions(-) + +diff --git a/requests/adapters.py b/requests/adapters.py +index dbc6dc2..464b57f 100644 +--- a/requests/adapters.py ++++ b/requests/adapters.py +@@ -375,10 +375,20 @@ class HTTPAdapter(BaseAdapter): + + return response + +- def _get_connection(self, request, verify, proxies=None, cert=None): +- # Replace the existing get_connection without breaking things and +- # ensure that TLS settings are considered when we interact with +- # urllib3 HTTP Pools ++ def get_connection_with_tls_context(self, request, verify, proxies=None, cert=None): ++ """Returns a urllib3 connection for the given request and TLS settings. ++ This should not be called from user code, and is only exposed for use ++ when subclassing the :class:`HTTPAdapter `. ++ ++ :param request: The :class:`PreparedRequest ` object ++ to be sent over the connection. ++ :param verify: Either a boolean, in which case it controls whether ++ we verify the server's TLS certificate, or a string, in which case it ++ must be a path to a CA bundle to use. ++ :param proxies: (optional) The proxies dictionary to apply to the request. ++ :param cert: (optional) Any user-provided SSL certificate to be trusted. ++ :rtype: urllib3.ConnectionPool ++ """ + proxy = select_proxy(request.url, proxies) + try: + host_params, pool_kwargs = _urllib3_request_context(request, verify, cert) +@@ -405,7 +415,10 @@ class HTTPAdapter(BaseAdapter): + return conn + + def get_connection(self, url, proxies=None): +- """Returns a urllib3 connection for the given URL. This should not be ++ """DEPRECATED: Users should move to `get_connection_with_tls_context` ++ for all subclasses of HTTPAdapter using Requests>=2.32.2. ++ ++ Returns a urllib3 connection for the given URL. This should not be + called from user code, and is only exposed for use when subclassing the + :class:`HTTPAdapter `. + +@@ -527,7 +540,9 @@ class HTTPAdapter(BaseAdapter): + """ + + try: +- conn = self._get_connection(request, verify, proxies=proxies, cert=cert) ++ conn = self.get_connection_with_tls_context( ++ request, verify, proxies=proxies, cert=cert ++ ) + except LocationValueError as e: + raise InvalidURL(e, request=request) + +-- +2.33.0 + diff --git a/backport-0005-CVE-2024-35195.patch b/backport-0005-CVE-2024-35195.patch new file mode 100644 index 0000000..f5f5286 --- /dev/null +++ b/backport-0005-CVE-2024-35195.patch @@ -0,0 +1,116 @@ +From a62a2d35d918baa8e793f7aa4fb41527644dfca5 Mon Sep 17 00:00:00 2001 +From: Ian Stapleton Cordasco +Date: Wed, 22 May 2024 06:51:48 -0500 +Subject: [PATCH] Allow for overriding of specific pool key params + +This re-enables the use case of providing a custom SSLContext via a +Transport Adapter as broken in #6655 and reported in #6715 + +Closes #6715 + +Reference:https://github.com/psf/requests/commit/a62a2d35d918baa8e793f7aa4fb41527644dfca5 +Conflict:no +--- + requests/adapters.py | 78 +++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 69 insertions(+), 9 deletions(-) + +diff --git a/requests/adapters.py b/requests/adapters.py +index 464b57f..9a795d5 100644 +--- a/requests/adapters.py ++++ b/requests/adapters.py +@@ -375,23 +375,83 @@ class HTTPAdapter(BaseAdapter): + + return response + ++ def build_connection_pool_key_attributes(self, request, verify, cert=None): ++ """Build the PoolKey attributes used by urllib3 to return a connection. ++ ++ This looks at the PreparedRequest, the user-specified verify value, ++ and the value of the cert parameter to determine what PoolKey values ++ to use to select a connection from a given urllib3 Connection Pool. ++ ++ The SSL related pool key arguments are not consistently set. As of ++ this writing, use the following to determine what keys may be in that ++ dictionary: ++ ++ * If ``verify`` is ``True``, ``"ssl_context"`` will be set and will be the ++ default Requests SSL Context ++ * If ``verify`` is ``False``, ``"ssl_context"`` will not be set but ++ ``"cert_reqs"`` will be set ++ * If ``verify`` is a string, (i.e., it is a user-specified trust bundle) ++ ``"ca_certs"`` will be set if the string is not a directory recognized ++ by :py:func:`os.path.isdir`, otherwise ``"ca_certs_dir"`` will be ++ set. ++ * If ``"cert"`` is specified, ``"cert_file"`` will always be set. If ++ ``"cert"`` is a tuple with a second item, ``"key_file"`` will also ++ be present ++ ++ To override these settings, one may subclass this class, call this ++ method and use the above logic to change parameters as desired. For ++ example, if one wishes to use a custom :py:class:`ssl.SSLContext` one ++ must both set ``"ssl_context"`` and based on what else they require, ++ alter the other keys to ensure the desired behaviour. ++ ++ :param request: ++ The PreparedReqest being sent over the connection. ++ :type request: ++ :class:`~requests.models.PreparedRequest` ++ :param verify: ++ Either a boolean, in which case it controls whether ++ we verify the server's TLS certificate, or a string, in which case it ++ must be a path to a CA bundle to use. ++ :param cert: ++ (optional) Any user-provided SSL certificate for client ++ authentication (a.k.a., mTLS). This may be a string (i.e., just ++ the path to a file which holds both certificate and key) or a ++ tuple of length 2 with the certificate file path and key file ++ path. ++ :returns: ++ A tuple of two dictionaries. The first is the "host parameters" ++ portion of the Pool Key including scheme, hostname, and port. The ++ second is a dictionary of SSLContext related parameters. ++ """ ++ return _urllib3_request_context(request, verify, cert) ++ + def get_connection_with_tls_context(self, request, verify, proxies=None, cert=None): + """Returns a urllib3 connection for the given request and TLS settings. + This should not be called from user code, and is only exposed for use + when subclassing the :class:`HTTPAdapter `. + +- :param request: The :class:`PreparedRequest ` object +- to be sent over the connection. +- :param verify: Either a boolean, in which case it controls whether +- we verify the server's TLS certificate, or a string, in which case it +- must be a path to a CA bundle to use. +- :param proxies: (optional) The proxies dictionary to apply to the request. +- :param cert: (optional) Any user-provided SSL certificate to be trusted. +- :rtype: urllib3.ConnectionPool ++ :param request: ++ The :class:`PreparedRequest ` object to be sent ++ over the connection. ++ :param verify: ++ Either a boolean, in which case it controls whether we verify the ++ server's TLS certificate, or a string, in which case it must be a ++ path to a CA bundle to use. ++ :param proxies: ++ (optional) The proxies dictionary to apply to the request. ++ :param cert: ++ (optional) Any user-provided SSL certificate to be used for client ++ authentication (a.k.a., mTLS). ++ :rtype: ++ urllib3.ConnectionPool + """ + proxy = select_proxy(request.url, proxies) + try: +- host_params, pool_kwargs = _urllib3_request_context(request, verify, cert) ++ host_params, pool_kwargs = self.build_connection_pool_key_attributes( ++ request, ++ verify, ++ cert, ++ ) + except ValueError as e: + raise InvalidURL(e, request=request) + if proxy: +-- +2.33.0 + diff --git a/backport-0006-CVE-2024-35195.patch b/backport-0006-CVE-2024-35195.patch new file mode 100644 index 0000000..fba4d07 --- /dev/null +++ b/backport-0006-CVE-2024-35195.patch @@ -0,0 +1,49 @@ +From b1d73ddb509a3a2d3e10744e85f9cdebdbde90f0 Mon Sep 17 00:00:00 2001 +From: Nate Prewitt +Date: Fri, 24 May 2024 09:00:52 -0700 +Subject: [PATCH] Don't use default SSLContext with custom poolmanager kwargs + +Reference:https://github.com/psf/requests/commit/b1d73ddb509a3a2d3e10744e85f9cdebdbde90f0 +Conflict:no +--- + requests/adapters.py | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/requests/adapters.py b/requests/adapters.py +index 9a795d5..b8fdfef 100644 +--- a/requests/adapters.py ++++ b/requests/adapters.py +@@ -82,16 +82,20 @@ def _urllib3_request_context( + request: "PreparedRequest", + verify: "bool | str | None", + client_cert: "typing.Tuple[str, str] | str | None", ++ poolmanager: "PoolManager", + ) -> "(typing.Dict[str, typing.Any], typing.Dict[str, typing.Any])": + host_params = {} + pool_kwargs = {} + parsed_request_url = urlparse(request.url) + scheme = parsed_request_url.scheme.lower() + port = parsed_request_url.port ++ poolmanager_kwargs = getattr(poolmanager, "connection_pool_kw", {}) ++ has_poolmanager_ssl_context = poolmanager_kwargs.get("ssl_context") ++ + cert_reqs = "CERT_REQUIRED" + if verify is False: + cert_reqs = "CERT_NONE" +- elif verify is True: ++ elif verify is True and not has_poolmanager_ssl_context: + pool_kwargs["ssl_context"] = _preloaded_ssl_context + elif isinstance(verify, str): + if not os.path.isdir(verify): +@@ -423,7 +427,7 @@ class HTTPAdapter(BaseAdapter): + portion of the Pool Key including scheme, hostname, and port. The + second is a dictionary of SSLContext related parameters. + """ +- return _urllib3_request_context(request, verify, cert) ++ return _urllib3_request_context(request, verify, cert, self.poolmanager) + + def get_connection_with_tls_context(self, request, verify, proxies=None, cert=None): + """Returns a urllib3 connection for the given request and TLS settings. +-- +2.33.0 + diff --git a/backport-0007-CVE-2024-35195.patch b/backport-0007-CVE-2024-35195.patch new file mode 100644 index 0000000..edaedc3 --- /dev/null +++ b/backport-0007-CVE-2024-35195.patch @@ -0,0 +1,63 @@ +From e18879932287c2bf4bcee4ddf6ccb8a69b6fc656 Mon Sep 17 00:00:00 2001 +From: Nate Prewitt +Date: Wed, 29 May 2024 08:23:39 -0700 +Subject: [PATCH] Don't create default SSLContext if ssl module isn't present + (#6724) + +Reference:https://github.com/psf/requests/commit/e18879932287c2bf4bcee4ddf6ccb8a69b6fc656 +Conflict:no +--- + requests/adapters.py | 24 +++++++++++++++++++----- + 1 file changed, 19 insertions(+), 5 deletions(-) + +diff --git a/requests/adapters.py b/requests/adapters.py +index b8fdfef..727aea7 100644 +--- a/requests/adapters.py ++++ b/requests/adapters.py +@@ -72,10 +72,18 @@ DEFAULT_POOLSIZE = 10 + DEFAULT_RETRIES = 0 + DEFAULT_POOL_TIMEOUT = None + +-_preloaded_ssl_context = create_urllib3_context() +-_preloaded_ssl_context.load_verify_locations( +- extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH) +-) ++ ++try: ++ import ssl # noqa: F401 ++ ++ _preloaded_ssl_context = create_urllib3_context() ++ _preloaded_ssl_context.load_verify_locations( ++ extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH) ++ ) ++except ImportError: ++ # Bypass default SSLContext creation when Python ++ # interpreter isn't built with the ssl module. ++ _preloaded_ssl_context = None + + + def _urllib3_request_context( +@@ -89,13 +97,19 @@ def _urllib3_request_context( + parsed_request_url = urlparse(request.url) + scheme = parsed_request_url.scheme.lower() + port = parsed_request_url.port ++ ++ # Determine if we have and should use our default SSLContext ++ # to optimize performance on standard requests. + poolmanager_kwargs = getattr(poolmanager, "connection_pool_kw", {}) + has_poolmanager_ssl_context = poolmanager_kwargs.get("ssl_context") ++ should_use_default_ssl_context = ( ++ _preloaded_ssl_context is not None and not has_poolmanager_ssl_context ++ ) + + cert_reqs = "CERT_REQUIRED" + if verify is False: + cert_reqs = "CERT_NONE" +- elif verify is True and not has_poolmanager_ssl_context: ++ elif verify is True and should_use_default_ssl_context: + pool_kwargs["ssl_context"] = _preloaded_ssl_context + elif isinstance(verify, str): + if not os.path.isdir(verify): +-- +2.33.0 + diff --git a/backport-0008-CVE-2024-35195.patch b/backport-0008-CVE-2024-35195.patch new file mode 100644 index 0000000..18c526a --- /dev/null +++ b/backport-0008-CVE-2024-35195.patch @@ -0,0 +1,109 @@ +From 90fee0876aea97c639b3bf698d83a12876d2f160 Mon Sep 17 00:00:00 2001 +From: Nate Prewitt +Date: Fri, 13 Jun 2025 09:42:08 -0700 +Subject: [PATCH] Revert caching a default SSLContext (#6767) + +Reference:https://github.com/psf/requests/commit/90fee0876aea97c639b3bf698d83a12876d2f160 +Conflict:Adapt context +--- + requests/adapters.py | 55 +++++++++++++------------------------------- + 1 file changed, 16 insertions(+), 39 deletions(-) + +diff --git a/requests/adapters.py b/requests/adapters.py +index 727aea7..7212ba5 100644 +--- a/requests/adapters.py ++++ b/requests/adapters.py +@@ -26,7 +26,6 @@ from urllib3.poolmanager import PoolManager, proxy_from_url + from urllib3.util import Timeout as TimeoutSauce + from urllib3.util import parse_url + from urllib3.util.retry import Retry +-from urllib3.util.ssl_ import create_urllib3_context + + from .auth import _basic_auth_str + from .compat import basestring, urlparse +@@ -73,19 +72,6 @@ DEFAULT_RETRIES = 0 + DEFAULT_POOL_TIMEOUT = None + + +-try: +- import ssl # noqa: F401 +- +- _preloaded_ssl_context = create_urllib3_context() +- _preloaded_ssl_context.load_verify_locations( +- extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH) +- ) +-except ImportError: +- # Bypass default SSLContext creation when Python +- # interpreter isn't built with the ssl module. +- _preloaded_ssl_context = None +- +- + def _urllib3_request_context( + request: "PreparedRequest", + verify: "bool | str | None", +@@ -98,19 +84,9 @@ def _urllib3_request_context( + scheme = parsed_request_url.scheme.lower() + port = parsed_request_url.port + +- # Determine if we have and should use our default SSLContext +- # to optimize performance on standard requests. +- poolmanager_kwargs = getattr(poolmanager, "connection_pool_kw", {}) +- has_poolmanager_ssl_context = poolmanager_kwargs.get("ssl_context") +- should_use_default_ssl_context = ( +- _preloaded_ssl_context is not None and not has_poolmanager_ssl_context +- ) +- + cert_reqs = "CERT_REQUIRED" + if verify is False: + cert_reqs = "CERT_NONE" +- elif verify is True and should_use_default_ssl_context: +- pool_kwargs["ssl_context"] = _preloaded_ssl_context + elif isinstance(verify, str): + if not os.path.isdir(verify): + pool_kwargs["ca_certs"] = verify +@@ -314,26 +290,27 @@ class HTTPAdapter(BaseAdapter): + """ + if url.lower().startswith("https") and verify: + +- conn.cert_reqs = "CERT_REQUIRED" ++ cert_loc = None + +- # Only load the CA certificates if 'verify' is a string indicating the CA bundle to use. +- # Otherwise, if verify is a boolean, we don't load anything since +- # the connection will be using a context with the default certificates already loaded, +- # and this avoids a call to the slow load_verify_locations() ++ # Allow self-specified cert location. + if verify is not True: +- # `verify` must be a str with a path then + cert_loc = verify + +- if not os.path.exists(cert_loc): +- raise OSError( +- f"Could not find a suitable TLS CA certificate bundle, " +- f"invalid path: {cert_loc}" +- ) ++ if not cert_loc: ++ cert_loc = extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH) + +- if not os.path.isdir(cert_loc): +- conn.ca_certs = cert_loc +- else: +- conn.ca_cert_dir = cert_loc ++ if not cert_loc or not os.path.exists(cert_loc): ++ raise OSError( ++ f"Could not find a suitable TLS CA certificate bundle, " ++ f"invalid path: {cert_loc}" ++ ) ++ ++ conn.cert_reqs = "CERT_REQUIRED" ++ ++ if not os.path.isdir(cert_loc): ++ conn.ca_certs = cert_loc ++ else: ++ conn.ca_cert_dir = cert_loc + else: + conn.cert_reqs = "CERT_NONE" + conn.ca_certs = None +-- +2.33.0 + diff --git a/python-requests.spec b/python-requests.spec index 4059a22..f4dd9dd 100644 --- a/python-requests.spec +++ b/python-requests.spec @@ -2,13 +2,21 @@ Name: python-requests Version: 2.31.0 -Release: 3 +Release: 4 Summary: Python HTTP Library License: ASL 2.0 URL: http://python-requests.org/ Source0: https://github.com/requests/requests/archive/v%{version}/requests-v%{version}.tar.gz#/requests-%{version}.tar.gz Patch6001: backport-requests-2.31.0-system-certs.patch Patch6002: backport-CVE-2024-47081.patch +Patch6003: backport-0001-CVE-2024-35195.patch +Patch6004: backport-0002-CVE-2024-35195.patch +Patch6005: backport-0003-CVE-2024-35195.patch +Patch6006: backport-0004-CVE-2024-35195.patch +Patch6007: backport-0005-CVE-2024-35195.patch +Patch6008: backport-0006-CVE-2024-35195.patch +Patch6009: backport-0007-CVE-2024-35195.patch +Patch6010: backport-0008-CVE-2024-35195.patch BuildArch: noarch %description @@ -93,6 +101,9 @@ PYTHONPATH=%{buildroot}%{python3_sitelib} %{__python3} -m pytest -v %doc HISTORY.md README.md %changelog +* Mon Nov 17 2025 lingsheng - 2.31.0-4 +- fix CVE-2024-35195 + * Mon Jun 23 2025 zhangpan - 2.31.0-3 - fix CVE-2024-47081 -- Gitee