Skip to content

Commit bce6931

Browse files
authored
gh-114572: Fix locking in cert_store_stats and get_ca_certs (#114573)
* gh-114572: 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 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. * Work around const-correctness problem * Add missing X509_STORE_get1_objects failure check * Add blurb
1 parent 58cb634 commit bce6931

File tree

2 files changed

+64
-5
lines changed

2 files changed

+64
-5
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

+60-5
Original file line numberDiff line numberDiff line change
@@ -4553,6 +4553,50 @@ set_sni_callback(PySSLContext *self, PyObject *arg, void *c)
45534553
return 0;
45544554
}
45554555

4556+
#if OPENSSL_VERSION_NUMBER < 0x30300000L
4557+
static X509_OBJECT *x509_object_dup(const X509_OBJECT *obj)
4558+
{
4559+
int ok;
4560+
X509_OBJECT *ret = X509_OBJECT_new();
4561+
if (ret == NULL) {
4562+
return NULL;
4563+
}
4564+
switch (X509_OBJECT_get_type(obj)) {
4565+
case X509_LU_X509:
4566+
ok = X509_OBJECT_set1_X509(ret, X509_OBJECT_get0_X509(obj));
4567+
break;
4568+
case X509_LU_CRL:
4569+
/* X509_OBJECT_get0_X509_CRL was not const-correct prior to 3.0.*/
4570+
ok = X509_OBJECT_set1_X509_CRL(
4571+
ret, X509_OBJECT_get0_X509_CRL((X509_OBJECT *)obj));
4572+
break;
4573+
default:
4574+
/* We cannot duplicate unrecognized types in a polyfill, but it is
4575+
* safe to leave an empty object. The caller will ignore it. */
4576+
ok = 1;
4577+
break;
4578+
}
4579+
if (!ok) {
4580+
X509_OBJECT_free(ret);
4581+
return NULL;
4582+
}
4583+
return ret;
4584+
}
4585+
4586+
static STACK_OF(X509_OBJECT) *
4587+
X509_STORE_get1_objects(X509_STORE *store)
4588+
{
4589+
STACK_OF(X509_OBJECT) *ret;
4590+
if (!X509_STORE_lock(store)) {
4591+
return NULL;
4592+
}
4593+
ret = sk_X509_OBJECT_deep_copy(X509_STORE_get0_objects(store),
4594+
x509_object_dup, X509_OBJECT_free);
4595+
X509_STORE_unlock(store);
4596+
return ret;
4597+
}
4598+
#endif
4599+
45564600
PyDoc_STRVAR(PySSLContext_sni_callback_doc,
45574601
"Set a callback that will be called when a server name is provided by the SSL/TLS client in the SNI extension.\n\
45584602
\n\
@@ -4582,7 +4626,12 @@ _ssl__SSLContext_cert_store_stats_impl(PySSLContext *self)
45824626
int x509 = 0, crl = 0, ca = 0, i;
45834627

45844628
store = SSL_CTX_get_cert_store(self->ctx);
4585-
objs = X509_STORE_get0_objects(store);
4629+
objs = X509_STORE_get1_objects(store);
4630+
if (objs == NULL) {
4631+
PyErr_SetString(PyExc_MemoryError, "failed to query cert store");
4632+
return NULL;
4633+
}
4634+
45864635
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
45874636
obj = sk_X509_OBJECT_value(objs, i);
45884637
switch (X509_OBJECT_get_type(obj)) {
@@ -4596,12 +4645,11 @@ _ssl__SSLContext_cert_store_stats_impl(PySSLContext *self)
45964645
crl++;
45974646
break;
45984647
default:
4599-
/* Ignore X509_LU_FAIL, X509_LU_RETRY, X509_LU_PKEY.
4600-
* As far as I can tell they are internal states and never
4601-
* stored in a cert store */
4648+
/* Ignore unrecognized types. */
46024649
break;
46034650
}
46044651
}
4652+
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
46054653
return Py_BuildValue("{sisisi}", "x509", x509, "crl", crl,
46064654
"x509_ca", ca);
46074655
}
@@ -4633,7 +4681,12 @@ _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form)
46334681
}
46344682

46354683
store = SSL_CTX_get_cert_store(self->ctx);
4636-
objs = X509_STORE_get0_objects(store);
4684+
objs = X509_STORE_get1_objects(store);
4685+
if (objs == NULL) {
4686+
PyErr_SetString(PyExc_MemoryError, "failed to query cert store");
4687+
goto error;
4688+
}
4689+
46374690
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
46384691
X509_OBJECT *obj;
46394692
X509 *cert;
@@ -4661,9 +4714,11 @@ _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form)
46614714
}
46624715
Py_CLEAR(ci);
46634716
}
4717+
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
46644718
return rlist;
46654719

46664720
error:
4721+
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
46674722
Py_XDECREF(ci);
46684723
Py_XDECREF(rlist);
46694724
return NULL;

0 commit comments

Comments
 (0)