-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
[3.6] bpo-41183: Skip ssl tests for disabled versions (GH-16427) #21882
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -93,6 +93,78 @@ def data_file(*name): | |
OP_ENABLE_MIDDLEBOX_COMPAT = getattr(ssl, "OP_ENABLE_MIDDLEBOX_COMPAT", 0) | ||
|
||
|
||
def has_tls_protocol(protocol): | ||
"""Check if a TLS protocol is available and enabled | ||
:param protocol: enum ssl._SSLMethod member or name | ||
:return: bool | ||
""" | ||
if isinstance(protocol, str): | ||
assert protocol.startswith('PROTOCOL_') | ||
protocol = getattr(ssl, protocol, None) | ||
if protocol is None: | ||
return False | ||
if protocol in { | ||
ssl.PROTOCOL_TLS, ssl.PROTOCOL_TLS_SERVER, | ||
ssl.PROTOCOL_TLS_CLIENT | ||
}: | ||
# auto-negotiate protocols are always available | ||
return True | ||
name = protocol.name | ||
return has_tls_version(name[len('PROTOCOL_'):]) | ||
|
||
|
||
@functools.lru_cache() | ||
def has_tls_version(version): | ||
"""Check if a TLS/SSL version is enabled | ||
:param version: TLS version name or ssl.TLSVersion member | ||
:return: bool | ||
""" | ||
if version == "SSLv2": | ||
# never supported and not even in TLSVersion enum | ||
return False | ||
|
||
if isinstance(version, str): | ||
version = ssl.TLSVersion.__members__[version] | ||
|
||
# check compile time flags like ssl.HAS_TLSv1_2 | ||
if not getattr(ssl, f'HAS_{version.name}'): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Neither does this. |
||
return False | ||
|
||
# check runtime and dynamic crypto policy settings. A TLS version may | ||
# be compiled in but disabled by a policy or config option. | ||
ctx = ssl.SSLContext() | ||
if ( | ||
hasattr(ctx, 'minimum_version') and | ||
ctx.minimum_version != ssl.TLSVersion.MINIMUM_SUPPORTED and | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yet the most importantly, and that I believe is what is problematic on newer systems is that this (and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change requires #5259 to be backported. However even if we backport that, it relies on some features from #5128 . So in general I don't think it's possible to pull this off with this approach. And it's easy to figure out the baked/compiled in versions of supported protocols in openssl but no easy way, without those features, to determine the runtime config. |
||
version < ctx.minimum_version | ||
): | ||
return False | ||
if ( | ||
hasattr(ctx, 'maximum_version') and | ||
ctx.maximum_version != ssl.TLSVersion.MAXIMUM_SUPPORTED and | ||
version > ctx.maximum_version | ||
): | ||
return False | ||
|
||
return True | ||
|
||
|
||
def requires_tls_version(version): | ||
"""Decorator to skip tests when a required TLS version is not available | ||
:param version: TLS version name or ssl.TLSVersion member | ||
:return: | ||
""" | ||
def decorator(func): | ||
@functools.wraps(func) | ||
def wrapper(*args, **kw): | ||
if not has_tls_version(version): | ||
raise unittest.SkipTest(f"{version} is not available.") | ||
else: | ||
return func(*args, **kw) | ||
return wrapper | ||
return decorator | ||
|
||
|
||
def handle_error(prefix): | ||
exc_format = ' '.join(traceback.format_exception(*sys.exc_info())) | ||
if support.verbose: | ||
|
@@ -132,7 +204,7 @@ def asn1time(cert_time): | |
|
||
# Issue #9415: Ubuntu hijacks their OpenSSL and forcefully disables SSLv2 | ||
def skip_if_broken_ubuntu_ssl(func): | ||
if hasattr(ssl, 'PROTOCOL_SSLv2'): | ||
if has_tls_version('SSLv2'): | ||
@functools.wraps(func) | ||
def f(*args, **kwargs): | ||
try: | ||
|
@@ -1751,7 +1823,7 @@ def test_get_ca_certs_capath(self): | |
self.assertEqual(len(ctx.get_ca_certs()), 1) | ||
|
||
@needs_sni | ||
@unittest.skipUnless(hasattr(ssl, "PROTOCOL_TLSv1_2"), "needs TLS 1.2") | ||
@requires_tls_version('TLSv1_2') | ||
def test_context_setget(self): | ||
# Check that the context of a connected socket can be replaced. | ||
ctx1 = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) | ||
|
@@ -2399,6 +2471,8 @@ def test_echo(self): | |
for protocol in PROTOCOLS: | ||
if protocol in {ssl.PROTOCOL_TLS_CLIENT, ssl.PROTOCOL_TLS_SERVER}: | ||
continue | ||
if not has_tls_protocol(protocol): | ||
continue | ||
with self.subTest(protocol=ssl._PROTOCOL_NAMES[protocol]): | ||
context = ssl.SSLContext(protocol) | ||
context.load_cert_chain(CERTFILE) | ||
|
@@ -2637,9 +2711,7 @@ def connector(): | |
finally: | ||
t.join() | ||
|
||
@skip_if_broken_ubuntu_ssl | ||
@unittest.skipUnless(hasattr(ssl, 'PROTOCOL_SSLv2'), | ||
"OpenSSL is compiled without SSLv2 support") | ||
@requires_tls_version('SSLv2') | ||
def test_protocol_sslv2(self): | ||
"""Connecting to an SSLv2 server with various client options""" | ||
if support.verbose: | ||
|
@@ -2648,7 +2720,7 @@ def test_protocol_sslv2(self): | |
try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True, ssl.CERT_OPTIONAL) | ||
try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True, ssl.CERT_REQUIRED) | ||
try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, False) | ||
if hasattr(ssl, 'PROTOCOL_SSLv3'): | ||
if has_tls_version('SSLv3'): | ||
try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv3, False) | ||
try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_TLSv1, False) | ||
# SSLv23 client with specific SSL options | ||
|
@@ -2667,7 +2739,7 @@ def test_protocol_sslv23(self): | |
"""Connecting to an SSLv23 server with various client options""" | ||
if support.verbose: | ||
sys.stdout.write("\n") | ||
if hasattr(ssl, 'PROTOCOL_SSLv2'): | ||
if has_tls_version('SSLv2'): | ||
try: | ||
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv2, True) | ||
except OSError as x: | ||
|
@@ -2676,23 +2748,23 @@ def test_protocol_sslv23(self): | |
sys.stdout.write( | ||
" SSL2 client to SSL23 server test unexpectedly failed:\n %s\n" | ||
% str(x)) | ||
if hasattr(ssl, 'PROTOCOL_SSLv3'): | ||
if has_tls_version('SSLv3'): | ||
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, False) | ||
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True) | ||
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, 'TLSv1') | ||
|
||
if hasattr(ssl, 'PROTOCOL_SSLv3'): | ||
if has_tls_version('SSLv3'): | ||
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, False, ssl.CERT_OPTIONAL) | ||
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_OPTIONAL) | ||
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, 'TLSv1', ssl.CERT_OPTIONAL) | ||
|
||
if hasattr(ssl, 'PROTOCOL_SSLv3'): | ||
if has_tls_version('SSLv3'): | ||
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, False, ssl.CERT_REQUIRED) | ||
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_REQUIRED) | ||
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, 'TLSv1', ssl.CERT_REQUIRED) | ||
|
||
# Server with specific SSL options | ||
if hasattr(ssl, 'PROTOCOL_SSLv3'): | ||
if has_tls_version('SSLv3'): | ||
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, False, | ||
server_options=ssl.OP_NO_SSLv3) | ||
# Will choose TLSv1 | ||
|
@@ -2702,17 +2774,15 @@ def test_protocol_sslv23(self): | |
server_options=ssl.OP_NO_TLSv1) | ||
|
||
|
||
@skip_if_broken_ubuntu_ssl | ||
@unittest.skipUnless(hasattr(ssl, 'PROTOCOL_SSLv3'), | ||
"OpenSSL is compiled without SSLv3 support") | ||
@requires_tls_version('SSLv3') | ||
def test_protocol_sslv3(self): | ||
"""Connecting to an SSLv3 server with various client options""" | ||
if support.verbose: | ||
sys.stdout.write("\n") | ||
try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, 'SSLv3') | ||
try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, 'SSLv3', ssl.CERT_OPTIONAL) | ||
try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, 'SSLv3', ssl.CERT_REQUIRED) | ||
if hasattr(ssl, 'PROTOCOL_SSLv2'): | ||
if has_tls_version('SSLv2'): | ||
try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv2, False) | ||
try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, False, | ||
client_options=ssl.OP_NO_SSLv3) | ||
|
@@ -2722,46 +2792,42 @@ def test_protocol_sslv3(self): | |
try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, | ||
False, client_options=ssl.OP_NO_SSLv2) | ||
|
||
@skip_if_broken_ubuntu_ssl | ||
@requires_tls_version('TLSv1') | ||
def test_protocol_tlsv1(self): | ||
"""Connecting to a TLSv1 server with various client options""" | ||
if support.verbose: | ||
sys.stdout.write("\n") | ||
try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, 'TLSv1') | ||
try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, 'TLSv1', ssl.CERT_OPTIONAL) | ||
try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, 'TLSv1', ssl.CERT_REQUIRED) | ||
if hasattr(ssl, 'PROTOCOL_SSLv2'): | ||
if has_tls_version('SSLv2'): | ||
try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv2, False) | ||
if hasattr(ssl, 'PROTOCOL_SSLv3'): | ||
if has_tls_version('SSLv3'): | ||
try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv3, False) | ||
try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv23, False, | ||
client_options=ssl.OP_NO_TLSv1) | ||
|
||
@skip_if_broken_ubuntu_ssl | ||
@unittest.skipUnless(hasattr(ssl, "PROTOCOL_TLSv1_1"), | ||
"TLS version 1.1 not supported.") | ||
@requires_tls_version('TLSv1_1') | ||
@skip_if_openssl_cnf_minprotocol_gt_tls1 | ||
def test_protocol_tlsv1_1(self): | ||
"""Connecting to a TLSv1.1 server with various client options. | ||
Testing against older TLS versions.""" | ||
if support.verbose: | ||
sys.stdout.write("\n") | ||
try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1_1, 'TLSv1.1') | ||
if hasattr(ssl, 'PROTOCOL_SSLv2'): | ||
if has_tls_version('SSLv2'): | ||
try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_SSLv2, False) | ||
if hasattr(ssl, 'PROTOCOL_SSLv3'): | ||
if has_tls_version('SSLv3'): | ||
try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_SSLv3, False) | ||
try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_SSLv23, False, | ||
client_options=ssl.OP_NO_TLSv1_1) | ||
|
||
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1_1, 'TLSv1.1') | ||
try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1, False) | ||
try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1_1, False) | ||
try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1_2, False) | ||
try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1_1, False) | ||
|
||
|
||
@skip_if_broken_ubuntu_ssl | ||
@unittest.skipUnless(hasattr(ssl, "PROTOCOL_TLSv1_2"), | ||
"TLS version 1.2 not supported.") | ||
@requires_tls_version('TLSv1_2') | ||
def test_protocol_tlsv1_2(self): | ||
"""Connecting to a TLSv1.2 server with various client options. | ||
Testing against older TLS versions.""" | ||
|
@@ -2770,9 +2836,9 @@ def test_protocol_tlsv1_2(self): | |
try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1_2, 'TLSv1.2', | ||
server_options=ssl.OP_NO_SSLv3|ssl.OP_NO_SSLv2, | ||
client_options=ssl.OP_NO_SSLv3|ssl.OP_NO_SSLv2,) | ||
if hasattr(ssl, 'PROTOCOL_SSLv2'): | ||
if has_tls_version('SSLv2'): | ||
try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_SSLv2, False) | ||
if hasattr(ssl, 'PROTOCOL_SSLv3'): | ||
if has_tls_version('SSLv3'): | ||
try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_SSLv3, False) | ||
try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_SSLv23, False, | ||
client_options=ssl.OP_NO_TLSv1_2) | ||
|
@@ -3218,11 +3284,13 @@ def test_version_basic(self): | |
with context.wrap_socket(socket.socket()) as s: | ||
self.assertIs(s.version(), None) | ||
s.connect((HOST, server.port)) | ||
self.assertEqual(s.version(), 'TLSv1') | ||
if IS_OPENSSL_1_1_1 and has_tls_version('TLSv1_3'): | ||
self.assertEqual(s.version(), 'TLSv1.3') | ||
elif ssl.OPENSSL_VERSION_INFO >= (1, 0, 2): | ||
self.assertEqual(s.version(), 'TLSv1.2') | ||
self.assertIs(s.version(), None) | ||
|
||
@unittest.skipUnless(ssl.HAS_TLSv1_3, | ||
"test requires TLSv1.3 enabled OpenSSL") | ||
@requires_tls_version('TLSv1_3') | ||
def test_tls1_3(self): | ||
context = ssl.SSLContext(ssl.PROTOCOL_TLS) | ||
context.load_cert_chain(CERTFILE) | ||
|
@@ -3740,7 +3808,7 @@ def testing_context(): | |
return client_context, server_context, 'localhost' | ||
|
||
|
||
@unittest.skipUnless(ssl.HAS_TLSv1_3, "Test needs TLS 1.3") | ||
@unittest.skipUnless(has_tls_version('TLSv1_3'), "Test needs TLS 1.3") | ||
class TestPostHandshakeAuth(unittest.TestCase): | ||
def test_pha_setter(self): | ||
protocols = [ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
test_ssl now handles disabled TLS/SSL versions better. OpenSSL's crypto | ||
policy and run-time settings are recognized and tests for disabled versions | ||
are skipped. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this entire thing does not exist on 3.6 yet.