summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorNick Coghlan <ncoghlan@gmail.com>2016-03-20 12:39:15 (GMT)
committerNick Coghlan <ncoghlan@gmail.com>2016-03-20 12:39:15 (GMT)
commitdbcd4576244b9c9acc6201034b1dfcc858c541ed (patch)
treeb7c797fa121078fe2fd39fc04dcde8bb4ec82ab7 /Lib
parent3a4bdb6322fb9cf52baebf5c5ab72c0f8b52a18a (diff)
downloadcpython-dbcd4576244b9c9acc6201034b1dfcc858c541ed.zip
cpython-dbcd4576244b9c9acc6201034b1dfcc858c541ed.tar.gz
cpython-dbcd4576244b9c9acc6201034b1dfcc858c541ed.tar.bz2
Issue #23857: Implement PEP 493
Adds a Python-2-only ssl module API and environment variable to configure the default handling of SSL/TLS certificates for HTTPS connections.
Diffstat (limited to 'Lib')
-rw-r--r--Lib/ssl.py25
-rw-r--r--Lib/test/test_ssl.py52
2 files changed, 73 insertions, 4 deletions
diff --git a/Lib/ssl.py b/Lib/ssl.py
index 34f7aaa..febc547 100644
--- a/Lib/ssl.py
+++ b/Lib/ssl.py
@@ -482,13 +482,30 @@ def _create_unverified_context(protocol=PROTOCOL_SSLv23, cert_reqs=None,
return context
-# Used by http.client if no context is explicitly passed.
-_create_default_https_context = create_default_context
-
-
# Backwards compatibility alias, even though it's not a public name.
_create_stdlib_context = _create_unverified_context
+# PEP 493: Verify HTTPS by default, but allow envvar to override that
+_https_verify_envvar = 'PYTHONHTTPSVERIFY'
+
+def _get_https_context_factory():
+ if not sys.flags.ignore_environment:
+ config_setting = os.environ.get(_https_verify_envvar)
+ if config_setting == '0':
+ return _create_unverified_context
+ return create_default_context
+
+_create_default_https_context = _get_https_context_factory()
+
+# PEP 493: "private" API to configure HTTPS defaults without monkeypatching
+def _https_verify_certificates(enable=True):
+ """Verify server HTTPS certificates by default?"""
+ global _create_default_https_context
+ if enable:
+ _create_default_https_context = create_default_context
+ else:
+ _create_default_https_context = _create_unverified_context
+
class SSLSocket(socket):
"""This class implements a subtype of socket.socket that wraps
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index e9723a7..86ba655 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -4,6 +4,7 @@
import sys
import unittest
from test import test_support as support
+from test.script_helper import assert_python_ok
import asyncore
import socket
import select
@@ -1174,6 +1175,57 @@ class ContextTests(unittest.TestCase):
self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2)
+ def test__https_verify_certificates(self):
+ # Unit test to check the contect factory mapping
+ # The factories themselves are tested above
+ # This test will fail by design if run under PYTHONHTTPSVERIFY=0
+ # (as will various test_httplib tests)
+
+ # Uses a fresh SSL module to avoid affecting the real one
+ local_ssl = support.import_fresh_module("ssl")
+ # Certificate verification is enabled by default
+ self.assertIs(local_ssl._create_default_https_context,
+ local_ssl.create_default_context)
+ # Turn default verification off
+ local_ssl._https_verify_certificates(enable=False)
+ self.assertIs(local_ssl._create_default_https_context,
+ local_ssl._create_unverified_context)
+ # And back on
+ local_ssl._https_verify_certificates(enable=True)
+ self.assertIs(local_ssl._create_default_https_context,
+ local_ssl.create_default_context)
+ # The default behaviour is to enable
+ local_ssl._https_verify_certificates(enable=False)
+ local_ssl._https_verify_certificates()
+ self.assertIs(local_ssl._create_default_https_context,
+ local_ssl.create_default_context)
+
+ def test__https_verify_envvar(self):
+ # Unit test to check the PYTHONHTTPSVERIFY handling
+ # Need to use a subprocess so it can still be run under -E
+ https_is_verified = """import ssl, sys; \
+ status = "Error: _create_default_https_context does not verify certs" \
+ if ssl._create_default_https_context is \
+ ssl._create_unverified_context \
+ else None; \
+ sys.exit(status)"""
+ https_is_not_verified = """import ssl, sys; \
+ status = "Error: _create_default_https_context verifies certs" \
+ if ssl._create_default_https_context is \
+ ssl.create_default_context \
+ else None; \
+ sys.exit(status)"""
+ extra_env = {}
+ # Omitting it leaves verification on
+ assert_python_ok("-c", https_is_verified, **extra_env)
+ # Setting it to zero turns verification off
+ extra_env[ssl._https_verify_envvar] = "0"
+ assert_python_ok("-c", https_is_not_verified, **extra_env)
+ # Any other value should also leave it on
+ for setting in ("", "1", "enabled", "foo"):
+ extra_env[ssl._https_verify_envvar] = setting
+ assert_python_ok("-c", https_is_verified, **extra_env)
+
def test_check_hostname(self):
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
self.assertFalse(ctx.check_hostname)