Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CVE-2024-0397 Fix for the problem, backported from Python3.8 gh-114572 #53

Merged
merged 1 commit into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
:meth:`ssl.SSLContext.cert_store_stats` and
:meth:`ssl.SSLContext.get_ca_certs` now correctly lock access to the
certificate store, when the :class:`ssl.SSLContext` is shared across
multiple threads.
93 changes: 91 additions & 2 deletions Modules/_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ struct py_ssl_library_code {
# define PY_OPENSSL_1_1_API 1
#endif

#if (OPENSSL_VERSION_NUMBER >= 0x30300000L) && !defined(LIBRESSL_VERSION_NUMBER)
# define OPENSSL_VERSION_3_3 1
#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
Expand Down Expand Up @@ -161,6 +165,16 @@ struct py_ssl_library_code {
#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
#endif

/* OpenSSL 3.3 added the X509_STORE_get1_objects API */
#ifdef OPENSSL_VERSION_3_3
#define HAVE_OPENSSL_X509_STORE_GET1_OBJECTS 1
#endif

#if defined(OPENSSL_VERSION_1_1) && !defined(OPENSSL_NO_SSL2)
#define OPENSSL_NO_SSL2
#endif
Expand Down Expand Up @@ -3504,7 +3518,15 @@ cert_store_stats(PySSLContext *self)
int x509 = 0, crl = 0, ca = 0, i;

store = SSL_CTX_get_cert_store(self->ctx);
objs = X509_STORE_get0_objects(store);
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
objs = X509_STORE_get1_objects(store);
if (objs == NULL) {
PyErr_SetString(PyExc_MemoryError, "failed to query cert store");
return NULL;
}
#else
objs = X509_STORE_get0_objects(store);
#endif
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
obj = sk_X509_OBJECT_value(objs, i);
switch (X509_OBJECT_get_type(obj)) {
Expand All @@ -3521,9 +3543,15 @@ cert_store_stats(PySSLContext *self)
/* Ignore X509_LU_FAIL, X509_LU_RETRY, X509_LU_PKEY.
* As far as I can tell they are internal states and never
* stored in a cert store */
/* Ignore enrecognized types */
break;
}
}

#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
#endif

return Py_BuildValue("{sisisi}", "x509", x509, "crl", crl,
"x509_ca", ca);
}
Expand Down Expand Up @@ -3558,9 +3586,16 @@ get_ca_certs(PySSLContext *self, PyObject *args, PyObject *kwds)
if ((rlist = PyList_New(0)) == NULL) {
return NULL;
}

store = SSL_CTX_get_cert_store(self->ctx);
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
objs = X509_STORE_get1_objects(store);
if (objs == NULL) {
PyErr_SetString(PyExc_MemoryError, "failed to query cert store");
return NULL;
}
#else
objs = X509_STORE_get0_objects(store);
#endif
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
X509_OBJECT *obj;
X509 *cert;
Expand Down Expand Up @@ -3588,9 +3623,15 @@ get_ca_certs(PySSLContext *self, PyObject *args, PyObject *kwds)
}
Py_CLEAR(ci);
}
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
#endif
return rlist;

error:
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
#endif
Py_XDECREF(ci);
Py_XDECREF(rlist);
return NULL;
Expand Down Expand Up @@ -4642,3 +4683,51 @@ init_ssl(void)
return;
}

/* Shim of X509_STORE_get1_objects API from OpenSSL 3.3
* Only available with the X509_STORE_lock() API */
#if defined(HAVE_OPENSSL_X509_STORE_LOCK) && !defined(OPENSSL_VERSION_3_3)
#define HAVE_OPENSSL_X509_STORE_GET1_OBJECTS 1

static X509_OBJECT *x509_object_dup(const X509_OBJECT *obj)
{
int ok;
X509_OBJECT *ret = X509_OBJECT_new();
if (ret == NULL) {
return NULL;
}
switch (X509_OBJECT_get_type(obj)) {
case X509_LU_X509:
ok = X509_OBJECT_set1_X509(ret, X509_OBJECT_get0_X509(obj));
break;
case X509_LU_CRL:
/* X509_OBJECT_get0_X509_CRL was not const-correct prior to 3.0.*/
ok = X509_OBJECT_set1_X509_CRL(
ret, X509_OBJECT_get0_X509_CRL((X509_OBJECT *)obj));
break;
default:
/* We cannot duplicate unrecognized types in a polyfill, but it is
* safe to leave an empty object. The caller will ignore it. */
ok = 1;
break;
}
if (!ok) {
X509_OBJECT_free(ret);
return NULL;
}
return ret;
}

static STACK_OF(X509_OBJECT) *
X509_STORE_get1_objects(X509_STORE *store)
{
STACK_OF(X509_OBJECT) *ret;
if (!X509_STORE_lock(store)) {
return NULL;
}
ret = sk_X509_OBJECT_deep_copy(X509_STORE_get0_objects(store),
x509_object_dup, X509_OBJECT_free);
X509_STORE_unlock(store);
return ret;
}
#endif