Index: Doc/library/ssl.rst =================================================================== --- Doc/library/ssl.rst (révision 80543) +++ Doc/library/ssl.rst (copie de travail) @@ -47,7 +47,7 @@ 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, ciphers=None) +.. 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, ssl_options=0) Takes an instance ``sock`` of :class:`socket.socket`, and returns an instance of :class:`ssl.SSLSocket`, a subtype of :class:`socket.socket`, which wraps @@ -141,9 +141,15 @@ 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. + The parameter ``ssl_options`` specifies various options related to the + functioning of the SSL protocol. It currently allows any combination + (by OR'ing them) of the three values :const:`OP_NO_SSLv2`, + :const:`OP_NO_SSLv3` and :const:`OP_NO_TLSv1`. + .. versionchanged:: 3.2 - New optional argument *ciphers*. + New optional arguments *ciphers* and *options*. + .. function:: RAND_status() Returns True if the SSL pseudo-random number generator has been seeded with @@ -279,7 +285,31 @@ .. versionadded:: 3.2 +.. data:: OP_NO_SSLv2 + Prevents an SSLv2 connection. This option is only applicable in + conjunction with 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 SSLv23. It prevents the peers from choosing SSLv3 as + the protocol version. + + .. versionadded:: 3.2 + +.. data:: OP_NO_TLSv1 + + Prevents an TLSv1 connection. This option is only applicable in + conjunction with SSLv23. It prevents the peers from choosing TLSv1 as + the protocol version. + + .. versionadded:: 3.2 + + SSLSocket Objects ----------------- Index: Lib/ssl.py =================================================================== --- Lib/ssl.py (révision 80543) +++ Lib/ssl.py (copie de travail) @@ -63,6 +63,7 @@ from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED from _ssl import (PROTOCOL_SSLv2, PROTOCOL_SSLv3, PROTOCOL_SSLv23, PROTOCOL_TLSv1) +from _ssl import OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_TLSv1 from _ssl import RAND_status, RAND_egd, RAND_add from _ssl import ( SSL_ERROR_ZERO_RETURN, @@ -95,7 +96,7 @@ ssl_version=PROTOCOL_SSLv23, ca_certs=None, do_handshake_on_connect=True, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None, - suppress_ragged_eofs=True, ciphers=None): + suppress_ragged_eofs=True, ciphers=None, ssl_options=0): connected = False if sock is not None: @@ -130,7 +131,7 @@ self._sslobj = _ssl.sslwrap(self, server_side, keyfile, certfile, cert_reqs, ssl_version, ca_certs, - ciphers) + ciphers, ssl_options) if do_handshake_on_connect: timeout = self.gettimeout() if timeout == 0.0: @@ -148,6 +149,7 @@ self.ssl_version = ssl_version self.ca_certs = ca_certs self.ciphers = ciphers + self.ssl_options = ssl_options self.do_handshake_on_connect = do_handshake_on_connect self.suppress_ragged_eofs = suppress_ragged_eofs @@ -333,7 +335,8 @@ socket.connect(self, addr) self._sslobj = _ssl.sslwrap(self, False, self.keyfile, self.certfile, self.cert_reqs, self.ssl_version, - self.ca_certs, self.ciphers) + self.ca_certs, self.ciphers, + self.ssl_options) try: if self.do_handshake_on_connect: self.do_handshake() @@ -354,6 +357,7 @@ ssl_version=self.ssl_version, ca_certs=self.ca_certs, ciphers=self.ciphers, + ssl_options=self.ssl_options, do_handshake_on_connect= self.do_handshake_on_connect), addr) @@ -367,14 +371,14 @@ server_side=False, cert_reqs=CERT_NONE, ssl_version=PROTOCOL_SSLv23, ca_certs=None, do_handshake_on_connect=True, - suppress_ragged_eofs=True, ciphers=None): + suppress_ragged_eofs=True, ciphers=None, ssl_options=0): return SSLSocket(sock=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, - ciphers=ciphers) + ciphers=ciphers, ssl_options=ssl_options) # some utility functions Index: Lib/test/test_ssl.py =================================================================== --- Lib/test/test_ssl.py (révision 80543) +++ Lib/test/test_ssl.py (copie de travail) @@ -313,7 +313,8 @@ ssl_version=self.server.protocol, ca_certs=self.server.cacerts, cert_reqs=self.server.certreqs, - ciphers=self.server.ciphers) + ciphers=self.server.ciphers, + ssl_options=self.server.ssl_options) except ssl.SSLError: # XXX Various errors can have happened here, for example # a mismatching protocol version, an invalid certificate, @@ -408,7 +409,7 @@ def __init__(self, certificate, ssl_version=None, certreqs=None, cacerts=None, chatty=True, connectionchatty=False, starttls_server=False, - ciphers=None): + ciphers=None, ssl_options=0): if ssl_version is None: ssl_version = ssl.PROTOCOL_TLSv1 if certreqs is None: @@ -418,6 +419,7 @@ self.certreqs = certreqs self.cacerts = cacerts self.ciphers = ciphers + self.ssl_options = ssl_options self.chatty = chatty self.connectionchatty = connectionchatty self.starttls_server = starttls_server @@ -690,14 +692,16 @@ def serverParamsTest (certfile, protocol, certreqs, cacertsfile, client_certfile, client_protocol=None, - indata="FOO\n", - ciphers=None, chatty=False, connectionchatty=False): + indata="FOO\n", ciphers=None, + client_options=0, server_options=0, + chatty=False, connectionchatty=False): server = ThreadedEchoServer(certfile, certreqs=certreqs, ssl_version=protocol, cacerts=cacertsfile, ciphers=ciphers, + ssl_options=server_options, chatty=chatty, connectionchatty=False) flag = threading.Event() @@ -713,7 +717,8 @@ ca_certs=cacertsfile, ciphers=ciphers, cert_reqs=certreqs, - ssl_version=client_protocol) + ssl_version=client_protocol, + ssl_options=client_options) s.connect((HOST, server.port)) bindata = indata.encode('ASCII', 'strict') for arg in [bindata, bytearray(bindata), memoryview(bindata)]: @@ -741,10 +746,8 @@ server.stop() server.join() - def tryProtocolCombo (server_protocol, - client_protocol, - expectedToWork, - certsreqs=None): + def tryProtocolCombo(server_protocol, client_protocol, expectedToWork, + certsreqs=None, server_options=0, client_options=0): if certsreqs is None: certsreqs = ssl.CERT_NONE @@ -768,6 +771,8 @@ serverParamsTest(CERTFILE, server_protocol, certsreqs, CERTFILE, CERTFILE, client_protocol, ciphers="ALL", + client_options=client_options, + server_options=server_options, chatty=False, connectionchatty=False) # Protocol mismatch can result in either an SSLError, or a # "Connection reset by peer" error. @@ -895,6 +900,13 @@ tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, True) tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv3, False) tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_TLSv1, False) + # SSLv23 client with specific SSL options + tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, False, + client_options=ssl.OP_NO_SSLv2) + tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, True, + client_options=ssl.OP_NO_SSLv3) + tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, True, + client_options=ssl.OP_NO_TLSv1) def testProtocolSSL23(self): if support.verbose: @@ -919,6 +931,15 @@ tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_REQUIRED) tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED) + # Server with specific SSL options + tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, False, + server_options=ssl.OP_NO_SSLv3) + # Will choose TLSv1 + tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, + server_options=ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3) + tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, False, + server_options=ssl.OP_NO_TLSv1) + def testProtocolSSL3(self): if support.verbose: sys.stdout.write("\n") @@ -928,6 +949,9 @@ tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv2, False) tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, False) tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_TLSv1, False) + # No SSLv2 => client will use an SSLv3 hello + tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, True, + client_options=ssl.OP_NO_SSLv2) def testProtocolTLS1(self): if support.verbose: Index: Modules/_ssl.c =================================================================== --- Modules/_ssl.c (révision 80543) +++ Modules/_ssl.c (copie de travail) @@ -266,7 +266,7 @@ enum py_ssl_server_or_client socket_type, enum py_ssl_cert_requirements certreq, enum py_ssl_version proto_version, - char *cacerts_file, char *ciphers) + char *cacerts_file, char *ciphers, int options) { PySSLObject *self; char *errstr = NULL; @@ -366,7 +366,8 @@ } /* ssl compatibility */ - SSL_CTX_set_options(self->ctx, SSL_OP_ALL); + options |= SSL_OP_ALL; + SSL_CTX_set_options(self->ctx, options); verification_mode = SSL_VERIFY_NONE; if (certreq == PY_SSL_CERT_OPTIONAL) @@ -421,14 +422,15 @@ char *cert_file = NULL; char *cacerts_file = NULL; char *ciphers = NULL; + int options = 0; - if (!PyArg_ParseTuple(args, "O!i|zziizz:sslwrap", + if (!PyArg_ParseTuple(args, "O!i|zziizzi:sslwrap", PySocketModule.Sock_Type, &Sock, &server_side, &key_file, &cert_file, &verification_mode, &protocol, - &cacerts_file, &ciphers)) + &cacerts_file, &ciphers, &options)) return NULL; /* @@ -442,12 +444,13 @@ return (PyObject *) newPySSLObject(Sock, key_file, cert_file, server_side, verification_mode, protocol, cacerts_file, - ciphers); + ciphers, options); } PyDoc_STRVAR(ssl_doc, -"sslwrap(socket, server_side, [keyfile, certfile, certs_mode, protocol,\n" -" cacertsfile, ciphers]) -> sslobject"); +"sslwrap(socket, server_side, [keyfile, certfile, certs_mode,\n" +" protocol, cacertsfile,\n" +" ciphers, ssl_options]) -> sslobject"); /* SSL object methods */ @@ -1788,6 +1791,11 @@ PyModule_AddIntConstant(m, "PROTOCOL_TLSv1", PY_SSL_VERSION_TLS1); + /* protocol options */ + 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 */ /* SSLeay() gives us the version of the library linked against, which could be different from the headers version.