Skip to content

Commit 29c9728

Browse files
authored
[3.8] gh-114572: Fix locking in cert_store_stats and get_ca_certs (#118442)
(cherry picked from commit 732c7d5)
1 parent 895f7e2 commit 29c9728

File tree

2 files changed

+92
-3
lines changed

2 files changed

+92
-3
lines changed
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

+88-3
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ extern const SSL_METHOD *TLSv1_2_method(void);
168168
# define PY_OPENSSL_1_1_API 1
169169
#endif
170170

171+
#if (OPENSSL_VERSION_NUMBER >= 0x30300000L) && !defined(LIBRESSL_VERSION_NUMBER)
172+
# define OPENSSL_VERSION_3_3 1
173+
#endif
174+
171175
/* SNI support (client- and server-side) appeared in OpenSSL 1.0.0 and 0.9.8f
172176
* This includes the SSL_set_SSL_CTX() function.
173177
*/
@@ -212,6 +216,16 @@ extern const SSL_METHOD *TLSv1_2_method(void);
212216
#define HAVE_OPENSSL_CRYPTO_LOCK
213217
#endif
214218

219+
/* OpenSSL 1.1+ allows locking X509_STORE, 1.0.2 doesn't. */
220+
#ifdef OPENSSL_VERSION_1_1
221+
#define HAVE_OPENSSL_X509_STORE_LOCK
222+
#endif
223+
224+
/* OpenSSL 3.3 added the X509_STORE_get1_objects API */
225+
#ifdef OPENSSL_VERSION_3_3
226+
#define HAVE_OPENSSL_X509_STORE_GET1_OBJECTS 1
227+
#endif
228+
215229
#if defined(OPENSSL_VERSION_1_1) && !defined(OPENSSL_NO_SSL2)
216230
#define OPENSSL_NO_SSL2
217231
#endif
@@ -4678,6 +4692,54 @@ set_sni_callback(PySSLContext *self, PyObject *arg, void *c)
46784692
#endif
46794693
}
46804694

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

47094771
store = SSL_CTX_get_cert_store(self->ctx);
4772+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
4773+
objs = X509_STORE_get1_objects(store);
4774+
if (objs == NULL) {
4775+
PyErr_SetString(PyExc_MemoryError, "failed to query cert store");
4776+
return NULL;
4777+
}
4778+
#else
47104779
objs = X509_STORE_get0_objects(store);
4780+
#endif
47114781
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
47124782
obj = sk_X509_OBJECT_value(objs, i);
47134783
switch (X509_OBJECT_get_type(obj)) {
@@ -4721,12 +4791,13 @@ _ssl__SSLContext_cert_store_stats_impl(PySSLContext *self)
47214791
crl++;
47224792
break;
47234793
default:
4724-
/* Ignore X509_LU_FAIL, X509_LU_RETRY, X509_LU_PKEY.
4725-
* As far as I can tell they are internal states and never
4726-
* stored in a cert store */
4794+
/* Ignore unrecognized types. */
47274795
break;
47284796
}
47294797
}
4798+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
4799+
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
4800+
#endif
47304801
return Py_BuildValue("{sisisi}", "x509", x509, "crl", crl,
47314802
"x509_ca", ca);
47324803
}
@@ -4758,7 +4829,15 @@ _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form)
47584829
}
47594830

47604831
store = SSL_CTX_get_cert_store(self->ctx);
4832+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
4833+
objs = X509_STORE_get1_objects(store);
4834+
if (objs == NULL) {
4835+
PyErr_SetString(PyExc_MemoryError, "failed to query cert store");
4836+
return NULL;
4837+
}
4838+
#else
47614839
objs = X509_STORE_get0_objects(store);
4840+
#endif
47624841
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
47634842
X509_OBJECT *obj;
47644843
X509 *cert;
@@ -4786,9 +4865,15 @@ _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form)
47864865
}
47874866
Py_CLEAR(ci);
47884867
}
4868+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
4869+
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
4870+
#endif
47894871
return rlist;
47904872

47914873
error:
4874+
#if HAVE_OPENSSL_X509_STORE_GET1_OBJECTS
4875+
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
4876+
#endif
47924877
Py_XDECREF(ci);
47934878
Py_XDECREF(rlist);
47944879
return NULL;

0 commit comments

Comments
 (0)