]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-36076: Add SNI support to ssl.get_server_certificate. (GH-16820)
authorjuhovh <juhovh@iki.fi>
Sun, 18 Apr 2021 11:11:48 +0000 (21:11 +1000)
committerGitHub <noreply@github.com>
Sun, 18 Apr 2021 11:11:48 +0000 (04:11 -0700)
Many servers in the cloud environment require SNI to be used during the
SSL/TLS handshake, therefore it is not possible to fetch their certificates
using the ssl.get_server_certificate interface.

This change adds an additional optional hostname argument that can be used to
set the SNI. Note that it is intentionally a separate argument instead of
using the host part of the addr tuple, because one might want to explicitly
fetch the default certificate or fetch a certificate from a specific IP
address with the specified SNI hostname. A separate argument also works better
for backwards compatibility.

Automerge-Triggered-By: GH:tiran
Lib/ssl.py
Lib/test/test_ssl.py
Misc/ACKS
Misc/NEWS.d/next/Library/2019-10-16-17-21-53.bpo-36076.FGeQQT.rst [new file with mode: 0644]

index 9c1ba581dd66f5e08dd323445299cbeea4b57945..99d0852dad18d5dd711f12fb86afd8d311d83815 100644 (file)
@@ -1475,7 +1475,7 @@ def get_server_certificate(addr, ssl_version=PROTOCOL_TLS, ca_certs=None):
                                      cert_reqs=cert_reqs,
                                      cafile=ca_certs)
     with  create_connection(addr) as sock:
-        with context.wrap_socket(sock) as sslsock:
+        with context.wrap_socket(sock, server_hostname=host) as sslsock:
             dercert = sslsock.getpeercert(True)
     return DER_cert_to_PEM_cert(dercert)
 
index 3ad14c63968e641e43c14766e84a59cbf1b87bcb..403261dda968092523e267d46417b77f5a2d5017 100644 (file)
@@ -1962,7 +1962,9 @@ class SimpleBackgroundTests(unittest.TestCase):
     """Tests that connect to a simple server running in the background"""
 
     def setUp(self):
-        server = ThreadedEchoServer(SIGNED_CERTFILE)
+        self.server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
+        self.server_context.load_cert_chain(SIGNED_CERTFILE)
+        server = ThreadedEchoServer(context=self.server_context)
         self.server_addr = (HOST, server.port)
         server.__enter__()
         self.addCleanup(server.__exit__, None, None, None)
@@ -2143,6 +2145,28 @@ class SimpleBackgroundTests(unittest.TestCase):
     def test_get_server_certificate(self):
         _test_get_server_certificate(self, *self.server_addr, cert=SIGNING_CA)
 
+    @needs_sni
+    def test_get_server_certificate_sni(self):
+        host, port = self.server_addr
+        server_names = []
+
+        # We store servername_cb arguments to make sure they match the host
+        def servername_cb(ssl_sock, server_name, initial_context):
+            server_names.append(server_name)
+        self.server_context.set_servername_callback(servername_cb)
+
+        pem = ssl.get_server_certificate((host, port))
+        if not pem:
+            self.fail("No server certificate on %s:%s!" % (host, port))
+
+        pem = ssl.get_server_certificate((host, port), ca_certs=SIGNING_CA)
+        if not pem:
+            self.fail("No server certificate on %s:%s!" % (host, port))
+        if support.verbose:
+            sys.stdout.write("\nVerified certificate for %s:%s is\n%s\n" % (host, port, pem))
+
+        self.assertEqual(server_names, [host, host])
+
     def test_get_server_certificate_fail(self):
         # Connection failure crashes ThreadedEchoServer, so run this in an
         # independent test method
index 20359762a521710cace6da69222c018745b178e4..1eeae0caef50a16a7cb6b24ab2ba321a6848dd7d 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1803,6 +1803,7 @@ Michael Urman
 Hector Urtubia
 Elizabeth Uselton
 Lukas Vacek
+Juho Vähä-Herttua
 Ville Vainio
 Yann Vaginay
 Andi Vajda
diff --git a/Misc/NEWS.d/next/Library/2019-10-16-17-21-53.bpo-36076.FGeQQT.rst b/Misc/NEWS.d/next/Library/2019-10-16-17-21-53.bpo-36076.FGeQQT.rst
new file mode 100644 (file)
index 0000000..7e9bc4e
--- /dev/null
@@ -0,0 +1 @@
+Added SNI support to :func:`ssl.get_server_certificate`.