From 8c544d9583c4172634f3180d6f90e6d4f37595ed Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Thu, 30 Oct 2014 11:52:14 -0400 Subject: [PATCH] Use NSS protocol range API to set available TLS protocols Protocols are configured as an inclusive range from SSLv3 through TLSv1.2. The allowed values in the range are ssl3, tls1.0, tls1.1 and tls1.2. This is overridable per client by setting tls_version_min and/or tls_version_max. https://fedorahosted.org/freeipa/ticket/4653 Reviewed-By: Jan Cholasta --- freeipa.spec.in | 2 +- ipalib/constants.py | 4 ++++ ipalib/rpc.py | 5 ++++- ipapython/dogtag.py | 4 +++- ipapython/nsslib.py | 17 +++++++++++++++-- 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/freeipa.spec.in b/freeipa.spec.in index e29f77de0db89035d15008c6be2da0ae7e96158a..1c975b3912d0a7470a32f1b7e314cfde74446e85 100644 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -271,7 +271,7 @@ Requires: gnupg Requires: iproute Requires: keyutils Requires: pyOpenSSL -Requires: python-nss >= 0.15 +Requires: python-nss >= 0.16 Requires: python-lxml Requires: python-netaddr Requires: libipa_hbac-python diff --git a/ipalib/constants.py b/ipalib/constants.py index 1eed7ca6ad0e5920318dadc68ed36fff6cf889f2..111bafe5ed0c3d2df58a1b6839feedc58a14fcc4 100644 --- a/ipalib/constants.py +++ b/ipalib/constants.py @@ -122,6 +122,10 @@ DEFAULT_CONFIG = ( ('rpc_protocol', 'jsonrpc'), + # Define an inclusive range of SSL/TLS version support + ('tls_version_min', 'tls1.0'), + ('tls_version_max', 'tls1.2'), + # Time to wait for a service to start, in seconds ('startup_timeout', 300), diff --git a/ipalib/rpc.py b/ipalib/rpc.py index 5934f0c26e4b7c0a44adbab978c1f9b319d72e9f..806f6bb9adf004660c9cb285cf31b09a988afa93 100644 --- a/ipalib/rpc.py +++ b/ipalib/rpc.py @@ -68,6 +68,7 @@ from ipalib.krb_utils import KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, KRB5KRB_AP_ERR_TKT KRB5_FCC_PERM, KRB5_FCC_NOFILE, KRB5_CC_FORMAT, KRB5_REALM_CANT_RESOLVE from ipapython.dn import DN from ipalib.capabilities import VERSION_WITHOUT_CAPABILITIES +from ipalib import api COOKIE_NAME = 'ipa_session' KEYRING_COOKIE_NAME = '%s_cookie:%%s' % COOKIE_NAME @@ -488,7 +489,9 @@ class SSLTransport(LanguageAwareTransport): if sys.version_info < (2, 7): conn = NSSHTTPS(host, 443, dbdir=dbdir, no_init=no_init) else: - conn = NSSConnection(host, 443, dbdir=dbdir, no_init=no_init) + conn = NSSConnection(host, 443, dbdir=dbdir, no_init=no_init, + tls_version_min=api.env.tls_version_min, + tls_version_max=api.env.tls_version_max) self.dbdir=dbdir conn.connect() diff --git a/ipapython/dogtag.py b/ipapython/dogtag.py index 14824b99431e85dd73613befd72e500d370cfe2c..0e0aacca798377517244075ed6b07dff63e87358 100644 --- a/ipapython/dogtag.py +++ b/ipapython/dogtag.py @@ -234,7 +234,9 @@ def https_request(host, port, url, secdir, password, nickname, **kw): """ def connection_factory(host, port): - conn = nsslib.NSSConnection(host, port, dbdir=secdir) + conn = nsslib.NSSConnection(host, port, dbdir=secdir, + tls_version_min=api.env.tls_version_min, + tls_version_max=api.env.tls_version_max) conn.set_debuglevel(0) conn.connect() conn.sock.set_client_auth_data_callback( diff --git a/ipapython/nsslib.py b/ipapython/nsslib.py index 93b0c56fcff4fc69841a6823aae8f694c1f76ff0..57fa3ff4fa5a044577f21fe43c2c0b0596c2e4f8 100644 --- a/ipapython/nsslib.py +++ b/ipapython/nsslib.py @@ -171,7 +171,8 @@ class NSSConnection(httplib.HTTPConnection, NSSAddressFamilyFallback): default_port = httplib.HTTPSConnection.default_port def __init__(self, host, port=None, strict=None, - dbdir=None, family=socket.AF_UNSPEC, no_init=False): + dbdir=None, family=socket.AF_UNSPEC, no_init=False, + tls_version_min='tls1.1', tls_version_max='tls1.2'): """ :param host: the server to connect to :param port: the port to use (default is set in HTTPConnection) @@ -180,6 +181,8 @@ class NSSConnection(httplib.HTTPConnection, NSSAddressFamilyFallback): :param no_init: do not initialize the NSS database. This requires that the database has already been initialized or the request will fail. + :param tls_min_version: mininum version of SSL/TLS supported + :param tls_max_version: maximum version of SSL/TLS supported. """ httplib.HTTPConnection.__init__(self, host, port, strict) NSSAddressFamilyFallback.__init__(self, family) @@ -199,6 +202,8 @@ class NSSConnection(httplib.HTTPConnection, NSSAddressFamilyFallback): nss.nss_init(dbdir) ssl.set_domestic_policy() nss.set_password_callback(self.password_callback) + self.tls_version_min = str(tls_version_min) + self.tls_version_max = str(tls_version_max) def _create_socket(self): # TODO: remove the try block once python-nss is guaranteed to contain @@ -218,6 +223,11 @@ class NSSConnection(httplib.HTTPConnection, NSSAddressFamilyFallback): self.sock = ssl.SSLSocket(family=self.family) self.sock.set_ssl_option(ssl.SSL_SECURITY, True) self.sock.set_ssl_option(ssl.SSL_HANDSHAKE_AS_CLIENT, True) + try: + self.sock.set_ssl_version_range(self.tls_version_min, self.tls_version_max) + except NSPRError, e: + root_logger.error('Failed to set TLS range to %s, %s' % (self.tls_version_min, self.tls_version_max)) + raise self.sock.set_ssl_option(ssl_require_safe_negotiation, False) self.sock.set_ssl_option(ssl_enable_renegotiation, ssl_renegotiate_requires_xtn) # Provide a callback which notifies us when the SSL handshake is complete @@ -236,8 +246,11 @@ class NSSConnection(httplib.HTTPConnection, NSSAddressFamilyFallback): """ Verify callback. If we get here then the certificate is ok. """ + channel = sock.get_ssl_channel_info() + suite = ssl.get_cipher_suite_info(channel.cipher_suite) root_logger.debug("handshake complete, peer = %s", sock.get_peer_name()) - pass + root_logger.debug('Protocol: %s' % channel.protocol_version_str.upper()) + root_logger.debug('Cipher: %s' % suite.cipher_suite_name) def connect(self): self.connect_socket(self.host, self.port) -- 2.1.0