|
|
|
@ -1,7 +1,6 @@
|
|
|
|
|
"""
|
|
|
|
|
SSL/TLS negotiation.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
import os
|
|
|
|
|
import socket
|
|
|
|
@ -10,6 +9,7 @@ import sys
|
|
|
|
|
|
|
|
|
|
from network.advanceddispatcher import AdvancedDispatcher
|
|
|
|
|
import network.asyncore_pollchoose as asyncore
|
|
|
|
|
|
|
|
|
|
from queues import receiveDataQueue
|
|
|
|
|
import paths
|
|
|
|
|
|
|
|
|
@ -24,7 +24,8 @@ if sys.version_info >= (2, 7, 13):
|
|
|
|
|
# ssl.PROTOCOL_TLS1.2
|
|
|
|
|
sslProtocolVersion = ssl.PROTOCOL_TLS # pylint: disable=no-member
|
|
|
|
|
elif sys.version_info >= (2, 7, 9):
|
|
|
|
|
# this means any SSL/TLS. SSLv2 and 3 are excluded with an option after context is created
|
|
|
|
|
# this means any SSL/TLS.
|
|
|
|
|
# SSLv2 and 3 are excluded with an option after context is created
|
|
|
|
|
sslProtocolVersion = ssl.PROTOCOL_SSLv23
|
|
|
|
|
else:
|
|
|
|
|
# this means TLSv1, there is no way to set "TLSv1 or higher" or
|
|
|
|
@ -33,7 +34,8 @@ else:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ciphers
|
|
|
|
|
if ssl.OPENSSL_VERSION_NUMBER >= 0x10100000 and not ssl.OPENSSL_VERSION.startswith("LibreSSL"):
|
|
|
|
|
if ssl.OPENSSL_VERSION_NUMBER >= 0x10100000 and not \
|
|
|
|
|
ssl.OPENSSL_VERSION.startswith("LibreSSL"):
|
|
|
|
|
sslProtocolCiphers = "AECDH-AES256-SHA@SECLEVEL=0"
|
|
|
|
|
else:
|
|
|
|
|
sslProtocolCiphers = "AECDH-AES256-SHA"
|
|
|
|
@ -41,19 +43,19 @@ else:
|
|
|
|
|
|
|
|
|
|
class TLSDispatcher(AdvancedDispatcher):
|
|
|
|
|
"""TLS functionality for classes derived from AdvancedDispatcher"""
|
|
|
|
|
# pylint: disable=too-many-instance-attributes
|
|
|
|
|
# pylint: disable=too-many-arguments,super-init-not-called,unused-argument
|
|
|
|
|
def __init__(
|
|
|
|
|
self, address=None, sock=None, certfile=None, keyfile=None,
|
|
|
|
|
server_side=False, ciphers=sslProtocolCiphers
|
|
|
|
|
):
|
|
|
|
|
# pylint: disable=too-many-instance-attributes, too-many-arguments
|
|
|
|
|
# pylint: disable=super-init-not-called
|
|
|
|
|
def __init__(self, _=None, sock=None, certfile=None, keyfile=None,
|
|
|
|
|
server_side=False, ciphers=sslProtocolCiphers):
|
|
|
|
|
self.want_read = self.want_write = True
|
|
|
|
|
if certfile is None:
|
|
|
|
|
self.certfile = os.path.join(paths.codePath(), 'sslkeys', 'cert.pem')
|
|
|
|
|
self.certfile = os.path.join(
|
|
|
|
|
paths.codePath(), 'sslkeys', 'cert.pem')
|
|
|
|
|
else:
|
|
|
|
|
self.certfile = certfile
|
|
|
|
|
if keyfile is None:
|
|
|
|
|
self.keyfile = os.path.join(paths.codePath(), 'sslkeys', 'key.pem')
|
|
|
|
|
self.keyfile = os.path.join(
|
|
|
|
|
paths.codePath(), 'sslkeys', 'key.pem')
|
|
|
|
|
else:
|
|
|
|
|
self.keyfile = keyfile
|
|
|
|
|
self.server_side = server_side
|
|
|
|
@ -68,20 +70,23 @@ class TLSDispatcher(AdvancedDispatcher):
|
|
|
|
|
# pylint: disable=attribute-defined-outside-init
|
|
|
|
|
self.isSSL = True
|
|
|
|
|
self.tlsStarted = True
|
|
|
|
|
# Once the connection has been established, it's safe to wrap the
|
|
|
|
|
# socket.
|
|
|
|
|
# Once the connection has been established,
|
|
|
|
|
# it's safe to wrap the socket.
|
|
|
|
|
if sys.version_info >= (2, 7, 9):
|
|
|
|
|
context = ssl.create_default_context(
|
|
|
|
|
purpose=ssl.Purpose.SERVER_AUTH if self.server_side else ssl.Purpose.CLIENT_AUTH)
|
|
|
|
|
purpose=ssl.Purpose.SERVER_AUTH
|
|
|
|
|
if self.server_side else ssl.Purpose.CLIENT_AUTH)
|
|
|
|
|
context.set_ciphers(self.ciphers)
|
|
|
|
|
context.set_ecdh_curve("secp256k1")
|
|
|
|
|
context.check_hostname = False
|
|
|
|
|
context.verify_mode = ssl.CERT_NONE
|
|
|
|
|
# also exclude TLSv1 and TLSv1.1 in the future
|
|
|
|
|
context.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 |\
|
|
|
|
|
ssl.OP_NO_SSLv3 | ssl.OP_SINGLE_ECDH_USE | ssl.OP_CIPHER_SERVER_PREFERENCE
|
|
|
|
|
ssl.OP_NO_SSLv3 | ssl.OP_SINGLE_ECDH_USE |\
|
|
|
|
|
ssl.OP_CIPHER_SERVER_PREFERENCE
|
|
|
|
|
self.sslSocket = context.wrap_socket(
|
|
|
|
|
self.socket, server_side=self.server_side, do_handshake_on_connect=False)
|
|
|
|
|
self.socket, server_side=self.server_side,
|
|
|
|
|
do_handshake_on_connect=False)
|
|
|
|
|
else:
|
|
|
|
|
self.sslSocket = ssl.wrap_socket(
|
|
|
|
|
self.socket, server_side=self.server_side,
|
|
|
|
@ -115,12 +120,15 @@ class TLSDispatcher(AdvancedDispatcher):
|
|
|
|
|
def readable(self):
|
|
|
|
|
"""Handle readable check for TLS-enabled sockets"""
|
|
|
|
|
try:
|
|
|
|
|
# during TLS handshake, and after flushing write buffer, return status of last handshake attempt
|
|
|
|
|
# during TLS handshake, and after flushing write buffer,
|
|
|
|
|
# return status of last handshake attempt
|
|
|
|
|
if self.tlsStarted and not self.tlsDone and not self.write_buf:
|
|
|
|
|
# print "tls readable, %r" % (self.want_read)
|
|
|
|
|
return self.want_read
|
|
|
|
|
# prior to TLS handshake, receiveDataThread should emulate synchronous behaviour
|
|
|
|
|
elif not self.fullyEstablished and (self.expectBytes == 0 or not self.write_buf_empty()):
|
|
|
|
|
# prior to TLS handshake,
|
|
|
|
|
# receiveDataThread should emulate synchronous behaviour
|
|
|
|
|
elif not self.fullyEstablished and (
|
|
|
|
|
self.expectBytes == 0 or not self.write_buf_empty()):
|
|
|
|
|
return False
|
|
|
|
|
return AdvancedDispatcher.readable(self)
|
|
|
|
|
except AttributeError:
|
|
|
|
@ -135,10 +143,14 @@ class TLSDispatcher(AdvancedDispatcher):
|
|
|
|
|
try:
|
|
|
|
|
# wait for write buffer flush
|
|
|
|
|
if self.tlsStarted and not self.tlsDone and not self.write_buf:
|
|
|
|
|
# logger.debug("%s:%i TLS handshaking (read)", self.destination.host, self.destination.port)
|
|
|
|
|
# logger.debug(
|
|
|
|
|
# "%s:%i TLS handshaking (read)", self.destination.host,
|
|
|
|
|
# self.destination.port)
|
|
|
|
|
self.tls_handshake()
|
|
|
|
|
else:
|
|
|
|
|
# logger.debug("%s:%i Not TLS handshaking (read)", self.destination.host, self.destination.port)
|
|
|
|
|
# logger.debug(
|
|
|
|
|
# "%s:%i Not TLS handshaking (read)", self.destination.host,
|
|
|
|
|
# self.destination.port)
|
|
|
|
|
return AdvancedDispatcher.handle_read(self)
|
|
|
|
|
except AttributeError:
|
|
|
|
|
return AdvancedDispatcher.handle_read(self)
|
|
|
|
@ -161,10 +173,14 @@ class TLSDispatcher(AdvancedDispatcher):
|
|
|
|
|
try:
|
|
|
|
|
# wait for write buffer flush
|
|
|
|
|
if self.tlsStarted and not self.tlsDone and not self.write_buf:
|
|
|
|
|
# logger.debug("%s:%i TLS handshaking (write)", self.destination.host, self.destination.port)
|
|
|
|
|
# logger.debug(
|
|
|
|
|
# "%s:%i TLS handshaking (write)", self.destination.host,
|
|
|
|
|
# self.destination.port)
|
|
|
|
|
self.tls_handshake()
|
|
|
|
|
else:
|
|
|
|
|
# logger.debug("%s:%i Not TLS handshaking (write)", self.destination.host, self.destination.port)
|
|
|
|
|
# logger.debug(
|
|
|
|
|
# "%s:%i Not TLS handshaking (write)", self.destination.host,
|
|
|
|
|
# self.destination.port)
|
|
|
|
|
return AdvancedDispatcher.handle_write(self)
|
|
|
|
|
except AttributeError:
|
|
|
|
|
return AdvancedDispatcher.handle_write(self)
|
|
|
|
@ -188,7 +204,8 @@ class TLSDispatcher(AdvancedDispatcher):
|
|
|
|
|
# print "handshaking (internal)"
|
|
|
|
|
self.sslSocket.do_handshake()
|
|
|
|
|
except ssl.SSLError as err:
|
|
|
|
|
# print "%s:%i: handshake fail" % (self.destination.host, self.destination.port)
|
|
|
|
|
# print "%s:%i: handshake fail" % (
|
|
|
|
|
# self.destination.host, self.destination.port)
|
|
|
|
|
self.want_read = self.want_write = False
|
|
|
|
|
if err.args[0] == ssl.SSL_ERROR_WANT_READ:
|
|
|
|
|
# print "want read"
|
|
|
|
|