# HG changeset patch # User Gregory Szorc # Date 2016-07-07 04:16:00 # Node ID 4b16a5bd99484cac96ade9af76e6deee1fc57b54 # Parent 9c5325c7968308c7c86928109564dbb8453a3e83 sslutil: try to find CA certficates in well-known locations Many Linux distros and other Nixen have CA certificates in well-defined locations. Rather than potentially fail to load any CA certificates at all (which will always result in a certificate verification failure), we scan for paths to known CA certificate files and load one if seen. Because a proper Mercurial install will have the path to the CA certificate file defined at install time, we print a warning that the install isn't proper and provide a URL with instructions to correct things. We only perform path-based fallback on Pythons that don't know how to call into OpenSSL to load the default verify locations. This is because we trust that Python/OpenSSL is properly configured and knows better than Mercurial. So this new code effectively only runs on Python <2.7.9 (technically Pythons without the modern ssl module). diff --git a/mercurial/sslutil.py b/mercurial/sslutil.py --- a/mercurial/sslutil.py +++ b/mercurial/sslutil.py @@ -430,12 +430,22 @@ def _plainapplepython(): return (exe.startswith('/usr/bin/python') or exe.startswith('/system/library/frameworks/python.framework/')) +_systemcacertpaths = [ + # RHEL, CentOS, and Fedora + '/etc/pki/tls/certs/ca-bundle.trust.crt', + # Debian, Ubuntu, Gentoo + '/etc/ssl/certs/ca-certificates.crt', +] + def _defaultcacerts(ui): """return path to default CA certificates or None. It is assumed this function is called when the returned certificates file will actually be used to validate connections. Therefore this function may print warnings or debug messages assuming this usage. + + We don't print a message when the Python is able to load default + CA certs because this scenario is detected at socket connect time. """ # The "certifi" Python package provides certificates. If it is installed, # assume the user intends it to be used and use it. @@ -480,6 +490,28 @@ def _defaultcacerts(ui): 'how to configure Mercurial to avoid this message)\n')) return None + # Try to find CA certificates in well-known locations. We print a warning + # when using a found file because we don't want too much silent magic + # for security settings. The expectation is that proper Mercurial + # installs will have the CA certs path defined at install time and the + # installer/packager will make an appropriate decision on the user's + # behalf. We only get here and perform this setting as a feature of + # last resort. + if not _canloaddefaultcerts: + for path in _systemcacertpaths: + if os.path.isfile(path): + ui.warn(_('(using CA certificates from %s; if you see this ' + 'message, your Mercurial install is not properly ' + 'configured; see ' + 'https://mercurial-scm.org/wiki/SecureConnections ' + 'for how to configure Mercurial to avoid this ' + 'message)\n') % path) + return path + + ui.warn(_('(unable to load CA certificates; see ' + 'https://mercurial-scm.org/wiki/SecureConnections for ' + 'how to configure Mercurial to avoid this message)\n')) + return None def validatesocket(sock): diff --git a/tests/test-https.t b/tests/test-https.t --- a/tests/test-https.t +++ b/tests/test-https.t @@ -56,6 +56,7 @@ we are able to load CA certs. #if no-sslcontext defaultcacerts $ hg clone https://localhost:$HGPORT/ copy-pull + (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?) abort: error: *certificate verify failed* (glob) [255] #endif @@ -77,6 +78,7 @@ we are able to load CA certs. #if defaultcacertsloaded $ hg clone https://localhost:$HGPORT/ copy-pull + (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?) abort: error: *certificate verify failed* (glob) [255] #endif diff --git a/tests/test-patchbomb-tls.t b/tests/test-patchbomb-tls.t --- a/tests/test-patchbomb-tls.t +++ b/tests/test-patchbomb-tls.t @@ -58,6 +58,7 @@ we are able to load CA certs: this patch series consists of 1 patches. + (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?) (?i)abort: .*?certificate.verify.failed.* (re) [255] #endif @@ -67,6 +68,7 @@ we are able to load CA certs: this patch series consists of 1 patches. + (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?) (?i)abort: .*?certificate.verify.failed.* (re) [255]