comparison mercurial/sslutil.py @ 29411:e1778b9c8d53

sslutil: abort when unable to verify peer connection (BC) Previously, when we connected to a server and were unable to verify its certificate against a trusted certificate authority we would issue a warning and continue to connect. This is obviously not great behavior because the x509 certificate model is based upon trust of specific CAs. Failure to enforce that trust erodes security. This behavior was defined several years ago when Python did not support loading the system trusted CA store (Python 2.7.9's backports of Python 3's improvements to the "ssl" module enabled this). This commit changes behavior when connecting to abort if the peer certificate can't be validated. With an empty/default Mercurial configuration, the peer certificate can be validated if Python is able to load the system trusted CA store. Environments able to load the system trusted CA store include: * Python 2.7.9+ on most platforms and installations * Python 2.7 distributions with a modern ssl module (e.g. RHEL7's patched 2.7.5 package) * Python shipped on OS X Environments unable to load the system trusted CA store include: * Python 2.6 * Python 2.7 on many existing Linux installs (because they don't ship 2.7.9+ or haven't backported modern ssl module) * Python 2.7.9+ on some installs where Python is unable to locate the system CA store (this is hopefully rare) Users of these Pythongs will need to configure Mercurial to load the system CA store using web.cacerts. This should ideally be performed by packagers (by setting web.cacerts in the global/system hgrc file). Where Mercurial packagers aren't setting this, the linked URL in the new abort message can contain instructions for users. In the future, we may want to add more code for finding the system CA store. For example, many Linux distributions have the CA store at well-known locations (such as /etc/ssl/certs/ca-certificates.crt in the case of Ubuntu). This will enable CA loading to "just work" on more Python configurations and will be best for our users since they won't have to change anything after upgrading to a Mercurial with this patch. We may also want to consider distributing a trusted CA store with Mercurial. Although we should think long and hard about that because most systems have a global CA store and Mercurial should almost certainly use the same store used by everything else on the system.
author Gregory Szorc <gregory.szorc@gmail.com>
date Sat, 25 Jun 2016 07:26:43 -0700
parents 222f6834c69a
children 2f7f1e10f840
comparison
equal deleted inserted replaced
29410:222f6834c69a 29411:e1778b9c8d53
205 if cafile or (_canloaddefaultcerts and s['allowloaddefaultcerts']): 205 if cafile or (_canloaddefaultcerts and s['allowloaddefaultcerts']):
206 s['verifymode'] = ssl.CERT_REQUIRED 206 s['verifymode'] = ssl.CERT_REQUIRED
207 else: 207 else:
208 # At this point we don't have a fingerprint, aren't being 208 # At this point we don't have a fingerprint, aren't being
209 # explicitly insecure, and can't load CA certs. Connecting 209 # explicitly insecure, and can't load CA certs. Connecting
210 # at this point is insecure. But we do it for BC reasons. 210 # is insecure. We allow the connection and abort during
211 # TODO abort here to make secure by default. 211 # validation (once we have the fingerprint to print to the
212 # user).
212 s['verifymode'] = ssl.CERT_NONE 213 s['verifymode'] = ssl.CERT_NONE
213 214
214 assert s['verifymode'] is not None 215 assert s['verifymode'] is not None
215 216
216 return s 217 return s
411 nice = '%s:%s' % (hash, fmtfingerprint(peerfingerprints[hash])) 412 nice = '%s:%s' % (hash, fmtfingerprint(peerfingerprints[hash]))
412 raise error.Abort(_('certificate for %s has unexpected ' 413 raise error.Abort(_('certificate for %s has unexpected '
413 'fingerprint %s') % (host, nice), 414 'fingerprint %s') % (host, nice),
414 hint=_('check %s configuration') % section) 415 hint=_('check %s configuration') % section)
415 416
417 # Security is enabled but no CAs are loaded. We can't establish trust
418 # for the cert so abort.
416 if not sock._hgstate['caloaded']: 419 if not sock._hgstate['caloaded']:
417 ui.warn(_('warning: certificate for %s not verified ' 420 raise error.Abort(
418 '(set hostsecurity.%s:certfingerprints=%s or web.cacerts ' 421 _('unable to verify security of %s (no loaded CA certificates); '
419 'config settings)\n') % (host, host, nicefingerprint)) 422 'refusing to connect') % host,
420 return 423 hint=_('see https://mercurial-scm.org/wiki/SecureConnections for '
424 'how to configure Mercurial to avoid this error or set '
425 'hostsecurity.%s:fingerprints=%s to trust this server') %
426 (host, nicefingerprint))
421 427
422 msg = _verifycert(peercert2, host) 428 msg = _verifycert(peercert2, host)
423 if msg: 429 if msg:
424 raise error.Abort(_('%s certificate error: %s') % (host, msg), 430 raise error.Abort(_('%s certificate error: %s') % (host, msg),
425 hint=_('set hostsecurity.%s:certfingerprints=%s ' 431 hint=_('set hostsecurity.%s:certfingerprints=%s '