From cbb6e77b20924aacd97762d6df43098902e7f934 Mon Sep 17 00:00:00 2001 From: j Date: Tue, 9 Sep 2014 16:22:44 +0200 Subject: [PATCH] cleanup fingerprint verification --- peerlink/link.py | 85 +++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 45 deletions(-) diff --git a/peerlink/link.py b/peerlink/link.py index 4101ab7..6b52e04 100644 --- a/peerlink/link.py +++ b/peerlink/link.py @@ -1,7 +1,8 @@ +# -*- coding: utf-8 -*- +# vi:si:et:sw=4:sts=4:ts=4 + import http.client -import socket import urllib.request, urllib.error, urllib.parse -import ssl import hashlib from .utils import valid @@ -15,66 +16,60 @@ logger = logging.getLogger('link') class InvalidCertificateException(http.client.HTTPException, urllib.error.URLError): def __init__(self, fingerprint, cert, reason): http.client.HTTPException.__init__(self) - self.fingerprint = fingerprint - self.cert_fingerprint = hashlib.sha1(cert).hexdigest() + self._fingerprint = fingerprint + self._cert_fingerprint = hashlib.sha1(cert).hexdigest() self.reason = reason def __str__(self): return ('%s (local) != %s (remote) (%s)\n' % - (self.fingerprint, self.cert_fingerprint, self.reason)) + (self._fingerprint, self._cert_fingerprint, self.reason)) -class CertValidatingHTTPSConnection(http.client.HTTPConnection): - default_port = http.client.HTTPS_PORT +class FingerprintHTTPSConnection(http.client.HTTPSConnection): - def __init__(self, host, port=None, fingerprint=None, strict=None, **kwargs): - http.client.HTTPConnection.__init__(self, host, port, strict, **kwargs) - self.fingerprint = fingerprint - if self.fingerprint: - self.cert_reqs = ssl.CERT_REQUIRED + def __init__(self, host, port=None, fingerprint=None, check_hostname=None, **kwargs): + self._fingerprint = fingerprint + if self._fingerprint: + check_hostname = None + http.client.HTTPSConnection.__init__(self, host, port, + check_hostname=check_hostname, **kwargs) + + def _check_fingerprint(self, cert): + if len(self._fingerprint) == 40: + fingerprint = hashlib.sha1(cert).hexdigest() + elif len(self._fingerprint) == 64: + fingerprint = hashlib.sha256(cert).hexdigest() + elif len(self._fingerprint) == 128: + fingerprint = hashlib.sha512(cert).hexdigest() else: - self.cert_reqs = ssl.CERT_NONE - self.cert_reqs = ssl.CERT_NONE - - def _ValidateCertificateFingerprint(self, cert): - fingerprint = hashlib.sha1(cert).hexdigest() - return fingerprint == self.fingerprint + logging.error('unkown _fingerprint length %s (%s)', + self._fingerprint, len(self._fingerprint)) + return False + return fingerprint == self._fingerprint def connect(self): - sock = socket.create_connection((self.host, self.port)) - self.sock = ssl.wrap_socket(sock, cert_reqs=self.cert_reqs) - #if self.cert_reqs & ssl.CERT_REQUIRED: - if self.fingerprint: + http.client.HTTPSConnection.connect(self) + if self._fingerprint: cert = self.sock.getpeercert(binary_form=True) - if not self._ValidateCertificateFingerprint(cert): - raise InvalidCertificateException(self.fingerprint, cert, + if not self._check_fingerprint(cert): + raise InvalidCertificateException(self._fingerprint, cert, 'fingerprint mismatch') #logger.debug('CIPHER %s VERSION %s', self.sock.cipher(), self.sock.ssl_version) -class VerifiedHTTPSHandler(urllib.request.HTTPSHandler): - def __init__(self, **kwargs): - urllib.request.AbstractHTTPHandler.__init__(self) - self._connection_args = kwargs +class FingerprintHTTPSHandler(urllib.request.HTTPSHandler): + + def __init__(self, debuglevel=0, context=None, check_hostname=None, fingerprint=None): + urllib.request.AbstractHTTPHandler.__init__(self, debuglevel) + self._context = context + self._check_hostname = check_hostname + self._fingerprint = fingerprint def https_open(self, req): - def http_class_wrapper(host, **kwargs): - full_kwargs = dict(self._connection_args) - full_kwargs.update(kwargs) - if 'timeout' in full_kwargs: - del full_kwargs['timeout'] - return CertValidatingHTTPSConnection(host, **full_kwargs) - - try: - return self.do_open(http_class_wrapper, req) - except urllib.error.URLError as e: - if type(e.reason) == ssl.SSLError and e.reason.args[0] == 1: - raise InvalidCertificateException(self.fingerprint, '', - e.reason.args[1]) - raise - - https_request = urllib.request.HTTPSHandler.do_request_ + return self.do_open(FingerprintHTTPSConnection, req, + context=self._context, check_hostname=self._check_hostname, + fingerprint=self._fingerprint) def get_opener(fingerprint): - handler = VerifiedHTTPSHandler(fingerprint=fingerprint) + handler = FingerprintHTTPSHandler(fingerprint=fingerprint) opener = urllib.request.build_opener(handler) return opener