Issue #8322: Add a *ciphers* argument to SSL sockets, so as to change the

available cipher list.  Helps fix test_ssl with OpenSSL 1.0.0.
This commit is contained in:
Antoine Pitrou 2010-04-17 17:10:38 +00:00
parent 7794b5b52f
commit 0a6373c1a9
5 changed files with 74 additions and 19 deletions

View File

@ -50,7 +50,7 @@ Functions, Constants, and Exceptions
is a subtype of :exc:`socket.error`, which in turn is a subtype of
:exc:`IOError`.
.. function:: wrap_socket (sock, keyfile=None, certfile=None, server_side=False, cert_reqs=CERT_NONE, ssl_version={see docs}, ca_certs=None, do_handshake_on_connect=True, suppress_ragged_eofs=True)
.. function:: wrap_socket (sock, keyfile=None, certfile=None, server_side=False, cert_reqs=CERT_NONE, ssl_version={see docs}, ca_certs=None, do_handshake_on_connect=True, suppress_ragged_eofs=True, ciphers=None)
Takes an instance ``sock`` of :class:`socket.socket`, and returns an instance
of :class:`ssl.SSLSocket`, a subtype of :class:`socket.socket`, which wraps
@ -113,14 +113,23 @@ Functions, Constants, and Exceptions
======================== ========= ========= ========== =========
*client* / **server** **SSLv2** **SSLv3** **SSLv23** **TLSv1**
------------------------ --------- --------- ---------- ---------
*SSLv2* yes no yes* no
*SSLv2* yes no yes no
*SSLv3* yes yes yes no
*SSLv23* yes no yes no
*TLSv1* no no yes yes
======================== ========= ========= ========== =========
In some older versions of OpenSSL (for instance, 0.9.7l on OS X 10.4), an
SSLv2 client could not connect to an SSLv23 server.
.. note::
This information varies depending on the version of OpenSSL.
For instance, in some older versions of OpenSSL (such as 0.9.7l on
OS X 10.4), an SSLv2 client could not connect to an SSLv23 server.
Conversely, starting from 1.0.0, an SSLv23 client will actually
try the SSLv3 protocol unless you explicitly enable SSLv2 ciphers.
The parameter ``ciphers`` sets the available ciphers for this SSL object.
It should be a string in the `OpenSSL cipher list format
<http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT>`_.
The parameter ``do_handshake_on_connect`` specifies whether to do the SSL
handshake automatically after doing a :meth:`socket.connect`, or whether the
@ -135,6 +144,9 @@ Functions, Constants, and Exceptions
normal EOF in response to unexpected EOF errors raised from the underlying
socket; if :const:`False`, it will raise the exceptions back to the caller.
.. versionchanged:: 2.7
New optional argument *ciphers*.
.. function:: RAND_status()
Returns True if the SSL pseudo-random number generator has been seeded with

View File

