cleanup fingerprint verification

This commit is contained in:
j 2014-09-09 16:22:44 +02:00
parent 417d02bdd4
commit cbb6e77b20

View file

@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
import http.client import http.client
import socket
import urllib.request, urllib.error, urllib.parse import urllib.request, urllib.error, urllib.parse
import ssl
import hashlib import hashlib
from .utils import valid from .utils import valid
@ -15,66 +16,60 @@ logger = logging.getLogger('link')
class InvalidCertificateException(http.client.HTTPException, urllib.error.URLError): class InvalidCertificateException(http.client.HTTPException, urllib.error.URLError):
def __init__(self, fingerprint, cert, reason): def __init__(self, fingerprint, cert, reason):
http.client.HTTPException.__init__(self) http.client.HTTPException.__init__(self)
self.fingerprint = fingerprint self._fingerprint = fingerprint
self.cert_fingerprint = hashlib.sha1(cert).hexdigest() self._cert_fingerprint = hashlib.sha1(cert).hexdigest()
self.reason = reason self.reason = reason
def __str__(self): def __str__(self):
return ('%s (local) != %s (remote) (%s)\n' % 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): class FingerprintHTTPSConnection(http.client.HTTPSConnection):
default_port = http.client.HTTPS_PORT
def __init__(self, host, port=None, fingerprint=None, strict=None, **kwargs): def __init__(self, host, port=None, fingerprint=None, check_hostname=None, **kwargs):
http.client.HTTPConnection.__init__(self, host, port, strict, **kwargs) self._fingerprint = fingerprint
self.fingerprint = fingerprint if self._fingerprint:
if self.fingerprint: check_hostname = None
self.cert_reqs = ssl.CERT_REQUIRED 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: else:
self.cert_reqs = ssl.CERT_NONE logging.error('unkown _fingerprint length %s (%s)',
self.cert_reqs = ssl.CERT_NONE self._fingerprint, len(self._fingerprint))
return False
def _ValidateCertificateFingerprint(self, cert): return fingerprint == self._fingerprint
fingerprint = hashlib.sha1(cert).hexdigest()
return fingerprint == self.fingerprint
def connect(self): def connect(self):
sock = socket.create_connection((self.host, self.port)) http.client.HTTPSConnection.connect(self)
self.sock = ssl.wrap_socket(sock, cert_reqs=self.cert_reqs) if self._fingerprint:
#if self.cert_reqs & ssl.CERT_REQUIRED:
if self.fingerprint:
cert = self.sock.getpeercert(binary_form=True) cert = self.sock.getpeercert(binary_form=True)
if not self._ValidateCertificateFingerprint(cert): if not self._check_fingerprint(cert):
raise InvalidCertificateException(self.fingerprint, cert, raise InvalidCertificateException(self._fingerprint, cert,
'fingerprint mismatch') 'fingerprint mismatch')
#logger.debug('CIPHER %s VERSION %s', self.sock.cipher(), self.sock.ssl_version) #logger.debug('CIPHER %s VERSION %s', self.sock.cipher(), self.sock.ssl_version)
class VerifiedHTTPSHandler(urllib.request.HTTPSHandler): class FingerprintHTTPSHandler(urllib.request.HTTPSHandler):
def __init__(self, **kwargs):
urllib.request.AbstractHTTPHandler.__init__(self) def __init__(self, debuglevel=0, context=None, check_hostname=None, fingerprint=None):
self._connection_args = kwargs urllib.request.AbstractHTTPHandler.__init__(self, debuglevel)
self._context = context
self._check_hostname = check_hostname
self._fingerprint = fingerprint
def https_open(self, req): def https_open(self, req):
def http_class_wrapper(host, **kwargs): return self.do_open(FingerprintHTTPSConnection, req,
full_kwargs = dict(self._connection_args) context=self._context, check_hostname=self._check_hostname,
full_kwargs.update(kwargs) fingerprint=self._fingerprint)
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_
def get_opener(fingerprint): def get_opener(fingerprint):
handler = VerifiedHTTPSHandler(fingerprint=fingerprint) handler = FingerprintHTTPSHandler(fingerprint=fingerprint)
opener = urllib.request.build_opener(handler) opener = urllib.request.build_opener(handler)
return opener return opener