Message117936
I added some extra verification to Mercurial (http://www.selenic.com/hg/rev/f2937d6492c5). Feel free to use the following under the Python license in Python or elsewhere. It could be a separate method/function or it could integrated in wrap_socket and controlled by a keyword. I would appreciate if you find the implementation insufficient or incorrect.
The purpose with this function is to verify if the received and validated certificate matches the host we intended to connect to.
I try to keep it simple and to fail to the safe side.
"Correct" subjectAltName handling seems not to be feasible.
Are CRLs checked by the SSL module? Otherwise it deserves a big fat warning.
(I now assume that notBefore is handled by the SSL module and shouldn't be checked here.)
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
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')
def check(a, b):
if a != b:
print (a, b)
# 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({'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') |
|
Date |
User |
Action |
Args |
2010-10-04 00:52:52 | kiilerix | set | recipients:
+ kiilerix, zooko, janssen, orsenthil, pitrou, giampaolo.rodola, vila, heikki, ahasenack, debatem1, jsamuel, devin, asdfasdfasdfasdfasdfasdfasdf, Ryan.Tucker |
2010-10-04 00:52:51 | kiilerix | set | messageid: <1286153571.56.0.333502733747.issue1589@psf.upfronthosting.co.za> |
2010-10-04 00:52:50 | kiilerix | link | issue1589 messages |
2010-10-04 00:52:47 | kiilerix | create | |
|