@ -89,7 +89,7 @@ class SSLSocket(socket):
server_side=False, cert_reqs=CERT_NONE,
ssl_version=PROTOCOL_SSLv23, ca_certs=None,
do_handshake_on_connect=True,
suppress_ragged_eofs=True):
suppress_ragged_eofs=True, ciphers=None):
socket.__init__(self, _sock=sock._sock)
# the initializer for socket trashes the methods (tsk, tsk), so...
self.send = lambda data, flags=0: SSLSocket.send(self, data, flags)
@ -111,7 +111,8 @@ class SSLSocket(socket):
# yes, create the SSL object
self._sslobj = _ssl.sslwrap(self._sock, server_side,
keyfile, certfile,
cert_reqs, ssl_version, ca_certs)
cert_reqs, ssl_version, ca_certs,
ciphers)
if do_handshake_on_connect:
timeout = self.gettimeout()
try:
@ -124,6 +125,7 @@ class SSLSocket(socket):
self.cert_reqs = cert_reqs
self.ssl_version = ssl_version
self.ca_certs = ca_certs
self.ciphers = ciphers
self.do_handshake_on_connect = do_handshake_on_connect
self.suppress_ragged_eofs = suppress_ragged_eofs
self._makefile_refs = 0
@ -291,7 +293,7 @@ class SSLSocket(socket):
socket.connect(self, addr)
self._sslobj = _ssl.sslwrap(self._sock, False, self.keyfile, self.certfile,
self.cert_reqs, self.ssl_version,
self.ca_certs)
self.ca_certs, self.ciphers)
if self.do_handshake_on_connect:
self.do_handshake()
@ -309,6 +311,7 @@ class SSLSocket(socket):
cert_reqs=self.cert_reqs,
ssl_version=self.ssl_version,
ca_certs=self.ca_certs,
ciphers=self.ciphers,
do_handshake_on_connect=self.do_handshake_on_connect,
suppress_ragged_eofs=self.suppress_ragged_eofs),
addr)
@ -328,13 +331,14 @@ def wrap_socket(sock, keyfile=None, certfile=None,
server_side=False, cert_reqs=CERT_NONE,
ssl_version=PROTOCOL_SSLv23, ca_certs=None,
do_handshake_on_connect=True,
suppress_ragged_eofs=True):
suppress_ragged_eofs=True, ciphers=None):
return SSLSocket(sock, keyfile=keyfile, certfile=certfile,
server_side=server_side, cert_reqs=cert_reqs,
ssl_version=ssl_version, ca_certs=ca_certs,
do_handshake_on_connect=do_handshake_on_connect,
suppress_ragged_eofs=suppress_ragged_eofs)
suppress_ragged_eofs=suppress_ragged_eofs,
ciphers=ciphers)
# some utility functions

View File

@ -137,6 +137,23 @@ class BasicTests(unittest.TestCase):
self.assertTrue(s.startswith("OpenSSL {:d}.{:d}.{:d}".format(major, minor, fix)),
(s, t))
def test_ciphers(self):
if not test_support.is_resource_enabled('network'):
return
remote = ("svn.python.org", 443)
s = ssl.wrap_socket(socket.socket(socket.AF_INET),
cert_reqs=ssl.CERT_NONE, ciphers="ALL")
s.connect(remote)
s = ssl.wrap_socket(socket.socket(socket.AF_INET),
cert_reqs=ssl.CERT_NONE, ciphers="DEFAULT")
s.connect(remote)
# Error checking occurs when connecting, because the SSL context
# isn't created before.
s = ssl.wrap_socket(socket.socket(socket.AF_INET),
cert_reqs=ssl.CERT_NONE, ciphers="^$:,;?*'dorothyx")
with self.assertRaisesRegexp(ssl.SSLError, "No cipher can be selected"):
s.connect(remote)
class NetworkedTests(unittest.TestCase):
@ -259,7 +276,8 @@ else:
certfile=self.server.certificate,
ssl_version=self.server.protocol,
ca_certs=self.server.cacerts,
cert_reqs=self.server.certreqs)
cert_reqs=self.server.certreqs,
ciphers=self.server.ciphers)
except:
if self.server.chatty:
handle_error("\n server: bad connection attempt from " +
@ -350,7 +368,7 @@ else:
def __init__(self, certificate, ssl_version=None,
certreqs=None, cacerts=None, expect_bad_connects=False,
chatty=True, connectionchatty=False, starttls_server=False,
wrap_accepting_socket=False):
wrap_accepting_socket=False, ciphers=None):
if ssl_version is None:
ssl_version = ssl.PROTOCOL_TLSv1
@ -360,6 +378,7 @@ else:
self.protocol = ssl_version
self.certreqs = certreqs
self.cacerts = cacerts
self.ciphers = ciphers
self.expect_bad_connects = expect_bad_connects
self.chatty = chatty
self.connectionchatty = connectionchatty
@ -371,7 +390,8 @@ else:
certfile=self.certificate,
cert_reqs = self.certreqs,
ca_certs = self.cacerts,
ssl_version = self.protocol)
ssl_version = self.protocol,
ciphers = self.ciphers)
if test_support.verbose and self.chatty:
sys.stdout.write(' server: wrapped server socket as %s\n' % str(self.sock))
self.port = test_support.bind_port(self.sock)
@ -657,13 +677,14 @@ else:
def serverParamsTest (certfile, protocol, certreqs, cacertsfile,
client_certfile, client_protocol=None, indata="FOO\n",
chatty=True, connectionchatty=False,
ciphers=None, chatty=True, connectionchatty=False,
wrap_accepting_socket=False):
server = ThreadedEchoServer(certfile,
certreqs=certreqs,
ssl_version=protocol,
cacerts=cacertsfile,
ciphers=ciphers,
chatty=chatty,
connectionchatty=connectionchatty,
wrap_accepting_socket=wrap_accepting_socket)
@ -679,6 +700,7 @@ else:
s = ssl.wrap_socket(socket.socket(),
certfile=client_certfile,
ca_certs=cacertsfile,
ciphers=ciphers,
cert_reqs=certreqs,
ssl_version=client_protocol)
s.connect((HOST, server.port))
@ -732,8 +754,12 @@ else:
ssl.get_protocol_name(server_protocol),
certtype))
try:
# NOTE: we must enable "ALL" ciphers, otherwise an SSLv23 client
# will send an SSLv3 hello (rather than SSLv2) starting from
# OpenSSL 1.0.0 (see issue #8322).
serverParamsTest(CERTFILE, server_protocol, certsreqs,
CERTFILE, CERTFILE, client_protocol, chatty=False)
CERTFILE, CERTFILE, client_protocol,
ciphers="ALL", chatty=False)
except test_support.TestFailed:
if expectedToWork:
raise

