Skip to content

Commit b2fac1a

Browse files
authored
bpo-31870: Add a timeout parameter to ssl.get_server_certificate() (GH-22270)
1 parent 6c681e1 commit b2fac1a

File tree

5 files changed

+27
-6
lines changed

5 files changed

+27
-6
lines changed

Doc/library/ssl.rst

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,8 @@ Certificate handling
426426
previously. Return an integer (no fractions of a second in the
427427
input format)
428428

429-
.. function:: get_server_certificate(addr, ssl_version=PROTOCOL_TLS_CLIENT, ca_certs=None)
429+
.. function:: get_server_certificate(addr, ssl_version=PROTOCOL_TLS_CLIENT, \
430+
ca_certs=None[, timeout])
430431

431432
Given the address ``addr`` of an SSL-protected server, as a (*hostname*,
432433
*port-number*) pair, fetches the server's certificate, and returns it as a
@@ -436,7 +437,8 @@ Certificate handling
436437
same format as used for the same parameter in
437438
:meth:`SSLContext.wrap_socket`. The call will attempt to validate the
438439
server certificate against that set of root certificates, and will fail
439-
if the validation attempt fails.
440+
if the validation attempt fails. A timeout can be specified with the
441+
``timeout`` parameter.
440442

441443
.. versionchanged:: 3.3
442444
This function is now IPv6-compatible.
@@ -445,6 +447,9 @@ Certificate handling
445447
The default *ssl_version* is changed from :data:`PROTOCOL_SSLv3` to
446448
:data:`PROTOCOL_TLS` for maximum compatibility with modern servers.
447449

450+
.. versionchanged:: 3.10
451+
The *timeout* parameter was added.
452+
448453
.. function:: DER_cert_to_PEM_cert(DER_cert_bytes)
449454

450455
Given a certificate as a DER-encoded blob of bytes, returns a PEM-encoded

Doc/whatsnew/3.10.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,12 @@ The exception :exc:`socket.timeout` is now an alias of :exc:`TimeoutError`.
10621062
Added option to create MPTCP sockets with ``IPPROTO_MPTCP``
10631063
(Contributed by Rui Cunha in :issue:`43571`.)
10641064
1065+
ssl
1066+
---
1067+
1068+
Add a *timeout* parameter to the :func:`ssl.get_server_certificate` function.
1069+
(Contributed by Zackery Spytz in :issue:`31870`.)
1070+
10651071
sys
10661072
---
10671073

Lib/ssl.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ class _TLSMessageType:
258258
from _ssl import enum_certificates, enum_crls
259259

260260
from socket import socket, SOCK_STREAM, create_connection
261-
from socket import SOL_SOCKET, SO_TYPE
261+
from socket import SOL_SOCKET, SO_TYPE, _GLOBAL_DEFAULT_TIMEOUT
262262
import socket as _socket
263263
import base64 # for DER-to-PEM translation
264264
import errno
@@ -1500,11 +1500,14 @@ def PEM_cert_to_DER_cert(pem_cert_string):
15001500
d = pem_cert_string.strip()[len(PEM_HEADER):-len(PEM_FOOTER)]
15011501
return base64.decodebytes(d.encode('ASCII', 'strict'))
15021502

1503-
def get_server_certificate(addr, ssl_version=PROTOCOL_TLS_CLIENT, ca_certs=None):
1503+
def get_server_certificate(addr, ssl_version=PROTOCOL_TLS_CLIENT,
1504+
ca_certs=None, timeout=_GLOBAL_DEFAULT_TIMEOUT):
15041505
"""Retrieve the certificate from the server at the specified address,
15051506
and return it as a PEM-encoded string.
15061507
If 'ca_certs' is specified, validate the server cert against it.
1507-
If 'ssl_version' is specified, use it in the connection attempt."""
1508+
If 'ssl_version' is specified, use it in the connection attempt.
1509+
If 'timeout' is specified, use it in the connection attempt.
1510+
"""
15081511

15091512
host, port = addr
15101513
if ca_certs is not None:
@@ -1514,7 +1517,7 @@ def get_server_certificate(addr, ssl_version=PROTOCOL_TLS_CLIENT, ca_certs=None)
15141517
context = _create_stdlib_context(ssl_version,
15151518
cert_reqs=cert_reqs,
15161519
cafile=ca_certs)
1517-
with create_connection(addr) as sock:
1520+
with create_connection(addr, timeout=timeout) as sock:
15181521
with context.wrap_socket(sock, server_hostname=host) as sslsock:
15191522
dercert = sslsock.getpeercert(True)
15201523
return DER_cert_to_PEM_cert(dercert)

Lib/test/test_ssl.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2136,6 +2136,11 @@ def test_get_server_certificate_fail(self):
21362136
# independent test method
21372137
_test_get_server_certificate_fail(self, *self.server_addr)
21382138

2139+
def test_get_server_certificate_timeout(self):
2140+
with self.assertRaises(socket.timeout):
2141+
ssl.get_server_certificate(self.server_addr, ca_certs=SIGNING_CA,
2142+
timeout=0.0001)
2143+
21392144
def test_ciphers(self):
21402145
with test_wrap_socket(socket.socket(socket.AF_INET),
21412146
cert_reqs=ssl.CERT_NONE, ciphers="ALL") as s:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The :func:`ssl.get_server_certificate` function now has a *timeout*
2+
parameter.

0 commit comments

Comments
 (0)