diff options
-rw-r--r-- | Doc/library/ssl.rst | 20 | ||||
-rw-r--r-- | Lib/ssl.py | 20 | ||||
-rw-r--r-- | Lib/test/test_ssl.py | 13 | ||||
-rw-r--r-- | Misc/NEWS | 3 | ||||
-rw-r--r-- | Modules/_ssl.c | 42 |
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. @@ -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 @@ -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 */ }; |