Add support for using server certificate fingerprint instead of CA signature.

This commit is contained in:
Valentin Lorentz 2016-02-21 14:18:14 +01:00
parent d922af1043
commit e77e78e79e
3 changed files with 49 additions and 18 deletions

View File

@ -320,13 +320,20 @@ def registerNetwork(name, password='', ssl=True, sasl_username='',
registerGlobalValue(network, 'channels', SpaceSeparatedSetOfChannels([], registerGlobalValue(network, 'channels', SpaceSeparatedSetOfChannels([],
_("""Space-separated list of channels the bot will join only on %s.""") _("""Space-separated list of channels the bot will join only on %s.""")
% name, private=True)) % name, private=True))
registerGlobalValue(network, 'ssl', registry.Boolean(ssl, registerGlobalValue(network, 'ssl', registry.Boolean(ssl,
_("""Determines whether the bot will attempt to connect with SSL _("""Determines whether the bot will attempt to connect with SSL
sockets to %s.""") % name)) sockets to %s.""") % name))
registerGlobalValue(network.ssl, 'serverFingerprints',
registry.SpaceSeparatedSetOfStrings([], _("""Space-separated list
of fingerprints of trusted certificates for this network.
If non-empty, Certification Authority signatures will not be used to
verify certificates.""")))
registerGlobalValue(network, 'requireStarttls', registry.Boolean(False, registerGlobalValue(network, 'requireStarttls', registry.Boolean(False,
_("""Determines whether the bot will connect in plain text to %s _("""Determines whether the bot will connect in plain text to %s
but require STARTTLS before authentication. This is ignored if the but require STARTTLS before authentication. This is ignored if the
connection already uses SSL.""") % name)) connection already uses SSL.""") % name))
registerGlobalValue(network, 'certfile', registry.String('', registerGlobalValue(network, 'certfile', registry.String('',
_("""Determines what certificate file (if any) the bot will use to _("""Determines what certificate file (if any) the bot will use to
connect with SSL sockets to %s.""") % name)) connect with SSL sockets to %s.""") % name))

View File

@ -351,8 +351,8 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin):
def starttls(self): def starttls(self):
assert 'ssl' in globals() assert 'ssl' in globals()
certfile = getattr(conf.supybot.networks, self.irc.network) \ network_config = getattr(conf.supybot.networks, self.irc.network)
.certfile() certfile = network_config.certfile()
if not certfile: if not certfile:
certfile = conf.supybot.protocols.irc.certfile() certfile = conf.supybot.protocols.irc.certfile()
if not certfile: if not certfile:
@ -365,12 +365,14 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin):
self.conn = utils.net.ssl_wrap_socket(self.conn, self.conn = utils.net.ssl_wrap_socket(self.conn,
logger=drivers.log, hostname=self.server[0], logger=drivers.log, hostname=self.server[0],
certfile=certfile, certfile=certfile,
verify_mode=conf.supybot.protocols.ssl.verifyMode()) trusted_fingerprints=network_config.ssl.serverFingerprints(),
)
except ssl.CertificateError as e: except ssl.CertificateError as e:
drivers.log.critical(('Certificate validation failed when ' drivers.log.critical(('Certificate validation failed when '
'connecting to %s: %s\n' 'connecting to %s: %s\n'
'This means someone is doing a man-in-the-middle attack ' 'This means someone is doing a man-in-the-middle attack '
'on your connection.') 'on your connection, or the server\'s certificate is '
'not in your trusted fingerprints list.')
% (self.irc.network, e.args[0])) % (self.irc.network, e.args[0]))
raise ssl.SSLError('Aborting because of failed certificate ' raise ssl.SSLError('Aborting because of failed certificate '
'verification.') 'verification.')

View File

@ -35,6 +35,7 @@ Simple utility modules.
import re import re
import ssl import ssl
import socket import socket
import hashlib
from .web import _ipAddr, _domain from .web import _ipAddr, _domain
@ -129,26 +130,47 @@ def isIPV6(s):
return bruteIsIPV6(s) return bruteIsIPV6(s)
return False return False
FINGERPRINT_ALGORITHMS = ('md5', 'sha1', 'sha224', 'sha256', 'sha384',
'sha512')
def check_certificate_fingerprint(conn, trusted_fingerprints):
cert = conn.getpeercert(binary_form=True)
for algorithm in FINGERPRINT_ALGORITHMS:
h = hashlib.new(algorithm)
h.update(cert)
if h.hexdigest() in trusted_fingerprints:
return
raise ssl.CertificateError('No matching fingerprint.')
if hasattr(ssl, 'create_default_context'): if hasattr(ssl, 'create_default_context'):
def ssl_wrap_socket(conn, hostname, logger, certfile=None, def ssl_wrap_socket(conn, hostname, logger, certfile=None,
verify_mode=ssl.CERT_REQUIRED, **kwargs): trusted_fingerprints=None,
**kwargs):
context = ssl.create_default_context(**kwargs) context = ssl.create_default_context(**kwargs)
context.verify_mode = verify_mode if trusted_fingerprints:
# Do not use Certification Authorities
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
if certfile: if certfile:
context.load_cert_chain(certfile) context.load_cert_chain(certfile)
return context.wrap_socket(conn, server_hostname=hostname) conn = context.wrap_socket(conn, server_hostname=hostname)
if trusted_fingerprints:
check_certificate_fingerprint(conn, trusted_fingerprints)
return conn
else: else:
def ssl_wrap_socket(conn, hostname, logger, certfile=None, ca_certs=None, def ssl_wrap_socket(conn, hostname, logger, certfile=None,
verify_mode=ssl.CERT_REQUIRED): ca_certs=None, trusted_fingerprints=None):
logger.critical('This Python version does not support SSL contexts, '
'which makes your connection vulnerable to man-in-the-middle '
'attacks. You should consider upgrading to Python 3 '
'(or at least 2.7.9).')
# TLSv1.0 is the only TLS version Python < 2.7.9 supports # TLSv1.0 is the only TLS version Python < 2.7.9 supports
# (besides SSLv2 and v3, which are known to be insecure) # (besides SSLv2 and v3, which are known to be insecure)
return ssl.wrap_socket(conn, certfile=certfile, ca_certs=ca_certs, conn = ssl.wrap_socket(conn, certfile=certfile, ca_certs=ca_certs,
ssl_version=ssl.ssl.PROTOCOL_TLSv1, verify_mode=verify_mode) ssl_version=ssl.ssl.PROTOCOL_TLSv1, verify_mode=ssl.CERT_NONE)
if trusted_fingerprints:
check_certificate_fingerprint(conn, trusted_fingerprints)
else:
logger.critical('This Python version does not support SSL/TLS '
'certification authority verification, which makes your '
'connection vulnerable to man-in-the-middle attacks.'
'You should consider upgrading to Python 3 '
'(or at least 2.7.9).')
return conn
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: