URL: https://github.com/freeipa/freeipa/pull/396
Author: stlaz
 Title: #396: Explicitly remove support of SSLv2
Action: synchronized

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/396/head:pr396
git checkout pr396
From 661ed55292dda18f1f09f89af883086f9b5a7ab0 Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slazn...@redhat.com>
Date: Fri, 13 Jan 2017 12:31:29 +0100
Subject: [PATCH] Explicitly remove support of SSLv2/3

It was possible to set tls_version_min/max to 'ssl2' or 'ssl3',
even though newer versions of NSS will fail to set this as a valid
TLS version. This patch explicitly checks for deprecated TLS versions
prior to creating a TLS connection.

Also, we don't allow tls_version_min/max to be set to a random
string anymore.

https://fedorahosted.org/freeipa/ticket/6607
---
 ipalib/config.py    | 27 ++++++++++++++++++++++--
 ipalib/constants.py | 10 +++++++++
 ipapython/nsslib.py | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 94 insertions(+), 4 deletions(-)

diff --git a/ipalib/config.py b/ipalib/config.py
index 20591db..1a59879 100644
--- a/ipalib/config.py
+++ b/ipalib/config.py
@@ -41,8 +41,11 @@
 
 from ipapython.dn import DN
 from ipalib.base import check_name
-from ipalib.constants import CONFIG_SECTION
-from ipalib.constants import OVERRIDE_ERROR, SET_ERROR, DEL_ERROR
+from ipalib.constants import (
+    CONFIG_SECTION,
+    OVERRIDE_ERROR, SET_ERROR, DEL_ERROR,
+    TLS_VERSIONS
+)
 from ipalib import errors
 
 if six.PY3:
@@ -578,6 +581,26 @@ def _finalize_core(self, **defaults):
 
         self._merge(**defaults)
 
+        # set the best known TLS version if min/max versions are not set
+        if 'tls_version_min' not in self:
+            self.tls_version_min = TLS_VERSIONS[-1]
+        elif self.tls_version_min not in TLS_VERSIONS:
+            raise errors.EnvironmentError(
+                "Unknown TLS version '{ver}' set in tls_version_min."
+                .format(ver=self.tls_version_min))
+
+        if 'tls_version_max' not in self:
+            self.tls_version_max = TLS_VERSIONS[-1]
+        elif self.tls_version_max not in TLS_VERSIONS:
+            raise errors.EnvironmentError(
+                "Unknown TLS version '{ver}' set in tls_version_max."
+                .format(ver=self.tls_version_max))
+
+        if self.tls_version_max < self.tls_version_min:
+            raise errors.EnvironmentError(
+                "tls_version_min is set to a higher TLS version than "
+                "tls_version_max.")
+
     def _finalize(self, **lastchance):
         """
         Finalize and lock environment.
diff --git a/ipalib/constants.py b/ipalib/constants.py
index fa20624..e64324f 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -283,3 +283,13 @@
 # IPA API Framework user
 IPAAPI_USER = 'ipaapi'
 IPAAPI_GROUP = 'ipaapi'
+
+# TLS related constants
+TLS_VERSIONS = [
+    "ssl2",
+    "ssl3",
+    "tls1.0",
+    "tls1.1",
+    "tls1.2"
+]
+TLS_VERSION_MINIMAL = "tls1.0"
diff --git a/ipapython/nsslib.py b/ipapython/nsslib.py
index 08d05fc..97bbf64 100644
--- a/ipapython/nsslib.py
+++ b/ipapython/nsslib.py
@@ -23,6 +23,8 @@
 import getpass
 import socket
 from ipapython.ipa_log_manager import root_logger
+from ipapython.ipa_log_manager import log_mgr
+from ipalib.constants import TLS_VERSIONS, TLS_VERSION_MINIMAL
 
 from nss.error import NSPRError
 import nss.io as io
@@ -38,6 +40,9 @@
     # pylint: disable=import-error
     import http.client as httplib
 
+# get a logger for this module
+logger = log_mgr.get_logger(__name__)
+
 # NSS database currently open
 current_dbdir = None
 
@@ -129,6 +134,56 @@ def client_auth_data_callback(ca_names, chosen_nickname, password, certdb):
     socket.AF_UNSPEC: io.PR_AF_UNSPEC
 }
 
+
+def get_proper_tls_version_span(tls_version_min, tls_version_max):
+    """
+    This function checks whether the given TLS versions are known in FreeIPA
+    and that these versions fulfill the requirements for minimal TLS version
+    (see `ipalib.constants: TLS_VERSIONS, TLS_VERSION_MINIMAL`).
+
+    :param tls_version_min:
+        the lower value in the TLS min-max span, raised to the lowest allowed
+        value if too low
+    :param tls_version_max:
+        the higher value in the TLS min-max span, raised to tls_version_min
+        if lower than TLS_VERSION_MINIMAL
+    :raises: ValueError
+    """
+    min_allowed_idx = TLS_VERSIONS.index(TLS_VERSION_MINIMAL)
+
+    try:
+        min_version_idx = TLS_VERSIONS.index(tls_version_min)
+    except ValueError:
+        raise ValueError("tls_version_min ('{val}') is not a known "
+                         "TLS version.".format(val=tls_version_min))
+
+    try:
+        max_version_idx = TLS_VERSIONS.index(tls_version_max)
+    except ValueError:
+        raise ValueError("tls_version_max ('{val}') is not a known "
+                         "TLS version.".format(val=tls_version_max))
+
+    if min_version_idx > max_version_idx:
+        raise ValueError("tls_version_min is higher than "
+                         "tls_version_max.")
+
+    if min_version_idx < min_allowed_idx:
+        min_version_idx = min_allowed_idx
+        logger.warning("tls_version_min set too low ('{old}'),"
+                       "using '{new}' instead"
+                       .format(old=tls_version_min,
+                               new=TLS_VERSIONS[min_version_idx]))
+
+    if max_version_idx < min_allowed_idx:
+        max_version_idx = min_version_idx
+        logger.warning("tls_version_max set too low ('{old}'),"
+                       "using '{new}' instead"
+                       .format(old=tls_version_max,
+                               new=TLS_VERSIONS[max_version_idx]))
+
+    return TLS_VERSIONS[min_version_idx:max_version_idx+1]
+
+
 class NSSAddressFamilyFallback(object):
     def __init__(self, family):
         self.sock_family = family
@@ -217,8 +272,10 @@ def __init__(self, host, port=None, strict=None,
 
         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)
+        tls_versions = get_proper_tls_version_span(
+            tls_version_min, tls_version_max)
+        self.tls_version_min = tls_versions[0]
+        self.tls_version_max = tls_versions[-1]
 
     def _create_socket(self):
         ssl_enable_renegotiation = getattr(
-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to