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 from i18n import _ |
11 from i18n import _ |
12 import keepalive, util |
12 import keepalive, util |
13 |
13 |
14 def _urlunparse(scheme, netloc, path, params, query, fragment, url): |
14 def _urlunparse(scheme, netloc, path, params, query, fragment, url): |
15 '''Handle cases where urlunparse(urlparse(x://)) doesn't preserve the "//"''' |
15 '''Handle cases where urlunparse(urlparse(x://)) doesn't preserve the "//"''' |
467 |
467 |
468 def _start_transaction(self, h, req): |
468 def _start_transaction(self, h, req): |
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 |
475 |
500 |
476 def connect(self): |
501 def connect(self): |
482 if cacerts: |
507 if cacerts: |
483 sock = _create_connection((self.host, self.port)) |
508 sock = _create_connection((self.host, self.port)) |
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 |
491 class httpsconnection(BetterHTTPS): |
520 class httpsconnection(BetterHTTPS): |
492 response_class = keepalive.HTTPResponse |
521 response_class = keepalive.HTTPResponse |