diff -r 16dd19aa64c8 Lib/asyncio/selector_events.py --- a/Lib/asyncio/selector_events.py Sat Nov 23 15:59:07 2013 +0100 +++ b/Lib/asyncio/selector_events.py Sat Nov 23 16:27:25 2013 +0100 @@ -571,10 +571,8 @@ # context; in that case the sslcontext passed is None. # The default is the same as used by urllib with # cadefault=True. - sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - sslcontext.options |= ssl.OP_NO_SSLv2 - sslcontext.set_default_verify_paths() - sslcontext.verify_mode = ssl.CERT_REQUIRED + sslcontext = ssl._create_stdlib_context( + cert_reqs=ssl.CERT_REQUIRED) wrap_kwargs = { 'server_side': server_side, diff -r 16dd19aa64c8 Lib/ftplib.py --- a/Lib/ftplib.py Sat Nov 23 15:59:07 2013 +0100 +++ b/Lib/ftplib.py Sat Nov 23 16:27:25 2013 +0100 @@ -727,6 +727,10 @@ "exclusive") self.keyfile = keyfile self.certfile = certfile + if context is None: + context = ssl._create_stdlib_context(self.ssl_version, + certfile=certfile, + keyfile=keyfile) self.context = context self._prot_p = False FTP.__init__(self, host, user, passwd, acct, timeout, source_address) @@ -744,12 +748,7 @@ resp = self.voidcmd('AUTH TLS') else: resp = self.voidcmd('AUTH SSL') - if self.context is not None: - self.sock = self.context.wrap_socket(self.sock) - else: - self.sock = ssl.wrap_socket(self.sock, self.keyfile, - self.certfile, - ssl_version=self.ssl_version) + self.sock = self.context.wrap_socket(self.sock) self.file = self.sock.makefile(mode='r', encoding=self.encoding) return resp @@ -788,11 +787,7 @@ def ntransfercmd(self, cmd, rest=None): conn, size = FTP.ntransfercmd(self, cmd, rest) if self._prot_p: - if self.context is not None: - conn = self.context.wrap_socket(conn) - else: - conn = ssl.wrap_socket(conn, self.keyfile, self.certfile, - ssl_version=self.ssl_version) + conn = self.context.wrap_socket(conn) return conn, size def abort(self): diff -r 16dd19aa64c8 Lib/http/client.py --- a/Lib/http/client.py Sat Nov 23 15:59:07 2013 +0100 +++ b/Lib/http/client.py Sat Nov 23 16:27:25 2013 +0100 @@ -1179,9 +1179,7 @@ self.key_file = key_file self.cert_file = cert_file if context is None: - # Some reasonable defaults - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - context.options |= ssl.OP_NO_SSLv2 + context = ssl._create_stdlib_context() will_verify = context.verify_mode != ssl.CERT_NONE if check_hostname is None: check_hostname = will_verify diff -r 16dd19aa64c8 Lib/imaplib.py --- a/Lib/imaplib.py Sat Nov 23 15:59:07 2013 +0100 +++ b/Lib/imaplib.py Sat Nov 23 16:27:25 2013 +0100 @@ -742,9 +742,7 @@ raise self.abort('TLS not supported by server') # Generate a default SSL context if none was passed. if ssl_context is None: - ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - # SSLv2 considered harmful. - ssl_context.options |= ssl.OP_NO_SSLv2 + ssl_context = ssl._create_stdlib_context() typ, dat = self._simple_command(name) if typ == 'OK': self.sock = ssl_context.wrap_socket(self.sock) @@ -1210,15 +1208,15 @@ self.keyfile = keyfile self.certfile = certfile + if ssl_context is None: + ssl_context = ssl._create_stdlib_context(certfile=certfile, + keyfile=keyfile) self.ssl_context = ssl_context IMAP4.__init__(self, host, port) def _create_socket(self): sock = IMAP4._create_socket(self) - if self.ssl_context: - return self.ssl_context.wrap_socket(sock) - else: - return ssl.wrap_socket(sock, self.keyfile, self.certfile) + return self.ssl_context.wrap_socket(sock) def open(self, host='', port=IMAP4_SSL_PORT): """Setup connection to remote server on "host:port". diff -r 16dd19aa64c8 Lib/nntplib.py --- a/Lib/nntplib.py Sat Nov 23 15:59:07 2013 +0100 +++ b/Lib/nntplib.py Sat Nov 23 16:27:25 2013 +0100 @@ -288,9 +288,7 @@ """ # Generate a default SSL context if none was passed. if context is None: - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - # SSLv2 considered harmful. - context.options |= ssl.OP_NO_SSLv2 + context = ssl._create_stdlib_context() return context.wrap_socket(sock) diff -r 16dd19aa64c8 Lib/poplib.py --- a/Lib/poplib.py Sat Nov 23 15:59:07 2013 +0100 +++ b/Lib/poplib.py Sat Nov 23 16:27:25 2013 +0100 @@ -385,8 +385,7 @@ if not 'STLS' in caps: raise error_proto('-ERR STLS not supported by server') if context is None: - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - context.options |= ssl.OP_NO_SSLv2 + context = ssl._create_stdlib_context() resp = self._shortcmd('STLS') self.sock = context.wrap_socket(self.sock) self.file = self.sock.makefile('rb') @@ -421,15 +420,15 @@ "exclusive") self.keyfile = keyfile self.certfile = certfile + if context is None: + context = ssl._create_stdlib_context(certfile=certfile, + keyfile=keyfile) self.context = context POP3.__init__(self, host, port, timeout) def _create_socket(self, timeout): sock = POP3._create_socket(self, timeout) - if self.context is not None: - sock = self.context.wrap_socket(sock) - else: - sock = ssl.wrap_socket(sock, self.keyfile, self.certfile) + sock = self.context.wrap_socket(sock) return sock def stls(self, keyfile=None, certfile=None, context=None): diff -r 16dd19aa64c8 Lib/smtplib.py --- a/Lib/smtplib.py Sat Nov 23 15:59:07 2013 +0100 +++ b/Lib/smtplib.py Sat Nov 23 16:27:25 2013 +0100 @@ -664,10 +664,10 @@ if context is not None and certfile is not None: raise ValueError("context and certfile arguments are mutually " "exclusive") - if context is not None: - self.sock = context.wrap_socket(self.sock) - else: - self.sock = ssl.wrap_socket(self.sock, keyfile, certfile) + if context is None: + context = ssl._create_stdlib_context(certfile=certfile, + keyfile=keyfile) + self.sock = context.wrap_socket(self.sock) self.file = None # RFC 3207: # The client MUST discard any knowledge obtained from @@ -880,6 +880,9 @@ "exclusive") self.keyfile = keyfile self.certfile = certfile + if context is None: + context = ssl._create_stdlib_context(certfile=certfile, + keyfile=keyfile) self.context = context SMTP.__init__(self, host, port, local_hostname, timeout, source_address) @@ -889,10 +892,7 @@ print('connect:', (host, port), file=stderr) new_socket = socket.create_connection((host, port), timeout, self.source_address) - if self.context is not None: - new_socket = self.context.wrap_socket(new_socket) - else: - new_socket = ssl.wrap_socket(new_socket, self.keyfile, self.certfile) + new_socket = self.context.wrap_socket(new_socket) return new_socket __all__.append("SMTP_SSL") diff -r 16dd19aa64c8 Lib/ssl.py --- a/Lib/ssl.py Sat Nov 23 15:59:07 2013 +0100 +++ b/Lib/ssl.py Sat Nov 23 16:27:25 2013 +0100 @@ -398,6 +398,43 @@ return context +def _create_stdlib_context(protocol=PROTOCOL_SSLv23, *, cert_reqs=None, + purpose=Purpose.SERVER_AUTH, + certfile=None, keyfile=None, + cafile=None, capath=None, cadata=None): + """Create a SSLContext object for Python stdlib modules + + All Python stdlib modules shall use this function to create SSLContext + objects in order to keep common settings in one place. The configuration + is less restrict than create_default_context()'s to increase backward + compatibility. + """ + if not isinstance(purpose, _ASN1Object): + raise TypeError(purpose) + + context = SSLContext(protocol) + # SSLv2 considered harmful. + context.options |= OP_NO_SSLv2 + + if cert_reqs is not None: + context.verify_mode = cert_reqs + + if keyfile and not certfile: + raise ValueError("certfile must be specified") + if certfile or keyfile: + context.load_cert_chain(certfile, keyfile) + + # load CA root certs + if cafile or capath or cadata: + context.load_verify_locations(cafile, capath, cadata) + elif context.verify_mode != CERT_NONE: + # no explicit cafile, capath or cadata but the verify mode is + # CERT_OPTIONAL or CERT_REQUIRED. Let's try to load default system + # root CA certificates for the given purpose. This may fail silently. + context.load_default_certs(purpose) + + return context + class SSLSocket(socket): """This class implements a subtype of socket.socket that wraps the underlying OS socket in an SSL context when necessary, and @@ -822,22 +859,22 @@ d = pem_cert_string.strip()[len(PEM_HEADER):-len(PEM_FOOTER)] return base64.decodebytes(d.encode('ASCII', 'strict')) -def get_server_certificate(addr, ssl_version=PROTOCOL_SSLv3, ca_certs=None): +def get_server_certificate(addr, ssl_version=PROTOCOL_SSLv3, ca_certs=None, + cert_reqs=None): """Retrieve the certificate from the server at the specified address, and return it as a PEM-encoded string. If 'ca_certs' is specified, validate the server cert against it. If 'ssl_version' is specified, use it in the connection attempt.""" host, port = addr - if (ca_certs is not None): + if ca_certs is not None: cert_reqs = CERT_REQUIRED - else: - cert_reqs = CERT_NONE - s = create_connection(addr) - s = wrap_socket(s, ssl_version=ssl_version, - cert_reqs=cert_reqs, ca_certs=ca_certs) - dercert = s.getpeercert(True) - s.close() + context = _create_stdlib_context(ssl_version, + cert_reqs=cert_reqs, + cafile=ca_certs) + with create_connection(addr) as sock: + with context.wrap_socket(sock) as sslsock: + dercert = sslsock.getpeercert(True) return DER_cert_to_PEM_cert(dercert) def get_protocol_name(protocol_code): diff -r 16dd19aa64c8 Lib/test/test_ssl.py --- a/Lib/test/test_ssl.py Sat Nov 23 15:59:07 2013 +0100 +++ b/Lib/test/test_ssl.py Sat Nov 23 16:27:25 2013 +0100 @@ -1018,6 +1018,27 @@ self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + def test__create_stdlib_context(self): + ctx = ssl._create_stdlib_context() + self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23) + self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + + ctx = ssl._create_stdlib_context(ssl.PROTOCOL_TLSv1) + self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1) + self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + + ctx = ssl._create_stdlib_context(ssl.PROTOCOL_TLSv1, + cert_reqs=ssl.CERT_REQUIRED) + self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1) + self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + + ctx = ssl._create_stdlib_context(purpose=ssl.Purpose.CLIENT_AUTH) + self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23) + self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) class SSLErrorTests(unittest.TestCase): diff -r 16dd19aa64c8 Lib/urllib/request.py --- a/Lib/urllib/request.py Sat Nov 23 15:59:07 2013 +0100 +++ b/Lib/urllib/request.py Sat Nov 23 16:27:25 2013 +0100 @@ -141,13 +141,9 @@ if cafile or capath or cadefault: if not _have_ssl: raise ValueError('SSL support not available') - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - context.options |= ssl.OP_NO_SSLv2 - context.verify_mode = ssl.CERT_REQUIRED - if cafile or capath: - context.load_verify_locations(cafile, capath) - else: - context.set_default_verify_paths() + context = ssl._create_stdlib_context(cert_reqs=ssl.CERT_REQUIRED, + cafile=cafile, + capath=capath) https_handler = HTTPSHandler(context=context, check_hostname=True) opener = build_opener(https_handler) elif _opener is None: