Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:joemonk
python39
CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch of Package python39
From 3ddf7fa83b19463e710b75ae6e8a28831e575f3d Mon Sep 17 00:00:00 2001 From: Christian Heimes <christian@python.org> 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 <openssl/x509_vfy.h>] - ], [ - [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'],
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor