Verify server certificate, and deprecate Python < 2.7.9. Closes GH-1031.

This commit is contained in:
Valentin Lorentz 2016-02-21 13:20:09 +01:00
parent c3dd5f8b64
commit d922af1043
4 changed files with 85 additions and 14 deletions

View File

@ -236,10 +236,11 @@ setup(
)
if sys.version_info < (2, 7, 0):
sys.stderr.write('+-----------------------------------------------+\n')
sys.stderr.write('| Running Limnoria on Python 2.6 is deprecated. |\n')
sys.stderr.write('| Please consider upgrading to Python 3.x. |\n')
sys.stderr.write('+-----------------------------------------------+\n')
if sys.version_info < (2, 7, 9):
sys.stderr.write('+------------------------------------------------+\n')
sys.stderr.write('| Running Limnoria on Python versions older than |\n')
sys.stderr.write('| 2.7.9 is deprecated. |\n')
sys.stderr.write('| Please consider upgrading to Python 3.x. |\n')
sys.stderr.write('+------------------------------------------------+\n')
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

View File

@ -1163,6 +1163,23 @@ registerGlobalValue(supybot.protocols.http, 'proxy',
through. The value should be of the form 'host:port'.""")))
utils.web.proxy = supybot.protocols.http.proxy
###
# supybot.protocols.ssl
###
registerGroup(supybot.protocols, 'ssl')
class SSLVerifyMode(registry.OnlySomeStrings):
validStrings = ('required', 'optional', 'none')
def __call__(self):
import ssl
value = super(SSLVerifyMode, self).__call__()
return getattr(ssl, 'CERT_' + value.upper())
registerGlobalValue(supybot.protocols.ssl, 'verifyMode',
SSLVerifyMode('required', _("""Determines whether server certificates '
'will be verified. Valid values are "required", "optional", and "none". '
'The default and recommended setting is "required", which checks the '
'server certificate is signed by a known Certificate Authority, and '
'aborts the connection if it is not.""")))
###
# HTTP server

View File

@ -238,9 +238,9 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin):
if wait:
self.scheduleReconnect()
return
server = self._getNextServer()
socks_proxy = getattr(conf.supybot.networks, self.irc.network) \
.socksproxy()
self.server = self._getNextServer()
network_config = getattr(conf.supybot.networks, self.irc.network)
socks_proxy = network_config.socksproxy()
try:
if socks_proxy:
import socks
@ -249,16 +249,16 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin):
'using direct connection instead.')
socks_proxy = ''
if socks_proxy:
address = server[0]
address = self.server[0]
else:
try:
address = utils.net.getAddressFromHostname(server[0],
address = utils.net.getAddressFromHostname(self.server[0],
attempt=self._attempt)
except (socket.gaierror, socket.error) as e:
drivers.log.connectError(self.currentServer, e)
self.scheduleReconnect()
return
port = server[1]
port = self.server[1]
drivers.log.connect(self.currentServer)
try:
self.conn = utils.net.getSocket(address, port=port,
@ -276,9 +276,17 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin):
try:
# Connect before SSL, otherwise SSL is disabled if we use SOCKS.
# See http://stackoverflow.com/q/16136916/539465
self.conn.connect((address, server[1]))
if getattr(conf.supybot.networks, self.irc.network).ssl():
self.conn.connect((address, port))
if network_config.ssl():
self.starttls()
elif not network_config.requireStarttls():
drivers.log.critical(('Connection to network %s'
'does not use SSL/TLS, which makes it vulnerable to '
'man-in-the-middle attacks and passive eavesdropping. '
'You should consider upgrading your connection to SSL/TLS '
'<http://doc.supybot.aperio.fr/en/latest/use/faq.html#how-to-make-a-connection-secure>')
% self.irc.network)
def setTimeout():
self.conn.settimeout(conf.supybot.drivers.poll())
conf.supybot.drivers.poll.addCallback(setTimeout)
@ -353,7 +361,29 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin):
drivers.log.warning('Could not find cert file %s.' %
certfile)
certfile = None
self.conn = ssl.wrap_socket(self.conn, certfile=certfile)
try:
self.conn = utils.net.ssl_wrap_socket(self.conn,
logger=drivers.log, hostname=self.server[0],
certfile=certfile,
verify_mode=conf.supybot.protocols.ssl.verifyMode())
except ssl.CertificateError as e:
drivers.log.critical(('Certificate validation failed when '
'connecting to %s: %s\n'
'This means someone is doing a man-in-the-middle attack '
'on your connection.')
% (self.irc.network, e.args[0]))
raise ssl.SSLError('Aborting because of failed certificate '
'verification.')
except ssl.SSLError as e:
drivers.log.critical(('Certificate validation failed when '
'connecting to %s: %s\n'
'This means someone is doing a man-in-the-middle attack '
'on your connection, or because the server\'s '
'certificate is not trusted.')
% (self.irc.network, e.args[1]))
raise ssl.SSLError('Aborting because of failed certificate '
'verification.')
Driver = SocketDriver

View File

@ -33,6 +33,7 @@ Simple utility modules.
"""
import re
import ssl
import socket
from .web import _ipAddr, _domain
@ -128,4 +129,26 @@ def isIPV6(s):
return bruteIsIPV6(s)
return False
if hasattr(ssl, 'create_default_context'):
def ssl_wrap_socket(conn, hostname, logger, certfile=None,
verify_mode=ssl.CERT_REQUIRED, **kwargs):
context = ssl.create_default_context(**kwargs)
context.verify_mode = verify_mode
if certfile:
context.load_cert_chain(certfile)
return context.wrap_socket(conn, server_hostname=hostname)
else:
def ssl_wrap_socket(conn, hostname, logger, certfile=None, ca_certs=None,
verify_mode=ssl.CERT_REQUIRED):
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
# (besides SSLv2 and v3, which are known to be insecure)
return ssl.wrap_socket(conn, certfile=certfile, ca_certs=ca_certs,
ssl_version=ssl.ssl.PROTOCOL_TLSv1, verify_mode=verify_mode)
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: