Skip to content

Commit ea1d3ab

Browse files
rickpriceicanhasmath
authored andcommitted
CVE-2024-0397 Fix locking in cert_store_stats and get_ca_certs
Backported from : [3.8] pythongh-114572: Fix locking in cert_store_stats and get_ca_certs python#118442
1 parent 76f11e1 commit ea1d3ab

File tree

2 files changed

+95
-2
lines changed

2 files changed

+95
-2
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: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ struct py_ssl_library_code {
106106
# define PY_OPENSSL_1_1_API 1
107107
#endif
108108

109+
#if (OPENSSL_VERSION_NUMBER >= 0x30300000L) && !defined(LIBRESSL_VERSION_NUMBER)
110+
# define OPENSSL_VERSION_3_3 1
111+
#endif
112+
109113
/* LibreSSL 2.7.0 provides necessary OpenSSL 1.1.0 APIs */
110114
#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x2070000fL
111115
# define PY_OPENSSL_1_1_API 1
@@ -161,6 +165,16 @@ struct py_ssl_library_code {
161165
#define HAVE_OPENSSL_CRYPTO_LOCK
162166
#endif
163167

168+
/* OpenSSL 1.1+ allows locking X509_STORE, 1.0.2 doesn't. */
169+
#ifdef OPENSSL_VERSION_1_1
170+
#define HAVE_OPENSSL_X509_STORE_LOCK
171+
#endif
172+
173+
/* OpenSSL 3.3 added the X509_STORE_get1_objects API */
174+
#ifdef OPENSSL_VERSION_3_3
175+
#define HAVE_OPENSSL_X509_STORE_GET1_OBJECTS 1
176+
#endif
177+
164178
#if defined(OPENSSL_VERSION_1_1) && !defined(OPENSSL_NO_SSL2)
165179
#define OPENSSL_NO_SSL2
166180
#endif
@@ -3504,7 +3518,15 @@ cert_store_stats(PySSLContext *self)
35043518
int x509 = 0, crl = 0, ca = 0, i;
35053519

35063520
store = SSL_CTX_get_cert_store(self->ctx);
3507-
objs = X509_STORE_get0_objects(store);
3521+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
3522+
objs = X509_STORE_get1_objects(store);
3523+
if (objs == NULL) {
3524+
PyErr_SetString(PyExc_MemoryError, "failed to query cert store");
3525+
return NULL;
3526+
}
3527+
#else
3528+
objs = X509_STORE_get0_objects(store);
3529+
#endif
35083530
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
35093531
obj = sk_X509_OBJECT_value(objs, i);
35103532
switch (X509_OBJECT_get_type(obj)) {
@@ -3521,9 +3543,15 @@ cert_store_stats(PySSLContext *self)
35213543
/* Ignore X509_LU_FAIL, X509_LU_RETRY, X509_LU_PKEY.
35223544
* As far as I can tell they are internal states and never
35233545
* stored in a cert store */
3546+
/* Ignore enrecognized types */
35243547
break;
35253548
}
35263549
}
3550+
3551+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
3552+
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
3553+
#endif
3554+
35273555
return Py_BuildValue("{sisisi}", "x509", x509, "crl", crl,
35283556
"x509_ca", ca);
35293557
}
@@ -3558,9 +3586,16 @@ get_ca_certs(PySSLContext *self, PyObject *args, PyObject *kwds)
35583586
if ((rlist = PyList_New(0)) == NULL) {
35593587
return NULL;
35603588
}
3561-
35623589
store = SSL_CTX_get_cert_store(self->ctx);
3590+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
3591+
objs = X509_STORE_get1_objects(store);
3592+
if (objs == NULL) {
3593+
PyErr_SetString(PyExc_MemoryError, "failed to query cert store");
3594+
return NULL;
3595+
}
3596+
#else
35633597
objs = X509_STORE_get0_objects(store);
3598+
#endif
35643599
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
35653600
X509_OBJECT *obj;
35663601
X509 *cert;
@@ -3588,9 +3623,15 @@ get_ca_certs(PySSLContext *self, PyObject *args, PyObject *kwds)
35883623
}
35893624
Py_CLEAR(ci);
35903625
}
3626+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
3627+
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
3628+
#endif
35913629
return rlist;
35923630

35933631
error:
3632+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
3633+
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
3634+
#endif
35943635
Py_XDECREF(ci);
35953636
Py_XDECREF(rlist);
35963637
return NULL;
@@ -4642,3 +4683,51 @@ init_ssl(void)
46424683
return;
46434684
}
46444685

4686+
/* Shim of X509_STORE_get1_objects API from OpenSSL 3.3
4687+
* Only available with the X509_STORE_lock() API */
4688+
#if defined(HAVE_OPENSSL_X509_STORE_LOCK) && !defined(OPENSSL_VERSION_3_3)
4689+
#define HAVE_OPENSSL_X509_STORE_GET1_OBJECTS 1
4690+
4691+
static X509_OBJECT *x509_object_dup(const X509_OBJECT *obj)
4692+
{
4693+
int ok;
4694+
X509_OBJECT *ret = X509_OBJECT_new();
4695+
if (ret == NULL) {
4696+
return NULL;
4697+
}
4698+
switch (X509_OBJECT_get_type(obj)) {
4699+
case X509_LU_X509:
4700+
ok = X509_OBJECT_set1_X509(ret, X509_OBJECT_get0_X509(obj));
4701+
break;
4702+
case X509_LU_CRL:
4703+
/* X509_OBJECT_get0_X509_CRL was not const-correct prior to 3.0.*/
4704+
ok = X509_OBJECT_set1_X509_CRL(
4705+
ret, X509_OBJECT_get0_X509_CRL((X509_OBJECT *)obj));
4706+
break;
4707+
default:
4708+
/* We cannot duplicate unrecognized types in a polyfill, but it is
4709+
* safe to leave an empty object. The caller will ignore it. */
4710+
ok = 1;
4711+
break;
4712+
}
4713+
if (!ok) {
4714+
X509_OBJECT_free(ret);
4715+
return NULL;
4716+
}
4717+
return ret;
4718+
}
4719+
4720+
static STACK_OF(X509_OBJECT) *
4721+
X509_STORE_get1_objects(X509_STORE *store)
4722+
{
4723+
STACK_OF(X509_OBJECT) *ret;
4724+
if (!X509_STORE_lock(store)) {
4725+
return NULL;
4726+
}
4727+
ret = sk_X509_OBJECT_deep_copy(X509_STORE_get0_objects(store),
4728+
x509_object_dup, X509_OBJECT_free);
4729+
X509_STORE_unlock(store);
4730+
return ret;
4731+
}
4732+
#endif
4733+

0 commit comments

Comments
 (0)