[PATCH 7 of 8] sslutil: allow fingerprints to be specified in [hostsecurity]

Augie Fackler raf at durin42.com
Sun May 29 17:01:21 EDT 2016


On Sat, May 28, 2016 at 01:04:29PM -0700, Gregory Szorc wrote:
> # HG changeset patch
> # User Gregory Szorc <gregory.szorc at gmail.com>
> # Date 1464464256 25200
> #      Sat May 28 12:37:36 2016 -0700
> # Node ID 969eddf5985326d5c4fd26da95fa19247339e6a0
> # Parent  fb7b49629e9c961517f576e8edfdfdc2bdb6d0e9
> sslutil: allow fingerprints to be specified in [hostsecurity]

I'm a fan of this overall, but I'm a little hesitant to introduce a
whole new section rather than figuring out how to sneak the new
behavior into the existing [hostfingerprints] section. Anyone want to
try and convince me one way or the other?

I've gone ahead and queued patches 1-6, since they seem to be on the
right track in any case.

>
> We introduce the [hostsecurity] config section. It holds per-host
> security settings.
>
> Currently, the section only contains a "fingerprints" option,
> which behaves like [hostfingerprints] but supports specifying the
> hashing algorithm.
>
> There is still some follow-up work, such as changing some error
> messages.
>
> diff --git a/mercurial/help/config.txt b/mercurial/help/config.txt
> --- a/mercurial/help/config.txt
> +++ b/mercurial/help/config.txt
> @@ -971,16 +971,18 @@ environment variables above are passed a
>
>  If a Python hook returns a "true" value or raises an exception, this
>  is treated as a failure.
>
>
>  ``hostfingerprints``
>  --------------------
>
> +(Deprecated. Use ``[hostsecurity]``'s ``fingerprints`` options instead.)
> +
>  Fingerprints of the certificates of known HTTPS servers.
>
>  A HTTPS connection to a server with a fingerprint configured here will
>  only succeed if the servers certificate matches the fingerprint.
>  This is very similar to how ssh known hosts works.
>
>  The fingerprint is the SHA-1 hash value of the DER encoded certificate.
>  Multiple values can be specified (separated by spaces or commas). This can
> @@ -990,16 +992,49 @@ to a new certificate.
>  The CA chain and web.cacerts is not used for servers with a fingerprint.
>
>  For example::
>
>      [hostfingerprints]
>      hg.intevation.de = fc:e2:8d:d9:51:cd:cb:c1:4d:18:6b:b7:44:8d:49:72:57:e6:cd:33
>      hg.intevation.org = fc:e2:8d:d9:51:cd:cb:c1:4d:18:6b:b7:44:8d:49:72:57:e6:cd:33
>
> +``hostsecurity``
> +----------------
> +
> +Used to specify per-host security settings.
> +
> +Options in this section have the form ``hostname``:``setting``. This allows
> +multiple settings to be defined on a per-host basis.
> +
> +The following per-host settings can be defined.
> +
> +``fingerprints``
> +    A list of hashes of the DER encoded peer/remote certificate. Values have
> +    the form ``algorithm``:``fingerprint``. e.g.
> +    ``sha256:c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2``.
> +
> +    The following algorithms/prefixes are supported: ``sha1``, ``sha256``,
> +    ``sha512``.
> +
> +    Use of ``sha256`` or ``sha512`` is preferred.
> +
> +    If a fingerprint is specified, the CA chain is not validated for this
> +    host and Mercurial will require the remote certificate to match one
> +    of the fingerprints specified. This means if the server updates its
> +    certificate, Mercurial will abort until a new fingerprint is defined.
> +    This can provide stronger security than traditional CA-based validation
> +    at the expense of convenience.
> +
> +For example::
> +
> +    [hostsecurity]
> +    hg.example.com:fingerprints = sha256:c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2
> +    hg2.example.com:fingerprints = sha1:914f1aff87249c09b6859b88b1906d30756491ca, sha1:fc:e2:8d:d9:51:cd:cb:c1:4d:18:6b:b7:44:8d:49:72:57:e6:cd:33
> +
>  ``http_proxy``
>  --------------
>
>  Used to access web-based Mercurial repositories through a HTTP
>  proxy.
>
>  ``host``
>      Host name and (optional) port of the proxy server, for example
> diff --git a/mercurial/sslutil.py b/mercurial/sslutil.py
> --- a/mercurial/sslutil.py
> +++ b/mercurial/sslutil.py
> @@ -116,16 +116,31 @@ def _hostsettings(ui, hostname):
>          'certfingerprints': [],
>          # Path to file containing concatenated CA certs. Used by
>          # SSLContext.load_verify_locations().
>          'cafile': None,
>          # ssl.CERT_* constant used by SSLContext.verify_mode.
>          'verifymode': None,
>      }
>
> +    # Look for fingerprints in [hostsecurity] section. Value is a list
> +    # of <alg>:<fingerprint> strings.
> +    fingerprints = ui.configlist('hostsecurity', '%s:fingerprints' % hostname,
> +                                 [])
> +    for fingerprint in fingerprints:
> +        if not (fingerprint.startswith(('sha1:', 'sha256:', 'sha512:'))):
> +            raise error.Abort(_('invalid fingerprint for %s: %s') % (
> +                                hostname, fingerprint),
> +                              hint=_('must begin with "sha1:", "sha256:", '
> +                                     'or "sha512:"'))
> +
> +        alg, fingerprint = fingerprint.split(':', 1)
> +        fingerprint = fingerprint.replace(':', '').lower()
> +        s['certfingerprints'].append((alg, fingerprint))
> +
>      # Fingerprints from [hostfingerprints] are always SHA-1.
>      for fingerprint in ui.configlist('hostfingerprints', hostname, []):
>          fingerprint = fingerprint.replace(':', '').lower()
>          s['certfingerprints'].append(('sha1', fingerprint))
>
>      # If a host cert fingerprint is defined, it is the only thing that
>      # matters. No need to validate CA certs.
>      if s['certfingerprints']:
> diff --git a/tests/test-https.t b/tests/test-https.t
> --- a/tests/test-https.t
> +++ b/tests/test-https.t
> @@ -277,35 +277,53 @@ Test server cert which no longer is vali
>    $ cat hg2.pid >> $DAEMON_PIDS
>    $ hg -R copy-pull pull --config web.cacerts=pub-expired.pem https://localhost:$HGPORT2/
>    pulling from https://localhost:$HGPORT2/
>    abort: error: *certificate verify failed* (glob)
>    [255]
>
>  Fingerprints
>
> -- works without cacerts
> +- works without cacerts (hostkeyfingerprints)
>    $ hg -R copy-pull id https://localhost:$HGPORT/ --insecure --config hostfingerprints.localhost=91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca
>    5fed3813f7f5
>
> +- works without cacerts (hostsecurity)
> +  $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha1:914f1aff87249c09b6859b88b1906d30756491ca
> +  5fed3813f7f5
> +
> +  $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=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
> +  5fed3813f7f5
> +
>  - multiple fingerprints specified and first matches
>    $ hg --config 'hostfingerprints.localhost=914f1aff87249c09b6859b88b1906d30756491ca, deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure
>    5fed3813f7f5
>
> +  $ hg --config 'hostsecurity.localhost:fingerprints=sha1:914f1aff87249c09b6859b88b1906d30756491ca, sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/
> +  5fed3813f7f5
> +
>  - multiple fingerprints specified and last matches
>    $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, 914f1aff87249c09b6859b88b1906d30756491ca' -R copy-pull id https://localhost:$HGPORT/ --insecure
>    5fed3813f7f5
>
> +  $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:914f1aff87249c09b6859b88b1906d30756491ca' -R copy-pull id https://localhost:$HGPORT/
> +  5fed3813f7f5
> +
>  - multiple fingerprints specified and none match
>
>    $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure
>    abort: certificate for localhost has unexpected fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca
>    (check hostfingerprint configuration)
>    [255]
>
> +  $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/
> +  abort: certificate for localhost has unexpected fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca
> +  (check hostfingerprint configuration)
> +  [255]
> +
>  - fails when cert doesn't match hostname (port is ignored)
>    $ hg -R copy-pull id https://localhost:$HGPORT1/ --config hostfingerprints.localhost=914f1aff87249c09b6859b88b1906d30756491ca
>    abort: certificate for localhost has unexpected fingerprint 28:ff:71:bf:65:31:14:23:ad:62:92:b4:0e:31:99:18:fc:83:e3:9b
>    (check hostfingerprint configuration)
>    [255]
>
>
>  - ignores that certificate doesn't match hostname
> _______________________________________________
> 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