Skip to content

Commit b228655

Browse files
authored
[3.9] gh-114572: Fix locking in cert_store_stats and get_ca_certs (#118109)
1 parent 22adf29 commit b228655

File tree

2 files changed

+92
-3
lines changed

2 files changed

+92
-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: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ extern const SSL_METHOD *TLSv1_2_method(void);
166166
# define PY_OPENSSL_1_1_API 1
167167
#endif
168168

169+
#if (OPENSSL_VERSION_NUMBER >= 0x30300000L) && !defined(LIBRESSL_VERSION_NUMBER)
170+
# define OPENSSL_VERSION_3_3 1
171+
#endif
172+
169173
/* SNI support (client- and server-side) appeared in OpenSSL 1.0.0 and 0.9.8f
170174
* This includes the SSL_set_SSL_CTX() function.
171175
*/
@@ -210,6 +214,16 @@ extern const SSL_METHOD *TLSv1_2_method(void);
210214
#define HAVE_OPENSSL_CRYPTO_LOCK
211215
#endif
212216

217+
/* OpenSSL 1.1+ allows locking X509_STORE, 1.0.2 doesn't. */
218+
#ifdef OPENSSL_VERSION_1_1
219+
#define HAVE_OPENSSL_X509_STORE_LOCK
220+
#endif
221+
222+
/* OpenSSL 3.3 added the X509_STORE_get1_objects API */
223+
#ifdef OPENSSL_VERSION_3_3
224+
#define HAVE_OPENSSL_X509_STORE_GET1_OBJECTS 1
225+
#endif
226+
213227
#if defined(OPENSSL_VERSION_1_1) && !defined(OPENSSL_NO_SSL2)
214228
#define OPENSSL_NO_SSL2
215229
#endif
@@ -4675,6 +4689,54 @@ set_sni_callback(PySSLContext *self, PyObject *arg, void *c)
46754689
#endif
46764690
}
46774691

4692+
/* Shim of X509_STORE_get1_objects API from OpenSSL 3.3
4693+
* Only available with the X509_STORE_lock() API */
4694+
#if defined(HAVE_OPENSSL_X509_STORE_LOCK) && !defined(OPENSSL_VERSION_3_3)
4695+
#define HAVE_OPENSSL_X509_STORE_GET1_OBJECTS 1
4696+
4697+
static X509_OBJECT *x509_object_dup(const X509_OBJECT *obj)
4698+
{
4699+
int ok;
4700+
X509_OBJECT *ret = X509_OBJECT_new();
4701+
if (ret == NULL) {
4702+
return NULL;
4703+
}
4704+
switch (X509_OBJECT_get_type(obj)) {
4705+
case X509_LU_X509:
4706+
ok = X509_OBJECT_set1_X509(ret, X509_OBJECT_get0_X509(obj));
4707+
break;
4708+
case X509_LU_CRL:
4709+
/* X509_OBJECT_get0_X509_CRL was not const-correct prior to 3.0.*/
4710+
ok = X509_OBJECT_set1_X509_CRL(
4711+
ret, X509_OBJECT_get0_X509_CRL((X509_OBJECT *)obj));
4712+
break;
4713+
default:
4714+
/* We cannot duplicate unrecognized types in a polyfill, but it is
4715+
* safe to leave an empty object. The caller will ignore it. */
4716+
ok = 1;
4717+
break;
4718+
}
4719+
if (!ok) {
4720+
X509_OBJECT_free(ret);
4721+
return NULL;
4722+
}
4723+
return ret;
4724+
}
4725+
4726+
static STACK_OF(X509_OBJECT) *
4727+
X509_STORE_get1_objects(X509_STORE *store)
4728+
{
4729+
STACK_OF(X509_OBJECT) *ret;
4730+
if (!X509_STORE_lock(store)) {
4731+
return NULL;
4732+
}
4733+
ret = sk_X509_OBJECT_deep_copy(X509_STORE_get0_objects(store),
4734+
x509_object_dup, X509_OBJECT_free);
4735+
X509_STORE_unlock(store);
4736+
return ret;
4737+
}
4738+
#endif
4739+
46784740
PyDoc_STRVAR(PySSLContext_sni_callback_doc,
46794741
"Set a callback that will be called when a server name is provided by the SSL/TLS client in the SNI extension.\n\
46804742
\n\
@@ -4704,7 +4766,15 @@ _ssl__SSLContext_cert_store_stats_impl(PySSLContext *self)
47044766
int x509 = 0, crl = 0, ca = 0, i;
47054767

47064768
store = SSL_CTX_get_cert_store(self->ctx);
4769+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
4770+
objs = X509_STORE_get1_objects(store);
4771+
if (objs == NULL) {
4772+
PyErr_SetString(PyExc_MemoryError, "failed to query cert store");
4773+
return NULL;
4774+
}
4775+
#else
47074776
objs = X509_STORE_get0_objects(store);
4777+
#endif
47084778
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
47094779
obj = sk_X509_OBJECT_value(objs, i);
47104780
switch (X509_OBJECT_get_type(obj)) {
@@ -4718,12 +4788,13 @@ _ssl__SSLContext_cert_store_stats_impl(PySSLContext *self)
47184788
crl++;
47194789
break;
47204790
default:
4721-
/* Ignore X509_LU_FAIL, X509_LU_RETRY, X509_LU_PKEY.
4722-
* As far as I can tell they are internal states and never
4723-
* stored in a cert store */
4791+
/* Ignore unrecognized types. */
47244792
break;
47254793
}
47264794
}
4795+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
4796+
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
4797+
#endif
47274798
return Py_BuildValue("{sisisi}", "x509", x509, "crl", crl,
47284799
"x509_ca", ca);
47294800
}
@@ -4755,7 +4826,15 @@ _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form)
47554826
}
47564827

47574828
store = SSL_CTX_get_cert_store(self->ctx);
4829+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
4830+
objs = X509_STORE_get1_objects(store);
4831+
if (objs == NULL) {
4832+
PyErr_SetString(PyExc_MemoryError, "failed to query cert store");
4833+
return NULL;
4834+
}
4835+
#else
47584836
objs = X509_STORE_get0_objects(store);
4837+
#endif
47594838
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
47604839
X509_OBJECT *obj;
47614840
X509 *cert;
@@ -4783,9 +4862,15 @@ _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form)
47834862
}
47844863
Py_CLEAR(ci);
47854864
}
4865+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
4866+
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
4867+
#endif
47864868
return rlist;
47874869

47884870
error:
4871+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
4872+
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
4873+
#endif
47894874
Py_XDECREF(ci);
47904875
Py_XDECREF(rlist);
47914876
return NULL;

0 commit comments

Comments
 (0)