summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/ssl.rst20
-rw-r--r--Lib/ssl.py20
-rw-r--r--Lib/test/test_ssl.py13
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/_ssl.c42
5 files changed, 97 insertions, 1 deletions
diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
index 2f8b9a2..5f46de1 100644
--- a/Doc/library/ssl.rst
+++ b/Doc/library/ssl.rst
@@ -343,6 +343,23 @@ Certificate handling
Given a certificate as an ASCII PEM string, returns a DER-encoded sequence of
bytes for that same certificate.
+.. function:: get_default_verify_paths()
+
+ Returns a named tuple with paths to OpenSSL's default cafile and capath.
+ The paths are the same as used by
+ :meth:`SSLContext.set_default_verify_paths`. The return value is a
+ :term:`named tuple` ``DefaultVerifyPaths``:
+
+ * :attr:`cafile` - resolved path to cafile or None if the file doesn't exist,
+ * :attr:`capath` - resolved path to capath or None if the directory doesn't exist,
+ * :attr:`openssl_cafile_env` - OpenSSL's environment key that points to a cafile,
+ * :attr:`openssl_cafile` - hard coded path to a cafile,
+ * :attr:`openssl_capath_env` - OpenSSL's environment key that points to a capath,
+ * :attr:`openssl_capath` - hard coded path to a capath directory
+
+ .. versionadded:: 3.4
+
+
Constants
^^^^^^^^^
@@ -787,7 +804,8 @@ to speed up repeated connections from the same clients.
other peers' certificates when :data:`verify_mode` is other than
:data:`CERT_NONE`. At least one of *cafile* or *capath* must be specified.
- The *cafile* string, if present, is the path to a file of concatenated
+ The *cafile* string, if present, is the p
+ ath to a file of concatenated
CA certificates in PEM format. See the discussion of
:ref:`ssl-certificates` for more information about how to arrange the
certificates in this file.
diff --git a/Lib/ssl.py b/Lib/ssl.py
index 8af22c7..5d48f1b 100644
--- a/Lib/ssl.py
+++ b/Lib/ssl.py
@@ -89,6 +89,8 @@ ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY
import textwrap
import re
+import os
+import collections
import _ssl # if we can't import it, let the error propagate
@@ -222,6 +224,24 @@ def match_hostname(cert, hostname):
"subjectAltName fields were found")
+DefaultVerifyPaths = collections.namedtuple("DefaultVerifyPaths",
+ "cafile capath openssl_cafile_env openssl_cafile openssl_capath_env "
+ "openssl_capath")
+
+def get_default_verify_paths():
+ """Return paths to default cafile and capath.
+ """
+ parts = _ssl.get_default_verify_paths()
+
+ # environment vars shadow paths
+ cafile = os.environ.get(parts[0], parts[1])
+ capath = os.environ.get(parts[2], parts[3])
+
+ return DefaultVerifyPaths(cafile if os.path.isfile(cafile) else None,
+ capath if os.path.isdir(capath) else None,
+ *parts)
+
+
class SSLContext(_SSLContext):
"""An SSLContext holds various SSL-related configuration options and
data, such as certificates and possibly a private key."""
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index 224e0e2..6cecc17 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -394,6 +394,19 @@ class BasicSocketTests(unittest.TestCase):
support.gc_collect()
self.assertIn(r, str(cm.warning.args[0]))
+ def test_get_default_verify_paths(self):
+ paths = ssl.get_default_verify_paths()
+ self.assertEqual(len(paths), 6)
+ self.assertIsInstance(paths, ssl.DefaultVerifyPaths)
+
+ with support.EnvironmentVarGuard() as env:
+ env["SSL_CERT_DIR"] = CAPATH
+ env["SSL_CERT_FILE"] = CERTFILE
+ paths = ssl.get_default_verify_paths()
+ self.assertEqual(paths.cafile, CERTFILE)
+ self.assertEqual(paths.capath, CAPATH)
+
+
class ContextTests(unittest.TestCase):
@skip_if_broken_ubuntu_ssl
diff --git a/Misc/NEWS b/Misc/NEWS
index cfddde9..fd54ea6 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -115,6 +115,9 @@ Core and Builtins
Library
-------
+- Issue #18143: Implement ssl.get_default_verify_paths() in order to debug
+ the default locations for cafile and capath.
+
- Issue #17314: Move multiprocessing.forking over to importlib.
- Issue #11959: SMTPServer and SMTPChannel now take an optional map, use of
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index c64d209..f4cd38b 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -2761,6 +2761,46 @@ fails or if it does provide enough data to seed PRNG.");
#endif
+PyDoc_STRVAR(PySSL_get_default_verify_paths_doc,
+"get_default_verify_paths() -> tuple\n\
+\n\
+Return search paths and environment vars that are used by SSLContext's\n\
+set_default_verify_paths() to load default CAs. The values are\n\
+'cert_file_env', 'cert_file', 'cert_dir_env', 'cert_dir'.");
+
+static PyObject *
+get_default_verify_paths(PyObject *self)
+{
+ PyObject *ofile_env = NULL;
+ PyObject *ofile = NULL;
+ PyObject *odir_env = NULL;
+ PyObject *odir = NULL;
+
+#define convert(info, target) { \
+ const char *tmp = (info); \
+ target = NULL; \
+ if (!tmp) { Py_INCREF(Py_None); target = Py_None; } \
+ else if ((target = PyUnicode_DecodeFSDefault(tmp)) == NULL) { \
+ target = PyBytes_FromString(tmp); } \
+ if (!target) goto error; \
+ } while(0)
+
+ convert(X509_get_default_cert_file_env(), ofile_env);
+ convert(X509_get_default_cert_file(), ofile);
+ convert(X509_get_default_cert_dir_env(), odir_env);
+ convert(X509_get_default_cert_dir(), odir);
+#undef convert
+
+ return Py_BuildValue("(OOOO)", ofile_env, ofile, odir_env, odir);
+
+ error:
+ Py_XDECREF(ofile_env);
+ Py_XDECREF(ofile);
+ Py_XDECREF(odir_env);
+ Py_XDECREF(odir);
+ return NULL;
+}
+
/* List of functions exported by this module. */
@@ -2779,6 +2819,8 @@ static PyMethodDef PySSL_methods[] = {
PySSL_RAND_egd_doc},
{"RAND_status", (PyCFunction)PySSL_RAND_status, METH_NOARGS,
PySSL_RAND_status_doc},
+ {"get_default_verify_paths", (PyCFunction)get_default_verify_paths,
+ METH_NOARGS, PySSL_get_default_verify_paths_doc},
#endif
{NULL, NULL} /* Sentinel */
};