View File

@ -18,6 +18,9 @@ Core and Builtins
Library
-------
- Issue #8322: Add a *ciphers* argument to SSL sockets, so as to change the
available cipher list. Helps fix test_ssl with OpenSSL 1.0.0.
- Issue #2987: RFC2732 support for urlparse (IPv6 addresses). Patch by Tony
Locke and Hans Ulrich Niedermann.

View File

@ -259,7 +259,7 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file,
enum py_ssl_server_or_client socket_type,
enum py_ssl_cert_requirements certreq,
enum py_ssl_version proto_version,
char *cacerts_file)
char *cacerts_file, char *ciphers)
{
PySSLObject *self;
char *errstr = NULL;
@ -309,6 +309,14 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file,
goto fail;
}
if (ciphers != NULL) {
ret = SSL_CTX_set_cipher_list(self->ctx, ciphers);
if (ret == 0) {
errstr = ERRSTR("No cipher can be selected.");
goto fail;
}
}
if (certreq != PY_SSL_CERT_NONE) {
if (cacerts_file == NULL) {
errstr = ERRSTR("No root certificates specified for "
@ -408,14 +416,15 @@ PySSL_sslwrap(PyObject *self, PyObject *args)
char *key_file = NULL;
char *cert_file = NULL;
char *cacerts_file = NULL;
char *ciphers = NULL;
if (!PyArg_ParseTuple(args, "O!i|zziiz:sslwrap",
if (!PyArg_ParseTuple(args, "O!i|zziizz:sslwrap",
PySocketModule.Sock_Type,
&Sock,
&server_side,
&key_file, &cert_file,
&verification_mode, &protocol,
&cacerts_file))
&cacerts_file, &ciphers))
return NULL;
/*
@ -428,12 +437,13 @@ PySSL_sslwrap(PyObject *self, PyObject *args)
return (PyObject *) newPySSLObject(Sock, key_file, cert_file,
server_side, verification_mode,
protocol, cacerts_file);
protocol, cacerts_file,
ciphers);
}
PyDoc_STRVAR(ssl_doc,
"sslwrap(socket, server_side, [keyfile, certfile, certs_mode, protocol,\n"
" cacertsfile]) -> sslobject");
" cacertsfile, ciphers]) -> sslobject");
/* SSL object methods */