Mercurial > hg-stable
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)