diff mercurial/url.py @ 13314:8dc488dfcdb4 stable

url: 'ssh known host'-like checking of fingerprints of HTTPS certificates Known fingerprints of HTTPS servers can now be configured in the hostfingerprints section. That makes it possible to verify the identify of web servers without configuring and trusting the CA chain. Limitations: * Portnumbers are ignored, just like with ordinary certificates. * Host name matching is case sensitive.
author Mads Kiilerich <mads@kiilerich.com>
date Fri, 28 Jan 2011 02:57:59 +0100
parents 75d0c38a0bca
children 0d1dca7d2a04 a939f08fae9c
line wrap: on
line diff
--- a/mercurial/url.py	Thu Jan 27 17:21:23 2011 -0600
+++ b/mercurial/url.py	Fri Jan 28 02:57:59 2011 +0100
@@ -533,7 +533,8 @@
             else:
                 cacerts = None
 
-            if cacerts:
+            hostfingerprint = self.ui.config('hostfingerprints', self.host)
+            if cacerts and not hostfingerprint:
                 sock = _create_connection((self.host, self.port))
                 self.sock = _ssl_wrap_socket(sock, self.key_file,
                         self.cert_file, cert_reqs=CERT_REQUIRED,
@@ -545,10 +546,33 @@
                 self.ui.debug('%s certificate successfully verified\n' %
                               self.host)
             else:
-                self.ui.warn(_("warning: %s certificate not verified "
-                               "(check web.cacerts config setting)\n") %
-                             self.host)
                 httplib.HTTPSConnection.connect(self)
+                if hasattr(self.sock, 'getpeercert'):
+                    peercert = self.sock.getpeercert(True)
+                    peerfingerprint = util.sha1(peercert).hexdigest()
+                    nicefingerprint = ":".join([peerfingerprint[x:x + 2]
+                        for x in xrange(0, len(peerfingerprint), 2)])
+                    if hostfingerprint:
+                        if peerfingerprint.lower() != \
+                                hostfingerprint.replace(':', '').lower():
+                            raise util.Abort(_('invalid certificate for %s '
+                                               'with fingerprint %s') %
+                                             (self.host, nicefingerprint))
+                        self.ui.debug('%s certificate matched fingerprint %s\n' %
+                                      (self.host, nicefingerprint))
+                    else:
+                        self.ui.warn(_('warning: %s certificate '
+                                       'with fingerprint %s not verified '
+                                       '(check hostfingerprints or web.cacerts '
+                                       'config setting)\n') %
+                                     (self.host, nicefingerprint))
+                else: # python 2.5 ?
+                    if hostfingerprint:
+                        raise util.Abort(_('no certificate for %s '
+                                           'with fingerprint') % self.host)
+                    self.ui.warn(_('warning: %s certificate not verified '
+                                   '(check web.cacerts config setting)\n') %
+                                 self.host)
 
     class httpsconnection(BetterHTTPS):
         response_class = keepalive.HTTPResponse