From 6a96b305d17f5e199da279c30d26439c950d128b Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Thu, 18 Mar 2021 10:35:13 +0100 Subject: [PATCH 1/2] bpo-41561: Add workaround for Ubuntu's custom security level Ubuntu 20.04 comes with a patched OpenSSL 1.1.1. Default security level 2 blocks TLS 1.0 and 1.1 connections. Regular OpenSSL 1.1.1 builds allow TLS 1.0 and 1.1 on security level 2. See: https://bugs.python.org/issue43382 See: https://bugs.launchpad.net/ubuntu/+source/openssl/+bug/1899878 See: https://bugs.launchpad.net/ubuntu/+source/openssl/+bug/1917625 Signed-off-by: Christian Heimes --- .github/workflows/build.yml | 2 +- Lib/test/test_ssl.py | 28 +++++++++++++++++++ .../2021-03-18-10-34-42.bpo-41561.pDg4w-.rst | 1 + 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Tests/2021-03-18-10-34-42.bpo-41561.pDg4w-.rst diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ef54865ed964c7..d6be2b68642660 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -127,7 +127,7 @@ jobs: build_ubuntu: name: 'Ubuntu' - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 needs: check_source if: needs.check_source.outputs.run_tests == 'true' env: diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 1710dda4389a04..25b464c77b1e3c 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -151,6 +151,26 @@ def data_file(*name): OP_CIPHER_SERVER_PREFERENCE = getattr(ssl, "OP_CIPHER_SERVER_PREFERENCE", 0) OP_ENABLE_MIDDLEBOX_COMPAT = getattr(ssl, "OP_ENABLE_MIDDLEBOX_COMPAT", 0) +# Ubuntu has patched OpenSSL and changed behavior of security level 2 +# see https://bugs.python.org/issue41561#msg389003 +def is_ubuntu(): + try: + # assume that any references of "ubuntu" implies Ubuntu-like distro + with open("/etc/os-release") as f: + return "ubuntu" in f.read() + except FileNotFoundError: + return False + +if is_ubuntu(): + def seclevel_workaround(*ctxs): + """"Lower security level to '1' and allow all ciphers for TLS 1.0/1""" + for ctx in ctxs: + if ctx.minimum_version <= ssl.TLSVersion.TLSv1_1: + ctx.set_ciphers("@SECLEVEL=1:ALL") +else: + def seclevel_workaround(*ctxs): + pass + def has_tls_protocol(protocol): """Check if a TLS protocol is available and enabled @@ -2802,6 +2822,8 @@ def try_protocol_combo(server_protocol, client_protocol, expect_success, if client_context.protocol == ssl.PROTOCOL_TLS: client_context.set_ciphers("ALL") + seclevel_workaround(server_context, client_context) + for ctx in (client_context, server_context): ctx.verify_mode = certsreqs ctx.load_cert_chain(SIGNED_CERTFILE) @@ -2843,6 +2865,7 @@ def test_echo(self): with self.subTest(protocol=ssl._PROTOCOL_NAMES[protocol]): context = ssl.SSLContext(protocol) context.load_cert_chain(CERTFILE) + seclevel_workaround(context) server_params_test(context, context, chatty=True, connectionchatty=True) @@ -3847,6 +3870,7 @@ def test_min_max_version_tlsv1_1(self): client_context.maximum_version = ssl.TLSVersion.TLSv1_2 server_context.minimum_version = ssl.TLSVersion.TLSv1 server_context.maximum_version = ssl.TLSVersion.TLSv1_1 + seclevel_workaround(client_context, server_context) with ThreadedEchoServer(context=server_context) as server: with client_context.wrap_socket(socket.socket(), @@ -3864,6 +3888,8 @@ def test_min_max_version_mismatch(self): server_context.minimum_version = ssl.TLSVersion.TLSv1_2 client_context.maximum_version = ssl.TLSVersion.TLSv1 client_context.minimum_version = ssl.TLSVersion.TLSv1 + seclevel_workaround(client_context, server_context) + with ThreadedEchoServer(context=server_context) as server: with client_context.wrap_socket(socket.socket(), server_hostname=hostname) as s: @@ -3878,6 +3904,8 @@ def test_min_max_version_sslv3(self): server_context.minimum_version = ssl.TLSVersion.SSLv3 client_context.minimum_version = ssl.TLSVersion.SSLv3 client_context.maximum_version = ssl.TLSVersion.SSLv3 + seclevel_workaround(client_context, server_context) + with ThreadedEchoServer(context=server_context) as server: with client_context.wrap_socket(socket.socket(), server_hostname=hostname) as s: diff --git a/Misc/NEWS.d/next/Tests/2021-03-18-10-34-42.bpo-41561.pDg4w-.rst b/Misc/NEWS.d/next/Tests/2021-03-18-10-34-42.bpo-41561.pDg4w-.rst new file mode 100644 index 00000000000000..214350729bff6c --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2021-03-18-10-34-42.bpo-41561.pDg4w-.rst @@ -0,0 +1 @@ +Add workaround for Ubuntu's custom OpenSSL security level policy. From 719f100c698fb2d961629f29c0028c9201cb0954 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Thu, 18 Mar 2021 21:02:45 +0100 Subject: [PATCH 2/2] Open with utf-8 encoding --- Lib/test/test_ssl.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 25b464c77b1e3c..ade7ef529e8122 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -155,8 +155,9 @@ def data_file(*name): # see https://bugs.python.org/issue41561#msg389003 def is_ubuntu(): try: - # assume that any references of "ubuntu" implies Ubuntu-like distro - with open("/etc/os-release") as f: + # Assume that any references of "ubuntu" implies Ubuntu-like distro + # The workaround is not required for 18.04, but doesn't hurt either. + with open("/etc/os-release", encoding="utf-8") as f: return "ubuntu" in f.read() except FileNotFoundError: return False