Skip to content

Commit e701181

Browse files
sethmlarsonmcepl
authored andcommitted
[CVE-2024-0397] Fix locking in cert_store_stats and get_ca_certs
cert_store_stats and get_ca_certs query the SSLContext's X509_STORE with X509_STORE_get0_objects, but reading the result requires a lock. See gh#openssl/openssl#23224 for details. Instead, use X509_STORE_get1_objects, newly added in that PR. X509_STORE_get1_objects does not exist in current OpenSSLs, but we can polyfill it with X509_STORE_lock and X509_STORE_unlock. From-PR: gh#python/cpython!114573 Fixes: gh#python#114572 Fixes: bsc#1226447 (CVE-2024-0397) Patch: CVE-2024-0397-memrace_ssl.SSLContext_cert_store.patch
1 parent 878403d commit e701181

File tree

2 files changed

+99
-3
lines changed

2 files changed

+99
-3
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:meth:`ssl.SSLContext.cert_store_stats` and
2+
:meth:`ssl.SSLContext.get_ca_certs` now correctly lock access to the
3+
certificate store, when the :class:`ssl.SSLContext` is shared across
4+
multiple threads.

Modules/_ssl.c

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ struct py_ssl_library_code {
124124
# define HAVE_TLSv1_2 0
125125
#endif
126126

127+
#if (OPENSSL_VERSION_NUMBER >= 0x30300000L) && !defined(LIBRESSL_VERSION_NUMBER)
128+
# define OPENSSL_VERSION_3_3 1
129+
#endif
130+
127131
/* SNI support (client- and server-side) appeared in OpenSSL 1.0.0 and 0.9.8f
128132
* This includes the SSL_set_SSL_CTX() function.
129133
*/
@@ -164,6 +168,16 @@ struct py_ssl_library_code {
164168
#define HAVE_OPENSSL_CRYPTO_LOCK
165169
#endif
166170

171+
/* OpenSSL 1.1+ allows locking X509_STORE, 1.0.2 doesn't. */
172+
#ifdef OPENSSL_VERSION_1_1
173+
#define HAVE_OPENSSL_X509_STORE_LOCK
174+
#endif
175+
176+
/* OpenSSL 3.3 added the X509_STORE_get1_objects API */
177+
#ifdef OPENSSL_VERSION_3_3
178+
#define HAVE_OPENSSL_X509_STORE_GET1_OBJECTS 1
179+
#endif
180+
167181
#if defined(OPENSSL_VERSION_1_1) && !defined(OPENSSL_NO_SSL2)
168182
#define OPENSSL_NO_SSL2
169183
#endif
@@ -4096,6 +4110,61 @@ _ssl__SSLContext_set_servername_callback(PySSLContext *self, PyObject *cb)
40964110
#endif
40974111
}
40984112

4113+
/* Shim of X509_STORE_get1_objects API from OpenSSL 3.3
4114+
* Only available with the X509_STORE_lock() API */
4115+
#if defined(HAVE_OPENSSL_X509_STORE_LOCK) && !defined(OPENSSL_VERSION_3_3)
4116+
#define HAVE_OPENSSL_X509_STORE_GET1_OBJECTS 1
4117+
4118+
static X509_OBJECT *x509_object_dup(const X509_OBJECT *obj)
4119+
{
4120+
int ok;
4121+
X509_OBJECT *ret = X509_OBJECT_new();
4122+
if (ret == NULL) {
4123+
return NULL;
4124+
}
4125+
switch (X509_OBJECT_get_type(obj)) {
4126+
case X509_LU_X509:
4127+
ok = X509_OBJECT_set1_X509(ret, X509_OBJECT_get0_X509(obj));
4128+
break;
4129+
case X509_LU_CRL:
4130+
/* X509_OBJECT_get0_X509_CRL was not const-correct prior to 3.0.*/
4131+
ok = X509_OBJECT_set1_X509_CRL(
4132+
ret, X509_OBJECT_get0_X509_CRL((X509_OBJECT *)obj));
4133+
break;
4134+
default:
4135+
/* We cannot duplicate unrecognized types in a polyfill, but it is
4136+
* safe to leave an empty object. The caller will ignore it. */
4137+
ok = 1;
4138+
break;
4139+
}
4140+
if (!ok) {
4141+
X509_OBJECT_free(ret);
4142+
return NULL;
4143+
}
4144+
return ret;
4145+
}
4146+
4147+
static STACK_OF(X509_OBJECT) *
4148+
X509_STORE_get1_objects(X509_STORE *store)
4149+
{
4150+
STACK_OF(X509_OBJECT) *ret;
4151+
if (!X509_STORE_lock(store)) {
4152+
return NULL;
4153+
}
4154+
ret = sk_X509_OBJECT_deep_copy(X509_STORE_get0_objects(store),
4155+
x509_object_dup, X509_OBJECT_free);
4156+
X509_STORE_unlock(store);
4157+
return ret;
4158+
}
4159+
#endif
4160+
4161+
PyDoc_STRVAR(PySSLContext_sni_callback_doc,
4162+
"Set a callback that will be called when a server name is provided by the SSL/TLS client in the SNI extension.\n\
4163+
\n\
4164+
If the argument is None then the callback is disabled. The method is called\n\
4165+
with the SSLSocket, the server name as a string, and the SSLContext object.\n\
4166+
See RFC 6066 for details of the SNI extension.");
4167+
40994168
/*[clinic input]
41004169
_ssl._SSLContext.cert_store_stats
41014170
@@ -4118,7 +4187,15 @@ _ssl__SSLContext_cert_store_stats_impl(PySSLContext *self)
41184187
int x509 = 0, crl = 0, ca = 0, i;
41194188

41204189
store = SSL_CTX_get_cert_store(self->ctx);
4190+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
4191+
objs = X509_STORE_get1_objects(store);
4192+
if (objs == NULL) {
4193+
PyErr_SetString(PyExc_MemoryError, "failed to query cert store");
4194+
return NULL;
4195+
}
4196+
#else
41214197
objs = X509_STORE_get0_objects(store);
4198+
#endif
41224199
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
41234200
obj = sk_X509_OBJECT_value(objs, i);
41244201
switch (X509_OBJECT_get_type(obj)) {
@@ -4132,12 +4209,13 @@ _ssl__SSLContext_cert_store_stats_impl(PySSLContext *self)
41324209
crl++;
41334210
break;
41344211
default:
4135-
/* Ignore X509_LU_FAIL, X509_LU_RETRY, X509_LU_PKEY.
4136-
* As far as I can tell they are internal states and never
4137-
* stored in a cert store */
4212+
/* Ignore unrecognized types. */
41384213
break;
41394214
}
41404215
}
4216+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
4217+
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
4218+
#endif
41414219
return Py_BuildValue("{sisisi}", "x509", x509, "crl", crl,
41424220
"x509_ca", ca);
41434221
}
@@ -4169,7 +4247,15 @@ _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form)
41694247
}
41704248

41714249
store = SSL_CTX_get_cert_store(self->ctx);
4250+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
4251+
objs = X509_STORE_get1_objects(store);
4252+
if (objs == NULL) {
4253+
PyErr_SetString(PyExc_MemoryError, "failed to query cert store");
4254+
return NULL;
4255+
}
4256+
#else
41724257
objs = X509_STORE_get0_objects(store);
4258+
#endif
41734259
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
41744260
X509_OBJECT *obj;
41754261
X509 *cert;
@@ -4197,9 +4283,15 @@ _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form)
41974283
}
41984284
Py_CLEAR(ci);
41994285
}
4286+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
4287+
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
4288+
#endif
42004289
return rlist;
42014290

42024291
error:
4292+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
4293+
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
4294+
#endif
42034295
Py_XDECREF(ci);
42044296
Py_XDECREF(rlist);
42054297
return NULL;

0 commit comments

Comments
 (0)