Skip to content

Commit 37324b4

Browse files
[3.10] gh-114572: Fix locking in cert_store_stats and get_ca_certs (GH-114573) (#115548)
gh-114572: Fix locking in cert_store_stats and get_ca_certs (GH-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 (cherry picked from commit bce6931) Co-authored-by: David Benjamin <[email protected]>
1 parent d0524ca commit 37324b4

File tree

2 files changed

+64
-5
lines changed

2 files changed

+64
-5
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: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4519,6 +4519,50 @@ set_sni_callback(PySSLContext *self, PyObject *arg, void *c)
45194519
return 0;
45204520
}
45214521

4522+
#if OPENSSL_VERSION_NUMBER < 0x30300000L
4523+
static X509_OBJECT *x509_object_dup(const X509_OBJECT *obj)
4524+
{
4525+
int ok;
4526+
X509_OBJECT *ret = X509_OBJECT_new();
4527+
if (ret == NULL) {
4528+
return NULL;
4529+
}
4530+
switch (X509_OBJECT_get_type(obj)) {
4531+
case X509_LU_X509:
4532+
ok = X509_OBJECT_set1_X509(ret, X509_OBJECT_get0_X509(obj));
4533+
break;
4534+
case X509_LU_CRL:
4535+
/* X509_OBJECT_get0_X509_CRL was not const-correct prior to 3.0.*/
4536+
ok = X509_OBJECT_set1_X509_CRL(
4537+
ret, X509_OBJECT_get0_X509_CRL((X509_OBJECT *)obj));
4538+
break;
4539+
default:
4540+
/* We cannot duplicate unrecognized types in a polyfill, but it is
4541+
* safe to leave an empty object. The caller will ignore it. */
4542+
ok = 1;
4543+
break;
4544+
}
4545+
if (!ok) {
4546+
X509_OBJECT_free(ret);
4547+
return NULL;
4548+
}
4549+
return ret;
4550+
}
4551+
4552+
static STACK_OF(X509_OBJECT) *
4553+
X509_STORE_get1_objects(X509_STORE *store)
4554+
{
4555+
STACK_OF(X509_OBJECT) *ret;
4556+
if (!X509_STORE_lock(store)) {
4557+
return NULL;
4558+
}
4559+
ret = sk_X509_OBJECT_deep_copy(X509_STORE_get0_objects(store),
4560+
x509_object_dup, X509_OBJECT_free);
4561+
X509_STORE_unlock(store);
4562+
return ret;
4563+
}
4564+
#endif
4565+
45224566
PyDoc_STRVAR(PySSLContext_sni_callback_doc,
45234567
"Set a callback that will be called when a server name is provided by the SSL/TLS client in the SNI extension.\n\
45244568
\n\
@@ -4548,7 +4592,12 @@ _ssl__SSLContext_cert_store_stats_impl(PySSLContext *self)
45484592
int x509 = 0, crl = 0, ca = 0, i;
45494593

45504594
store = SSL_CTX_get_cert_store(self->ctx);
4551-
objs = X509_STORE_get0_objects(store);
4595+
objs = X509_STORE_get1_objects(store);
4596+
if (objs == NULL) {
4597+
PyErr_SetString(PyExc_MemoryError, "failed to query cert store");
4598+
return NULL;
4599+
}
4600+
45524601
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
45534602
obj = sk_X509_OBJECT_value(objs, i);
45544603
switch (X509_OBJECT_get_type(obj)) {
@@ -4562,12 +4611,11 @@ _ssl__SSLContext_cert_store_stats_impl(PySSLContext *self)
45624611
crl++;
45634612
break;
45644613
default:
4565-
/* Ignore X509_LU_FAIL, X509_LU_RETRY, X509_LU_PKEY.
4566-
* As far as I can tell they are internal states and never
4567-
* stored in a cert store */
4614+
/* Ignore unrecognized types. */
45684615
break;
45694616
}
45704617
}
4618+
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
45714619
return Py_BuildValue("{sisisi}", "x509", x509, "crl", crl,
45724620
"x509_ca", ca);
45734621
}
@@ -4599,7 +4647,12 @@ _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form)
45994647
}
46004648

46014649
store = SSL_CTX_get_cert_store(self->ctx);
4602-
objs = X509_STORE_get0_objects(store);
4650+
objs = X509_STORE_get1_objects(store);
4651+
if (objs == NULL) {
4652+
PyErr_SetString(PyExc_MemoryError, "failed to query cert store");
4653+
goto error;
4654+
}
4655+
46034656
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
46044657
X509_OBJECT *obj;
46054658
X509 *cert;
@@ -4627,9 +4680,11 @@ _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form)
46274680
}
46284681
Py_CLEAR(ci);
46294682
}
4683+
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
46304684
return rlist;
46314685

46324686
error:
4687+
sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
46334688
Py_XDECREF(ci);
46344689
Py_XDECREF(rlist);
46354690
return NULL;

0 commit comments

Comments
 (0)