##// END OF EJS Templates
url: verify correctness of https server certificates (issue2407)...
Mads Kiilerich -
r12592:f2937d64 stable
parent child Browse files
Show More
@@ -0,0 +1,41 b''
1 #!/usr/bin/env python
2
3 def check(a, b):
4 if a != b:
5 print (a, b)
6
7 from mercurial.url import _verifycert
8
9 # Test non-wildcard certificates
10 check(_verifycert({'subject': ((('commonName', 'example.com'),),)}, 'example.com'),
11 None)
12 check(_verifycert({'subject': ((('commonName', 'example.com'),),)}, 'www.example.com'),
13 'certificate is for example.com')
14 check(_verifycert({'subject': ((('commonName', 'www.example.com'),),)}, 'example.com'),
15 'certificate is for www.example.com')
16
17 # Test wildcard certificates
18 check(_verifycert({'subject': ((('commonName', '*.example.com'),),)}, 'www.example.com'),
19 None)
20 check(_verifycert({'subject': ((('commonName', '*.example.com'),),)}, 'example.com'),
21 'certificate is for *.example.com')
22 check(_verifycert({'subject': ((('commonName', '*.example.com'),),)}, 'w.w.example.com'),
23 'certificate is for *.example.com')
24
25 # Avoid some pitfalls
26 check(_verifycert({'subject': ((('commonName', '*.foo'),),)}, 'foo'),
27 'certificate is for *.foo')
28 check(_verifycert({'subject': ((('commonName', '*o'),),)}, 'foo'),
29 'certificate is for *o')
30
31 import time
32 lastyear = time.gmtime().tm_year - 1
33 nextyear = time.gmtime().tm_year + 1
34 check(_verifycert({'notAfter': 'May 9 00:00:00 %s GMT' % lastyear}, 'example.com'),
35 'certificate expired May 9 00:00:00 %s GMT' % lastyear)
36 check(_verifycert({'notBefore': 'May 9 00:00:00 %s GMT' % nextyear}, 'example.com'),
37 'certificate not valid before May 9 00:00:00 %s GMT' % nextyear)
38 check(_verifycert({'notAfter': 'Sep 29 15:29:48 %s GMT' % nextyear, 'subject': ()}, 'example.com'),
39 'no commonName found in certificate')
40 check(_verifycert(None, 'example.com'),
41 'no certificate received')
@@ -7,7 +7,7 b''
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 import urllib, urllib2, urlparse, httplib, os, re, socket, cStringIO
10 import urllib, urllib2, urlparse, httplib, os, re, socket, cStringIO, time
11 from i18n import _
11 from i18n import _
12 import keepalive, util
12 import keepalive, util
13
13
@@ -469,6 +469,31 b' class httphandler(keepalive.HTTPHandler)'
469 _generic_start_transaction(self, h, req)
469 _generic_start_transaction(self, h, req)
470 return keepalive.HTTPHandler._start_transaction(self, h, req)
470 return keepalive.HTTPHandler._start_transaction(self, h, req)
471
471
472 def _verifycert(cert, hostname):
473 '''Verify that cert (in socket.getpeercert() format) matches hostname and is
474 valid at this time. CRLs and subjectAltName are not handled.
475
476 Returns error message if any problems are found and None on success.
477 '''
478 if not cert:
479 return _('no certificate received')
480 notafter = cert.get('notAfter')
481 if notafter and time.time() > ssl.cert_time_to_seconds(notafter):
482 return _('certificate expired %s') % notafter
483 notbefore = cert.get('notBefore')
484 if notbefore and time.time() < ssl.cert_time_to_seconds(notbefore):
485 return _('certificate not valid before %s') % notbefore
486 dnsname = hostname.lower()
487 for s in cert.get('subject', []):
488 key, value = s[0]
489 if key == 'commonName':
490 certname = value.lower()
491 if (certname == dnsname or
492 '.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1]):
493 return None
494 return _('certificate is for %s') % certname
495 return _('no commonName found in certificate')
496
472 if has_https:
497 if has_https:
473 class BetterHTTPS(httplib.HTTPSConnection):
498 class BetterHTTPS(httplib.HTTPSConnection):
474 send = keepalive.safesend
499 send = keepalive.safesend
@@ -484,7 +509,11 b' if has_https:'
484 self.sock = _ssl_wrap_socket(sock, self.key_file,
509 self.sock = _ssl_wrap_socket(sock, self.key_file,
485 self.cert_file, cert_reqs=CERT_REQUIRED,
510 self.cert_file, cert_reqs=CERT_REQUIRED,
486 ca_certs=cacerts)
511 ca_certs=cacerts)
487 self.ui.debug(_('server identity verification succeeded\n'))
512 msg = _verifycert(self.sock.getpeercert(), self.host)
513 if msg:
514 raise util.Abort('%s certificate error: %s' % (self.host, msg))
515 self.ui.debug(_('%s certificate successfully verified\n') %
516 self.host)
488 else:
517 else:
489 httplib.HTTPSConnection.connect(self)
518 httplib.HTTPSConnection.connect(self)
490
519
General Comments 0
You need to be logged in to leave comments. Login now