5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> |
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> |
6 # |
6 # |
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 |
14 |
14 |
15 def _urlunparse(scheme, netloc, path, params, query, fragment, url): |
15 def _urlunparse(scheme, netloc, path, params, query, fragment, url): |
484 |
484 |
485 def _start_transaction(self, h, req): |
485 def _start_transaction(self, h, req): |
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 |
492 |
517 |
493 def connect(self): |
518 def connect(self): |
499 if cacerts: |
524 if cacerts: |
500 sock = _create_connection((self.host, self.port)) |
525 sock = _create_connection((self.host, self.port)) |
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 |
508 class httpsconnection(BetterHTTPS): |
537 class httpsconnection(BetterHTTPS): |
509 response_class = keepalive.HTTPResponse |
538 response_class = keepalive.HTTPResponse |