Jan Cholasta wrote:
> Hi,
> 
> Dne 20.11.2014 v 23:26 Rob Crittenden napsal(a):
>> 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
> 
> 1) The patch needs a rebase on top of ipa-4-1 (applies fine on master)

Attached.

> 2) Could you split the option into two options, say "tls_version_min"
> and "tls_version_max"? IMO it would be easier to manage the version
> range that way, when for example you have to lower just the minimal
> version on a client to make it able to connect to a SSL3-only server.

Sure. I waffled back and forth before deciding on a single value.
Separate values are probably less error-prone.

> 3) Would it make sense to print a warning when the configured minimal
> TLS version is not safe and the connection uses a safe TLS version? This
> is for the case when you have to lower the minimal version on the client
> because of an old server, then the server gets updated, then you
> probably no longer want to have unsafe minimal version configured on the
> client.

I see what you're saying but I think it could end up being just spam
that user's get used to. That and given that I'd probably want to set it
up to require tls1.1 as a minimum but we can't do that because dogtag
only supports through tls1.0 right now AFAICT. That'd be a lot of warnings.

> Functionally the patch is OK.

rob

>From 3cdf7b21d3472d0710bee26a8fcabbc159739554 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 |  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 a417ab56f843b202787c6112553f8c16f2c1dde0..95ec6210a157fd158d81d97efbd46f3d35facbc6 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -284,7 +284,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)
-- 
1.9.3

>From 842faddbd19c3dcd1b1d7492cc23e6b48a1f860a 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 |  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 d393fee36de3ee17a0e7f1f0f18a77bcc8e7659b..9b12c20899e729cedacdee470f8f2b13250af4e0 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..df31a208822bc6901307424019e1fb70b0e3933c 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 001b7f1ca06edadfc7aad635d9d564e517008a63..ab5b2f630143ff1a1cf88e7b87cbb10767a06094 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,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 bd92fe65c7780e4f73a6f3c0edfb5dd46e704a4f..67180d59b5dddbe22a0a30e7f66c90f292ff8eb8 100644
--- a/ipapython/dogtag.py
+++ b/ipapython/dogtag.py
@@ -236,7 +236,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 1452a2a5844a5fb017d4408aadf56f7fcfc7fa25..def6b104e18fa67268a8c5a8629b533783fb5a95 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,
+                 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)
@@ -183,6 +184,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)
@@ -210,6 +213,8 @@ class NSSConnection(httplib.HTTPConnection, NSSAddressFamilyFallback):
 
         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
@@ -229,6 +234,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
@@ -247,8 +257,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