[PATCH] https: support tls sni (server name indication) for https urls (issue3090)

Augie Fackler raf at durin42.com
Tue Jan 13 13:05:55 CST 2015


On Mon, Jan 12, 2015 at 06:11:05PM -0700, Alex Orange wrote:
> # HG changeset patch
> # User Alex Orange <crazycasta at gmail.com>
> # Date 1421110880 25200
> #      Mon Jan 12 18:01:20 2015 -0700
> # Node ID b683de04cfd910f8f9c1c9400c748a11ef853c35
> # Parent  678f53865c6860a950392691814766957ee89316
> https: support tls sni (server name indication) for https urls (issue3090)

This looks like a good start. I'll do Antoine's proposal as a
followup, as well as a couple of other tweaks I see that'll make doing
that less of a headache.

Many thanks! This has been on my todo list since the backport was announced.

>
> SNI is a common way of sharing servers across multiple domains using separate
> SSL certificates. As of Python 2.7.9 SSLContext has been backported from
> Python 3. This patch changes sslutil's ssl_wrap_socket to use SSLContext and
> take a server hostname as and argument. It also changes the url module to make
> use of this argument.
>
> diff -r 678f53865c68 -r b683de04cfd9 mercurial/sslutil.py
> --- a/mercurial/sslutil.py	Thu Jan 08 00:01:03 2015 +0100
> +++ b/mercurial/sslutil.py	Mon Jan 12 18:01:20 2015 -0700
> @@ -15,16 +15,39 @@
>      import ssl
>      CERT_REQUIRED = ssl.CERT_REQUIRED
>      PROTOCOL_TLSv1 = ssl.PROTOCOL_TLSv1
> -    def ssl_wrap_socket(sock, keyfile, certfile, ssl_version=PROTOCOL_TLSv1,
> -                cert_reqs=ssl.CERT_NONE, ca_certs=None):
> -        sslsocket = ssl.wrap_socket(sock, keyfile, certfile,
> -                                    cert_reqs=cert_reqs, ca_certs=ca_certs,
> -                                    ssl_version=ssl_version)
> -        # check if wrap_socket failed silently because socket had been closed
> -        # - see http://bugs.python.org/issue13721
> -        if not sslsocket.cipher():
> -            raise util.Abort(_('ssl connection failed'))
> -        return sslsocket
> +    try:
> +        ssl_context = ssl.SSLContext
> +
> +        def ssl_wrap_socket(sock, keyfile, certfile, ssl_version=PROTOCOL_TLSv1,
> +                            cert_reqs=ssl.CERT_NONE, ca_certs=None,
> +                            server_hostname=None):
> +            sslcontext = ssl.SSLContext(ssl_version)
> +            if keyfile is not None:
> +                sslcontext.load_cert_chain(certfile, keyfile)
> +            sslcontext.verify_mode = cert_reqs
> +            sslcontext.load_verify_locations(cafile=ca_certs)
> +
> +            sslsocket = sslcontext.wrap_socket(sock,
> +                                               server_hostname=server_hostname)
> +            # check if wrap_socket failed silently because socket had been
> +            # closed
> +            # - see http://bugs.python.org/issue13721
> +            if not sslsocket.cipher():
> +                raise util.Abort(_('ssl connection failed'))
> +            return sslsocket
> +    except AttributeError:
> +        def ssl_wrap_socket(sock, keyfile, certfile, ssl_version=PROTOCOL_TLSv1,
> +                            cert_reqs=ssl.CERT_NONE, ca_certs=None,
> +                            server_hostname=None):
> +            sslsocket = ssl.wrap_socket(sock, keyfile, certfile,
> +                                        cert_reqs=cert_reqs, ca_certs=ca_certs,
> +                                        ssl_version=ssl_version)
> +            # check if wrap_socket failed silently because socket had been
> +            # closed
> +            # - see http://bugs.python.org/issue13721
> +            if not sslsocket.cipher():
> +                raise util.Abort(_('ssl connection failed'))
> +            return sslsocket
>  except ImportError:
>      CERT_REQUIRED = 2
>
> @@ -33,7 +56,8 @@
>      import socket, httplib
>
>      def ssl_wrap_socket(sock, keyfile, certfile, ssl_version=PROTOCOL_TLSv1,
> -                        cert_reqs=CERT_REQUIRED, ca_certs=None):
> +                        cert_reqs=CERT_REQUIRED, ca_certs=None,
> +                        server_hostname=None):
>          if not util.safehasattr(socket, 'ssl'):
>              raise util.Abort(_('Python SSL support not found'))
>          if ca_certs:
> diff -r 678f53865c68 -r b683de04cfd9 mercurial/url.py
> --- a/mercurial/url.py	Thu Jan 08 00:01:03 2015 +0100
> +++ b/mercurial/url.py	Mon Jan 12 18:01:20 2015 -0700
> @@ -185,7 +185,8 @@
>              self.sock.connect((self.host, self.port))
>              if _generic_proxytunnel(self):
>                  # we do not support client X.509 certificates
> -                self.sock = sslutil.ssl_wrap_socket(self.sock, None, None)
> +                self.sock = sslutil.ssl_wrap_socket(self.sock, None, None,
> +                                                    server_hostname=self.host)
>          else:
>              keepalive.HTTPConnection.connect(self)
>
> @@ -341,7 +342,7 @@
>                  _generic_proxytunnel(self)
>                  host = self.realhostport.rsplit(':', 1)[0]
>              self.sock = sslutil.ssl_wrap_socket(
> -                self.sock, self.key_file, self.cert_file,
> +                self.sock, self.key_file, self.cert_file, server_hostname=host,
>                  **sslutil.sslkwargs(self.ui, host))
>              sslutil.validator(self.ui, host)(self.sock)
>
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel


More information about the Mercurial-devel mailing list