changeset 29113:5b9577edf745

sslutil: use CA loaded state to drive validation logic Until now, sslkwargs may set web.cacerts=! to indicate that system certs could not be found. This is really obtuse because sslkwargs effectively sets state on a global object which bypasses wrapsocket() and is later consulted by validator.__call__. This is madness. This patch introduces an attribute on the wrapped socket instance indicating whether system CAs were loaded. We can set this directly inside wrapsocket() because that function knows everything that sslkwargs() does - and more. With this attribute set on the socket, we refactor validator.__call__ to use it. Since we no longer have a need for setting web.cacerts=! in sslkwargs, we remove that. I think the new logic is much easier to understand and will enable behavior to be changed more easily.
author Gregory Szorc <gregory.szorc@gmail.com>
date Thu, 05 May 2016 00:38:18 -0700
parents 5edc5acecc83
children f1081aea19b1
files mercurial/sslutil.py
diffstat 1 files changed, 25 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/sslutil.py	Thu May 05 00:37:28 2016 -0700
+++ b/mercurial/sslutil.py	Thu May 05 00:38:18 2016 -0700
@@ -155,9 +155,11 @@
 
     if ca_certs is not None:
         sslcontext.load_verify_locations(cafile=ca_certs)
+        caloaded = True
     else:
         # This is a no-op on old Python.
         sslcontext.load_default_certs()
+        caloaded = _canloaddefaultcerts
 
     sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname)
     # check if wrap_socket failed silently because socket had been
@@ -165,6 +167,9 @@
     # - see http://bugs.python.org/issue13721
     if not sslsocket.cipher():
         raise error.Abort(_('ssl connection failed'))
+
+    sslsocket._hgcaloaded = caloaded
+
     return sslsocket
 
 def _verifycert(cert, hostname):
@@ -280,12 +285,6 @@
         kws['cert_reqs'] = ssl.CERT_REQUIRED
         return kws
 
-    # This is effectively indicating that no CAs can be loaded because
-    # we can't get here if web.cacerts is set or if we can find
-    # CA certs elsewhere. Using a config option (which is later
-    # consulted by validator.__call__ is not very obvious).
-    # FUTURE fix this
-    ui.setconfig('web', 'cacerts', '!', 'defaultcacerts')
     return kws
 
 class validator(object):
@@ -342,23 +341,23 @@
                          (host, nicefingerprint))
             return
 
-        # No pinned fingerprint. Establish trust by looking at the CAs.
-        cacerts = self.ui.config('web', 'cacerts')
-        if cacerts != '!':
-            msg = _verifycert(peercert2, host)
-            if msg:
-                raise error.Abort(_('%s certificate error: %s') % (host, msg),
-                                 hint=_('configure hostfingerprint %s or use '
-                                        '--insecure to connect insecurely') %
-                                      nicefingerprint)
-            self.ui.debug('%s certificate successfully verified\n' % host)
-        elif strict:
-            raise error.Abort(_('%s certificate with fingerprint %s not '
-                               'verified') % (host, nicefingerprint),
-                             hint=_('check hostfingerprints or web.cacerts '
-                                     'config setting'))
-        else:
-            self.ui.warn(_('warning: %s certificate with fingerprint %s not '
-                           'verified (check hostfingerprints or web.cacerts '
-                           'config setting)\n') %
-                         (host, nicefingerprint))
+        if not sock._hgcaloaded:
+            if strict:
+                raise error.Abort(_('%s certificate with fingerprint %s not '
+                                    'verified') % (host, nicefingerprint),
+                                  hint=_('check hostfingerprints or '
+                                         'web.cacerts config setting'))
+            else:
+                self.ui.warn(_('warning: %s certificate with fingerprint %s '
+                               'not verified (check hostfingerprints or '
+                               'web.cacerts config setting)\n') %
+                             (host, nicefingerprint))
+
+            return
+
+        msg = _verifycert(peercert2, host)
+        if msg:
+            raise error.Abort(_('%s certificate error: %s') % (host, msg),
+                             hint=_('configure hostfingerprint %s or use '
+                                    '--insecure to connect insecurely') %
+                                  nicefingerprint)