mirror of https://github.com/Mikaela/Limnoria.git
Add support for using server certificate fingerprint instead of CA signature.
This commit is contained in:
parent
d922af1043
commit
e77e78e79e
|
@ -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))
|
||||||
|
|
|
@ -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.')
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue