[PATCH 2 of 2] sslutil: try to find CA certficates in well-known locations
Augie Fackler
raf at durin42.com
Sun Jul 10 14:38:44 EDT 2016
On Wed, Jul 06, 2016 at 09:19:37PM -0700, Gregory Szorc wrote:
> # HG changeset patch
> # User Gregory Szorc <gregory.szorc at gmail.com>
> # Date 1467864960 25200
> # Wed Jul 06 21:16:00 2016 -0700
> # Node ID af8b4f956c0599ebf3749fe9e7740bc35e4f02fe
> # Parent 93723841473c4aa7bb794144e00a8377198c79f3
> sslutil: try to find CA certficates in well-known locations
Queued these, thanks.
>
> 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
> @@ -425,22 +425,32 @@ def _plainapplepython():
> cacerts file
> """
> if sys.platform != 'darwin' or util.mainfrozen() or not sys.executable:
> return False
> exe = os.path.realpath(sys.executable).lower()
> 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.
> try:
> import certifi
> certs = certifi.where()
> ui.debug('using ca certificates from certifi\n')
> return certs
> @@ -475,16 +485,38 @@ def _defaultcacerts(ui):
> # files. Also consider exporting the keychain certs to a file during
> # Mercurial install.
> if not _canloaddefaultcerts:
> 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
>
> + # 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):
> """Validate a socket meets security requiremnets.
>
> The passed socket must have been created with ``wrapsocket()``.
> """
> host = sock._hgstate['hostname']
> diff --git a/tests/test-https.t b/tests/test-https.t
> --- a/tests/test-https.t
> +++ b/tests/test-https.t
> @@ -51,16 +51,17 @@ we are able to load CA certs.
> $ hg clone https://localhost:$HGPORT/ copy-pull
> (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
> abort: error: *certificate verify failed* (glob)
> [255]
> #endif
>
> #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
>
> #if no-sslcontext windows
> $ hg clone https://localhost:$HGPORT/ copy-pull
> (unable to load Windows CA certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message)
> abort: error: *certificate verify failed* (glob)
> @@ -72,16 +73,17 @@ we are able to load CA certs.
> (unable to load CA certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message)
> abort: localhost certificate error: no certificate received
> (set hostsecurity.localhost:certfingerprints=sha256:62:09:97:2f:97:60:e3:65:8f:12:5d:78:9e:35:a1:36:7a:65:4b:0e:9f:ac:db:c3:bc:6e:b6:a3:c0:16:e0:30 config setting or use --insecure to connect insecurely)
> [255]
> #endif
>
> #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
>
> #if no-defaultcacerts
> $ hg clone https://localhost:$HGPORT/ copy-pull
> (unable to load * certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
> abort: localhost certificate error: no certificate received
> 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
> @@ -53,25 +53,27 @@ we are able to load CA certs:
> [255]
> #endif
>
> #if no-sslcontext defaultcacerts
> $ try
> 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
>
> #if defaultcacertsloaded
> $ try
> 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
>
> #if no-defaultcacerts
> $ try
> this patch series consists of 1 patches.
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
More information about the Mercurial-devel
mailing list