Comments
Patch
@@ -101,16 +101,33 @@ except AttributeError:
'ca_certs': self._cacerts,
}
if self._supportsciphers:
args['ciphers'] = self._ciphers
return ssl.wrap_socket(socket, **args)
+def _hostsettings(ui, hostname):
+ """Obtain security settings for a hostname.
+
+ Returns a dict of settings relevant to that hostname.
+ """
+ s = {
+ # List of 2-tuple of (hash algorithm, hash).
+ 'certfingerprints': [],
+ }
+
+ # Fingerprints from [hostfingerprints] are always SHA-1.
+ for fingerprint in ui.configlist('hostfingerprints', hostname, []):
+ fingerprint = fingerprint.replace(':', '').lower()
+ s['certfingerprints'].append(('sha1', fingerprint))
+
+ return s
+
def _determinecertoptions(ui, host):
"""Determine certificate options for a connections.
Returns a tuple of (cert_reqs, ca_certs).
"""
# If a host key fingerprint is on file, it is the only thing that matters
# and CA certs don't come into play.
hostfingerprint = ui.config('hostfingerprints', host)
@@ -212,16 +229,17 @@ def wrapsocket(sock, keyfile, certfile,
# closed
# - see http://bugs.python.org/issue13721
if not sslsocket.cipher():
raise error.Abort(_('ssl connection failed'))
sslsocket._hgstate = {
'caloaded': caloaded,
'hostname': serverhostname,
+ 'settings': _hostsettings(ui, serverhostname),
'ui': ui,
}
return sslsocket
def _verifycert(cert, hostname):
'''Verify that cert (in socket.getpeercert() format) matches hostname.
CRLs is not handled.
@@ -287,38 +305,37 @@ def _defaultcacerts():
def validatesocket(sock, strict=False):
"""Validate a socket meets security requiremnets.
The passed socket must have been created with ``wrapsocket()``.
"""
host = sock._hgstate['hostname']
ui = sock._hgstate['ui']
+ settings = sock._hgstate['settings']
try:
peercert = sock.getpeercert(True)
peercert2 = sock.getpeercert()
except AttributeError:
raise error.Abort(_('%s ssl connection error') % host)
if not peercert:
raise error.Abort(_('%s certificate error: '
'no certificate received') % host)
# If a certificate fingerprint is pinned, use it and only it to
# validate the remote cert.
- hostfingerprints = ui.configlist('hostfingerprints', host)
peerfingerprint = util.sha1(peercert).hexdigest()
nicefingerprint = ":".join([peerfingerprint[x:x + 2]
for x in xrange(0, len(peerfingerprint), 2)])
- if hostfingerprints:
+ if settings['certfingerprints']:
fingerprintmatch = False
- for hostfingerprint in hostfingerprints:
- if peerfingerprint.lower() == \
- hostfingerprint.replace(':', '').lower():
+ for hash, fingerprint in settings['certfingerprints']:
+ if peerfingerprint.lower() == fingerprint:
fingerprintmatch = True
break
if not fingerprintmatch:
raise error.Abort(_('certificate for %s has unexpected '
'fingerprint %s') % (host, nicefingerprint),
hint=_('check hostfingerprint configuration'))
ui.debug('%s certificate matched fingerprint %s\n' %
(host, nicefingerprint))