VYPR
Moderate severityNVD Advisory· Published Oct 29, 2019· Updated Aug 7, 2024

CVE-2010-4237

CVE-2010-4237

Description

Mercurial before 1.6.4 fails to verify the Common Name field of SSL certificates which allows remote attackers who acquire a certificate signed by a Certificate Authority to perform a man-in-the-middle attack.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
mercurialPyPI
< 1.6.41.6.4

Affected products

1

Patches

2
4ea63fb25cee

url: validity (notBefore/notAfter) is checked by OpenSSL (issue2407)

https://github.com/dscho/hgMads KiilerichOct 17, 2010via ghsa
2 files changed · +4 24
  • mercurial/url.py+3 9 modified
    @@ -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, time
    +import urllib, urllib2, urlparse, httplib, os, re, socket, cStringIO
     import __builtin__
     from i18n import _
     import keepalive, util
    @@ -487,19 +487,13 @@ def _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.
    +    '''Verify that cert (in socket.getpeercert() format) matches hostname.
    +    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]
    
  • tests/test-url.py+1 15 modified
    @@ -1,9 +1,5 @@
     #!/usr/bin/env python
     import sys
    -try:
    -    import ssl
    -except ImportError:
    -    sys.exit(80)
     
     def check(a, b):
         if a != b:
    @@ -36,17 +32,7 @@ def cert(cn):
     check(_verifycert(cert('*o'), 'foo'),
           'certificate is for *o')
     
    -import time
    -lastyear = time.gmtime().tm_year - 1
    -nextyear = time.gmtime().tm_year + 1
    -check(_verifycert({'notAfter': 'May  9 00:00:00 %s GMT' % lastyear},
    -                  'example.com'),
    -      'certificate expired May  9 00:00:00 %s GMT' % lastyear)
    -check(_verifycert({'notBefore': 'May  9 00:00:00 %s GMT' % nextyear},
    -                  'example.com'),
    -      'certificate not valid before May  9 00:00:00 %s GMT' % nextyear)
    -check(_verifycert({'notAfter': 'Sep 29 15:29:48 %s GMT' % nextyear,
    -                   'subject': ()},
    +check(_verifycert({'subject': ()},
                       'example.com'),
           'no commonName found in certificate')
     check(_verifycert(None, 'example.com'),
    
89baabf4fb7a

url: verify correctness of https server certificates (issue2407)

https://github.com/dscho/hgMads KiilerichSep 30, 2010via ghsa
2 files changed · +72 2
  • mercurial/url.py+31 2 modified
    @@ -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 @@ def _start_transaction(self, h, req):
             _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 @@ def connect(self):
                     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)
     
    
  • tests/test-url.py+41 0 added
    @@ -0,0 +1,41 @@
    +#!/usr/bin/env python
    +
    +def check(a, b):
    +    if a != b:
    +        print (a, b)
    +
    +from mercurial.url import _verifycert
    +
    +# Test non-wildcard certificates        
    +check(_verifycert({'subject': ((('commonName', 'example.com'),),)}, 'example.com'),
    +    None)
    +check(_verifycert({'subject': ((('commonName', 'example.com'),),)}, 'www.example.com'),
    +    'certificate is for example.com')
    +check(_verifycert({'subject': ((('commonName', 'www.example.com'),),)}, 'example.com'),
    +    'certificate is for www.example.com')
    +
    +# Test wildcard certificates
    +check(_verifycert({'subject': ((('commonName', '*.example.com'),),)}, 'www.example.com'),
    +    None)
    +check(_verifycert({'subject': ((('commonName', '*.example.com'),),)}, 'example.com'),
    +    'certificate is for *.example.com')
    +check(_verifycert({'subject': ((('commonName', '*.example.com'),),)}, 'w.w.example.com'),
    +    'certificate is for *.example.com')
    +
    +# Avoid some pitfalls
    +check(_verifycert({'subject': ((('commonName', '*.foo'),),)}, 'foo'),
    +    'certificate is for *.foo')
    +check(_verifycert({'subject': ((('commonName', '*o'),),)}, 'foo'),
    +    'certificate is for *o')
    +
    +import time
    +lastyear = time.gmtime().tm_year - 1
    +nextyear = time.gmtime().tm_year + 1
    +check(_verifycert({'notAfter': 'May  9 00:00:00 %s GMT' % lastyear}, 'example.com'),
    +    'certificate expired May  9 00:00:00 %s GMT' % lastyear)
    +check(_verifycert({'notBefore': 'May  9 00:00:00 %s GMT' % nextyear}, 'example.com'),
    +    'certificate not valid before May  9 00:00:00 %s GMT' % nextyear)
    +check(_verifycert({'notAfter': 'Sep 29 15:29:48 %s GMT' % nextyear, 'subject': ()}, 'example.com'),
    +    'no commonName found in certificate')
    +check(_verifycert(None, 'example.com'),
    +    'no certificate received')
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

10

News mentions

0

No linked articles in our index yet.