mercurial/url.py
branchstable
changeset 12592 f2937d6492c5
parent 12391 ca5fd84d62c6
child 12595 0f83a402faa0
child 12602 14198926975d
--- a/mercurial/url.py	Tue Sep 28 00:41:08 2010 +0200
+++ b/mercurial/url.py	Fri Oct 01 00:46:59 2010 +0200
@@ -7,7 +7,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import urllib, urllib2, urlparse, httplib, os, re, socket, cStringIO
+import urllib, urllib2, urlparse, httplib, os, re, socket, cStringIO, time
 from i18n import _
 import keepalive, util
 
@@ -469,6 +469,31 @@
         _generic_start_transaction(self, h, req)
         return keepalive.HTTPHandler._start_transaction(self, h, req)
 
+def _verifycert(cert, hostname):
+    '''Verify that cert (in socket.getpeercert() format) matches hostname and is 
+    valid at this time. CRLs and subjectAltName are not handled.
+    
+    Returns error message if any problems are found and None on success.
+    '''
+    if not cert:
+        return _('no certificate received')
+    notafter = cert.get('notAfter')
+    if notafter and time.time() > ssl.cert_time_to_seconds(notafter):
+        return _('certificate expired %s') % notafter
+    notbefore = cert.get('notBefore')
+    if notbefore and time.time() < ssl.cert_time_to_seconds(notbefore):
+        return _('certificate not valid before %s') % notbefore
+    dnsname = hostname.lower()
+    for s in cert.get('subject', []):
+        key, value = s[0]
+        if key == 'commonName':
+            certname = value.lower()
+            if (certname == dnsname or
+                '.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1]):
+                return None
+            return _('certificate is for %s') % certname
+    return _('no commonName found in certificate')
+
 if has_https:
     class BetterHTTPS(httplib.HTTPSConnection):
         send = keepalive.safesend
@@ -484,7 +509,11 @@
                 self.sock = _ssl_wrap_socket(sock, self.key_file,
                         self.cert_file, cert_reqs=CERT_REQUIRED,
                         ca_certs=cacerts)
-                self.ui.debug(_('server identity verification succeeded\n'))
+                msg = _verifycert(self.sock.getpeercert(), self.host)
+                if msg:
+                    raise util.Abort('%s certificate error: %s' % (self.host, msg))
+                self.ui.debug(_('%s certificate successfully verified\n') % 
+                    self.host)
             else:
                 httplib.HTTPSConnection.connect(self)