Index: Doc/library/ssl.rst =================================================================== --- Doc/library/ssl.rst (révision 80291) +++ 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 80291) +++ 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, @@ -94,7 +95,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): if sock is not None: socket.__init__(self, @@ -124,7 +125,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: @@ -142,6 +143,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 @@ -327,7 +329,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() @@ -348,6 +351,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) @@ -361,14 +365,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 80291) +++ Lib/test/test_ssl.py (copie de travail) @@ -252,7 +252,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: if self.server.chatty: handle_error("\n server: bad connection attempt from " + repr(self.addr) + ":\n") @@ -352,7 +353,7 @@ def __init__(self, certificate, ssl_version=None, certreqs=None, cacerts=None, expect_bad_connects=False, 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: @@ -362,6 +363,7 @@ self.certreqs = certreqs self.cacerts = cacerts self.ciphers = ciphers + self.ssl_options = ssl_options self.expect_bad_connects = expect_bad_connects self.chatty = chatty self.connectionchatty = connectionchatty @@ -667,14 +669,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() @@ -691,6 +695,7 @@ ca_certs=cacertsfile, cert_reqs=certreqs, ciphers=ciphers, + ssl_options=client_options, ssl_version=client_protocol) s.connect((HOST, server.port)) except ssl.SSLError as x: @@ -724,10 +729,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 @@ -751,6 +754,8 @@ serverParamsTest(CERTFILE, server_protocol, certsreqs, CERTFILE, CERTFILE, client_protocol, ciphers="ALL", + client_options=client_options, + server_options=server_options, chatty=False, connectionchatty=False) except support.TestFailed: if expectedToWork: @@ -887,6 +892,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: @@ -911,6 +923,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") @@ -920,6 +941,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 80291) +++ Modules/_ssl.c (copie de travail) @@ -262,7 +262,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; @@ -362,7 +362,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) @@ -417,14 +418,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; /* @@ -438,12 +440,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 */ @@ -1725,6 +1728,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.