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