Use new capability in python-nss-0.16 to use the NSS protocol range
setter. This lets us enable TLSv1.1 and TLSv1.2 for client connections.

I made this configurable via tls_protocol_range in case somebody wants
to override it.

There isn't a whole ton of error handling on bad input but there is
enough, I think, to point the user in the the right direction.

Added a couple more lines of debug output to include the negotiated
protocol and cipher.

rob
>From 599ff6072d4775becc49f2bbe3fd65e171f6db65 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcrit...@redhat.com>
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. If only a single value is provided then it
represents both the min and max.

This is overridable per client by setting tls_protocol_range.

https://fedorahosted.org/freeipa/ticket/4653
---
 freeipa.spec.in     |  2 +-
 ipalib/constants.py |  2 ++
 ipalib/rpc.py       |  4 +++-
 ipapython/dogtag.py |  3 ++-
 ipapython/nsslib.py | 18 ++++++++++++++++--
 5 files changed, 24 insertions(+), 5 deletions(-)

diff --git a/freeipa.spec.in b/freeipa.spec.in
index 703ef9e1988c2171b815a680ad126793b2fef82d..7149066b8b6c55424843fe5a3515c92cfc92c850 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -279,7 +279,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 325414b64fdacd4d8df261588cfc9b7481923be1..c50a806b4a0d275525a2bc2850116a05dd01d97e 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -122,6 +122,8 @@ DEFAULT_CONFIG = (
 
     ('rpc_protocol', 'jsonrpc'),
 
+    ('tls_protocol_range', 'tls1.0,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 001b7f1ca06edadfc7aad635d9d564e517008a63..5e1eaf1db1f6d5539c604b748a876a7a33c0e009 100644
--- a/ipalib/rpc.py
+++ b/ipalib/rpc.py
@@ -69,6 +69,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
@@ -492,7 +493,8 @@ 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,
+                                 protocol_range=api.env.tls_protocol_range)
         self.dbdir=dbdir
 
         conn.connect()
diff --git a/ipapython/dogtag.py b/ipapython/dogtag.py
index bd92fe65c7780e4f73a6f3c0edfb5dd46e704a4f..e6a0f5d8498d5c785bfcb3e1d2782814e2a7256e 100644
--- a/ipapython/dogtag.py
+++ b/ipapython/dogtag.py
@@ -236,7 +236,8 @@ 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,
+                                    protocol_range=api.env.tls_protocol_range)
         conn.set_debuglevel(0)
         conn.connect()
         conn.sock.set_client_auth_data_callback(
diff --git a/ipapython/nsslib.py b/ipapython/nsslib.py
index 1452a2a5844a5fb017d4408aadf56f7fcfc7fa25..f874ac425b0c5ab201b6661c012c142c7510d345 100644
--- a/ipapython/nsslib.py
+++ b/ipapython/nsslib.py
@@ -174,7 +174,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,
+                 protocol_range='tls1.1,tls1.2'):
         """
         :param host: the server to connect to
         :param port: the port to use (default is set in HTTPConnection)
@@ -183,6 +184,7 @@ 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 protocol_range: min and max SSL/TLS protocol range supported.
         """
         httplib.HTTPConnection.__init__(self, host, port, strict)
         NSSAddressFamilyFallback.__init__(self, family)
@@ -210,6 +212,10 @@ class NSSConnection(httplib.HTTPConnection, NSSAddressFamilyFallback):
 
         ssl.set_domestic_policy()
         nss.set_password_callback(self.password_callback)
+        protocol_range = str(protocol_range.replace(' ', ''))
+        (self.range_low, comma, self.range_high) = str(protocol_range).partition(',')
+        if self.range_high == '':
+            self.range_high = self.range_low
 
     def _create_socket(self):
         # TODO: remove the try block once python-nss is guaranteed to contain
@@ -229,6 +235,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.range_low, self.range_high)
+        except NSPRError, e:
+            root_logger.error('Failed to set TLS range to %s, %s' % (self.range_low, self.range_high))
+            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
@@ -247,8 +258,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)
-- 
1.9.3

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to