[PATCH 4 of 4 STABLE] sslutil: improve messaging around unsupported protocols (issue5303)

Gregory Szorc gregory.szorc at gmail.com
Wed Jul 20 00:31:56 EDT 2016


# HG changeset patch
# User Gregory Szorc <gregory.szorc at gmail.com>
# Date 1468987798 25200
#      Tue Jul 19 21:09:58 2016 -0700
# Branch stable
# Node ID 139d8ab2ebb7368d1dc78e18f27b63df483b4e31
# Parent  925cb3b2baebcbf8f0498f2a7e7cbd6610732cbb
sslutil: improve messaging around unsupported protocols (issue5303)

There are various causes for the inability to negotiate common SSL/TLS
protocol between client and server. Previously, we had a single, not
very actionable warning message for all of them.

As people encountered TLS 1.0 servers in real life, it was quickly
obvious that the existing messaging was inadequate to help users
rectify the situation.

This patch makes the warning messages much more verbose in hopes of
making them more actionable while simultaneously encouraging users
and servers to adopt better security practices.

This messaging flirts with the anti-pattern of "never blame the
user" by signaling out poorly-configured servers. But if we're going to
disallow TLS 1.0 by default, I think we need to say *something* or
people are just going to blame Mercurial for not being able to connect.
The messaging tries to exonerate Mercurial from being the at fault
party by pointing out the server is the entity that doesn't support
proper security (when appropriate, of course).

