mirror of
https://github.com/python/cpython.git
synced 2025-10-06 15:11:58 +00:00
gh-107361: strengthen default SSL context flags (#112389)
This adds `VERIFY_X509_STRICT` to make the default SSL context perform stricter (per RFC 5280) validation, as well as `VERIFY_X509_PARTIAL_CHAIN` to enforce more standards-compliant path-building behavior. As part of this changeset, I had to tweak `make_ssl_certs.py` slightly to emit 5280-conforming CA certs. This changeset includes the regenerated certificates after that change. Signed-off-by: William Woodruff <william@yossarian.net> Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
parent
ea1803e608
commit
0876b921b2
26 changed files with 1184 additions and 1067 deletions
|
@ -38,7 +38,7 @@ except ImportError:
|
|||
ssl = import_helper.import_module("ssl")
|
||||
import _ssl
|
||||
|
||||
from ssl import TLSVersion, _TLSContentType, _TLSMessageType, _TLSAlertType
|
||||
from ssl import Purpose, TLSVersion, _TLSContentType, _TLSMessageType, _TLSAlertType
|
||||
|
||||
Py_DEBUG_WIN32 = support.Py_DEBUG and sys.platform == 'win32'
|
||||
|
||||
|
@ -87,9 +87,9 @@ CERTFILE_INFO = {
|
|||
(('localityName', 'Castle Anthrax'),),
|
||||
(('organizationName', 'Python Software Foundation'),),
|
||||
(('commonName', 'localhost'),)),
|
||||
'notAfter': 'Aug 26 14:23:15 2028 GMT',
|
||||
'notBefore': 'Aug 29 14:23:15 2018 GMT',
|
||||
'serialNumber': '98A7CF88C74A32ED',
|
||||
'notAfter': 'Jan 24 04:21:36 2043 GMT',
|
||||
'notBefore': 'Nov 25 04:21:36 2023 GMT',
|
||||
'serialNumber': '53E14833F7546C29256DD0F034F776C5E983004C',
|
||||
'subject': ((('countryName', 'XY'),),
|
||||
(('localityName', 'Castle Anthrax'),),
|
||||
(('organizationName', 'Python Software Foundation'),),
|
||||
|
@ -128,6 +128,13 @@ SIGNED_CERTFILE2_HOSTNAME = 'fakehostname'
|
|||
SIGNED_CERTFILE_ECC = data_file("keycertecc.pem")
|
||||
SIGNED_CERTFILE_ECC_HOSTNAME = 'localhost-ecc'
|
||||
|
||||
# A custom testcase, extracted from `rfc5280::aki::leaf-missing-aki` in x509-limbo:
|
||||
# The leaf (server) certificate has no AKI, which is forbidden under RFC 5280.
|
||||
# See: https://x509-limbo.com/testcases/rfc5280/#rfc5280akileaf-missing-aki
|
||||
LEAF_MISSING_AKI_CERTFILE = data_file("leaf-missing-aki.keycert.pem")
|
||||
LEAF_MISSING_AKI_CERTFILE_HOSTNAME = "example.com"
|
||||
LEAF_MISSING_AKI_CA = data_file("leaf-missing-aki.ca.pem")
|
||||
|
||||
# Same certificate as pycacert.pem, but without extra text in file
|
||||
SIGNING_CA = data_file("capath", "ceff1710.0")
|
||||
# cert with all kinds of subject alt names
|
||||
|
@ -1497,6 +1504,10 @@ class ContextTests(unittest.TestCase):
|
|||
|
||||
self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLS_CLIENT)
|
||||
self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED)
|
||||
self.assertEqual(ctx.verify_flags & ssl.VERIFY_X509_PARTIAL_CHAIN,
|
||||
ssl.VERIFY_X509_PARTIAL_CHAIN)
|
||||
self.assertEqual(ctx.verify_flags & ssl.VERIFY_X509_STRICT,
|
||||
ssl.VERIFY_X509_STRICT)
|
||||
self.assertTrue(ctx.check_hostname)
|
||||
self._assert_context_options(ctx)
|
||||
|
||||
|
@ -2946,6 +2957,38 @@ class ThreadedTests(unittest.TestCase):
|
|||
cipher = s.cipher()[0].split('-')
|
||||
self.assertTrue(cipher[:2], ('ECDHE', 'ECDSA'))
|
||||
|
||||
@unittest.skipUnless(IS_OPENSSL_3_0_0,
|
||||
"test requires RFC 5280 check added in OpenSSL 3.0+")
|
||||
def test_verify_strict(self):
|
||||
# verification fails by default, since the server cert is non-conforming
|
||||
client_context = ssl.create_default_context()
|
||||
client_context.load_verify_locations(LEAF_MISSING_AKI_CA)
|
||||
hostname = LEAF_MISSING_AKI_CERTFILE_HOSTNAME
|
||||
|
||||
server_context = ssl.create_default_context(purpose=Purpose.CLIENT_AUTH)
|
||||
server_context.load_cert_chain(LEAF_MISSING_AKI_CERTFILE)
|
||||
server = ThreadedEchoServer(context=server_context, chatty=True)
|
||||
with server:
|
||||
with client_context.wrap_socket(socket.socket(),
|
||||
server_hostname=hostname) as s:
|
||||
with self.assertRaises(ssl.SSLError):
|
||||
s.connect((HOST, server.port))
|
||||
|
||||
# explicitly disabling VERIFY_X509_STRICT allows it to succeed
|
||||
client_context = ssl.create_default_context()
|
||||
client_context.load_verify_locations(LEAF_MISSING_AKI_CA)
|
||||
client_context.verify_flags &= ~ssl.VERIFY_X509_STRICT
|
||||
|
||||
server_context = ssl.create_default_context(purpose=Purpose.CLIENT_AUTH)
|
||||
server_context.load_cert_chain(LEAF_MISSING_AKI_CERTFILE)
|
||||
server = ThreadedEchoServer(context=server_context, chatty=True)
|
||||
with server:
|
||||
with client_context.wrap_socket(socket.socket(),
|
||||
server_hostname=hostname) as s:
|
||||
s.connect((HOST, server.port))
|
||||
cert = s.getpeercert()
|
||||
self.assertTrue(cert, "Can't get peer certificate.")
|
||||
|
||||
def test_dual_rsa_ecc(self):
|
||||
client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||
client_context.load_verify_locations(SIGNING_CA)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue