diff mercurial/url.py @ 13249:75d0c38a0bca stable

url: check subjectAltName when verifying ssl certificate Now it verifies certificate in the same manner as py3k implementation: http://svn.python.org/view/python/branches/py3k/Lib/ssl.py?view=markup#match_hostname
author Yuya Nishihara <yuya@tcha.org>
date Sun, 09 Jan 2011 00:35:36 +0900
parents 00411a4fa1bb
children 1a4330e30017 8dc488dfcdb4
line wrap: on
line diff
--- a/mercurial/url.py	Sat Jan 08 21:52:25 2011 +0900
+++ b/mercurial/url.py	Sun Jan 09 00:35:36 2011 +0900
@@ -488,13 +488,26 @@
 
 def _verifycert(cert, hostname):
     '''Verify that cert (in socket.getpeercert() format) matches hostname.
-    CRLs and subjectAltName are not handled.
+    CRLs is not handled.
 
     Returns error message if any problems are found and None on success.
     '''
     if not cert:
         return _('no certificate received')
     dnsname = hostname.lower()
+    def matchdnsname(certname):
+        return (certname == dnsname or
+                '.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1])
+
+    san = cert.get('subjectAltName', [])
+    if san:
+        certnames = [value.lower() for key, value in san if key == 'DNS']
+        for name in certnames:
+            if matchdnsname(name):
+                return None
+        return _('certificate is for %s') % ', '.join(certnames)
+
+    # subject is only checked when subjectAltName is empty
     for s in cert.get('subject', []):
         key, value = s[0]
         if key == 'commonName':
@@ -503,11 +516,10 @@
                 certname = value.lower().encode('ascii')
             except UnicodeEncodeError:
                 return _('IDN in certificate not supported')
-            if (certname == dnsname or
-                '.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1]):
+            if matchdnsname(certname):
                 return None
             return _('certificate is for %s') % certname
-    return _('no commonName found in certificate')
+    return _('no commonName or subjectAltName found in certificate')
 
 if has_https:
     class BetterHTTPS(httplib.HTTPSConnection):