diff --git a/mercurial/sslutil.py b/mercurial/sslutil.py
--- a/mercurial/sslutil.py
+++ b/mercurial/sslutil.py
@@ -412,21 +412,67 @@ def wrapsocket(sock, keyfile, certfile, 
         if (caloaded and settings['verifymode'] == ssl.CERT_REQUIRED and
             modernssl and not sslcontext.get_ca_certs()):
             ui.warn(_('(an attempt was made to load CA certificates but none '
                       'were loaded; see '
                       'https://mercurial-scm.org/wiki/SecureConnections for '
                       'how to configure Mercurial to avoid this error)\n'))
         # Try to print more helpful error messages for known failures.
         if util.safehasattr(e, 'reason'):
+            # This error occurs when the client and server don't share a
+            # common/supported SSL/TLS protocol. We've disabled SSLv2 and SSLv3
+            # outright. Hopefully the reason for this error is that we require
+            # TLS 1.1+ and the server only supports TLS 1.0. Whatever the
+            # reason, try to emit an actionable warning.
             if e.reason == 'UNSUPPORTED_PROTOCOL':
-                ui.warn(_('(could not negotiate a common protocol; see '
-                          'https://mercurial-scm.org/wiki/SecureConnections '
-                          'for how to configure Mercurial to avoid this '
-                          'error)\n'))
+                # We attempted TLS 1.0+.
+                if settings['protocolui'] == 'tls1.0':
+                    # We support more than just TLS 1.0+. If this happens,
+                    # the likely scenario is either the client or the server
+                    # is really old. (e.g. server doesn't support TLS 1.0+ or
+                    # client doesn't support modern TLS versions introduced
+                    # several years from when this comment was written).
+                    if supportedprotocols != set(['tls1.0']):
+                        ui.warn(_(
+                            '(could not communicate with %s using security '
+                            'protocols %s; if you are using a modern Mercurial '
+                            'version, consider contacting the operator of this '
+                            'server; see '
+                            'https://mercurial-scm.org/wiki/SecureConnections '
+                            'for more info)\n') % (
+                                serverhostname,
+                                ', '.join(sorted(supportedprotocols))))
+                    else:
+                        ui.warn(_(
+                            '(could not communicate with %s using TLS 1.0; the '
+                            'likely cause of this is the server no longer '
+                            'supports TLS 1.0 because it has known security '
+                            'vulnerabilities; see '
+                            'https://mercurial-scm.org/wiki/SecureConnections '
+                            'for more info)\n') % serverhostname)
+                else:
+                    # We attempted TLS 1.1+. We can only get here if the client
+                    # supports the configured protocol. So the likely reason is
+                    # the client wants better security than the server can
+                    # offer.
+                    ui.warn(_(
+                        '(could not negotiate a common security protocol (%s+) '
+                        'with %s; the likely cause is Mercurial is configured '
+                        'to be more secure than the server can support)\n') % (
+                        settings['protocolui'], serverhostname))
+                    ui.warn(_('(consider contacting the operator of this '
+                              'server and ask them to support modern TLS '
+                              'protocol versions; or, set '
+                              'hostsecurity.%s:minimumprotocol=tls1.0 to allow '
+                              'use of legacy, less secure protocols when '
+                              'communicating with this server)\n') %
+                            serverhostname)
+                    ui.warn(_(
+                        '(see https://mercurial-scm.org/wiki/SecureConnections '
+                        'for more info)\n'))
         raise
 
     # check if wrap_socket failed silently because socket had been
     # closed
     # - see http://bugs.python.org/issue13721
     if not sslsocket.cipher():
         raise error.Abort(_('ssl connection failed'))
 
diff --git a/tests/test-https.t b/tests/test-https.t
--- a/tests/test-https.t
+++ b/tests/test-https.t
@@ -464,30 +464,38 @@ Clients talking same TLS versions work
   $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.1 id https://localhost:$HGPORT1/
   5fed3813f7f5
   $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT2/
   5fed3813f7f5
 
 Clients requiring newer TLS version than what server supports fail
 
   $ P="$CERTSDIR" hg id https://localhost:$HGPORT/
-  (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
+  (could not negotiate a common security protocol (tls1.1+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
+  (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
+  (see https://mercurial-scm.org/wiki/SecureConnections for more info)
   abort: error: *unsupported protocol* (glob)
   [255]
 
   $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.1 id https://localhost:$HGPORT/
-  (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
+  (could not negotiate a common security protocol (tls1.1+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
+  (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
+  (see https://mercurial-scm.org/wiki/SecureConnections for more info)
   abort: error: *unsupported protocol* (glob)
   [255]
   $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT/
-  (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
+  (could not negotiate a common security protocol (tls1.2+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
+  (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
+  (see https://mercurial-scm.org/wiki/SecureConnections for more info)
   abort: error: *unsupported protocol* (glob)
   [255]
   $ P="$CERTSDIR" hg --config hostsecurity.minimumprotocol=tls1.2 id https://localhost:$HGPORT1/
-  (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
+  (could not negotiate a common security protocol (tls1.2+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
+  (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
+  (see https://mercurial-scm.org/wiki/SecureConnections for more info)
   abort: error: *unsupported protocol* (glob)
   [255]
 
 --insecure will allow TLS 1.0 connections and override configs
 
   $ hg --config hostsecurity.minimumprotocol=tls1.2 id --insecure https://localhost:$HGPORT1/
   warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
   5fed3813f7f5
@@ -498,28 +506,32 @@ The per-host config option overrides the
   > --config hostsecurity.minimumprotocol=tls1.2 \
   > --config hostsecurity.localhost:minimumprotocol=tls1.0
   5fed3813f7f5
 
 The per-host config option by itself works
 
   $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
   > --config hostsecurity.localhost:minimumprotocol=tls1.2
-  (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
+  (could not negotiate a common security protocol (tls1.2+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
+  (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
+  (see https://mercurial-scm.org/wiki/SecureConnections for more info)
   abort: error: *unsupported protocol* (glob)
   [255]
 
 .hg/hgrc file [hostsecurity] settings are applied to remote ui instances (issue5305)
 
   $ cat >> copy-pull/.hg/hgrc << EOF
   > [hostsecurity]
   > localhost:minimumprotocol=tls1.2
   > EOF
   $ P="$CERTSDIR" hg -R copy-pull id https://localhost:$HGPORT/
-  (could not negotiate a common protocol; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
+  (could not negotiate a common security protocol (tls1.2+) with localhost; the likely cause is Mercurial is configured to be more secure than the server can support)
+  (consider contacting the operator of this server and ask them to support modern TLS protocol versions; or, set hostsecurity.localhost:minimumprotocol=tls1.0 to allow use of legacy, less secure protocols when communicating with this server)
+  (see https://mercurial-scm.org/wiki/SecureConnections for more info)
   abort: error: [SSL: UNSUPPORTED_PROTOCOL] unsupported protocol (_ssl.c:590)
   [255]
 
   $ killdaemons.py hg0.pid
   $ killdaemons.py hg1.pid
   $ killdaemons.py hg2.pid
 #endif
 


More information about the Mercurial-devel mailing list