# HG changeset patch # User Gregory Szorc # Date 2016-05-05 07:38:18 # Node ID 5b9577edf745e12ec22a6b16612e7a8fe1f0833c # Parent 5edc5acecc83aa0155119f16333aded1180215e3 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. diff --git a/mercurial/sslutil.py b/mercurial/sslutil.py --- a/mercurial/sslutil.py +++ b/mercurial/sslutil.py @@ -155,9 +155,11 @@ def wrapsocket(sock, keyfile, certfile, 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 @@ def wrapsocket(sock, keyfile, certfile, # - 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 @@ def sslkwargs(ui, host): 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 @@ class validator(object): (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)