diff --git a/CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch b/CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch new file mode 100644 index 0000000..94f6c70 --- /dev/null +++ b/CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch @@ -0,0 +1,1891 @@ +From 3ddf7fa83b19463e710b75ae6e8a28831e575f3d Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Wed, 28 Oct 2020 09:26:39 +0100 +Subject: [PATCH] PEP-644: Require OpenSSL 1.1.1 or newer + +- Remove HAVE_X509_VERIFY_PARAM_SET1_HOST check +- Update hashopenssl to require OpenSSL 1.1.1 +- multissltests only OpenSSL > 1.1.0 +- ALPN is always supported +- SNI is always supported +- Remove deprecated NPN code. Python wrappers are no-op. +- ECDH is always supported +- Remove OPENSSL_VERSION_1_1 macro +- Remove locking callbacks +- Drop PY_OPENSSL_1_1_API macro +- Drop HAVE_SSL_CTX_CLEAR_OPTIONS macro +- SSL_CTRL_GET_MAX_PROTO_VERSION is always defined now +- security level is always available now +- get_num_tickets is available with TLS 1.3 +- X509_V_ERR MISMATCH is always available now +- Always set SSL_MODE_RELEASE_BUFFERS +- X509_V_FLAG_TRUSTED_FIRST is always available +- get_ciphers is always supported +- SSL_CTX_set_keylog_callback is always available +- Update Modules/Setup with static link example +- Mention PEP in whatsnew +- Drop 1.0.2 and 1.1.0 from GHA tests +--- + Doc/using/unix.rst | 1 + Lib/ssl.py | 10 + Lib/test/test_ssl.py | 119 -- + Misc/NEWS.d/next/Build/2021-03-30-14-19-39.bpo-43669.lWMUYx.rst | 1 + Modules/Setup | 22 + Modules/_hashopenssl.c | 108 -- + Modules/_ssl.c | 518 ---------- + Modules/_ssl/debughelpers.c | 4 + Modules/clinic/_hashopenssl.c.h | 11 + Modules/clinic/_ssl.c.h | 85 - + Tools/ssl/multissltests.py | 4 + configure | 9 + configure.ac | 36 + pyconfig.h.in | 3 + setup.py | 19 + 15 files changed, 77 insertions(+), 873 deletions(-) + create mode 100644 Misc/NEWS.d/next/Build/2021-03-30-14-19-39.bpo-43669.lWMUYx.rst + +--- a/Doc/using/unix.rst ++++ b/Doc/using/unix.rst +@@ -113,6 +113,7 @@ For example, on most Linux systems, the + | | embedding the interpreter. | + +-----------------------------------------------+------------------------------------------+ + ++.. _unix_custom_openssl: + + Miscellaneous + ============= +--- a/Lib/ssl.py ++++ b/Lib/ssl.py +@@ -910,15 +910,12 @@ class SSLObject: + """Return the currently selected NPN protocol as a string, or ``None`` + if a next protocol was not negotiated or if NPN is not supported by one + of the peers.""" +- if _ssl.HAS_NPN: +- return self._sslobj.selected_npn_protocol() + + def selected_alpn_protocol(self): + """Return the currently selected ALPN protocol as a string, or ``None`` + if a next protocol was not negotiated or if ALPN is not supported by one + of the peers.""" +- if _ssl.HAS_ALPN: +- return self._sslobj.selected_alpn_protocol() ++ return self._sslobj.selected_alpn_protocol() + + def cipher(self): + """Return the currently selected cipher as a 3-tuple ``(name, +@@ -1160,10 +1157,7 @@ class SSLSocket(socket): + @_sslcopydoc + def selected_npn_protocol(self): + self._checkClosed() +- if self._sslobj is None or not _ssl.HAS_NPN: +- return None +- else: +- return self._sslobj.selected_npn_protocol() ++ return None + + @_sslcopydoc + def selected_alpn_protocol(self): +--- a/Lib/test/test_ssl.py ++++ b/Lib/test/test_ssl.py +@@ -39,7 +39,6 @@ Py_DEBUG_WIN32 = Py_DEBUG and sys.platfo + PROTOCOLS = sorted(ssl._PROTOCOL_NAMES) + HOST = socket_helper.HOST + IS_LIBRESSL = ssl.OPENSSL_VERSION.startswith('LibreSSL') +-IS_OPENSSL_1_1_0 = not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (1, 1, 0) + IS_OPENSSL_1_1_1 = not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (1, 1, 1) + IS_OPENSSL_3_0_0 = not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (3, 0, 0) + PY_SSL_DEFAULT_CIPHERS = sysconfig.get_config_var('PY_SSL_DEFAULT_CIPHERS') +@@ -269,18 +268,6 @@ def handle_error(prefix): + if support.verbose: + sys.stdout.write(prefix + exc_format) + +-def can_clear_options(): +- # 0.9.8m or higher +- return ssl._OPENSSL_API_VERSION >= (0, 9, 8, 13, 15) +- +-def no_sslv2_implies_sslv3_hello(): +- # 0.9.7h or higher +- return ssl.OPENSSL_VERSION_INFO >= (0, 9, 7, 8, 15) +- +-def have_verify_flags(): +- # 0.9.8 or higher +- return ssl.OPENSSL_VERSION_INFO >= (0, 9, 8, 0, 15) +- + def _have_secp_curves(): + if not ssl.HAS_ECDH: + return False +@@ -371,17 +358,15 @@ class BasicSocketTests(unittest.TestCase + ssl.OP_SINGLE_DH_USE + if ssl.HAS_ECDH: + ssl.OP_SINGLE_ECDH_USE +- if ssl.OPENSSL_VERSION_INFO >= (1, 0): +- ssl.OP_NO_COMPRESSION ++ ssl.OP_NO_COMPRESSION + self.assertIn(ssl.HAS_SNI, {True, False}) + self.assertIn(ssl.HAS_ECDH, {True, False}) + ssl.OP_NO_SSLv2 + ssl.OP_NO_SSLv3 + ssl.OP_NO_TLSv1 + ssl.OP_NO_TLSv1_3 +- if ssl.OPENSSL_VERSION_INFO >= (1, 0, 1): +- ssl.OP_NO_TLSv1_1 +- ssl.OP_NO_TLSv1_2 ++ ssl.OP_NO_TLSv1_1 ++ ssl.OP_NO_TLSv1_2 + self.assertEqual(ssl.PROTOCOL_TLS, ssl.PROTOCOL_SSLv23) + + def test_private_init(self): +@@ -1169,7 +1154,6 @@ class ContextTests(unittest.TestCase): + self.assertNotIn("RC4", name) + self.assertNotIn("3DES", name) + +- @unittest.skipIf(ssl.OPENSSL_VERSION_INFO < (1, 0, 2, 0, 0), 'OpenSSL too old') + def test_get_ciphers(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + ctx.set_ciphers('AESGCM') +@@ -1201,15 +1185,11 @@ class ContextTests(unittest.TestCase): + self.assertEqual(default, ctx.options) + ctx.options |= ssl.OP_NO_TLSv1 + self.assertEqual(default | ssl.OP_NO_TLSv1, ctx.options) +- if can_clear_options(): +- ctx.options = (ctx.options & ~ssl.OP_NO_TLSv1) +- self.assertEqual(default, ctx.options) +- ctx.options = 0 +- # Ubuntu has OP_NO_SSLv3 forced on by default +- self.assertEqual(0, ctx.options & ~ssl.OP_NO_SSLv3) +- else: +- with self.assertRaises(ValueError): +- ctx.options = 0 ++ ctx.options = (ctx.options & ~ssl.OP_NO_TLSv1) ++ self.assertEqual(default, ctx.options) ++ ctx.options = 0 ++ # Ubuntu has OP_NO_SSLv3 forced on by default ++ self.assertEqual(0, ctx.options & ~ssl.OP_NO_SSLv3) + + def test_verify_mode_protocol(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS) +@@ -1328,8 +1308,6 @@ class ContextTests(unittest.TestCase): + with self.assertRaises(ValueError): + ctx.maximum_version = ssl.TLSVersion.TLSv1 + +- @unittest.skipUnless(have_verify_flags(), +- "verify_flags need OpenSSL > 0.9.8") + def test_verify_flags(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + # default value +@@ -1807,7 +1785,6 @@ class ContextTests(unittest.TestCase): + obj = ctx.wrap_bio(ssl.MemoryBIO(), ssl.MemoryBIO()) + self.assertIsInstance(obj, MySSLObject) + +- @unittest.skipUnless(IS_OPENSSL_1_1_1, "Test requires OpenSSL 1.1.1") + def test_num_tickest(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + self.assertEqual(ctx.num_tickets, 2) +@@ -2972,8 +2949,6 @@ class ThreadedTests(unittest.TestCase): + after = ssl.cert_time_to_seconds(cert['notAfter']) + self.assertLess(before, after) + +- @unittest.skipUnless(have_verify_flags(), +- "verify_flags need OpenSSL > 0.9.8") + def test_crl_check(self): + if support.verbose: + sys.stdout.write("\n") +@@ -3877,12 +3852,7 @@ class ThreadedTests(unittest.TestCase): + self.assertIs(s.version(), None) + self.assertIs(s._sslobj, None) + s.connect((HOST, server.port)) +- 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') +- else: # 0.9.8 to 1.0.1 +- self.assertIn(s.version(), ('TLSv1', 'TLSv1.2')) ++ self.assertEqual(s.version(), 'TLSv1.3') + self.assertIs(s._sslobj, None) + self.assertIs(s.version(), None) + +@@ -3984,8 +3954,6 @@ class ThreadedTests(unittest.TestCase): + # explicitly using the 'ECCdraft' cipher alias. Otherwise, + # our default cipher list should prefer ECDH-based ciphers + # automatically. +- if ssl.OPENSSL_VERSION_INFO < (1, 0, 0): +- context.set_ciphers("ECCdraft:ECDH") + with ThreadedEchoServer(context=context) as server: + with context.wrap_socket(socket.socket()) as s: + s.connect((HOST, server.port)) +@@ -4117,15 +4085,11 @@ class ThreadedTests(unittest.TestCase): + server_context.set_ciphers("ECDHE:!eNULL:!aNULL") + server_context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 + try: +- stats = server_params_test(client_context, server_context, +- chatty=True, connectionchatty=True, +- sni_name=hostname) ++ server_params_test(client_context, server_context, ++ chatty=True, connectionchatty=True, ++ sni_name=hostname) + except ssl.SSLError: +- pass +- else: +- # OpenSSL 1.0.2 does not fail although it should. +- if IS_OPENSSL_1_1_0: +- self.fail("mismatch curve did not fail") ++ self.fail("mismatch curve did not fail") + + def test_selected_alpn_protocol(self): + # selected_alpn_protocol() is None unless ALPN is used. +@@ -4135,7 +4099,6 @@ class ThreadedTests(unittest.TestCase): + sni_name=hostname) + self.assertIs(stats['client_alpn_protocol'], None) + +- @unittest.skipUnless(ssl.HAS_ALPN, "ALPN support required") + def test_selected_alpn_protocol_if_server_uses_alpn(self): + # selected_alpn_protocol() is None unless ALPN is used by the client. + client_context, server_context, hostname = testing_context() +@@ -4145,7 +4108,6 @@ class ThreadedTests(unittest.TestCase): + sni_name=hostname) + self.assertIs(stats['client_alpn_protocol'], None) + +- @unittest.skipUnless(ssl.HAS_ALPN, "ALPN support needed for this test") + def test_alpn_protocols(self): + server_protocols = ['foo', 'bar', 'milkshake'] + protocol_tests = [ +@@ -4168,22 +4130,17 @@ class ThreadedTests(unittest.TestCase): + except ssl.SSLError as e: + stats = e + +- if (expected is None and IS_OPENSSL_1_1_0 +- and ssl.OPENSSL_VERSION_INFO < (1, 1, 0, 6)): +- # OpenSSL 1.1.0 to 1.1.0e raises handshake error +- self.assertIsInstance(stats, ssl.SSLError) +- else: +- msg = "failed trying %s (s) and %s (c).\n" \ +- "was expecting %s, but got %%s from the %%s" \ +- % (str(server_protocols), str(client_protocols), +- str(expected)) +- client_result = stats['client_alpn_protocol'] +- self.assertEqual(client_result, expected, +- msg % (client_result, "client")) +- server_result = stats['server_alpn_protocols'][-1] \ +- if len(stats['server_alpn_protocols']) else 'nothing' +- self.assertEqual(server_result, expected, +- msg % (server_result, "server")) ++ msg = "failed trying %s (s) and %s (c).\n" \ ++ "was expecting %s, but got %%s from the %%s" \ ++ % (str(server_protocols), str(client_protocols), ++ str(expected)) ++ client_result = stats['client_alpn_protocol'] ++ self.assertEqual(client_result, expected, ++ msg % (client_result, "client")) ++ server_result = stats['server_alpn_protocols'][-1] \ ++ if len(stats['server_alpn_protocols']) else 'nothing' ++ self.assertEqual(server_result, expected, ++ msg % (server_result, "server")) + + def test_selected_npn_protocol(self): + # selected_npn_protocol() is None unless NPN is used +@@ -4193,31 +4150,8 @@ class ThreadedTests(unittest.TestCase): + sni_name=hostname) + self.assertIs(stats['client_npn_protocol'], None) + +- @unittest.skipUnless(ssl.HAS_NPN, "NPN support needed for this test") + def test_npn_protocols(self): +- server_protocols = ['http/1.1', 'spdy/2'] +- protocol_tests = [ +- (['http/1.1', 'spdy/2'], 'http/1.1'), +- (['spdy/2', 'http/1.1'], 'http/1.1'), +- (['spdy/2', 'test'], 'spdy/2'), +- (['abc', 'def'], 'abc') +- ] +- for client_protocols, expected in protocol_tests: +- client_context, server_context, hostname = testing_context() +- server_context.set_npn_protocols(server_protocols) +- client_context.set_npn_protocols(client_protocols) +- stats = server_params_test(client_context, server_context, +- chatty=True, connectionchatty=True, +- sni_name=hostname) +- msg = "failed trying %s (s) and %s (c).\n" \ +- "was expecting %s, but got %%s from the %%s" \ +- % (str(server_protocols), str(client_protocols), +- str(expected)) +- client_result = stats['client_npn_protocol'] +- self.assertEqual(client_result, expected, msg % (client_result, "client")) +- server_result = stats['server_npn_protocols'][-1] \ +- if len(stats['server_npn_protocols']) else 'nothing' +- self.assertEqual(server_result, expected, msg % (server_result, "server")) ++ assert not ssl.HAS_NPN + + def sni_contexts(self): + server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) +@@ -4387,8 +4321,7 @@ class ThreadedTests(unittest.TestCase): + self.assertGreater(session.time, 0) + self.assertGreater(session.timeout, 0) + self.assertTrue(session.has_ticket) +- if ssl.OPENSSL_VERSION_INFO > (1, 0, 1): +- self.assertGreater(session.ticket_lifetime_hint, 0) ++ self.assertGreater(session.ticket_lifetime_hint, 0) + self.assertFalse(stats['session_reused']) + sess_stat = server_context.session_stats() + self.assertEqual(sess_stat['accept'], 1) +--- /dev/null ++++ b/Misc/NEWS.d/next/Build/2021-03-30-14-19-39.bpo-43669.lWMUYx.rst +@@ -0,0 +1 @@ ++Implement :pep:`644`. Python now requires OpenSSL 1.1.1 or newer. +--- a/Modules/Setup ++++ b/Modules/Setup +@@ -210,11 +210,23 @@ _symtable symtablemodule.c + #_socket socketmodule.c + + # Socket module helper for SSL support; you must comment out the other +-# socket line above, and possibly edit the SSL variable: +-#SSL=/usr/local/ssl +-#_ssl _ssl.c \ +-# -DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl \ +-# -L$(SSL)/lib -lssl -lcrypto ++# socket line above, and edit the OPENSSL variable: ++# OPENSSL=/path/to/openssl/directory ++# _ssl _ssl.c \ ++# -I$(OPENSSL)/include -L$(OPENSSL)/lib \ ++# -lssl -lcrypto ++#_hashlib _hashopenssl.c \ ++# -I$(OPENSSL)/include -L$(OPENSSL)/lib \ ++# -lcrypto ++ ++# To statically link OpenSSL: ++# _ssl _ssl.c \ ++# -I$(OPENSSL)/include -L$(OPENSSL)/lib \ ++# -l:libssl.a -Wl,--exclude-libs,libssl.a \ ++# -l:libcrypto.a -Wl,--exclude-libs,libcrypto.a ++#_hashlib _hashopenssl.c \ ++# -I$(OPENSSL)/include -L$(OPENSSL)/lib \ ++# -l:libcrypto.a -Wl,--exclude-libs,libcrypto.a + + # The crypt module is now disabled by default because it breaks builds + # on many systems (where -lcrypt is needed), e.g. Linux (I believe). +--- a/Modules/_hashopenssl.c ++++ b/Modules/_hashopenssl.c +@@ -43,51 +43,12 @@ + # error "OPENSSL_THREADS is not defined, Python requires thread-safe OpenSSL" + #endif + +-#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) +-/* OpenSSL < 1.1.0 */ +-#define EVP_MD_CTX_new EVP_MD_CTX_create +-#define EVP_MD_CTX_free EVP_MD_CTX_destroy +- +-HMAC_CTX * +-HMAC_CTX_new(void) +-{ +- HMAC_CTX *ctx = OPENSSL_malloc(sizeof(HMAC_CTX)); +- if (ctx != NULL) { +- memset(ctx, 0, sizeof(HMAC_CTX)); +- HMAC_CTX_init(ctx); +- } +- return ctx; +-} +- +-void +-HMAC_CTX_free(HMAC_CTX *ctx) +-{ +- if (ctx != NULL) { +- HMAC_CTX_cleanup(ctx); +- OPENSSL_free(ctx); +- } +-} +- +-const EVP_MD * +-HMAC_CTX_get_md(const HMAC_CTX *ctx) +-{ +- return ctx->md; +-} +-#endif +- + #define MUNCH_SIZE INT_MAX + +-#ifdef NID_sha3_224 ++#define PY_OPENSSL_HAS_SCRYPT 1 + #define PY_OPENSSL_HAS_SHA3 1 +-#endif +- +-#if defined(EVP_MD_FLAG_XOF) && defined(NID_shake128) + #define PY_OPENSSL_HAS_SHAKE 1 +-#endif +- +-#if defined(NID_blake2b512) && !defined(OPENSSL_NO_BLAKE2) + #define PY_OPENSSL_HAS_BLAKE2 1 +-#endif + + #if OPENSSL_VERSION_NUMBER >= 0x30000000L + #define PY_EVP_MD EVP_MD +@@ -1311,8 +1272,7 @@ pbkdf2_hmac_impl(PyObject *module, const + return key_obj; + } + +-#if OPENSSL_VERSION_NUMBER > 0x10100000L && !defined(OPENSSL_NO_SCRYPT) && !defined(LIBRESSL_VERSION_NUMBER) +-#define PY_SCRYPT 1 ++#ifdef PY_OPENSSL_HAS_SCRYPT + + /* XXX: Parameters salt, n, r and p should be required keyword-only parameters. + They are optional in the Argument Clinic declaration only due to a +@@ -1433,7 +1393,7 @@ _hashlib_scrypt_impl(PyObject *module, P + } + return key_obj; + } +-#endif ++#endif /* PY_OPENSSL_HAS_SCRYPT */ + + /* Fast HMAC for hmac.digest() + */ +@@ -1920,12 +1880,6 @@ hashlib_md_meth_names(PyObject *module) + return 0; + } + +-/* LibreSSL doesn't support FIPS: +- https://marc.info/?l=openbsd-misc&m=139819485423701&w=2 +- +- Ted Unangst wrote: "I figured I should mention our current libressl policy +- wrt FIPS mode. It's gone and it's not coming back." */ +-#ifndef LIBRESSL_VERSION_NUMBER + /*[clinic input] + _hashlib.get_fips_mode -> int + +@@ -1963,7 +1917,6 @@ _hashlib_get_fips_mode_impl(PyObject *mo + return result; + #endif + } +-#endif // !LIBRESSL_VERSION_NUMBER + + + static int +@@ -2144,17 +2097,6 @@ hashlib_free(void *m) + + /* Py_mod_exec functions */ + static int +-hashlib_openssl_legacy_init(PyObject *module) +-{ +-#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) +- /* Load all digest algorithms and initialize cpuid */ +- OPENSSL_add_all_algorithms_noconf(); +- ERR_load_crypto_strings(); +-#endif +- return 0; +-} +- +-static int + hashlib_init_hashtable(PyObject *module) + { + _hashlibstate *state = get_hashlib_state(module); +@@ -2227,10 +2169,7 @@ hashlib_init_hmactype(PyObject *module) + return 0; + } + +-#if 0 + static PyModuleDef_Slot hashlib_slots[] = { +- /* OpenSSL 1.0.2 and LibreSSL */ +- {Py_mod_exec, hashlib_openssl_legacy_init}, + {Py_mod_exec, hashlib_init_hashtable}, + {Py_mod_exec, hashlib_init_evptype}, + {Py_mod_exec, hashlib_init_evpxoftype}, +@@ -2238,7 +2177,6 @@ static PyModuleDef_Slot hashlib_slots[] + {Py_mod_exec, hashlib_md_meth_names}, + {0, NULL} + }; +-#endif + + static struct PyModuleDef _hashlibmodule = { + PyModuleDef_HEAD_INIT, +@@ -2246,7 +2184,7 @@ static struct PyModuleDef _hashlibmodule + .m_doc = "OpenSSL interface for hashlib module", + .m_size = sizeof(_hashlibstate), + .m_methods = EVP_functions, +- .m_slots = NULL, ++ .m_slots = hashlib_slots, + .m_traverse = hashlib_traverse, + .m_clear = hashlib_clear, + .m_free = hashlib_free +@@ -2255,41 +2193,5 @@ static struct PyModuleDef _hashlibmodule + PyMODINIT_FUNC + PyInit__hashlib(void) + { +- PyObject *m = PyState_FindModule(&_hashlibmodule); +- if (m != NULL) { +- Py_INCREF(m); +- return m; +- } +- +- m = PyModule_Create(&_hashlibmodule); +- if (m == NULL) { +- return NULL; +- } +- +- if (hashlib_openssl_legacy_init(m) < 0) { +- Py_DECREF(m); +- return NULL; +- } +- if (hashlib_init_hashtable(m) < 0) { +- Py_DECREF(m); +- return NULL; +- } +- if (hashlib_init_evptype(m) < 0) { +- Py_DECREF(m); +- return NULL; +- } +- if (hashlib_init_evpxoftype(m) < 0) { +- Py_DECREF(m); +- return NULL; +- } +- if (hashlib_init_hmactype(m) < 0) { +- Py_DECREF(m); +- return NULL; +- } +- if (hashlib_md_meth_names(m) == -1) { +- Py_DECREF(m); +- return NULL; +- } +- +- return m; ++ return PyModuleDef_Init(&_hashlibmodule); + } +--- a/Modules/_ssl.c ++++ b/Modules/_ssl.c +@@ -29,9 +29,9 @@ + #define _PySSL_FIX_ERRNO + + #define PySSL_BEGIN_ALLOW_THREADS_S(save) \ +- do { if (_ssl_locks_count>0) { (save) = PyEval_SaveThread(); } } while (0) ++ do { (save) = PyEval_SaveThread(); } while(0) + #define PySSL_END_ALLOW_THREADS_S(save) \ +- do { if (_ssl_locks_count>0) { PyEval_RestoreThread(save); } _PySSL_FIX_ERRNO; } while (0) ++ do { PyEval_RestoreThread(save); _PySSL_FIX_ERRNO; } while(0) + #define PySSL_BEGIN_ALLOW_THREADS { \ + PyThreadState *_save = NULL; \ + PySSL_BEGIN_ALLOW_THREADS_S(_save); +@@ -62,16 +62,6 @@ static PySocketModule_APIObject PySocket + #include "openssl/bio.h" + #include "openssl/dh.h" + +-#ifndef HAVE_X509_VERIFY_PARAM_SET1_HOST +-# ifdef LIBRESSL_VERSION_NUMBER +-# error "LibreSSL is missing X509_VERIFY_PARAM_set1_host(), see https://github.com/libressl-portable/portable/issues/381" +-# elif OPENSSL_VERSION_NUMBER > 0x1000200fL +-# define HAVE_X509_VERIFY_PARAM_SET1_HOST +-# else +-# error "libssl is too old and does not support X509_VERIFY_PARAM_set1_host()" +-# endif +-#endif +- + #ifndef OPENSSL_THREADS + # error "OPENSSL_THREADS is not defined, Python requires thread-safe OpenSSL" + #endif +@@ -142,15 +132,7 @@ static void _PySSLFixErrno(void) { + #include "_ssl_data.h" + #endif + +-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) +-# define OPENSSL_VERSION_1_1 1 +-# define PY_OPENSSL_1_1_API 1 +-#endif +- +-/* OpenSSL API 1.1.0+ does not include version methods. Define the methods +- * unless OpenSSL is compiled without the methods. It's the easiest way to +- * make 1.0.2, 1.1.0, 1.1.1, and 3.0.0 happy without deprecation warnings. +- */ ++/* OpenSSL API 1.1.0+ does not include version methods */ + #ifndef OPENSSL_NO_TLS1_METHOD + extern const SSL_METHOD *TLSv1_method(void); + #endif +@@ -161,59 +143,10 @@ extern const SSL_METHOD *TLSv1_1_method( + extern const SSL_METHOD *TLSv1_2_method(void); + #endif + +-/* LibreSSL 2.7.0 provides necessary OpenSSL 1.1.0 APIs */ +-#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x2070000fL +-# define PY_OPENSSL_1_1_API 1 +-#endif +- +-#if (OPENSSL_VERSION_NUMBER >= 0x30300000L) && !defined(LIBRESSL_VERSION_NUMBER) +-# define OPENSSL_VERSION_3_3 1 +-#endif +- +-/* SNI support (client- and server-side) appeared in OpenSSL 1.0.0 and 0.9.8f +- * This includes the SSL_set_SSL_CTX() function. +- */ +-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME +-# define HAVE_SNI 1 +-#else +-# define HAVE_SNI 0 +-#endif +- +-#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation +-# define HAVE_ALPN 1 +-#else +-# define HAVE_ALPN 0 +-#endif +- +-/* We cannot rely on OPENSSL_NO_NEXTPROTONEG because LibreSSL 2.6.1 dropped +- * NPN support but did not set OPENSSL_NO_NEXTPROTONEG for compatibility +- * reasons. The check for TLSEXT_TYPE_next_proto_neg works with +- * OpenSSL 1.0.1+ and LibreSSL. +- * OpenSSL 1.1.1-pre1 dropped NPN but still has TLSEXT_TYPE_next_proto_neg. +- */ +-#ifdef OPENSSL_NO_NEXTPROTONEG +-# define HAVE_NPN 0 +-#elif (OPENSSL_VERSION_NUMBER >= 0x10101000L) && !defined(LIBRESSL_VERSION_NUMBER) +-# define HAVE_NPN 0 +-#elif defined(TLSEXT_TYPE_next_proto_neg) +-# define HAVE_NPN 1 +-#else +-# define HAVE_NPN 0 +-#endif +- +-#if (OPENSSL_VERSION_NUMBER >= 0x10101000L) && !defined(LIBRESSL_VERSION_NUMBER) +-#define HAVE_OPENSSL_KEYLOG 1 +-#endif +- + #ifndef INVALID_SOCKET /* MS defines this */ + #define INVALID_SOCKET (-1) + #endif + +-/* OpenSSL 1.0.2 and LibreSSL needs extra code for locking */ +-#ifndef OPENSSL_VERSION_1_1 +-#define HAVE_OPENSSL_CRYPTO_LOCK +-#endif +- + /* OpenSSL 1.1+ allows locking X509_STORE, 1.0.2 doesn't. */ + #ifdef OPENSSL_VERSION_1_1 + #define HAVE_OPENSSL_X509_STORE_LOCK +@@ -224,80 +157,8 @@ extern const SSL_METHOD *TLSv1_2_method( + #define HAVE_OPENSSL_X509_STORE_GET1_OBJECTS 1 + #endif + +-#if defined(OPENSSL_VERSION_1_1) && !defined(OPENSSL_NO_SSL2) ++/* OpenSSL 1.1 does not have SSL 2.0 */ + #define OPENSSL_NO_SSL2 +-#endif +- +-#ifndef PY_OPENSSL_1_1_API +-/* OpenSSL 1.1 API shims for OpenSSL < 1.1.0 and LibreSSL < 2.7.0 */ +- +-#define TLS_method SSLv23_method +-#define TLS_client_method SSLv23_client_method +-#define TLS_server_method SSLv23_server_method +-#define ASN1_STRING_get0_data ASN1_STRING_data +-#define X509_get0_notBefore X509_get_notBefore +-#define X509_get0_notAfter X509_get_notAfter +-#define OpenSSL_version_num SSLeay +-#define OpenSSL_version SSLeay_version +-#define OPENSSL_VERSION SSLEAY_VERSION +- +-static int X509_NAME_ENTRY_set(const X509_NAME_ENTRY *ne) +-{ +- return ne->set; +-} +- +-#ifndef OPENSSL_NO_COMP +-/* LCOV_EXCL_START */ +-static int COMP_get_type(const COMP_METHOD *meth) +-{ +- return meth->type; +-} +-/* LCOV_EXCL_STOP */ +-#endif +- +-static pem_password_cb *SSL_CTX_get_default_passwd_cb(SSL_CTX *ctx) +-{ +- return ctx->default_passwd_callback; +-} +- +-static void *SSL_CTX_get_default_passwd_cb_userdata(SSL_CTX *ctx) +-{ +- return ctx->default_passwd_callback_userdata; +-} +- +-static int X509_OBJECT_get_type(X509_OBJECT *x) +-{ +- return x->type; +-} +- +-static X509 *X509_OBJECT_get0_X509(X509_OBJECT *x) +-{ +- return x->data.x509; +-} +- +-static int BIO_up_ref(BIO *b) +-{ +- CRYPTO_add(&b->references, 1, CRYPTO_LOCK_BIO); +- return 1; +-} +- +-static STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(X509_STORE *store) { +- return store->objs; +-} +- +-static int +-SSL_SESSION_has_ticket(const SSL_SESSION *s) +-{ +- return (s->tlsext_ticklen > 0) ? 1 : 0; +-} +- +-static unsigned long +-SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *s) +-{ +- return s->tlsext_tick_lifetime_hint; +-} +- +-#endif /* OpenSSL < 1.1.0 or LibreSSL < 2.7.0 */ + + /* Default cipher suites */ + #ifndef PY_SSL_DEFAULT_CIPHERS +@@ -409,24 +270,10 @@ enum py_proto_version { + #endif + }; + +- +-/* serves as a flag to see whether we've initialized the SSL thread support. */ +-/* 0 means no, greater than 0 means yes */ +- +-static unsigned int _ssl_locks_count = 0; +- + /* SSL socket object */ + + #define X509_NAME_MAXLEN 256 + +-/* SSL_CTX_clear_options() and SSL_clear_options() were first added in +- * OpenSSL 0.9.8m but do not appear in some 0.9.9-dev versions such the +- * 0.9.9 from "May 2008" that NetBSD 5.0 uses. */ +-#if OPENSSL_VERSION_NUMBER >= 0x009080dfL && OPENSSL_VERSION_NUMBER != 0x00909000L +-# define HAVE_SSL_CTX_CLEAR_OPTIONS +-#else +-# undef HAVE_SSL_CTX_CLEAR_OPTIONS +-#endif + + /* In case of 'tls-unique' it will be 12 bytes for TLS, 36 bytes for + * older SSL, but let's be safe */ +@@ -436,17 +283,9 @@ static unsigned int _ssl_locks_count = 0 + typedef struct { + PyObject_HEAD + SSL_CTX *ctx; +-#if HAVE_NPN +- unsigned char *npn_protocols; +- int npn_protocols_len; +-#endif +-#if HAVE_ALPN + unsigned char *alpn_protocols; + unsigned int alpn_protocols_len; +-#endif +-#ifndef OPENSSL_NO_TLSEXT + PyObject *set_sni_cb; +-#endif + int check_hostname; + /* OpenSSL has no API to get hostflags from X509_VERIFY_PARAM* struct. + * We have to maintain our own copy. OpenSSL's hostflags default to 0. +@@ -457,10 +296,8 @@ typedef struct { + int post_handshake_auth; + #endif + PyObject *msg_cb; +-#ifdef HAVE_OPENSSL_KEYLOG + PyObject *keylog_filename; + BIO *keylog_bio; +-#endif + } PySSLContext; + + typedef struct { +@@ -667,23 +504,18 @@ fill_and_set_sslerror(PySSLSocket *sslso + } + + switch (verify_code) { +-#ifdef X509_V_ERR_HOSTNAME_MISMATCH +- /* OpenSSL >= 1.0.2, LibreSSL >= 2.5.3 */ + case X509_V_ERR_HOSTNAME_MISMATCH: + verify_obj = PyUnicode_FromFormat( + "Hostname mismatch, certificate is not valid for '%S'.", + sslsock->server_hostname + ); + break; +-#endif +-#ifdef X509_V_ERR_IP_ADDRESS_MISMATCH + case X509_V_ERR_IP_ADDRESS_MISMATCH: + verify_obj = PyUnicode_FromFormat( + "IP address mismatch, certificate is not valid for '%S'.", + sslsock->server_hostname + ); + break; +-#endif + default: + verify_str = X509_verify_cert_error_string(verify_code); + if (verify_str != NULL) { +@@ -2014,7 +1846,6 @@ cipher_to_tuple(const SSL_CIPHER *cipher + return NULL; + } + +-#if OPENSSL_VERSION_NUMBER >= 0x10002000UL + static PyObject * + cipher_to_dict(const SSL_CIPHER *cipher) + { +@@ -2023,10 +1854,8 @@ cipher_to_dict(const SSL_CIPHER *cipher) + unsigned long cipher_id; + int alg_bits, strength_bits, len; + char buf[512] = {0}; +-#if OPENSSL_VERSION_1_1 + int aead, nid; + const char *skcipher = NULL, *digest = NULL, *kx = NULL, *auth = NULL; +-#endif + + /* can be NULL */ + cipher_name = SSL_CIPHER_get_name(cipher); +@@ -2039,7 +1868,6 @@ cipher_to_dict(const SSL_CIPHER *cipher) + buf[len-1] = '\0'; + strength_bits = SSL_CIPHER_get_bits(cipher, &alg_bits); + +-#if OPENSSL_VERSION_1_1 + aead = SSL_CIPHER_is_aead(cipher); + nid = SSL_CIPHER_get_cipher_nid(cipher); + skcipher = nid != NID_undef ? OBJ_nid2ln(nid) : NULL; +@@ -2049,13 +1877,10 @@ cipher_to_dict(const SSL_CIPHER *cipher) + kx = nid != NID_undef ? OBJ_nid2ln(nid) : NULL; + nid = SSL_CIPHER_get_auth_nid(cipher); + auth = nid != NID_undef ? OBJ_nid2ln(nid) : NULL; +-#endif + + return Py_BuildValue( + "{sksssssssisi" +-#if OPENSSL_VERSION_1_1 + "sOssssssss" +-#endif + "}", + "id", cipher_id, + "name", cipher_name, +@@ -2063,16 +1888,13 @@ cipher_to_dict(const SSL_CIPHER *cipher) + "description", buf, + "strength_bits", strength_bits, + "alg_bits", alg_bits +-#if OPENSSL_VERSION_1_1 + ,"aead", aead ? Py_True : Py_False, + "symmetric", skcipher, + "digest", digest, + "kea", kx, + "auth", auth +-#endif + ); + } +-#endif + + /*[clinic input] + _ssl._SSLSocket.shared_ciphers +@@ -2143,28 +1965,6 @@ _ssl__SSLSocket_version_impl(PySSLSocket + return PyUnicode_FromString(version); + } + +-#if HAVE_NPN +-/*[clinic input] +-_ssl._SSLSocket.selected_npn_protocol +-[clinic start generated code]*/ +- +-static PyObject * +-_ssl__SSLSocket_selected_npn_protocol_impl(PySSLSocket *self) +-/*[clinic end generated code: output=b91d494cd207ecf6 input=c28fde139204b826]*/ +-{ +- const unsigned char *out; +- unsigned int outlen; +- +- SSL_get0_next_proto_negotiated(self->ssl, +- &out, &outlen); +- +- if (out == NULL) +- Py_RETURN_NONE; +- return PyUnicode_FromStringAndSize((char *)out, outlen); +-} +-#endif +- +-#if HAVE_ALPN + /*[clinic input] + _ssl._SSLSocket.selected_alpn_protocol + [clinic start generated code]*/ +@@ -2182,7 +1982,6 @@ _ssl__SSLSocket_selected_alpn_protocol_i + Py_RETURN_NONE; + return PyUnicode_FromStringAndSize((char *)out, outlen); + } +-#endif + + /*[clinic input] + _ssl._SSLSocket.compression +@@ -2219,11 +2018,6 @@ static int PySSL_set_context(PySSLSocket + void *closure) { + + if (PyObject_TypeCheck(value, &PySSLContext_Type)) { +-#if !HAVE_SNI +- PyErr_SetString(PyExc_NotImplementedError, "setting a socket's " +- "context is not supported by your OpenSSL library"); +- return -1; +-#else + Py_INCREF(value); + Py_SETREF(self->ctx, (PySSLContext *)value); + SSL_set_SSL_CTX(self->ssl, self->ctx->ctx); +@@ -2232,7 +2026,6 @@ static int PySSL_set_context(PySSLSocket + self->ssl, + self->ctx->msg_cb ? _PySSL_msg_callback : NULL + ); +-#endif + } else { + PyErr_SetString(PyExc_TypeError, "The value must be a SSLContext"); + return -1; +@@ -2857,8 +2650,6 @@ _ssl__SSLSocket_verify_client_post_hands + #endif + } + +-#ifdef OPENSSL_VERSION_1_1 +- + static SSL_SESSION* + _ssl_session_dup(SSL_SESSION *session) { + SSL_SESSION *newsession = NULL; +@@ -2899,7 +2690,6 @@ _ssl_session_dup(SSL_SESSION *session) { + } + return NULL; + } +-#endif + + static PyObject * + PySSL_get_session(PySSLSocket *self, void *closure) { +@@ -2908,7 +2698,6 @@ PySSL_get_session(PySSLSocket *self, voi + PySSLSession *pysess; + SSL_SESSION *session; + +-#ifdef OPENSSL_VERSION_1_1 + /* duplicate session as workaround for session bug in OpenSSL 1.1.0, + * https://github.com/openssl/openssl/issues/1550 */ + session = SSL_get0_session(self->ssl); /* borrowed reference */ +@@ -2918,12 +2707,10 @@ PySSL_get_session(PySSLSocket *self, voi + if ((session = _ssl_session_dup(session)) == NULL) { + return NULL; + } +-#else + session = SSL_get1_session(self->ssl); + if (session == NULL) { + Py_RETURN_NONE; + } +-#endif + pysess = PyObject_GC_New(PySSLSession, &PySSLSession_Type); + if (pysess == NULL) { + SSL_SESSION_free(session); +@@ -2942,9 +2729,7 @@ static int PySSL_set_session(PySSLSocket + void *closure) + { + PySSLSession *pysess; +-#ifdef OPENSSL_VERSION_1_1 + SSL_SESSION *session; +-#endif + int result; + + if (!PySSLSession_Check(value)) { +@@ -2968,7 +2753,6 @@ static int PySSL_set_session(PySSLSocket + "Cannot set session after handshake."); + return -1; + } +-#ifdef OPENSSL_VERSION_1_1 + /* duplicate session */ + if ((session = _ssl_session_dup(pysess->session)) == NULL) { + return -1; +@@ -2976,9 +2760,6 @@ static int PySSL_set_session(PySSLSocket + result = SSL_set_session(self->ssl, session); + /* free duplicate, SSL_set_session() bumps ref count */ + SSL_SESSION_free(session); +-#else +- result = SSL_set_session(self->ssl, pysess->session); +-#endif + if (result == 0) { + _setSSLError(NULL, 0, __FILE__, __LINE__); + return -1; +@@ -3029,7 +2810,6 @@ static PyMethodDef PySSLMethods[] = { + _SSL__SSLSOCKET_CIPHER_METHODDEF + _SSL__SSLSOCKET_SHARED_CIPHERS_METHODDEF + _SSL__SSLSOCKET_VERSION_METHODDEF +- _SSL__SSLSOCKET_SELECTED_NPN_PROTOCOL_METHODDEF + _SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF + _SSL__SSLSOCKET_COMPRESSION_METHODDEF + _SSL__SSLSOCKET_SHUTDOWN_METHODDEF +@@ -3123,9 +2903,6 @@ _ssl__SSLContext_impl(PyTypeObject *type + SSL_CTX *ctx = NULL; + X509_VERIFY_PARAM *params; + int result; +-#if defined(SSL_MODE_RELEASE_BUFFERS) +- unsigned long libver; +-#endif + + PySSL_BEGIN_ALLOW_THREADS + switch(proto_version) { +@@ -3190,19 +2967,10 @@ _ssl__SSLContext_impl(PyTypeObject *type + self->hostflags = X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS; + self->protocol = proto_version; + self->msg_cb = NULL; +-#ifdef HAVE_OPENSSL_KEYLOG + self->keylog_filename = NULL; + self->keylog_bio = NULL; +-#endif +-#if HAVE_NPN +- self->npn_protocols = NULL; +-#endif +-#if HAVE_ALPN + self->alpn_protocols = NULL; +-#endif +-#ifndef OPENSSL_NO_TLSEXT + self->set_sni_cb = NULL; +-#endif + /* Don't check host name by default */ + if (proto_version == PY_SSL_VERSION_TLS_CLIENT) { + self->check_hostname = 1; +@@ -3264,37 +3032,9 @@ _ssl__SSLContext_impl(PyTypeObject *type + return NULL; + } + +-#if defined(SSL_MODE_RELEASE_BUFFERS) + /* Set SSL_MODE_RELEASE_BUFFERS. This potentially greatly reduces memory +- usage for no cost at all. However, don't do this for OpenSSL versions +- between 1.0.1 and 1.0.1h or 1.0.0 and 1.0.0m, which are affected by CVE +- 2014-0198. I can't find exactly which beta fixed this CVE, so be +- conservative and assume it wasn't fixed until release. We do this check +- at runtime to avoid problems from the dynamic linker. +- See #25672 for more on this. */ +- libver = OpenSSL_version_num(); +- if (!(libver >= 0x10001000UL && libver < 0x1000108fUL) && +- !(libver >= 0x10000000UL && libver < 0x100000dfUL)) { +- SSL_CTX_set_mode(self->ctx, SSL_MODE_RELEASE_BUFFERS); +- } +-#endif +- +- +-#if !defined(OPENSSL_NO_ECDH) && !defined(OPENSSL_VERSION_1_1) +- /* Allow automatic ECDH curve selection (on OpenSSL 1.0.2+), or use +- prime256v1 by default. This is Apache mod_ssl's initialization +- policy, so we should be safe. OpenSSL 1.1 has it enabled by default. +- */ +-#if defined(SSL_CTX_set_ecdh_auto) +- SSL_CTX_set_ecdh_auto(self->ctx, 1); +-#else +- { +- EC_KEY *key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); +- SSL_CTX_set_tmp_ecdh(self->ctx, key); +- EC_KEY_free(key); +- } +-#endif +-#endif ++ usage for no cost at all. */ ++ SSL_CTX_set_mode(self->ctx, SSL_MODE_RELEASE_BUFFERS); + + #define SID_CTX "Python" + SSL_CTX_set_session_id_context(self->ctx, (const unsigned char *) SID_CTX, +@@ -3302,11 +3042,9 @@ _ssl__SSLContext_impl(PyTypeObject *type + #undef SID_CTX + + params = SSL_CTX_get0_param(self->ctx); +-#ifdef X509_V_FLAG_TRUSTED_FIRST + /* Improve trust chain building when cross-signed intermediate + certificates are present. See https://bugs.python.org/issue23476. */ + X509_VERIFY_PARAM_set_flags(params, X509_V_FLAG_TRUSTED_FIRST); +-#endif + X509_VERIFY_PARAM_set_hostflags(params, self->hostflags); + + #ifdef TLS1_3_VERSION +@@ -3320,9 +3058,7 @@ _ssl__SSLContext_impl(PyTypeObject *type + static int + context_traverse(PySSLContext *self, visitproc visit, void *arg) + { +-#ifndef OPENSSL_NO_TLSEXT + Py_VISIT(self->set_sni_cb); +-#endif + Py_VISIT(self->msg_cb); + return 0; + } +@@ -3330,11 +3066,8 @@ context_traverse(PySSLContext *self, vis + static int + context_clear(PySSLContext *self) + { +-#ifndef OPENSSL_NO_TLSEXT + Py_CLEAR(self->set_sni_cb); +-#endif + Py_CLEAR(self->msg_cb); +-#ifdef HAVE_OPENSSL_KEYLOG + Py_CLEAR(self->keylog_filename); + if (self->keylog_bio != NULL) { + PySSL_BEGIN_ALLOW_THREADS +@@ -3342,7 +3075,6 @@ context_clear(PySSLContext *self) + PySSL_END_ALLOW_THREADS + self->keylog_bio = NULL; + } +-#endif + return 0; + } + +@@ -3353,12 +3085,7 @@ context_dealloc(PySSLContext *self) + PyObject_GC_UnTrack(self); + context_clear(self); + SSL_CTX_free(self->ctx); +-#if HAVE_NPN +- PyMem_FREE(self->npn_protocols); +-#endif +-#if HAVE_ALPN + PyMem_FREE(self->alpn_protocols); +-#endif + Py_TYPE(self)->tp_free(self); + } + +@@ -3385,7 +3112,6 @@ _ssl__SSLContext_set_ciphers_impl(PySSLC + Py_RETURN_NONE; + } + +-#if OPENSSL_VERSION_NUMBER >= 0x10002000UL + /*[clinic input] + _ssl._SSLContext.get_ciphers + [clinic start generated code]*/ +@@ -3428,10 +3154,8 @@ _ssl__SSLContext_get_ciphers_impl(PySSLC + return result; + + } +-#endif + + +-#if HAVE_NPN || HAVE_ALPN + static int + do_protocol_selection(int alpn, unsigned char **out, unsigned char *outlen, + const unsigned char *server_protocols, unsigned int server_protocols_len, +@@ -3455,77 +3179,7 @@ do_protocol_selection(int alpn, unsigned + + return SSL_TLSEXT_ERR_OK; + } +-#endif + +-#if HAVE_NPN +-/* this callback gets passed to SSL_CTX_set_next_protos_advertise_cb */ +-static int +-_advertiseNPN_cb(SSL *s, +- const unsigned char **data, unsigned int *len, +- void *args) +-{ +- PySSLContext *ssl_ctx = (PySSLContext *) args; +- +- if (ssl_ctx->npn_protocols == NULL) { +- *data = (unsigned char *)""; +- *len = 0; +- } else { +- *data = ssl_ctx->npn_protocols; +- *len = ssl_ctx->npn_protocols_len; +- } +- +- return SSL_TLSEXT_ERR_OK; +-} +-/* this callback gets passed to SSL_CTX_set_next_proto_select_cb */ +-static int +-_selectNPN_cb(SSL *s, +- unsigned char **out, unsigned char *outlen, +- const unsigned char *server, unsigned int server_len, +- void *args) +-{ +- PySSLContext *ctx = (PySSLContext *)args; +- return do_protocol_selection(0, out, outlen, server, server_len, +- ctx->npn_protocols, ctx->npn_protocols_len); +-} +-#endif +- +-/*[clinic input] +-_ssl._SSLContext._set_npn_protocols +- protos: Py_buffer +- / +-[clinic start generated code]*/ +- +-static PyObject * +-_ssl__SSLContext__set_npn_protocols_impl(PySSLContext *self, +- Py_buffer *protos) +-/*[clinic end generated code: output=72b002c3324390c6 input=319fcb66abf95bd7]*/ +-{ +-#if HAVE_NPN +- PyMem_Free(self->npn_protocols); +- self->npn_protocols = PyMem_Malloc(protos->len); +- if (self->npn_protocols == NULL) +- return PyErr_NoMemory(); +- memcpy(self->npn_protocols, protos->buf, protos->len); +- self->npn_protocols_len = (int) protos->len; +- +- /* set both server and client callbacks, because the context can +- * be used to create both types of sockets */ +- SSL_CTX_set_next_protos_advertised_cb(self->ctx, +- _advertiseNPN_cb, +- self); +- SSL_CTX_set_next_proto_select_cb(self->ctx, +- _selectNPN_cb, +- self); +- +- Py_RETURN_NONE; +-#else +- PyErr_SetString(PyExc_NotImplementedError, +- "The NPN extension requires OpenSSL 1.0.1 or later."); +- return NULL; +-#endif +-} +- +-#if HAVE_ALPN + static int + _selectALPN_cb(SSL *s, + const unsigned char **out, unsigned char *outlen, +@@ -3537,7 +3191,6 @@ _selectALPN_cb(SSL *s, + ctx->alpn_protocols, ctx->alpn_protocols_len, + client_protocols, client_protocols_len); + } +-#endif + + /*[clinic input] + _ssl._SSLContext._set_alpn_protocols +@@ -3550,7 +3203,6 @@ _ssl__SSLContext__set_alpn_protocols_imp + Py_buffer *protos) + /*[clinic end generated code: output=87599a7f76651a9b input=9bba964595d519be]*/ + { +-#if HAVE_ALPN + if ((size_t)protos->len > UINT_MAX) { + PyErr_Format(PyExc_OverflowError, + "protocols longer than %u bytes", UINT_MAX); +@@ -3569,11 +3221,6 @@ _ssl__SSLContext__set_alpn_protocols_imp + SSL_CTX_set_alpn_select_cb(self->ctx, _selectALPN_cb, self); + + Py_RETURN_NONE; +-#else +- PyErr_SetString(PyExc_NotImplementedError, +- "The ALPN extension requires OpenSSL 1.0.2 or later."); +- return NULL; +-#endif + } + + static PyObject * +@@ -3649,9 +3296,6 @@ set_verify_flags(PySSLContext *self, PyO + } + + /* Getter and setter for protocol version */ +-#if defined(SSL_CTRL_GET_MAX_PROTO_VERSION) +- +- + static int + set_min_max_proto_version(PySSLContext *self, PyObject *arg, int what) + { +@@ -3746,9 +3390,8 @@ set_maximum_version(PySSLContext *self, + { + return set_min_max_proto_version(self, arg, 1); + } +-#endif /* SSL_CTRL_GET_MAX_PROTO_VERSION */ + +-#if (OPENSSL_VERSION_NUMBER >= 0x10101000L) && !defined(LIBRESSL_VERSION_NUMBER) ++#ifdef TLS1_3_VERSION + static PyObject * + get_num_tickets(PySSLContext *self, void *c) + { +@@ -3779,7 +3422,7 @@ set_num_tickets(PySSLContext *self, PyOb + + PyDoc_STRVAR(PySSLContext_num_tickets_doc, + "Control the number of TLSv1.3 session tickets"); +-#endif /* OpenSSL 1.1.1 */ ++#endif /* TLS1_3_VERSION */ + + static PyObject * + get_options(PySSLContext *self, void *c) +@@ -3797,13 +3440,7 @@ set_options(PySSLContext *self, PyObject + clear = opts & ~new_opts; + set = ~opts & new_opts; + if (clear) { +-#ifdef HAVE_SSL_CTX_CLEAR_OPTIONS + SSL_CTX_clear_options(self->ctx, clear); +-#else +- PyErr_SetString(PyExc_ValueError, +- "can't clear options before OpenSSL 0.9.8m"); +- return -1; +-#endif + } + if (set) + SSL_CTX_set_options(self->ctx, set); +@@ -4500,7 +4137,6 @@ _ssl__SSLContext_set_default_verify_path + Py_RETURN_NONE; + } + +-#ifndef OPENSSL_NO_ECDH + /*[clinic input] + _ssl._SSLContext.set_ecdh_curve + name: object +@@ -4535,9 +4171,7 @@ _ssl__SSLContext_set_ecdh_curve(PySSLCon + EC_KEY_free(key); + Py_RETURN_NONE; + } +-#endif + +-#if HAVE_SNI && !defined(OPENSSL_NO_TLSEXT) + static int + _servername_callback(SSL *s, int *al, void *args) + { +@@ -4641,7 +4275,6 @@ error: + PyGILState_Release(gstate); + return ret; + } +-#endif + + static PyObject * + get_sni_callback(PySSLContext *self, void *c) +@@ -4662,7 +4295,6 @@ set_sni_callback(PySSLContext *self, PyO + "sni_callback cannot be set on TLS_CLIENT context"); + return -1; + } +-#if HAVE_SNI && !defined(OPENSSL_NO_TLSEXT) + Py_CLEAR(self->set_sni_cb); + if (arg == Py_None) { + SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL); +@@ -4680,13 +4312,6 @@ set_sni_callback(PySSLContext *self, PyO + SSL_CTX_set_tlsext_servername_arg(self->ctx, self); + } + return 0; +-#else +- PyErr_SetString(PyExc_NotImplementedError, +- "The TLS extension servername callback, " +- "SSL_CTX_set_tlsext_servername_callback, " +- "is not in the current OpenSSL library."); +- return -1; +-#endif + } + + /* Shim of X509_STORE_get1_objects API from OpenSSL 3.3 +@@ -4882,21 +4507,17 @@ static PyGetSetDef context_getsetlist[] + (setter) set_check_hostname, NULL}, + {"_host_flags", (getter) get_host_flags, + (setter) set_host_flags, NULL}, +-#if SSL_CTRL_GET_MAX_PROTO_VERSION + {"minimum_version", (getter) get_minimum_version, + (setter) set_minimum_version, NULL}, + {"maximum_version", (getter) get_maximum_version, + (setter) set_maximum_version, NULL}, +-#endif +-#ifdef HAVE_OPENSSL_KEYLOG + {"keylog_filename", (getter) _PySSLContext_get_keylog_filename, + (setter) _PySSLContext_set_keylog_filename, NULL}, +-#endif + {"_msg_callback", (getter) _PySSLContext_get_msg_callback, + (setter) _PySSLContext_set_msg_callback, NULL}, + {"sni_callback", (getter) get_sni_callback, + (setter) set_sni_callback, PySSLContext_sni_callback_doc}, +-#if (OPENSSL_VERSION_NUMBER >= 0x10101000L) && !defined(LIBRESSL_VERSION_NUMBER) ++#ifdef TLS1_3_VERSION + {"num_tickets", (getter) get_num_tickets, + (setter) set_num_tickets, PySSLContext_num_tickets_doc}, + #endif +@@ -4923,7 +4544,6 @@ static struct PyMethodDef context_method + _SSL__SSLCONTEXT__WRAP_BIO_METHODDEF + _SSL__SSLCONTEXT_SET_CIPHERS_METHODDEF + _SSL__SSLCONTEXT__SET_ALPN_PROTOCOLS_METHODDEF +- _SSL__SSLCONTEXT__SET_NPN_PROTOCOLS_METHODDEF + _SSL__SSLCONTEXT_LOAD_CERT_CHAIN_METHODDEF + _SSL__SSLCONTEXT_LOAD_DH_PARAMS_METHODDEF + _SSL__SSLCONTEXT_LOAD_VERIFY_LOCATIONS_METHODDEF +@@ -5441,11 +5061,7 @@ PySSL_RAND(int len, int pseudo) + if (bytes == NULL) + return NULL; + if (pseudo) { +-#ifdef PY_OPENSSL_1_1_API + ok = RAND_bytes((unsigned char*)PyBytes_AS_STRING(bytes), len); +-#else +- ok = RAND_pseudo_bytes((unsigned char*)PyBytes_AS_STRING(bytes), len); +-#endif + if (ok == 0 || ok == 1) + return Py_BuildValue("NO", bytes, ok == 1 ? Py_True : Py_False); + } +@@ -6000,92 +5616,6 @@ static PyMethodDef PySSL_methods[] = { + }; + + +-#ifdef HAVE_OPENSSL_CRYPTO_LOCK +- +-/* an implementation of OpenSSL threading operations in terms +- * of the Python C thread library +- * Only used up to 1.0.2. OpenSSL 1.1.0+ has its own locking code. +- */ +- +-static PyThread_type_lock *_ssl_locks = NULL; +- +-#if OPENSSL_VERSION_NUMBER >= 0x10000000 +-/* use new CRYPTO_THREADID API. */ +-static void +-_ssl_threadid_callback(CRYPTO_THREADID *id) +-{ +- CRYPTO_THREADID_set_numeric(id, PyThread_get_thread_ident()); +-} +-#else +-/* deprecated CRYPTO_set_id_callback() API. */ +-static unsigned long +-_ssl_thread_id_function (void) { +- return PyThread_get_thread_ident(); +-} +-#endif +- +-static void _ssl_thread_locking_function +- (int mode, int n, const char *file, int line) { +- /* this function is needed to perform locking on shared data +- structures. (Note that OpenSSL uses a number of global data +- structures that will be implicitly shared whenever multiple +- threads use OpenSSL.) Multi-threaded applications will +- crash at random if it is not set. +- +- locking_function() must be able to handle up to +- CRYPTO_num_locks() different mutex locks. It sets the n-th +- lock if mode & CRYPTO_LOCK, and releases it otherwise. +- +- file and line are the file number of the function setting the +- lock. They can be useful for debugging. +- */ +- +- if ((_ssl_locks == NULL) || +- (n < 0) || ((unsigned)n >= _ssl_locks_count)) +- return; +- +- if (mode & CRYPTO_LOCK) { +- PyThread_acquire_lock(_ssl_locks[n], 1); +- } else { +- PyThread_release_lock(_ssl_locks[n]); +- } +-} +- +-static int _setup_ssl_threads(void) { +- +- unsigned int i; +- +- if (_ssl_locks == NULL) { +- _ssl_locks_count = CRYPTO_num_locks(); +- _ssl_locks = PyMem_Calloc(_ssl_locks_count, +- sizeof(PyThread_type_lock)); +- if (_ssl_locks == NULL) { +- PyErr_NoMemory(); +- return 0; +- } +- for (i = 0; i < _ssl_locks_count; i++) { +- _ssl_locks[i] = PyThread_allocate_lock(); +- if (_ssl_locks[i] == NULL) { +- unsigned int j; +- for (j = 0; j < i; j++) { +- PyThread_free_lock(_ssl_locks[j]); +- } +- PyMem_Free(_ssl_locks); +- return 0; +- } +- } +- CRYPTO_set_locking_callback(_ssl_thread_locking_function); +-#if OPENSSL_VERSION_NUMBER >= 0x10000000 +- CRYPTO_THREADID_set_callback(_ssl_threadid_callback); +-#else +- CRYPTO_set_id_callback(_ssl_thread_id_function); +-#endif +- } +- return 1; +-} +- +-#endif /* HAVE_OPENSSL_CRYPTO_LOCK for OpenSSL < 1.1.0 */ +- + PyDoc_STRVAR(module_doc, + "Implementation module for SSL socket operations. See the socket module\n\ + for documentation."); +@@ -6152,14 +5682,6 @@ PyInit__ssl(void) + return NULL; + PySocketModule = *socket_api; + +-#ifndef OPENSSL_VERSION_1_1 +- /* Load all algorithms and initialize cpuid */ +- OPENSSL_add_all_algorithms_noconf(); +- /* Init OpenSSL */ +- SSL_load_error_strings(); +- SSL_library_init(); +-#endif +- + #ifdef HAVE_OPENSSL_CRYPTO_LOCK + /* note that this will start threading if not already started */ + if (!_setup_ssl_threads()) { +@@ -6266,10 +5788,8 @@ PyInit__ssl(void) + X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); + PyModule_AddIntConstant(m, "VERIFY_X509_STRICT", + X509_V_FLAG_X509_STRICT); +-#ifdef X509_V_FLAG_TRUSTED_FIRST + PyModule_AddIntConstant(m, "VERIFY_X509_TRUSTED_FIRST", + X509_V_FLAG_TRUSTED_FIRST); +-#endif + + /* Alert Descriptions from ssl.h */ + /* note RESERVED constants no longer intended for use have been removed */ +@@ -6426,31 +5946,11 @@ PyInit__ssl(void) + PyModule_AddObject((m), (key), bool_obj); \ + } while (0) + +-#if HAVE_SNI + addbool(m, "HAS_SNI", 1); +-#else +- addbool(m, "HAS_SNI", 0); +-#endif +- + addbool(m, "HAS_TLS_UNIQUE", 1); +- +-#ifndef OPENSSL_NO_ECDH + addbool(m, "HAS_ECDH", 1); +-#else +- addbool(m, "HAS_ECDH", 0); +-#endif +- +-#if HAVE_NPN +- addbool(m, "HAS_NPN", 1); +-#else + addbool(m, "HAS_NPN", 0); +-#endif +- +-#if HAVE_ALPN + addbool(m, "HAS_ALPN", 1); +-#else +- addbool(m, "HAS_ALPN", 0); +-#endif + + #if defined(SSL2_VERSION) && !defined(OPENSSL_NO_SSL2) + addbool(m, "HAS_SSLv2", 1); +--- a/Modules/_ssl/debughelpers.c ++++ b/Modules/_ssl/debughelpers.c +@@ -114,8 +114,6 @@ _PySSLContext_set_msg_callback(PySSLCont + return 0; + } + +-#ifdef HAVE_OPENSSL_KEYLOG +- + static void + _PySSL_keylog_callback(const SSL *ssl, const char *line) + { +@@ -219,5 +217,3 @@ _PySSLContext_set_keylog_filename(PySSLC + SSL_CTX_set_keylog_callback(self->ctx, _PySSL_keylog_callback); + return 0; + } +- +-#endif +--- a/Modules/clinic/_hashopenssl.c.h ++++ b/Modules/clinic/_hashopenssl.c.h +@@ -965,7 +965,7 @@ exit: + return return_value; + } + +-#if (OPENSSL_VERSION_NUMBER > 0x10100000L && !defined(OPENSSL_NO_SCRYPT) && !defined(LIBRESSL_VERSION_NUMBER)) ++#if defined(PY_OPENSSL_HAS_SCRYPT) + + PyDoc_STRVAR(_hashlib_scrypt__doc__, + "scrypt($module, /, password, *, salt=None, n=None, r=None, p=None,\n" +@@ -1093,7 +1093,7 @@ exit: + return return_value; + } + +-#endif /* (OPENSSL_VERSION_NUMBER > 0x10100000L && !defined(OPENSSL_NO_SCRYPT) && !defined(LIBRESSL_VERSION_NUMBER)) */ ++#endif /* defined(PY_OPENSSL_HAS_SCRYPT) */ + + PyDoc_STRVAR(_hashlib_hmac_singleshot__doc__, + "hmac_digest($module, /, key, msg, digest)\n" +@@ -1324,8 +1324,6 @@ _hashlib_HMAC_hexdigest(HMACobject *self + return _hashlib_HMAC_hexdigest_impl(self); + } + +-#if !defined(LIBRESSL_VERSION_NUMBER) +- + PyDoc_STRVAR(_hashlib_get_fips_mode__doc__, + "get_fips_mode($module, /)\n" + "--\n" +@@ -1361,8 +1359,6 @@ exit: + return return_value; + } + +-#endif /* !defined(LIBRESSL_VERSION_NUMBER) */ +- + PyDoc_STRVAR(_hashlib_compare_digest__doc__, + "compare_digest($module, a, b, /)\n" + "--\n" +@@ -1439,7 +1435,4 @@ exit: + #define _HASHLIB_SCRYPT_METHODDEF + #endif /* !defined(_HASHLIB_SCRYPT_METHODDEF) */ + +-#ifndef _HASHLIB_GET_FIPS_MODE_METHODDEF +- #define _HASHLIB_GET_FIPS_MODE_METHODDEF +-#endif /* !defined(_HASHLIB_GET_FIPS_MODE_METHODDEF) */ + /*[clinic end generated code: output=b6b280e46bf0b139 input=a9049054013a1b77]*/ +--- a/Modules/clinic/_ssl.c.h ++++ b/Modules/clinic/_ssl.c.h +@@ -139,29 +139,6 @@ _ssl__SSLSocket_version(PySSLSocket *sel + return _ssl__SSLSocket_version_impl(self); + } + +-#if (HAVE_NPN) +- +-PyDoc_STRVAR(_ssl__SSLSocket_selected_npn_protocol__doc__, +-"selected_npn_protocol($self, /)\n" +-"--\n" +-"\n"); +- +-#define _SSL__SSLSOCKET_SELECTED_NPN_PROTOCOL_METHODDEF \ +- {"selected_npn_protocol", (PyCFunction)_ssl__SSLSocket_selected_npn_protocol, METH_NOARGS, _ssl__SSLSocket_selected_npn_protocol__doc__}, +- +-static PyObject * +-_ssl__SSLSocket_selected_npn_protocol_impl(PySSLSocket *self); +- +-static PyObject * +-_ssl__SSLSocket_selected_npn_protocol(PySSLSocket *self, PyObject *Py_UNUSED(ignored)) +-{ +- return _ssl__SSLSocket_selected_npn_protocol_impl(self); +-} +- +-#endif /* (HAVE_NPN) */ +- +-#if (HAVE_ALPN) +- + PyDoc_STRVAR(_ssl__SSLSocket_selected_alpn_protocol__doc__, + "selected_alpn_protocol($self, /)\n" + "--\n" +@@ -179,8 +156,6 @@ _ssl__SSLSocket_selected_alpn_protocol(P + return _ssl__SSLSocket_selected_alpn_protocol_impl(self); + } + +-#endif /* (HAVE_ALPN) */ +- + PyDoc_STRVAR(_ssl__SSLSocket_compression__doc__, + "compression($self, /)\n" + "--\n" +@@ -457,8 +432,6 @@ exit: + return return_value; + } + +-#if (OPENSSL_VERSION_NUMBER >= 0x10002000UL) +- + PyDoc_STRVAR(_ssl__SSLContext_get_ciphers__doc__, + "get_ciphers($self, /)\n" + "--\n" +@@ -476,44 +449,6 @@ _ssl__SSLContext_get_ciphers(PySSLContex + return _ssl__SSLContext_get_ciphers_impl(self); + } + +-#endif /* (OPENSSL_VERSION_NUMBER >= 0x10002000UL) */ +- +-PyDoc_STRVAR(_ssl__SSLContext__set_npn_protocols__doc__, +-"_set_npn_protocols($self, protos, /)\n" +-"--\n" +-"\n"); +- +-#define _SSL__SSLCONTEXT__SET_NPN_PROTOCOLS_METHODDEF \ +- {"_set_npn_protocols", (PyCFunction)_ssl__SSLContext__set_npn_protocols, METH_O, _ssl__SSLContext__set_npn_protocols__doc__}, +- +-static PyObject * +-_ssl__SSLContext__set_npn_protocols_impl(PySSLContext *self, +- Py_buffer *protos); +- +-static PyObject * +-_ssl__SSLContext__set_npn_protocols(PySSLContext *self, PyObject *arg) +-{ +- PyObject *return_value = NULL; +- Py_buffer protos = {NULL, NULL}; +- +- if (PyObject_GetBuffer(arg, &protos, PyBUF_SIMPLE) != 0) { +- goto exit; +- } +- if (!PyBuffer_IsContiguous(&protos, 'C')) { +- _PyArg_BadArgument("_set_npn_protocols", "argument", "contiguous buffer", arg); +- goto exit; +- } +- return_value = _ssl__SSLContext__set_npn_protocols_impl(self, &protos); +- +-exit: +- /* Cleanup for protos */ +- if (protos.obj) { +- PyBuffer_Release(&protos); +- } +- +- return return_value; +-} +- + PyDoc_STRVAR(_ssl__SSLContext__set_alpn_protocols__doc__, + "_set_alpn_protocols($self, protos, /)\n" + "--\n" +@@ -844,8 +779,6 @@ _ssl__SSLContext_set_default_verify_path + return _ssl__SSLContext_set_default_verify_paths_impl(self); + } + +-#if !defined(OPENSSL_NO_ECDH) +- + PyDoc_STRVAR(_ssl__SSLContext_set_ecdh_curve__doc__, + "set_ecdh_curve($self, name, /)\n" + "--\n" +@@ -854,8 +787,6 @@ PyDoc_STRVAR(_ssl__SSLContext_set_ecdh_c + #define _SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF \ + {"set_ecdh_curve", (PyCFunction)_ssl__SSLContext_set_ecdh_curve, METH_O, _ssl__SSLContext_set_ecdh_curve__doc__}, + +-#endif /* !defined(OPENSSL_NO_ECDH) */ +- + PyDoc_STRVAR(_ssl__SSLContext_cert_store_stats__doc__, + "cert_store_stats($self, /)\n" + "--\n" +@@ -1455,22 +1386,6 @@ exit: + + #endif /* defined(_MSC_VER) */ + +-#ifndef _SSL__SSLSOCKET_SELECTED_NPN_PROTOCOL_METHODDEF +- #define _SSL__SSLSOCKET_SELECTED_NPN_PROTOCOL_METHODDEF +-#endif /* !defined(_SSL__SSLSOCKET_SELECTED_NPN_PROTOCOL_METHODDEF) */ +- +-#ifndef _SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF +- #define _SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF +-#endif /* !defined(_SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF) */ +- +-#ifndef _SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF +- #define _SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF +-#endif /* !defined(_SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF) */ +- +-#ifndef _SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF +- #define _SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF +-#endif /* !defined(_SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF) */ +- + #ifndef _SSL_RAND_EGD_METHODDEF + #define _SSL_RAND_EGD_METHODDEF + #endif /* !defined(_SSL_RAND_EGD_METHODDEF) */ +--- a/Tools/ssl/multissltests.py ++++ b/Tools/ssl/multissltests.py +@@ -43,8 +43,6 @@ import tarfile + log = logging.getLogger("multissl") + + OPENSSL_OLD_VERSIONS = [ +- "1.0.2u", +- "1.1.0l", + ] + + OPENSSL_RECENT_VERSIONS = [ +@@ -53,11 +51,9 @@ OPENSSL_RECENT_VERSIONS = [ + ] + + LIBRESSL_OLD_VERSIONS = [ +- "2.9.2", + ] + + LIBRESSL_RECENT_VERSIONS = [ +- "3.1.0", + ] + + # store files in ../multissl +--- a/configure ++++ b/configure +@@ -88,6 +88,13 @@ fi + # splitting by setting IFS to empty value.) + IFS=" "" $as_nl" + ++# IFS ++# We need space, tab and new line, in precisely that order. Quoting is ++# there to prevent editors from complaining about space-tab. ++# (If _AS_PATH_WALK were called with IFS unset, it would disable word ++# splitting by setting IFS to empty value.) ++IFS=" "" $as_nl" ++ + # Find who we are. Look in the path if we contain no directory separator. + as_myself= + case $0 in #(( +@@ -17997,7 +18004,6 @@ as_fn_error () + as_fn_exit $as_status + } # as_fn_error + +- + # as_fn_set_status STATUS + # ----------------------- + # Set $? to STATUS, without forking. +@@ -19043,4 +19049,3 @@ if test "$Py_OPT" = 'false' -a "$Py_DEBU + echo "" >&6 + echo "" >&6 + fi +- +--- a/configure.ac ++++ b/configure.ac +@@ -5756,42 +5756,6 @@ ac_includes_default="$save_includes_defa + # Check for usable OpenSSL + AX_CHECK_OPENSSL([have_openssl=yes],[have_openssl=no]) + +-if test "$have_openssl" = yes; then +- AC_MSG_CHECKING([for X509_VERIFY_PARAM_set1_host in libssl]) +- +- save_LIBS="$LIBS" +- save_LDFLAGS="$LDFLAGS" +- save_CPPFLAGS="$CPPFLAGS" +- LDFLAGS="$LDFLAGS $OPENSSL_LDFLAGS" +- LIBS="$OPENSSL_LIBS $LIBS" +- CPPFLAGS="$OPENSSL_INCLUDES $CPPFLAGS" +- +- AC_LINK_IFELSE([AC_LANG_PROGRAM([ +- [#include ] +- ], [ +- [X509_VERIFY_PARAM *p = X509_VERIFY_PARAM_new();] +- [X509_VERIFY_PARAM_set1_host(p, "localhost", 0);] +- [X509_VERIFY_PARAM_set1_ip_asc(p, "127.0.0.1");] +- [X509_VERIFY_PARAM_set_hostflags(p, 0);] +- ]) +- ], +- [ +- ac_cv_has_x509_verify_param_set1_host=yes +- ], +- [ +- ac_cv_has_x509_verify_param_set1_host=no +- ]) +- AC_MSG_RESULT($ac_cv_has_x509_verify_param_set1_host) +- if test "$ac_cv_has_x509_verify_param_set1_host" = "yes"; then +- AC_DEFINE(HAVE_X509_VERIFY_PARAM_SET1_HOST, 1, +- [Define if libssl has X509_VERIFY_PARAM_set1_host and related function]) +- fi +- +- CPPFLAGS="$save_CPPFLAGS" +- LDFLAGS="$save_LDFLAGS" +- LIBS="$save_LIBS" +-fi +- + # ssl module default cipher suite string + AH_TEMPLATE(PY_SSL_DEFAULT_CIPHERS, + [Default cipher suites list for ssl module. +--- a/pyconfig.h.in ++++ b/pyconfig.h.in +@@ -1351,9 +1351,6 @@ + /* Define to 1 if you have the `writev' function. */ + #undef HAVE_WRITEV + +-/* Define if libssl has X509_VERIFY_PARAM_set1_host and related function */ +-#undef HAVE_X509_VERIFY_PARAM_SET1_HOST +- + /* Define if the zlib library has inflateCopy */ + #undef HAVE_ZLIB_COPY + +--- a/setup.py ++++ b/setup.py +@@ -539,10 +539,7 @@ class PyBuildExt(build_ext): + for l in (self.missing, self.failed, self.failed_on_import)): + print() + print("Could not build the ssl module!") +- print("Python requires an OpenSSL 1.0.2 or 1.1 compatible " +- "libssl with X509_VERIFY_PARAM_set1_host().") +- print("LibreSSL 2.6.4 and earlier do not provide the necessary " +- "APIs, https://github.com/libressl-portable/portable/issues/381") ++ print("Python requires a OpenSSL 1.1.1 or newer") + print() + + if os.environ.get("PYTHONSTRICTEXTENSIONBUILD") and (self.failed or self.failed_on_import): +@@ -2346,13 +2343,13 @@ class PyBuildExt(build_ext): + self.missing.extend(['_ssl', '_hashlib']) + return None, None + +- # OpenSSL 1.0.2 uses Kerberos for KRB5 ciphers +- krb5_h = find_file( +- 'krb5.h', self.inc_dirs, +- ['/usr/kerberos/include'] ++ self.add(Extension( ++ '_ssl', ['_ssl.c'], ++ include_dirs=openssl_includes, ++ library_dirs=openssl_libdirs, ++ libraries=openssl_libs, ++ depends=['socketmodule.h', '_ssl/debughelpers.c']) + ) +- if krb5_h: +- ssl_incs.extend(krb5_h) + + if config_vars.get("HAVE_X509_VERIFY_PARAM_SET1_HOST"): + self.add(Extension( +@@ -2367,8 +2364,6 @@ class PyBuildExt(build_ext): + '_ssl_data_111.h', + '_ssl_data_300.h', + ])) +- else: +- self.missing.append('_ssl') + + self.add(Extension('_hashlib', ['_hashopenssl.c'], + depends=['hashlib.h'], diff --git a/CVE-2024-6923-email-hdr-inject.patch b/CVE-2024-6923-email-hdr-inject.patch new file mode 100644 index 0000000..b580c78 --- /dev/null +++ b/CVE-2024-6923-email-hdr-inject.patch @@ -0,0 +1,339 @@ +From f9ddc53ea850fb02d640a9b3263756d43fb6d868 Mon Sep 17 00:00:00 2001 +From: Petr Viktorin +Date: Wed, 31 Jul 2024 00:19:48 +0200 +Subject: [PATCH] [3.9] gh-121650: Encode newlines in headers, and verify + headers are sound (GH-122233) + +GH-GH- Encode header parts that contain newlines + +Per RFC 2047: + +> [...] these encoding schemes allow the +> encoding of arbitrary octet values, mail readers that implement this +> decoding should also ensure that display of the decoded data on the +> recipient's terminal will not cause unwanted side-effects + +It seems that the "quoted-word" scheme is a valid way to include +a newline character in a header value, just like we already allow +undecodable bytes or control characters. +They do need to be properly quoted when serialized to text, though. + +GH-GH- Verify that email headers are well-formed + +This should fail for custom fold() implementations that aren't careful +about newlines. + +(cherry picked from commit 097633981879b3c9de9a1dd120d3aa585ecc2384) + +Co-authored-by: Petr Viktorin +Co-authored-by: Bas Bloemsaat +Co-authored-by: Serhiy Storchaka +--- + Doc/library/email.errors.rst | 6 + Doc/library/email.policy.rst | 18 ++ + Doc/whatsnew/3.9.rst | 12 + + Lib/email/_header_value_parser.py | 12 + + Lib/email/_policybase.py | 8 + + Lib/email/errors.py | 4 + Lib/email/generator.py | 13 +- + Lib/test/test_email/test_generator.py | 62 ++++++++++ + Lib/test/test_email/test_policy.py | 26 ++++ + Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst | 5 + 10 files changed, 162 insertions(+), 4 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst + +--- a/Doc/library/email.errors.rst ++++ b/Doc/library/email.errors.rst +@@ -59,6 +59,12 @@ The following exception classes are defi + :class:`~email.mime.image.MIMEImage`). + + ++.. exception:: HeaderWriteError() ++ ++ Raised when an error occurs when the :mod:`~email.generator` outputs ++ headers. ++ ++ + Here is the list of the defects that the :class:`~email.parser.FeedParser` + can find while parsing messages. Note that the defects are added to the message + where the problem was found, so for example, if a message nested inside a +--- a/Doc/library/email.policy.rst ++++ b/Doc/library/email.policy.rst +@@ -229,6 +229,24 @@ added matters. To illustrate:: + + .. versionadded:: 3.6 + ++ ++ .. attribute:: verify_generated_headers ++ ++ If ``True`` (the default), the generator will raise ++ :exc:`~email.errors.HeaderWriteError` instead of writing a header ++ that is improperly folded or delimited, such that it would ++ be parsed as multiple headers or joined with adjacent data. ++ Such headers can be generated by custom header classes or bugs ++ in the ``email`` module. ++ ++ As it's a security feature, this defaults to ``True`` even in the ++ :class:`~email.policy.Compat32` policy. ++ For backwards compatible, but unsafe, behavior, it must be set to ++ ``False`` explicitly. ++ ++ .. versionadded:: 3.11.10 ++ ++ + The following :class:`Policy` method is intended to be called by code using + the email library to create policy instances with custom settings: + +--- a/Doc/whatsnew/3.9.rst ++++ b/Doc/whatsnew/3.9.rst +@@ -1625,3 +1625,15 @@ ipaddress + + * Fixed ``is_global`` and ``is_private`` behavior in ``IPv4Address``, + ``IPv6Address``, ``IPv4Network`` and ``IPv6Network``. ++ ++email ++----- ++ ++* Headers with embedded newlines are now quoted on output. ++ ++ The :mod:`~email.generator` will now refuse to serialize (write) headers ++ that are improperly folded or delimited, such that they would be parsed as ++ multiple headers or joined with adjacent data. ++ If you need to turn this safety feature off, ++ set :attr:`~email.policy.Policy.verify_generated_headers`. ++ (Contributed by Bas Bloemsaat and Petr Viktorin in :gh:`121650`.) +--- a/Lib/email/_header_value_parser.py ++++ b/Lib/email/_header_value_parser.py +@@ -92,6 +92,8 @@ TOKEN_ENDS = TSPECIALS | WSP + ASPECIALS = TSPECIALS | set("*'%") + ATTRIBUTE_ENDS = ASPECIALS | WSP + EXTENDED_ATTRIBUTE_ENDS = ATTRIBUTE_ENDS - set('%') ++NLSET = {'\n', '\r'} ++SPECIALSNL = SPECIALS | NLSET + + def quote_string(value): + return '"'+str(value).replace('\\', '\\\\').replace('"', r'\"')+'"' +@@ -2778,9 +2780,13 @@ def _refold_parse_tree(parse_tree, *, po + wrap_as_ew_blocked -= 1 + continue + tstr = str(part) +- if part.token_type == 'ptext' and set(tstr) & SPECIALS: +- # Encode if tstr contains special characters. +- want_encoding = True ++ if not want_encoding: ++ if part.token_type == 'ptext': ++ # Encode if tstr contains special characters. ++ want_encoding = not SPECIALSNL.isdisjoint(tstr) ++ else: ++ # Encode if tstr contains newlines. ++ want_encoding = not NLSET.isdisjoint(tstr) + try: + tstr.encode(encoding) + charset = encoding +--- a/Lib/email/_policybase.py ++++ b/Lib/email/_policybase.py +@@ -157,6 +157,13 @@ class Policy(_PolicyBase, metaclass=abc. + message_factory -- the class to use to create new message objects. + If the value is None, the default is Message. + ++ verify_generated_headers ++ -- if true, the generator verifies that each header ++ they are properly folded, so that a parser won't ++ treat it as multiple headers, start-of-body, or ++ part of another header. ++ This is a check against custom Header & fold() ++ implementations. + """ + + raise_on_defect = False +@@ -165,6 +172,7 @@ class Policy(_PolicyBase, metaclass=abc. + max_line_length = 78 + mangle_from_ = False + message_factory = None ++ verify_generated_headers = True + + def handle_defect(self, obj, defect): + """Based on policy, either raise defect or call register_defect. +--- a/Lib/email/errors.py ++++ b/Lib/email/errors.py +@@ -29,6 +29,10 @@ class CharsetError(MessageError): + """An illegal charset was given.""" + + ++class HeaderWriteError(MessageError): ++ """Error while writing headers.""" ++ ++ + # These are parsing defects which the parser was able to work around. + class MessageDefect(ValueError): + """Base class for a message defect.""" +--- a/Lib/email/generator.py ++++ b/Lib/email/generator.py +@@ -14,12 +14,14 @@ import random + from copy import deepcopy + from io import StringIO, BytesIO + from email.utils import _has_surrogates ++from email.errors import HeaderWriteError + + UNDERSCORE = '_' + NL = '\n' # XXX: no longer used by the code below. + + NLCRE = re.compile(r'\r\n|\r|\n') + fcre = re.compile(r'^From ', re.MULTILINE) ++NEWLINE_WITHOUT_FWSP = re.compile(r'\r\n[^ \t]|\r[^ \n\t]|\n[^ \t]') + + + +@@ -223,7 +225,16 @@ class Generator: + + def _write_headers(self, msg): + for h, v in msg.raw_items(): +- self.write(self.policy.fold(h, v)) ++ folded = self.policy.fold(h, v) ++ if self.policy.verify_generated_headers: ++ linesep = self.policy.linesep ++ if not folded.endswith(self.policy.linesep): ++ raise HeaderWriteError( ++ f'folded header does not end with {linesep!r}: {folded!r}') ++ if NEWLINE_WITHOUT_FWSP.search(folded.removesuffix(linesep)): ++ raise HeaderWriteError( ++ f'folded header contains newline: {folded!r}') ++ self.write(folded) + # A blank line always separates headers from body + self.write(self._NL) + +--- a/Lib/test/test_email/test_generator.py ++++ b/Lib/test/test_email/test_generator.py +@@ -6,6 +6,7 @@ from email.message import EmailMessage + from email.generator import Generator, BytesGenerator + from email.headerregistry import Address + from email import policy ++import email.errors + from test.test_email import TestEmailBase, parameterize + + +@@ -216,6 +217,44 @@ class TestGeneratorBase: + g.flatten(msg) + self.assertEqual(s.getvalue(), self.typ(expected)) + ++ def test_keep_encoded_newlines(self): ++ msg = self.msgmaker(self.typ(textwrap.dedent("""\ ++ To: nobody ++ Subject: Bad subject=?UTF-8?Q?=0A?=Bcc: injection@example.com ++ ++ None ++ """))) ++ expected = textwrap.dedent("""\ ++ To: nobody ++ Subject: Bad subject=?UTF-8?Q?=0A?=Bcc: injection@example.com ++ ++ None ++ """) ++ s = self.ioclass() ++ g = self.genclass(s, policy=self.policy.clone(max_line_length=80)) ++ g.flatten(msg) ++ self.assertEqual(s.getvalue(), self.typ(expected)) ++ ++ def test_keep_long_encoded_newlines(self): ++ msg = self.msgmaker(self.typ(textwrap.dedent("""\ ++ To: nobody ++ Subject: Bad subject=?UTF-8?Q?=0A?=Bcc: injection@example.com ++ ++ None ++ """))) ++ expected = textwrap.dedent("""\ ++ To: nobody ++ Subject: Bad subject ++ =?utf-8?q?=0A?=Bcc: ++ injection@example.com ++ ++ None ++ """) ++ s = self.ioclass() ++ g = self.genclass(s, policy=self.policy.clone(max_line_length=30)) ++ g.flatten(msg) ++ self.assertEqual(s.getvalue(), self.typ(expected)) ++ + + class TestGenerator(TestGeneratorBase, TestEmailBase): + +@@ -224,6 +263,29 @@ class TestGenerator(TestGeneratorBase, T + ioclass = io.StringIO + typ = str + ++ def test_verify_generated_headers(self): ++ """gh-121650: by default the generator prevents header injection""" ++ class LiteralHeader(str): ++ name = 'Header' ++ def fold(self, **kwargs): ++ return self ++ ++ for text in ( ++ 'Value\r\nBad Injection\r\n', ++ 'NoNewLine' ++ ): ++ with self.subTest(text=text): ++ message = message_from_string( ++ "Header: Value\r\n\r\nBody", ++ policy=self.policy, ++ ) ++ ++ del message['Header'] ++ message['Header'] = LiteralHeader(text) ++ ++ with self.assertRaises(email.errors.HeaderWriteError): ++ message.as_string() ++ + + class TestBytesGenerator(TestGeneratorBase, TestEmailBase): + +--- a/Lib/test/test_email/test_policy.py ++++ b/Lib/test/test_email/test_policy.py +@@ -26,6 +26,7 @@ class PolicyAPITests(unittest.TestCase): + 'raise_on_defect': False, + 'mangle_from_': True, + 'message_factory': None, ++ 'verify_generated_headers': True, + } + # These default values are the ones set on email.policy.default. + # If any of these defaults change, the docs must be updated. +@@ -277,6 +278,31 @@ class PolicyAPITests(unittest.TestCase): + with self.assertRaises(email.errors.HeaderParseError): + policy.fold("Subject", subject) + ++ def test_verify_generated_headers(self): ++ """Turning protection off allows header injection""" ++ policy = email.policy.default.clone(verify_generated_headers=False) ++ for text in ( ++ 'Header: Value\r\nBad: Injection\r\n', ++ 'Header: NoNewLine' ++ ): ++ with self.subTest(text=text): ++ message = email.message_from_string( ++ "Header: Value\r\n\r\nBody", ++ policy=policy, ++ ) ++ class LiteralHeader(str): ++ name = 'Header' ++ def fold(self, **kwargs): ++ return self ++ ++ del message['Header'] ++ message['Header'] = LiteralHeader(text) ++ ++ self.assertEqual( ++ message.as_string(), ++ f"{text}\nBody", ++ ) ++ + # XXX: Need subclassing tests. + # For adding subclassed objects, make sure the usual rules apply (subclass + # wins), but that the order still works (right overrides left). +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst +@@ -0,0 +1,5 @@ ++:mod:`email` headers with embedded newlines are now quoted on output. The ++:mod:`~email.generator` will now refuse to serialize (write) headers that ++are unsafely folded or delimited; see ++:attr:`~email.policy.Policy.verify_generated_headers`. (Contributed by Bas ++Bloemsaat and Petr Viktorin in :gh:`121650`.) diff --git a/bso1227999-reproducible-builds.patch b/bso1227999-reproducible-builds.patch new file mode 100644 index 0000000..6f1930b --- /dev/null +++ b/bso1227999-reproducible-builds.patch @@ -0,0 +1,37 @@ +From ac2b8869724d7a57d9b5efbdce2f20423214e8bb Mon Sep 17 00:00:00 2001 +From: "Bernhard M. Wiedemann" +Date: Tue, 16 Jul 2024 21:39:33 +0200 +Subject: [PATCH] Allow to override build date with SOURCE_DATE_EPOCH + +to make builds reproducible. +See https://reproducible-builds.org/ for why this is good +and https://reproducible-builds.org/specs/source-date-epoch/ +for the definition of this variable. +--- + Doc/conf.py | 3 ++- + Doc/library/functions.rst | 2 +- + 2 files changed, 3 insertions(+), 2 deletions(-) + +--- a/Doc/conf.py ++++ b/Doc/conf.py +@@ -80,7 +80,8 @@ html_short_title = '%s Documentation' % + + # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, + # using the given strftime format. +-html_last_updated_fmt = '%b %d, %Y' ++html_time = int(os.environ.get('SOURCE_DATE_EPOCH', time.time())) ++html_last_updated_fmt = time.strftime('%b %d, %Y (%H:%M UTC)', time.gmtime(html_time)) + + # Path to find HTML templates. + templates_path = ['tools/templates'] +--- a/Doc/library/functions.rst ++++ b/Doc/library/functions.rst +@@ -1254,7 +1254,7 @@ are always available. They are listed h + (where :func:`open` is declared), :mod:`os`, :mod:`os.path`, :mod:`tempfile`, + and :mod:`shutil`. + +- .. audit-event:: open file,mode,flags open ++ .. audit-event:: open path,mode,flags open + + The ``mode`` and ``flags`` arguments may have been modified or inferred from + the original call. diff --git a/python39.changes b/python39.changes index e5d58e1..af26b44 100644 --- a/python39.changes +++ b/python39.changes @@ -1,3 +1,18 @@ +------------------------------------------------------------------- +Wed Aug 7 12:12:42 UTC 2024 - Matej Cepl + +- Add CVE-2024-6923-email-hdr-inject.patch to prevent email + header injection due to unquoted newlines (bsc#1228780, + CVE-2024-6923). +- Adding bso1227999-reproducible-builds.patch fixing bsc#1227999 + adding reproducibility patches from gh#python/cpython!121872 + and gh#python/cpython!121883. +- Add CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch removing + support for anything but OpenSSL 1.1.1 or newer (bsc#1227233, + CVE-2024-5642). +- %{profileopt} variable is set according to the variable + %{do_profiling} (bsc#1227999) + ------------------------------------------------------------------- Mon Jul 22 21:20:54 UTC 2024 - Matej Cepl diff --git a/python39.spec b/python39.spec index 7291550..876b50c 100644 --- a/python39.spec +++ b/python39.spec @@ -36,6 +36,12 @@ %bcond_without general %endif +%if 0%{?do_profiling} +%bcond_without profileopt +%else +%bcond_with profileopt +%endif + %define python_pkg_name python39 %if "%{python_pkg_name}" == "%{primary_python}" %define primary_interpreter 1 @@ -187,6 +193,15 @@ Patch44: CVE-2024-0397-memrace_ssl.SSLContext_cert_store.patch # PATCH-FIX-UPSTREAM CVE-2024-4032-private-IP-addrs.patch bsc#1226448 mcepl@suse.com # rearrange definition of private v global IP addresses Patch45: CVE-2024-4032-private-IP-addrs.patch +# PATCH-FIX-UPSTREAM bso1227999-reproducible-builds.patch bsc#1227999 mcepl@suse.com +# reproducibility patches +Patch46: bso1227999-reproducible-builds.patch +# PATCH-FIX-UPSTREAM CVE-2024-6923-email-hdr-inject.patch bsc#1228780 mcepl@suse.com +# prevent email header injection, patch from gh#python/cpython!122608 +Patch47: CVE-2024-6923-email-hdr-inject.patch +# PATCH-FIX-UPSTREAM CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch bsc#1227233 mcepl@suse.com +# Remove for support for anything but OpenSSL 1.1.1 or newer +Patch48: CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -447,12 +462,15 @@ other applications. %patch -P 39 -p1 %patch -P 40 -p1 %if 0%{?sle_version} && 0%{?sle_version} <= 150500 -%patch -P 41 -p1 +%patch -p1 -P 41 %endif -%patch -P 42 -p1 -%patch -P 43 -p1 -%patch -P 44 -p1 -%patch -P 45 -p1 +%patch -p1 -P 42 +%patch -p1 -P 43 +%patch -p1 -P 44 +%patch -p1 -P 45 +%patch -p1 -P 46 +%patch -p1 -P 47 +%patch -p1 -P 48 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac