Submitter | Gregory Szorc |
---|---|
Date | May 28, 2016, 8:04 p.m. |
Message ID | <969eddf5985326d5c4fd.1464465869@ubuntu-vm-main> |
Download | mbox | patch |
Permalink | /patch/15235/ |
State | Accepted |
Headers | show |
Comments
Please provide an example of a host with two fingerprints (load balancing or incremental rollout) On May 28, 2016 4:05 PM, "Gregory Szorc" <gregory.szorc@gmail.com> wrote: > # HG changeset patch > # User Gregory Szorc <gregory.szorc@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] > > 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@mercurial-scm.org > https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel >
On Sat, May 28, 2016 at 7:14 PM, timeless <timeless@gmail.com> wrote: > Please provide an example of a host with two fingerprints (load balancing > or incremental rollout) > The last added line in config.txt does this. > On May 28, 2016 4:05 PM, "Gregory Szorc" <gregory.szorc@gmail.com> wrote: > >> # HG changeset patch >> # User Gregory Szorc <gregory.szorc@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] >> >> 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@mercurial-scm.org >> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel >> >
On Sat, May 28, 2016 at 01:04:29PM -0700, Gregory Szorc wrote: > # HG changeset patch > # User Gregory Szorc <gregory.szorc@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@mercurial-scm.org > https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
> On May 29, 2016, at 14:01, Augie Fackler <raf@durin42.com> wrote: > >> On Sat, May 28, 2016 at 01:04:29PM -0700, Gregory Szorc wrote: >> # HG changeset patch >> # User Gregory Szorc <gregory.szorc@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 plan on introducing a handful of additional per-host config options. CA file, SSL/TLS protocol version, etc. I would have shoehorned the fingerprint prefixing into [hostfingerprints] if that's all it was. > 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@mercurial-scm.org >> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
> On May 29, 2016, at 14:07, Gregory Szorc <gregory.szorc@gmail.com> wrote: > > > >> On May 29, 2016, at 14:01, Augie Fackler <raf@durin42.com> wrote: >> >>> On Sat, May 28, 2016 at 01:04:29PM -0700, Gregory Szorc wrote: >>> # HG changeset patch >>> # User Gregory Szorc <gregory.szorc@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 plan on introducing a handful of additional per-host config options. CA file, SSL/TLS protocol version, etc. I would have shoehorned the fingerprint prefixing into [hostfingerprints] if that's all it was. Sold. Thanks for the quick response. :) > >> 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@mercurial-scm.org >>> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Patch
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