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') |
@@ -951,8 +951,9 b' Web interface configuration.' | |||||
951 | third-party tools like email notification hooks can construct |
|
951 | third-party tools like email notification hooks can construct | |
952 | URLs. Example: ``http://hgserver/repos/``. |
|
952 | URLs. Example: ``http://hgserver/repos/``. | |
953 | ``cacerts`` |
|
953 | ``cacerts`` | |
954 |
Path to file containing a list of PEM encoded certificate authorit |
|
954 | Path to file containing a list of PEM encoded certificate authority | |
955 | that may be used to verify an SSL server's identity. The form must be |
|
955 | certificates. If specified on the client, then it will verify the identity | |
|
956 | of remote HTTPS servers with these certificates. The form must be | |||
956 | as follows:: |
|
957 | as follows:: | |
957 |
|
958 | |||
958 | -----BEGIN CERTIFICATE----- |
|
959 | -----BEGIN CERTIFICATE----- | |
@@ -962,8 +963,8 b' Web interface configuration.' | |||||
962 | ... (certificate in base64 PEM encoding) ... |
|
963 | ... (certificate in base64 PEM encoding) ... | |
963 | -----END CERTIFICATE----- |
|
964 | -----END CERTIFICATE----- | |
964 |
|
965 | |||
965 |
This feature is only supported when using Python 2.6. If you wish |
|
966 | This feature is only supported when using Python 2.6 or later. If you wish | |
966 | use it with earlier versions of Python, install the backported |
|
967 | to use it with earlier versions of Python, install the backported | |
967 | version of the ssl library that is available from |
|
968 | version of the ssl library that is available from | |
968 | ``http://pypi.python.org``. |
|
969 | ``http://pypi.python.org``. | |
969 |
|
970 |
@@ -18,6 +18,9 b' Some features, such as pushing to http:/' | |||||
18 | possible if the feature is explicitly enabled on the remote Mercurial |
|
18 | possible if the feature is explicitly enabled on the remote Mercurial | |
19 | server. |
|
19 | server. | |
20 |
|
20 | |||
|
21 | Note that the security of HTTPS URLs depends on proper configuration of | |||
|
22 | web.cacerts. | |||
|
23 | ||||
21 | Some notes about using SSH with Mercurial: |
|
24 | Some notes about using SSH with Mercurial: | |
22 |
|
25 | |||
23 | - SSH requires an accessible shell account on the destination machine |
|
26 | - SSH requires an accessible shell account on the destination machine |
@@ -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 | import __builtin__ |
|
11 | import __builtin__ | |
12 | from i18n import _ |
|
12 | from i18n import _ | |
13 | import keepalive, util |
|
13 | import keepalive, util | |
@@ -486,6 +486,31 b' class httphandler(keepalive.HTTPHandler)' | |||||
486 | _generic_start_transaction(self, h, req) |
|
486 | _generic_start_transaction(self, h, req) | |
487 | return keepalive.HTTPHandler._start_transaction(self, h, req) |
|
487 | return keepalive.HTTPHandler._start_transaction(self, h, req) | |
488 |
|
488 | |||
|
489 | def _verifycert(cert, hostname): | |||
|
490 | '''Verify that cert (in socket.getpeercert() format) matches hostname and is | |||
|
491 | valid at this time. CRLs and subjectAltName are not handled. | |||
|
492 | ||||
|
493 | Returns error message if any problems are found and None on success. | |||
|
494 | ''' | |||
|
495 | if not cert: | |||
|
496 | return _('no certificate received') | |||
|
497 | notafter = cert.get('notAfter') | |||
|
498 | if notafter and time.time() > ssl.cert_time_to_seconds(notafter): | |||
|
499 | return _('certificate expired %s') % notafter | |||
|
500 | notbefore = cert.get('notBefore') | |||
|
501 | if notbefore and time.time() < ssl.cert_time_to_seconds(notbefore): | |||
|
502 | return _('certificate not valid before %s') % notbefore | |||
|
503 | dnsname = hostname.lower() | |||
|
504 | for s in cert.get('subject', []): | |||
|
505 | key, value = s[0] | |||
|
506 | if key == 'commonName': | |||
|
507 | certname = value.lower() | |||
|
508 | if (certname == dnsname or | |||
|
509 | '.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1]): | |||
|
510 | return None | |||
|
511 | return _('certificate is for %s') % certname | |||
|
512 | return _('no commonName found in certificate') | |||
|
513 | ||||
489 | if has_https: |
|
514 | if has_https: | |
490 | class BetterHTTPS(httplib.HTTPSConnection): |
|
515 | class BetterHTTPS(httplib.HTTPSConnection): | |
491 | send = keepalive.safesend |
|
516 | send = keepalive.safesend | |
@@ -501,7 +526,11 b' if has_https:' | |||||
501 | self.sock = _ssl_wrap_socket(sock, self.key_file, |
|
526 | self.sock = _ssl_wrap_socket(sock, self.key_file, | |
502 | self.cert_file, cert_reqs=CERT_REQUIRED, |
|
527 | self.cert_file, cert_reqs=CERT_REQUIRED, | |
503 | ca_certs=cacerts) |
|
528 | ca_certs=cacerts) | |
504 | self.ui.debug(_('server identity verification succeeded\n')) |
|
529 | msg = _verifycert(self.sock.getpeercert(), self.host) | |
|
530 | if msg: | |||
|
531 | raise util.Abort('%s certificate error: %s' % (self.host, msg)) | |||
|
532 | self.ui.debug(_('%s certificate successfully verified\n') % | |||
|
533 | self.host) | |||
505 | else: |
|
534 | else: | |
506 | httplib.HTTPSConnection.connect(self) |
|
535 | httplib.HTTPSConnection.connect(self) | |
507 |
|
536 |
@@ -5,21 +5,16 b" if 'TERM' in os.environ:" | |||||
5 | import doctest |
|
5 | import doctest | |
6 |
|
6 | |||
7 | import mercurial.changelog |
|
7 | import mercurial.changelog | |
8 | # test doctest from changelog |
|
|||
9 |
|
||||
10 | doctest.testmod(mercurial.changelog) |
|
8 | doctest.testmod(mercurial.changelog) | |
11 |
|
9 | |||
12 |
import mercurial. |
|
10 | import mercurial.dagparser | |
13 | doctest.testmod(mercurial.httprepo) |
|
11 | doctest.testmod(mercurial.dagparser, optionflags=doctest.NORMALIZE_WHITESPACE) | |
14 |
|
||||
15 | import mercurial.util |
|
|||
16 | doctest.testmod(mercurial.util) |
|
|||
17 |
|
12 | |||
18 | import mercurial.match |
|
13 | import mercurial.match | |
19 | doctest.testmod(mercurial.match) |
|
14 | doctest.testmod(mercurial.match) | |
20 |
|
15 | |||
21 |
import mercurial. |
|
16 | import mercurial.url | |
22 | doctest.testmod(mercurial.dagparser, optionflags=doctest.NORMALIZE_WHITESPACE) |
|
17 | doctest.testmod(mercurial.url) | |
23 |
|
18 | |||
24 | import hgext.convert.cvsps |
|
19 | import hgext.convert.cvsps | |
25 | doctest.testmod(hgext.convert.cvsps) |
|
20 | doctest.testmod(hgext.convert.cvsps) |
General Comments 0
You need to be logged in to leave comments.
Login now