diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index a4f911f5f54..5d8ca3c96bd 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -240,6 +240,36 @@ Functions, Constants, and Exceptions modern version, and probably the best choice for maximum protection, if both sides can speak it. +.. data:: OPENSSL_VERSION + + The version string of the OpenSSL library loaded by the interpreter:: + + >>> ssl.OPENSSL_VERSION + 'OpenSSL 0.9.8k 25 Mar 2009' + + .. versionadded:: 2.7 + +.. data:: OPENSSL_VERSION_INFO + + A tuple of five integers representing version information about the + OpenSSL library:: + + >>> ssl.OPENSSL_VERSION_INFO + (0, 9, 8, 11, 15) + + .. versionadded:: 2.7 + +.. data:: OPENSSL_VERSION_NUMBER + + The raw version number of the OpenSSL library, as a single integer:: + + >>> ssl.OPENSSL_VERSION_NUMBER + 9470143L + >>> hex(ssl.OPENSSL_VERSION_NUMBER) + '0x9080bfL' + + .. versionadded:: 2.7 + SSLSocket Objects ----------------- diff --git a/Lib/ssl.py b/Lib/ssl.py index 21664b94cd3..cd1d865c921 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -59,6 +59,7 @@ import textwrap import _ssl # if we can't import it, let the error propagate +from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION from _ssl import SSLError from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED from _ssl import PROTOCOL_SSLv2, PROTOCOL_SSLv3, PROTOCOL_SSLv23, PROTOCOL_TLSv1 diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index f299d362898..0f9822ab85f 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -110,6 +110,34 @@ class BasicTests(unittest.TestCase): if (d1 != d2): raise test_support.TestFailed("PEM-to-DER or DER-to-PEM translation failed") + def test_openssl_version(self): + n = ssl.OPENSSL_VERSION_NUMBER + t = ssl.OPENSSL_VERSION_INFO + s = ssl.OPENSSL_VERSION + self.assertIsInstance(n, (int, long)) + self.assertIsInstance(t, tuple) + self.assertIsInstance(s, str) + # Some sanity checks follow + # >= 0.9 + self.assertGreaterEqual(n, 0x900000) + # < 2.0 + self.assertLess(n, 0x20000000) + major, minor, fix, patch, status = t + self.assertGreaterEqual(major, 0) + self.assertLess(major, 2) + self.assertGreaterEqual(minor, 0) + self.assertLess(minor, 256) + self.assertGreaterEqual(fix, 0) + self.assertLess(fix, 256) + self.assertGreaterEqual(patch, 0) + self.assertLessEqual(patch, 26) + self.assertGreaterEqual(status, 0) + self.assertLessEqual(status, 15) + # Version string as returned by OpenSSL, the format might change + self.assertTrue(s.startswith("OpenSSL {:d}.{:d}.{:d}".format(major, minor, fix)), + (s, t)) + + class NetworkedTests(unittest.TestCase): def testConnect(self): diff --git a/Misc/NEWS b/Misc/NEWS index 0ee0c07e4af..6c9e55ddf4a 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -49,6 +49,10 @@ Core and Builtins Library ------- +- Issue #8321: Give access to OpenSSL version numbers from the `ssl` module, + using the new attributes `ssl.OPENSSL_VERSION`, `ssl.OPENSSL_VERSION_INFO` + and `ssl.OPENSSL_VERSION_NUMBER`. + - Issue #8310: Allow dis to examine new style classes. - Issue #8257: The Decimal construct now accepts a float instance diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 52fdf0ffd32..6befbefc9c0 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -1574,7 +1574,9 @@ for documentation."); PyMODINIT_FUNC init_ssl(void) { - PyObject *m, *d; + PyObject *m, *d, *r; + unsigned long libver; + unsigned int major, minor, fix, patch, status; Py_TYPE(&PySSL_Type) = &PyType_Type; @@ -1644,4 +1646,30 @@ init_ssl(void) PY_SSL_VERSION_SSL23); PyModule_AddIntConstant(m, "PROTOCOL_TLSv1", PY_SSL_VERSION_TLS1); + + /* OpenSSL version */ + /* SSLeay() gives us the version of the library linked against, + which could be different from the headers version. + */ + libver = SSLeay(); + r = PyLong_FromUnsignedLong(libver); + if (r == NULL) + return; + if (PyModule_AddObject(m, "OPENSSL_VERSION_NUMBER", r)) + return; + status = libver & 0xF; + libver >>= 4; + patch = libver & 0xFF; + libver >>= 8; + fix = libver & 0xFF; + libver >>= 8; + minor = libver & 0xFF; + libver >>= 8; + major = libver & 0xFF; + r = Py_BuildValue("IIIII", major, minor, fix, patch, status); + if (r == NULL || PyModule_AddObject(m, "OPENSSL_VERSION_INFO", r)) + return; + r = PyString_FromString(SSLeay_version(SSLEAY_VERSION)); + if (r == NULL || PyModule_AddObject(m, "OPENSSL_VERSION", r)) + return; }