Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 67986f9

Browse files
committed
Issue #19735: Implement private function ssl._create_stdlib_context() to
create SSLContext objects in Python's stdlib module. It provides a single configuration point and makes use of SSLContext.load_default_certs().
1 parent 32eddc1 commit 67986f9

11 files changed

Lines changed: 100 additions & 55 deletions

File tree

Lib/asyncio/selector_events.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -571,10 +571,8 @@ def __init__(self, loop, rawsock, protocol, sslcontext, waiter=None,
571571
# context; in that case the sslcontext passed is None.
572572
# The default is the same as used by urllib with
573573
# cadefault=True.
574-
sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
575-
sslcontext.options |= ssl.OP_NO_SSLv2
576-
sslcontext.set_default_verify_paths()
577-
sslcontext.verify_mode = ssl.CERT_REQUIRED
574+
sslcontext = ssl._create_stdlib_context(
575+
cert_reqs=ssl.CERT_REQUIRED)
578576

579577
wrap_kwargs = {
580578
'server_side': server_side,

Lib/ftplib.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,10 @@ def __init__(self, host='', user='', passwd='', acct='', keyfile=None,
727727
"exclusive")
728728
self.keyfile = keyfile
729729
self.certfile = certfile
730+
if context is None:
731+
context = ssl._create_stdlib_context(self.ssl_version,
732+
certfile=certfile,
733+
keyfile=keyfile)
730734
self.context = context
731735
self._prot_p = False
732736
FTP.__init__(self, host, user, passwd, acct, timeout, source_address)
@@ -744,12 +748,7 @@ def auth(self):
744748
resp = self.voidcmd('AUTH TLS')
745749
else:
746750
resp = self.voidcmd('AUTH SSL')
747-
if self.context is not None:
748-
self.sock = self.context.wrap_socket(self.sock)
749-
else:
750-
self.sock = ssl.wrap_socket(self.sock, self.keyfile,
751-
self.certfile,
752-
ssl_version=self.ssl_version)
751+
self.sock = self.context.wrap_socket(self.sock)
753752
self.file = self.sock.makefile(mode='r', encoding=self.encoding)
754753
return resp
755754

@@ -788,11 +787,7 @@ def prot_c(self):
788787
def ntransfercmd(self, cmd, rest=None):
789788
conn, size = FTP.ntransfercmd(self, cmd, rest)
790789
if self._prot_p:
791-
if self.context is not None:
792-
conn = self.context.wrap_socket(conn)
793-
else:
794-
conn = ssl.wrap_socket(conn, self.keyfile, self.certfile,
795-
ssl_version=self.ssl_version)
790+
conn = self.context.wrap_socket(conn)
796791
return conn, size
797792

798793
def abort(self):

Lib/http/client.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,9 +1179,7 @@ def __init__(self, host, port=None, key_file=None, cert_file=None,
11791179
self.key_file = key_file
11801180
self.cert_file = cert_file
11811181
if context is None:
1182-
# Some reasonable defaults
1183-
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
1184-
context.options |= ssl.OP_NO_SSLv2
1182+
context = ssl._create_stdlib_context()
11851183
will_verify = context.verify_mode != ssl.CERT_NONE
11861184
if check_hostname is None:
11871185
check_hostname = will_verify

Lib/imaplib.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -742,9 +742,7 @@ def starttls(self, ssl_context=None):
742742
raise self.abort('TLS not supported by server')
743743
# Generate a default SSL context if none was passed.
744744
if ssl_context is None:
745-
ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
746-
# SSLv2 considered harmful.
747-
ssl_context.options |= ssl.OP_NO_SSLv2
745+
ssl_context = ssl._create_stdlib_context()
748746
typ, dat = self._simple_command(name)
749747
if typ == 'OK':
750748
self.sock = ssl_context.wrap_socket(self.sock)
@@ -1210,15 +1208,15 @@ def __init__(self, host='', port=IMAP4_SSL_PORT, keyfile=None, certfile=None, ss
12101208

12111209
self.keyfile = keyfile
12121210
self.certfile = certfile
1211+
if ssl_context is None:
1212+
ssl_context = ssl._create_stdlib_context(certfile=certfile,
1213+
keyfile=keyfile)
12131214
self.ssl_context = ssl_context
12141215
IMAP4.__init__(self, host, port)
12151216

12161217
def _create_socket(self):
12171218
sock = IMAP4._create_socket(self)
1218-
if self.ssl_context:
1219-
return self.ssl_context.wrap_socket(sock)
1220-
else:
1221-
return ssl.wrap_socket(sock, self.keyfile, self.certfile)
1219+
return self.ssl_context.wrap_socket(sock)
12221220

12231221
def open(self, host='', port=IMAP4_SSL_PORT):
12241222
"""Setup connection to remote server on "host:port".

Lib/nntplib.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -288,9 +288,7 @@ def _encrypt_on(sock, context):
288288
"""
289289
# Generate a default SSL context if none was passed.
290290
if context is None:
291-
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
292-
# SSLv2 considered harmful.
293-
context.options |= ssl.OP_NO_SSLv2
291+
context = ssl._create_stdlib_context()
294292
return context.wrap_socket(sock)
295293

296294

Lib/poplib.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -385,8 +385,7 @@ def stls(self, context=None):
385385
if not 'STLS' in caps:
386386
raise error_proto('-ERR STLS not supported by server')
387387
if context is None:
388-
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
389-
context.options |= ssl.OP_NO_SSLv2
388+
context = ssl._create_stdlib_context()
390389
resp = self._shortcmd('STLS')
391390
self.sock = context.wrap_socket(self.sock)
392391
self.file = self.sock.makefile('rb')
@@ -421,15 +420,15 @@ def __init__(self, host, port=POP3_SSL_PORT, keyfile=None, certfile=None,
421420
"exclusive")
422421
self.keyfile = keyfile
423422
self.certfile = certfile
423+
if context is None:
424+
context = ssl._create_stdlib_context(certfile=certfile,
425+
keyfile=keyfile)
424426
self.context = context
425427
POP3.__init__(self, host, port, timeout)
426428

427429
def _create_socket(self, timeout):
428430
sock = POP3._create_socket(self, timeout)
429-
if self.context is not None:
430-
sock = self.context.wrap_socket(sock)
431-
else:
432-
sock = ssl.wrap_socket(sock, self.keyfile, self.certfile)
431+
sock = self.context.wrap_socket(sock)
433432
return sock
434433

435434
def stls(self, keyfile=None, certfile=None, context=None):

Lib/smtplib.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -664,10 +664,10 @@ def starttls(self, keyfile=None, certfile=None, context=None):
664664
if context is not None and certfile is not None:
665665
raise ValueError("context and certfile arguments are mutually "
666666
"exclusive")
667-
if context is not None:
668-
self.sock = context.wrap_socket(self.sock)
669-
else:
670-
self.sock = ssl.wrap_socket(self.sock, keyfile, certfile)
667+
if context is None:
668+
context = ssl._create_stdlib_context(certfile=certfile,
669+
keyfile=keyfile)
670+
self.sock = context.wrap_socket(self.sock)
671671
self.file = None
672672
# RFC 3207:
673673
# The client MUST discard any knowledge obtained from
@@ -880,6 +880,9 @@ def __init__(self, host='', port=0, local_hostname=None,
880880
"exclusive")
881881
self.keyfile = keyfile
882882
self.certfile = certfile
883+
if context is None:
884+
context = ssl._create_stdlib_context(certfile=certfile,
885+
keyfile=keyfile)
883886
self.context = context
884887
SMTP.__init__(self, host, port, local_hostname, timeout,
885888
source_address)
@@ -889,10 +892,7 @@ def _get_socket(self, host, port, timeout):
889892
print('connect:', (host, port), file=stderr)
890893
new_socket = socket.create_connection((host, port), timeout,
891894
self.source_address)
892-
if self.context is not None:
893-
new_socket = self.context.wrap_socket(new_socket)
894-
else:
895-
new_socket = ssl.wrap_socket(new_socket, self.keyfile, self.certfile)
895+
new_socket = self.context.wrap_socket(new_socket)
896896
return new_socket
897897

898898
__all__.append("SMTP_SSL")

Lib/ssl.py

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,43 @@ def create_default_context(purpose=Purpose.SERVER_AUTH, *, cafile=None,
398398
return context
399399

400400

401+
def _create_stdlib_context(protocol=PROTOCOL_SSLv23, *, cert_reqs=None,
402+
purpose=Purpose.SERVER_AUTH,
403+
certfile=None, keyfile=None,
404+
cafile=None, capath=None, cadata=None):
405+
"""Create a SSLContext object for Python stdlib modules
406+
407+
All Python stdlib modules shall use this function to create SSLContext
408+
objects in order to keep common settings in one place. The configuration
409+
is less restrict than create_default_context()'s to increase backward
410+
compatibility.
411+
"""
412+
if not isinstance(purpose, _ASN1Object):
413+
raise TypeError(purpose)
414+
415+
context = SSLContext(protocol)
416+
# SSLv2 considered harmful.
417+
context.options |= OP_NO_SSLv2
418+
419+
if cert_reqs is not None:
420+
context.verify_mode = cert_reqs
421+
422+
if keyfile and not certfile:
423+
raise ValueError("certfile must be specified")
424+
if certfile or keyfile:
425+
context.load_cert_chain(certfile, keyfile)
426+
427+
# load CA root certs
428+
if cafile or capath or cadata:
429+
context.load_verify_locations(cafile, capath, cadata)
430+
elif context.verify_mode != CERT_NONE:
431+
# no explicit cafile, capath or cadata but the verify mode is
432+
# CERT_OPTIONAL or CERT_REQUIRED. Let's try to load default system
433+
# root CA certificates for the given purpose. This may fail silently.
434+
context.load_default_certs(purpose)
435+
436+
return context
437+
401438
class SSLSocket(socket):
402439
"""This class implements a subtype of socket.socket that wraps
403440
the underlying OS socket in an SSL context when necessary, and
@@ -829,15 +866,16 @@ def get_server_certificate(addr, ssl_version=PROTOCOL_SSLv3, ca_certs=None):
829866
If 'ssl_version' is specified, use it in the connection attempt."""
830867

831868
host, port = addr
832-
if (ca_certs is not None):
869+
if ca_certs is not None:
833870
cert_reqs = CERT_REQUIRED
834871
else:
835872
cert_reqs = CERT_NONE
836-
s = create_connection(addr)
837-
s = wrap_socket(s, ssl_version=ssl_version,
838-
cert_reqs=cert_reqs, ca_certs=ca_certs)
839-
dercert = s.getpeercert(True)
840-
s.close()
873+
context = _create_stdlib_context(ssl_version,
874+
cert_reqs=cert_reqs,
875+
cafile=ca_certs)
876+
with create_connection(addr) as sock:
877+
with context.wrap_socket(sock) as sslsock:
878+
dercert = sslsock.getpeercert(True)
841879
return DER_cert_to_PEM_cert(dercert)
842880

843881
def get_protocol_name(protocol_code):

Lib/test/test_ssl.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,27 @@ def test_create_default_context(self):
10181018
self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
10191019
self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2)
10201020

1021+
def test__create_stdlib_context(self):
1022+
ctx = ssl._create_stdlib_context()
1023+
self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23)
1024+
self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
1025+
self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2)
1026+
1027+
ctx = ssl._create_stdlib_context(ssl.PROTOCOL_TLSv1)
1028+
self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1)
1029+
self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
1030+
self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2)
1031+
1032+
ctx = ssl._create_stdlib_context(ssl.PROTOCOL_TLSv1,
1033+
cert_reqs=ssl.CERT_REQUIRED)
1034+
self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1)
1035+
self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED)
1036+
self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2)
1037+
1038+
ctx = ssl._create_stdlib_context(purpose=ssl.Purpose.CLIENT_AUTH)
1039+
self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23)
1040+
self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
1041+
self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2)
10211042

10221043

10231044
class SSLErrorTests(unittest.TestCase):

Lib/urllib/request.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,9 @@ def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
141141
if cafile or capath or cadefault:
142142
if not _have_ssl:
143143
raise ValueError('SSL support not available')
144-
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
145-
context.options |= ssl.OP_NO_SSLv2
146-
context.verify_mode = ssl.CERT_REQUIRED
147-
if cafile or capath:
148-
context.load_verify_locations(cafile, capath)
149-
else:
150-
context.set_default_verify_paths()
144+
context = ssl._create_stdlib_context(cert_reqs=ssl.CERT_REQUIRED,
145+
cafile=cafile,
146+
capath=capath)
151147
https_handler = HTTPSHandler(context=context, check_hostname=True)
152148
opener = build_opener(https_handler)
153149
elif _opener is None:

0 commit comments

Comments
 (0)