Index: Doc/library/ssl.rst =================================================================== --- Doc/library/ssl.rst (revision 74602) +++ Doc/library/ssl.rst (working copy) @@ -51,7 +51,7 @@ Functions, Constants, and Exceptions network connection. This error 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, server_hostname=None, do_handshake_on_connect=True, suppress_ragged_eofs=True) Takes an instance ``sock`` of :class:`socket.socket`, and returns an instance of :class:`ssl.SSLSocket`, a subtype of :class:`socket.socket`, which wraps the underlying socket in an SSL context. @@ -119,6 +119,10 @@ Functions, Constants, and Exceptions 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. + The parameter ``server_hostname`` specifies the hostname in the SSL client connection + (SSLv3 or TLSv1) allowing the server to run multiple certificates on a single host + (Server Name Indication extension defined in RFC 4366). + The parameter ``do_handshake_on_connect`` specifies whether to do the SSL handshake automatically after doing a :meth:`socket.connect`, or whether the application program will call it explicitly, by invoking the :meth:`SSLSocket.do_handshake` @@ -598,3 +602,6 @@ And go back to listening for new client connection `RFC 3280: Internet X.509 Public Key Infrastructure Certificate and CRL Profile `_ Housley et. al. + + `RFC 4366: Transport Layer Security (TLS) Extensions `_ + Blake-Wilson et. al. Index: Lib/ssl.py =================================================================== --- Lib/ssl.py (revision 74602) +++ Lib/ssl.py (working copy) @@ -91,6 +91,7 @@ class SSLSocket(socket): def __init__(self, sock=None, keyfile=None, certfile=None, server_side=False, cert_reqs=CERT_NONE, ssl_version=PROTOCOL_SSLv23, ca_certs=None, + server_hostname=None, do_handshake_on_connect=True, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None, suppress_ragged_eofs=True): @@ -122,7 +123,8 @@ class SSLSocket(socket): try: self._sslobj = _ssl.sslwrap(self, server_side, keyfile, certfile, - cert_reqs, ssl_version, ca_certs) + cert_reqs, ssl_version, + ca_certs, server_hostname) if do_handshake_on_connect: timeout = self.gettimeout() if timeout == 0.0: @@ -139,6 +141,7 @@ class SSLSocket(socket): self.cert_reqs = cert_reqs self.ssl_version = ssl_version self.ca_certs = ca_certs + self.server_hostname = server_hostname self.do_handshake_on_connect = do_handshake_on_connect self.suppress_ragged_eofs = suppress_ragged_eofs @@ -339,7 +342,7 @@ class SSLSocket(socket): socket.connect(self, addr) self._sslobj = _ssl.sslwrap(self, False, self.keyfile, self.certfile, self.cert_reqs, self.ssl_version, - self.ca_certs) + self.ca_certs, self.server_hostname) try: if self.do_handshake_on_connect: self.do_handshake() @@ -371,12 +374,14 @@ class SSLSocket(socket): def wrap_socket(sock, keyfile=None, certfile=None, server_side=False, cert_reqs=CERT_NONE, ssl_version=PROTOCOL_SSLv23, ca_certs=None, + server_hostname=None, do_handshake_on_connect=True, suppress_ragged_eofs=True): return SSLSocket(sock=sock, keyfile=keyfile, certfile=certfile, server_side=server_side, cert_reqs=cert_reqs, ssl_version=ssl_version, ca_certs=ca_certs, + server_hostname=server_hostname, do_handshake_on_connect=do_handshake_on_connect, suppress_ragged_eofs=suppress_ragged_eofs) Index: Lib/test/test_ssl.py =================================================================== --- Lib/test/test_ssl.py (revision 74602) +++ Lib/test/test_ssl.py (working copy) @@ -623,7 +623,8 @@ else: def serverParamsTest (certfile, protocol, certreqs, cacertsfile, client_certfile, client_protocol=None, indata="FOO\n", - chatty=False, connectionchatty=False): + chatty=False, connectionchatty=False, + server_hostname=None): server = ThreadedEchoServer(certfile, certreqs=certreqs, @@ -644,7 +645,8 @@ else: certfile=client_certfile, ca_certs=cacertsfile, cert_reqs=certreqs, - ssl_version=client_protocol) + ssl_version=client_protocol, + server_hostname=server_hostname) s.connect((HOST, server.port)) except ssl.SSLError as x: raise support.TestFailed("Unexpected SSL error: " + str(x)) @@ -720,6 +722,24 @@ else: CERTFILE, CERTFILE, ssl.PROTOCOL_TLSv1, chatty=True, connectionchatty=True) + def testSNI_TLS (self): + + if support.verbose: + sys.stdout.write("\n") + serverParamsTest(CERTFILE, ssl.PROTOCOL_TLSv1, ssl.CERT_NONE, + CERTFILE, CERTFILE, ssl.PROTOCOL_TLSv1, + chatty=True,connectionchatty=True, + server_hostname='arbitary.host.name') + + def testSNI_SSL2 (self): + + if support.verbose: + sys.stdout.write("\n") + serverParamsTest(CERTFILE, ssl.PROTOCOL_SSLv2, ssl.CERT_NONE, + CERTFILE, CERTFILE, ssl.PROTOCOL_SSLv2, + chatty=True,connectionchatty=True, + server_hostname='arbitary.host.name') + def testReadCert(self): if support.verbose: Index: Modules/_ssl.c =================================================================== --- Modules/_ssl.c (revision 74602) +++ Modules/_ssl.c (working copy) @@ -264,7 +264,7 @@ newPySSLObject(PySocketSockObject *Sock, char *key 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 *server_hostname) { PySSLObject *self; char *errstr = NULL; @@ -369,6 +369,14 @@ newPySSLObject(PySocketSockObject *Sock, char *key PySSL_BEGIN_ALLOW_THREADS self->ssl = SSL_new(self->ctx); /* New ssl struct */ +#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) + /* If SNI isn't supported, we just don't call it and fail silently, + * as there's not much else we can do. + */ + if ((socket_type == PY_SSL_CLIENT) && + (proto_version != PY_SSL_VERSION_SSL2) && server_hostname) + SSL_set_tlsext_host_name(self->ssl, server_hostname); +#endif PySSL_END_ALLOW_THREADS SSL_set_fd(self->ssl, Sock->sock_fd); /* Set the socket for SSL */ @@ -407,32 +415,34 @@ PySSL_sslwrap(PyObject *self, PyObject *args) char *key_file = NULL; char *cert_file = NULL; char *cacerts_file = NULL; + char *server_hostname = 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, &server_hostname)) return NULL; /* fprintf(stderr, "server_side is %d, keyfile %p, certfile %p, verify_mode %d, " - "protocol %d, certs %p\n", + "protocol %d, certs %p, server_hostname %p\n", server_side, key_file, cert_file, verification_mode, - protocol, cacerts_file); + protocol, cacerts_file, server_hostname); */ return (PyObject *) newPySSLObject(Sock, key_file, cert_file, server_side, verification_mode, - protocol, cacerts_file); + protocol, cacerts_file, + server_hostname); } PyDoc_STRVAR(ssl_doc, "sslwrap(socket, server_side, [keyfile, certfile, certs_mode, protocol,\n" -" cacertsfile]) -> sslobject"); +" cacertsfile, server_hostname]) -> sslobject"); /* SSL object methods */