Issue #4870: Add an options
attribute to SSL contexts, as well as
several ``OP_*`` constants to the `ssl` module. This allows to selectively disable protocol versions, when used in combination with `PROTOCOL_SSLv23`.
This commit is contained in:
parent
955d1b22e2
commit
b52187710e
@ -257,6 +257,37 @@ Functions, Constants, and Exceptions
|
|||||||
modern version, and probably the best choice for maximum protection, if both
|
modern version, and probably the best choice for maximum protection, if both
|
||||||
sides can speak it.
|
sides can speak it.
|
||||||
|
|
||||||
|
.. data:: OP_ALL
|
||||||
|
|
||||||
|
Enables workarounds for various bugs present in other SSL implementations.
|
||||||
|
This option is set by default.
|
||||||
|
|
||||||
|
.. versionadded:: 3.2
|
||||||
|
|
||||||
|
.. data:: OP_NO_SSLv2
|
||||||
|
|
||||||
|
Prevents an SSLv2 connection. This option is only applicable in
|
||||||
|
conjunction with :const:`PROTOCOL_SSLv23`. It prevents the peers from
|
||||||
|
choosing SSLv2 as the protocol version.
|
||||||
|
|
||||||
|
.. versionadded:: 3.2
|
||||||
|
|
||||||
|
.. data:: OP_NO_SSLv3
|
||||||
|
|
||||||
|
Prevents an SSLv3 connection. This option is only applicable in
|
||||||
|
conjunction with :const:`PROTOCOL_SSLv23`. It prevents the peers from
|
||||||
|
choosing SSLv3 as the protocol version.
|
||||||
|
|
||||||
|
.. versionadded:: 3.2
|
||||||
|
|
||||||
|
.. data:: OP_NO_TLSv1
|
||||||
|
|
||||||
|
Prevents a TLSv1 connection. This option is only applicable in
|
||||||
|
conjunction with :const:`PROTOCOL_SSLv23`. It prevents the peers from
|
||||||
|
choosing TLSv1 as the protocol version.
|
||||||
|
|
||||||
|
.. versionadded:: 3.2
|
||||||
|
|
||||||
.. data:: OPENSSL_VERSION
|
.. data:: OPENSSL_VERSION
|
||||||
|
|
||||||
The version string of the OpenSSL library loaded by the interpreter::
|
The version string of the OpenSSL library loaded by the interpreter::
|
||||||
@ -440,6 +471,17 @@ SSL Contexts
|
|||||||
and *suppress_ragged_eofs* have the same meaning as in the top-level
|
and *suppress_ragged_eofs* have the same meaning as in the top-level
|
||||||
:func:`wrap_socket` function.
|
:func:`wrap_socket` function.
|
||||||
|
|
||||||
|
.. attribute:: SSLContext.options
|
||||||
|
|
||||||
|
An integer representing the set of SSL options enabled on this context.
|
||||||
|
The default value is :data:`OP_ALL`, but you can specify other options
|
||||||
|
such as :data:`OP_NO_SSLv2` by ORing them together.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
With versions of OpenSSL older than 0.9.8m, it is only possible
|
||||||
|
to set options, not to clear them. Attempting to clear an option
|
||||||
|
(by resetting the corresponding bits) will raise a ``ValueError``.
|
||||||
|
|
||||||
.. attribute:: SSLContext.protocol
|
.. attribute:: SSLContext.protocol
|
||||||
|
|
||||||
The protocol version chosen when constructing the context. This attribute
|
The protocol version chosen when constructing the context. This attribute
|
||||||
@ -794,6 +836,20 @@ to specify :const:`CERT_REQUIRED` and similarly check the client certificate.
|
|||||||
equivalent unless anonymous ciphers are enabled (they are disabled
|
equivalent unless anonymous ciphers are enabled (they are disabled
|
||||||
by default).
|
by default).
|
||||||
|
|
||||||
|
Protocol versions
|
||||||
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
SSL version 2 is considered insecure and is therefore dangerous to use. If
|
||||||
|
you want maximum compatibility between clients and servers, it is recommended
|
||||||
|
to use :const:`PROTOCOL_SSLv23` as the protocol version and then disable
|
||||||
|
SSLv2 explicitly using the :data:`SSLContext.options` attribute::
|
||||||
|
|
||||||
|
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||||
|
context.options |= ssl.OP_NO_SSLv2
|
||||||
|
|
||||||
|
The SSL context created above will allow SSLv3 and TLSv1 connections, but
|
||||||
|
not SSLv2.
|
||||||
|
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
|
@ -63,6 +63,7 @@ from _ssl import _SSLContext, SSLError
|
|||||||
from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
|
from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
|
||||||
from _ssl import (PROTOCOL_SSLv2, PROTOCOL_SSLv3, PROTOCOL_SSLv23,
|
from _ssl import (PROTOCOL_SSLv2, PROTOCOL_SSLv3, PROTOCOL_SSLv23,
|
||||||
PROTOCOL_TLSv1)
|
PROTOCOL_TLSv1)
|
||||||
|
from _ssl import OP_ALL, OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_TLSv1
|
||||||
from _ssl import RAND_status, RAND_egd, RAND_add
|
from _ssl import RAND_status, RAND_egd, RAND_add
|
||||||
from _ssl import (
|
from _ssl import (
|
||||||
SSL_ERROR_ZERO_RETURN,
|
SSL_ERROR_ZERO_RETURN,
|
||||||
|
@ -57,6 +57,14 @@ def handle_error(prefix):
|
|||||||
if support.verbose:
|
if support.verbose:
|
||||||
sys.stdout.write(prefix + exc_format)
|
sys.stdout.write(prefix + exc_format)
|
||||||
|
|
||||||
|
def can_clear_options():
|
||||||
|
# 0.9.8m or higher
|
||||||
|
return ssl.OPENSSL_VERSION_INFO >= (0, 9, 8, 13, 15)
|
||||||
|
|
||||||
|
def no_sslv2_implies_sslv3_hello():
|
||||||
|
# 0.9.7h or higher
|
||||||
|
return ssl.OPENSSL_VERSION_INFO >= (0, 9, 7, 8, 15)
|
||||||
|
|
||||||
|
|
||||||
class BasicSocketTests(unittest.TestCase):
|
class BasicSocketTests(unittest.TestCase):
|
||||||
|
|
||||||
@ -189,6 +197,26 @@ class ContextTests(unittest.TestCase):
|
|||||||
with self.assertRaisesRegexp(ssl.SSLError, "No cipher can be selected"):
|
with self.assertRaisesRegexp(ssl.SSLError, "No cipher can be selected"):
|
||||||
ctx.set_ciphers("^$:,;?*'dorothyx")
|
ctx.set_ciphers("^$:,;?*'dorothyx")
|
||||||
|
|
||||||
|
def test_options(self):
|
||||||
|
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||||
|
# OP_ALL is the default value
|
||||||
|
self.assertEqual(ssl.OP_ALL, ctx.options)
|
||||||
|
ctx.options |= ssl.OP_NO_SSLv2
|
||||||
|
self.assertEqual(ssl.OP_ALL | ssl.OP_NO_SSLv2,
|
||||||
|
ctx.options)
|
||||||
|
ctx.options |= ssl.OP_NO_SSLv3
|
||||||
|
self.assertEqual(ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3,
|
||||||
|
ctx.options)
|
||||||
|
if can_clear_options():
|
||||||
|
ctx.options = (ctx.options & ~ssl.OP_NO_SSLv2) | ssl.OP_NO_TLSv1
|
||||||
|
self.assertEqual(ssl.OP_ALL | ssl.OP_NO_TLSv1 | ssl.OP_NO_SSLv3,
|
||||||
|
ctx.options)
|
||||||
|
ctx.options = 0
|
||||||
|
self.assertEqual(0, ctx.options)
|
||||||
|
else:
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
ctx.options = 0
|
||||||
|
|
||||||
def test_verify(self):
|
def test_verify(self):
|
||||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||||
# Default value
|
# Default value
|
||||||
@ -445,12 +473,8 @@ else:
|
|||||||
|
|
||||||
def wrap_conn(self):
|
def wrap_conn(self):
|
||||||
try:
|
try:
|
||||||
self.sslconn = ssl.wrap_socket(self.sock, server_side=True,
|
self.sslconn = self.server.context.wrap_socket(
|
||||||
certfile=self.server.certificate,
|
self.sock, server_side=True)
|
||||||
ssl_version=self.server.protocol,
|
|
||||||
ca_certs=self.server.cacerts,
|
|
||||||
cert_reqs=self.server.certreqs,
|
|
||||||
ciphers=self.server.ciphers)
|
|
||||||
except ssl.SSLError:
|
except ssl.SSLError:
|
||||||
# XXX Various errors can have happened here, for example
|
# XXX Various errors can have happened here, for example
|
||||||
# a mismatching protocol version, an invalid certificate,
|
# a mismatching protocol version, an invalid certificate,
|
||||||
@ -462,7 +486,7 @@ else:
|
|||||||
self.close()
|
self.close()
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
if self.server.certreqs == ssl.CERT_REQUIRED:
|
if self.server.context.verify_mode == ssl.CERT_REQUIRED:
|
||||||
cert = self.sslconn.getpeercert()
|
cert = self.sslconn.getpeercert()
|
||||||
if support.verbose and self.server.chatty:
|
if support.verbose and self.server.chatty:
|
||||||
sys.stdout.write(" client cert is " + pprint.pformat(cert) + "\n")
|
sys.stdout.write(" client cert is " + pprint.pformat(cert) + "\n")
|
||||||
@ -542,19 +566,24 @@ else:
|
|||||||
# harness, we want to stop the server
|
# harness, we want to stop the server
|
||||||
self.server.stop()
|
self.server.stop()
|
||||||
|
|
||||||
def __init__(self, certificate, ssl_version=None,
|
def __init__(self, certificate=None, ssl_version=None,
|
||||||
certreqs=None, cacerts=None,
|
certreqs=None, cacerts=None,
|
||||||
chatty=True, connectionchatty=False, starttls_server=False,
|
chatty=True, connectionchatty=False, starttls_server=False,
|
||||||
ciphers=None):
|
ciphers=None, context=None):
|
||||||
if ssl_version is None:
|
if context:
|
||||||
ssl_version = ssl.PROTOCOL_TLSv1
|
self.context = context
|
||||||
if certreqs is None:
|
else:
|
||||||
certreqs = ssl.CERT_NONE
|
self.context = ssl.SSLContext(ssl_version
|
||||||
self.certificate = certificate
|
if ssl_version is not None
|
||||||
self.protocol = ssl_version
|
else ssl.PROTOCOL_TLSv1)
|
||||||
self.certreqs = certreqs
|
self.context.verify_mode = (certreqs if certreqs is not None
|
||||||
self.cacerts = cacerts
|
else ssl.CERT_NONE)
|
||||||
self.ciphers = ciphers
|
if cacerts:
|
||||||
|
self.context.load_verify_locations(cacerts)
|
||||||
|
if certificate:
|
||||||
|
self.context.load_cert_chain(certificate)
|
||||||
|
if ciphers:
|
||||||
|
self.context.set_ciphers(ciphers)
|
||||||
self.chatty = chatty
|
self.chatty = chatty
|
||||||
self.connectionchatty = connectionchatty
|
self.connectionchatty = connectionchatty
|
||||||
self.starttls_server = starttls_server
|
self.starttls_server = starttls_server
|
||||||
@ -820,18 +849,13 @@ else:
|
|||||||
server.stop()
|
server.stop()
|
||||||
server.join()
|
server.join()
|
||||||
|
|
||||||
def server_params_test(certfile, protocol, certreqs, cacertsfile,
|
def server_params_test(client_context, server_context, indata=b"FOO\n",
|
||||||
client_certfile, client_protocol=None, indata=b"FOO\n",
|
chatty=True, connectionchatty=False):
|
||||||
ciphers=None, chatty=True, connectionchatty=False):
|
|
||||||
"""
|
"""
|
||||||
Launch a server, connect a client to it and try various reads
|
Launch a server, connect a client to it and try various reads
|
||||||
and writes.
|
and writes.
|
||||||
"""
|
"""
|
||||||
server = ThreadedEchoServer(certfile,
|
server = ThreadedEchoServer(context=server_context,
|
||||||
certreqs=certreqs,
|
|
||||||
ssl_version=protocol,
|
|
||||||
cacerts=cacertsfile,
|
|
||||||
ciphers=ciphers,
|
|
||||||
chatty=chatty,
|
chatty=chatty,
|
||||||
connectionchatty=False)
|
connectionchatty=False)
|
||||||
flag = threading.Event()
|
flag = threading.Event()
|
||||||
@ -839,15 +863,8 @@ else:
|
|||||||
# wait for it to start
|
# wait for it to start
|
||||||
flag.wait()
|
flag.wait()
|
||||||
# try to connect
|
# try to connect
|
||||||
if client_protocol is None:
|
|
||||||
client_protocol = protocol
|
|
||||||
try:
|
try:
|
||||||
s = ssl.wrap_socket(socket.socket(),
|
s = client_context.wrap_socket(socket.socket())
|
||||||
certfile=client_certfile,
|
|
||||||
ca_certs=cacertsfile,
|
|
||||||
ciphers=ciphers,
|
|
||||||
cert_reqs=certreqs,
|
|
||||||
ssl_version=client_protocol)
|
|
||||||
s.connect((HOST, server.port))
|
s.connect((HOST, server.port))
|
||||||
for arg in [indata, bytearray(indata), memoryview(indata)]:
|
for arg in [indata, bytearray(indata), memoryview(indata)]:
|
||||||
if connectionchatty:
|
if connectionchatty:
|
||||||
@ -873,10 +890,8 @@ else:
|
|||||||
server.stop()
|
server.stop()
|
||||||
server.join()
|
server.join()
|
||||||
|
|
||||||
def try_protocol_combo(server_protocol,
|
def try_protocol_combo(server_protocol, client_protocol, expect_success,
|
||||||
client_protocol,
|
certsreqs=None, server_options=0, client_options=0):
|
||||||
expect_success,
|
|
||||||
certsreqs=None):
|
|
||||||
if certsreqs is None:
|
if certsreqs is None:
|
||||||
certsreqs = ssl.CERT_NONE
|
certsreqs = ssl.CERT_NONE
|
||||||
certtype = {
|
certtype = {
|
||||||
@ -890,14 +905,21 @@ else:
|
|||||||
(ssl.get_protocol_name(client_protocol),
|
(ssl.get_protocol_name(client_protocol),
|
||||||
ssl.get_protocol_name(server_protocol),
|
ssl.get_protocol_name(server_protocol),
|
||||||
certtype))
|
certtype))
|
||||||
try:
|
client_context = ssl.SSLContext(client_protocol)
|
||||||
|
client_context.options = ssl.OP_ALL | client_options
|
||||||
|
server_context = ssl.SSLContext(server_protocol)
|
||||||
|
server_context.options = ssl.OP_ALL | server_options
|
||||||
|
for ctx in (client_context, server_context):
|
||||||
|
ctx.verify_mode = certsreqs
|
||||||
# NOTE: we must enable "ALL" ciphers, otherwise an SSLv23 client
|
# NOTE: we must enable "ALL" ciphers, otherwise an SSLv23 client
|
||||||
# will send an SSLv3 hello (rather than SSLv2) starting from
|
# will send an SSLv3 hello (rather than SSLv2) starting from
|
||||||
# OpenSSL 1.0.0 (see issue #8322).
|
# OpenSSL 1.0.0 (see issue #8322).
|
||||||
server_params_test(CERTFILE, server_protocol, certsreqs,
|
ctx.set_ciphers("ALL")
|
||||||
CERTFILE, CERTFILE, client_protocol,
|
ctx.load_cert_chain(CERTFILE)
|
||||||
ciphers="ALL", chatty=False,
|
ctx.load_verify_locations(CERTFILE)
|
||||||
connectionchatty=False)
|
try:
|
||||||
|
server_params_test(client_context, server_context,
|
||||||
|
chatty=False, connectionchatty=False)
|
||||||
# Protocol mismatch can result in either an SSLError, or a
|
# Protocol mismatch can result in either an SSLError, or a
|
||||||
# "Connection reset by peer" error.
|
# "Connection reset by peer" error.
|
||||||
except ssl.SSLError:
|
except ssl.SSLError:
|
||||||
@ -920,30 +942,27 @@ else:
|
|||||||
"""Basic test of an SSL client connecting to a server"""
|
"""Basic test of an SSL client connecting to a server"""
|
||||||
if support.verbose:
|
if support.verbose:
|
||||||
sys.stdout.write("\n")
|
sys.stdout.write("\n")
|
||||||
server_params_test(CERTFILE, ssl.PROTOCOL_TLSv1, ssl.CERT_NONE,
|
for protocol in PROTOCOLS:
|
||||||
CERTFILE, CERTFILE, ssl.PROTOCOL_TLSv1,
|
context = ssl.SSLContext(protocol)
|
||||||
|
context.load_cert_chain(CERTFILE)
|
||||||
|
server_params_test(context, context,
|
||||||
chatty=True, connectionchatty=True)
|
chatty=True, connectionchatty=True)
|
||||||
|
|
||||||
def test_getpeercert(self):
|
def test_getpeercert(self):
|
||||||
if support.verbose:
|
if support.verbose:
|
||||||
sys.stdout.write("\n")
|
sys.stdout.write("\n")
|
||||||
s2 = socket.socket()
|
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||||
server = ThreadedEchoServer(CERTFILE,
|
context.verify_mode = ssl.CERT_REQUIRED
|
||||||
certreqs=ssl.CERT_NONE,
|
context.load_verify_locations(CERTFILE)
|
||||||
ssl_version=ssl.PROTOCOL_SSLv23,
|
context.load_cert_chain(CERTFILE)
|
||||||
cacerts=CERTFILE,
|
server = ThreadedEchoServer(context=context, chatty=False)
|
||||||
chatty=False)
|
|
||||||
flag = threading.Event()
|
flag = threading.Event()
|
||||||
server.start(flag)
|
server.start(flag)
|
||||||
# wait for it to start
|
# wait for it to start
|
||||||
flag.wait()
|
flag.wait()
|
||||||
# try to connect
|
# try to connect
|
||||||
try:
|
try:
|
||||||
s = ssl.wrap_socket(socket.socket(),
|
s = context.wrap_socket(socket.socket())
|
||||||
certfile=CERTFILE,
|
|
||||||
ca_certs=CERTFILE,
|
|
||||||
cert_reqs=ssl.CERT_REQUIRED,
|
|
||||||
ssl_version=ssl.PROTOCOL_SSLv23)
|
|
||||||
s.connect((HOST, server.port))
|
s.connect((HOST, server.port))
|
||||||
cert = s.getpeercert()
|
cert = s.getpeercert()
|
||||||
self.assertTrue(cert, "Can't get peer certificate.")
|
self.assertTrue(cert, "Can't get peer certificate.")
|
||||||
@ -1031,6 +1050,15 @@ else:
|
|||||||
try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, True)
|
try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, True)
|
||||||
try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv3, False)
|
try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv3, False)
|
||||||
try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_TLSv1, False)
|
try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_TLSv1, False)
|
||||||
|
# SSLv23 client with specific SSL options
|
||||||
|
if no_sslv2_implies_sslv3_hello():
|
||||||
|
# No SSLv2 => client will use an SSLv3 hello on recent OpenSSLs
|
||||||
|
try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, False,
|
||||||
|
client_options=ssl.OP_NO_SSLv2)
|
||||||
|
try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, True,
|
||||||
|
client_options=ssl.OP_NO_SSLv3)
|
||||||
|
try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, True,
|
||||||
|
client_options=ssl.OP_NO_TLSv1)
|
||||||
|
|
||||||
def test_protocol_sslv23(self):
|
def test_protocol_sslv23(self):
|
||||||
"""Connecting to an SSLv23 server with various client options"""
|
"""Connecting to an SSLv23 server with various client options"""
|
||||||
@ -1056,6 +1084,16 @@ else:
|
|||||||
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_REQUIRED)
|
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_REQUIRED)
|
||||||
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED)
|
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED)
|
||||||
|
|
||||||
|
# Server with specific SSL options
|
||||||
|
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, False,
|
||||||
|
server_options=ssl.OP_NO_SSLv3)
|
||||||
|
# Will choose TLSv1
|
||||||
|
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True,
|
||||||
|
server_options=ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3)
|
||||||
|
try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, False,
|
||||||
|
server_options=ssl.OP_NO_TLSv1)
|
||||||
|
|
||||||
|
|
||||||
def test_protocol_sslv3(self):
|
def test_protocol_sslv3(self):
|
||||||
"""Connecting to an SSLv3 server with various client options"""
|
"""Connecting to an SSLv3 server with various client options"""
|
||||||
if support.verbose:
|
if support.verbose:
|
||||||
@ -1066,6 +1104,10 @@ else:
|
|||||||
try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv2, False)
|
try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv2, False)
|
||||||
try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, False)
|
try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, False)
|
||||||
try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_TLSv1, False)
|
try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_TLSv1, False)
|
||||||
|
if no_sslv2_implies_sslv3_hello():
|
||||||
|
# No SSLv2 => client will use an SSLv3 hello on recent OpenSSLs
|
||||||
|
try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, True,
|
||||||
|
client_options=ssl.OP_NO_SSLv2)
|
||||||
|
|
||||||
def test_protocol_tlsv1(self):
|
def test_protocol_tlsv1(self):
|
||||||
"""Connecting to a TLSv1 server with various client options"""
|
"""Connecting to a TLSv1 server with various client options"""
|
||||||
|
@ -375,6 +375,10 @@ C-API
|
|||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #4870: Add an `options` attribute to SSL contexts, as well as
|
||||||
|
several ``OP_*`` constants to the `ssl` module. This allows to selectively
|
||||||
|
disable protocol versions, when used in combination with `PROTOCOL_SSLv23`.
|
||||||
|
|
||||||
- Issue #8759: Fixed user paths in sysconfig for posix and os2 schemes.
|
- Issue #8759: Fixed user paths in sysconfig for posix and os2 schemes.
|
||||||
|
|
||||||
- Issue #8663: distutils.log emulates backslashreplace error handler. Fix
|
- Issue #8663: distutils.log emulates backslashreplace error handler. Fix
|
||||||
|
@ -113,6 +113,13 @@ static unsigned int _ssl_locks_count = 0;
|
|||||||
# undef HAVE_OPENSSL_RAND
|
# undef HAVE_OPENSSL_RAND
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* SSL_CTX_clear_options() and SSL_clear_options() were first added in OpenSSL 0.9.8m */
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x009080dfL
|
||||||
|
# define HAVE_SSL_CTX_CLEAR_OPTIONS
|
||||||
|
#else
|
||||||
|
# undef HAVE_SSL_CTX_CLEAR_OPTIONS
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
SSL_CTX *ctx;
|
SSL_CTX *ctx;
|
||||||
@ -1513,6 +1520,35 @@ set_verify_mode(PySSLContext *self, PyObject *arg, void *c)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
get_options(PySSLContext *self, void *c)
|
||||||
|
{
|
||||||
|
return PyLong_FromLong(SSL_CTX_get_options(self->ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
set_options(PySSLContext *self, PyObject *arg, void *c)
|
||||||
|
{
|
||||||
|
long new_opts, opts, set, clear;
|
||||||
|
if (!PyArg_Parse(arg, "l", &new_opts))
|
||||||
|
return -1;
|
||||||
|
opts = SSL_CTX_get_options(self->ctx);
|
||||||
|
clear = opts & ~new_opts;
|
||||||
|
set = ~opts & new_opts;
|
||||||
|
if (clear) {
|
||||||
|
#ifdef HAVE_SSL_CTX_CLEAR_OPTIONS
|
||||||
|
SSL_CTX_clear_options(self->ctx, clear);
|
||||||
|
#else
|
||||||
|
PyErr_SetString(PyExc_ValueError,
|
||||||
|
"can't clear options before OpenSSL 0.9.8m");
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (set)
|
||||||
|
SSL_CTX_set_options(self->ctx, set);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
load_cert_chain(PySSLContext *self, PyObject *args, PyObject *kwds)
|
load_cert_chain(PySSLContext *self, PyObject *args, PyObject *kwds)
|
||||||
{
|
{
|
||||||
@ -1636,6 +1672,8 @@ context_wrap_socket(PySSLContext *self, PyObject *args, PyObject *kwds)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyGetSetDef context_getsetlist[] = {
|
static PyGetSetDef context_getsetlist[] = {
|
||||||
|
{"options", (getter) get_options,
|
||||||
|
(setter) set_options, NULL},
|
||||||
{"verify_mode", (getter) get_verify_mode,
|
{"verify_mode", (getter) get_verify_mode,
|
||||||
(setter) set_verify_mode, NULL},
|
(setter) set_verify_mode, NULL},
|
||||||
{NULL}, /* sentinel */
|
{NULL}, /* sentinel */
|
||||||
@ -1953,6 +1991,12 @@ PyInit__ssl(void)
|
|||||||
PyModule_AddIntConstant(m, "PROTOCOL_TLSv1",
|
PyModule_AddIntConstant(m, "PROTOCOL_TLSv1",
|
||||||
PY_SSL_VERSION_TLS1);
|
PY_SSL_VERSION_TLS1);
|
||||||
|
|
||||||
|
/* protocol options */
|
||||||
|
PyModule_AddIntConstant(m, "OP_ALL", SSL_OP_ALL);
|
||||||
|
PyModule_AddIntConstant(m, "OP_NO_SSLv2", SSL_OP_NO_SSLv2);
|
||||||
|
PyModule_AddIntConstant(m, "OP_NO_SSLv3", SSL_OP_NO_SSLv3);
|
||||||
|
PyModule_AddIntConstant(m, "OP_NO_TLSv1", SSL_OP_NO_TLSv1);
|
||||||
|
|
||||||
/* OpenSSL version */
|
/* OpenSSL version */
|
||||||
/* SSLeay() gives us the version of the library linked against,
|
/* SSLeay() gives us the version of the library linked against,
|
||||||
which could be different from the headers version.
|
which could be different from the headers version.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user