@@ -4,16 +4,18 @@
# Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from __future__ import absolute_import
+import base64
+import binascii
import hashlib
import os
import re
import ssl
import sys
from .i18n import _
from . import (
@@ -137,24 +139,28 @@ def _hostsettings(ui, 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))
- s['legacyfingerprint'] = True
+ # Internally fingerprints are stored as base64.
+ try:
+ raw = binascii.unhexlify(fingerprint)
+ s['certfingerprints'].append(('sha1', base64.b64encode(raw)))
+ s['legacyfingerprint'] = True
+ except TypeError:
+ pass
# If a host cert fingerprint is defined, it is the only thing that
# matters. No need to validate CA certs.
if s['certfingerprints']:
s['verifymode'] = ssl.CERT_NONE
s['allowloaddefaultcerts'] = False
# If --insecure is used, don't take CAs into consideration.
@@ -499,40 +505,41 @@ def validatesocket(sock):
ui.warn(_('warning: connection security to %s is disabled per current '
'settings; communication is susceptible to eavesdropping '
'and tampering\n') % host)
return
# If a certificate fingerprint is pinned, use it and only it to
# validate the remote cert.
peerfingerprints = {
- 'sha1': hashlib.sha1(peercert).hexdigest(),
- 'sha256': hashlib.sha256(peercert).hexdigest(),
- 'sha512': hashlib.sha512(peercert).hexdigest(),
+ 'sha1': base64.b64encode(hashlib.sha1(peercert).digest()),
+ 'sha256': base64.b64encode(hashlib.sha256(peercert).digest()),
+ 'sha512': base64.b64encode(hashlib.sha512(peercert).digest()),
}
- def fmtfingerprint(s):
- return ':'.join([s[x:x + 2] for x in range(0, len(s), 2)])
-
- nicefingerprint = 'sha256:%s' % fmtfingerprint(peerfingerprints['sha256'])
+ nicefingerprint = 'sha256:%s' % peerfingerprints['sha256'].rstrip('=')
if settings['certfingerprints']:
for hash, fingerprint in settings['certfingerprints']:
- if peerfingerprints[hash].lower() == fingerprint:
+ # Compare without '=' padding characters.
+ if peerfingerprints[hash].rstrip('=') == fingerprint.rstrip('='):
ui.debug('%s certificate matched fingerprint %s:%s\n' %
- (host, hash, fmtfingerprint(fingerprint)))
+ (host, hash, fingerprint))
return
# Pinned fingerprint didn't match. This is a fatal error.
if settings['legacyfingerprint']:
section = 'hostfingerprint'
- nice = fmtfingerprint(peerfingerprints['sha1'])
+ raw = base64.b64decode(peerfingerprints['sha1'])
+ hexprint = binascii.hexlify(raw)
+ nice = ':'.join([hexprint[x:x + 2]
+ for x in range(0, len(hexprint), 2)])
else:
section = 'hostsecurity'
- nice = '%s:%s' % (hash, fmtfingerprint(peerfingerprints[hash]))
+ nice = '%s:%s' % (hash, peerfingerprints[hash].rstrip('='))
raise error.Abort(_('certificate for %s has unexpected '
'fingerprint %s') % (host, nice),
hint=_('check %s configuration') % section)
# Security is enabled but no CAs are loaded. We can't establish trust
# for the cert so abort.
if not sock._hgstate['caloaded']:
raise error.Abort(
@@ -71,17 +71,17 @@ we are able to load CA certs.
$ hg clone https://localhost:$HGPORT/ copy-pull
abort: error: *certificate verify failed* (glob)
[255]
#endif
#if no-defaultcacerts
$ hg clone https://localhost:$HGPORT/ copy-pull
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)
+ (set hostsecurity.localhost:certfingerprints=sha256:YgmXL5dg42WPEl14njWhNnplSw6frNvDvG62o8AW4DA config setting or use --insecure to connect insecurely)
[255]
#endif
Specifying a per-host certificate file that doesn't exist will abort
$ hg --config hostsecurity.localhost:verifycertsfile=/does/not/exist clone https://localhost:$HGPORT/
abort: path specified by hostsecurity.localhost:verifycertsfile does not exist: /does/not/exist
[255]
@@ -130,31 +130,31 @@ A per-host certificate with multiple cer
requesting all changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 4 changes to 4 files
Defining both per-host certificate and a fingerprint will print a warning
- $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" --config hostsecurity.localhost:fingerprints=sha1:914f1aff87249c09b6859b88b1906d30756491ca clone -U https://localhost:$HGPORT/ caandfingerwarning
+ $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" --config hostsecurity.localhost:fingerprints=sha1:kU8a/4cknAm2hZuIsZBtMHVkkco clone -U https://localhost:$HGPORT/ caandfingerwarning
(hostsecurity.localhost:verifycertsfile ignored when host fingerprints defined; using host fingerprints for verification)
requesting all changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 4 changes to 4 files
$ DISABLECACERTS="--config devel.disableloaddefaultcerts=true"
Inability to verify peer certificate will result in abort
$ hg clone https://localhost:$HGPORT/ copy-pull $DISABLECACERTS
abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
- (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set 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 to trust this server)
+ (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:YgmXL5dg42WPEl14njWhNnplSw6frNvDvG62o8AW4DA to trust this server)
[255]
$ hg clone --insecure https://localhost:$HGPORT/ copy-pull
warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
requesting all changes
adding changesets
adding manifests
adding file changes
@@ -176,17 +176,17 @@ Inability to verify peer certificate wil
pull without cacert
$ cd copy-pull
$ echo '[hooks]' >> .hg/hgrc
$ echo "changegroup = printenv.py changegroup" >> .hg/hgrc
$ hg pull $DISABLECACERTS
pulling from https://localhost:$HGPORT/
abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
- (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set 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 to trust this server)
+ (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:YgmXL5dg42WPEl14njWhNnplSw6frNvDvG62o8AW4DA to trust this server)
[255]
$ hg pull --insecure
pulling from https://localhost:$HGPORT/
warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
searching for changes
adding changesets
adding manifests
@@ -240,17 +240,17 @@ empty cacert file
#endif
cacert mismatch
$ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \
> https://127.0.0.1:$HGPORT/
pulling from https://127.0.0.1:$HGPORT/
abort: 127.0.0.1 certificate error: certificate is for localhost
- (set hostsecurity.127.0.0.1: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)
+ (set hostsecurity.127.0.0.1:certfingerprints=sha256:YgmXL5dg42WPEl14njWhNnplSw6frNvDvG62o8AW4DA config setting or use --insecure to connect insecurely)
[255]
$ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \
> https://127.0.0.1:$HGPORT/ --insecure
pulling from https://127.0.0.1:$HGPORT/
warning: connection security to 127.0.0.1 is disabled per current settings; communication is susceptible to eavesdropping and tampering
searching for changes
no changes found
$ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem"
@@ -286,48 +286,53 @@ Test server cert which no longer is vali
Fingerprints
- 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
+ $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha1:kU8a/4cknAm2hZuIsZBtMHVkkco
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
+ $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha256:YgmXL5dg42WPEl14njWhNnplSw6frNvDvG62o8AW4DA
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/
+ $ hg --config 'hostsecurity.localhost:fingerprints=sha1:kU8a/4cknAm2hZuIsZBtMHVkkco, sha1:deadbeefdeadbeefdeadbeefdead' -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/
+ $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdead, sha1:kU8a/4cknAm2hZuIsZBtMHVkkco' -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 sha1:91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca
+ abort: certificate for localhost has unexpected fingerprint sha1:kU8a/4cknAm2hZuIsZBtMHVkkco
(check hostsecurity configuration)
[255]
+- trailing = is optional in fingerprint
+
+ $ hg --config 'hostsecurity.localhost:fingerprints=sha256:YgmXL5dg42WPEl14njWhNnplSw6frNvDvG62o8AW4DA=' -R copy-pull id https://localhost:$HGPORT/
+ 5fed3813f7f5
+
- 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
@@ -89,17 +89,17 @@ Without certificates:
$ try --debug
this patch series consists of 1 patches.
(using smtps)
sending mail: smtp host localhost, port * (glob)
(verifying remote certificate)
abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
- (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set 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 to trust this server)
+ (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:YgmXL5dg42WPEl14njWhNnplSw6frNvDvG62o8AW4DA to trust this server)
[255]
With global certificates:
$ try --debug --config web.cacerts="$CERTSDIR/pub.pem"
this patch series consists of 1 patches.