The list of supported TLS cipher suites in /etc/httpd/conf.d/nss.conf
has been modernized. Insecure or less secure algorithms such as RC4,
DES and 3DES are removed. Perfect forward secrecy suites with ephemeral
ECDH key exchange have been added. IE 8 on Windows XP is no longer
supported.

The list of enabled cipher suites has been generated with the script
contrib/nssciphersuite/nssciphersuite.py.

The supported suites are currently:

TLS_RSA_WITH_AES_128_CBC_SHA256
TLS_RSA_WITH_AES_256_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
TLS_RSA_WITH_AES_128_GCM_SHA256
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_256_GCM_SHA384
TLS_RSA_WITH_AES_256_CBC_SHA

https://fedorahosted.org/freeipa/ticket/5589
From 26d356970ef1f7de7b00fe237f67345c507c7989 Mon Sep 17 00:00:00 2001
From: Christian Heimes <chei...@redhat.com>
Date: Thu, 21 Jan 2016 16:09:10 +0100
Subject: [PATCH] Modernize mod_nss's cipher suites

The list of supported TLS cipher suites in /etc/httpd/conf.d/nss.conf
has been modernized. Insecure or less secure algorithms such as RC4,
DES and 3DES are removed. Perfect forward secrecy suites with ephemeral
ECDH key exchange have been added. IE 8 on Windows XP is no longer
supported.

The list of enabled cipher suites has been generated with the script
contrib/nssciphersuite/nssciphersuite.py.

The supported suites are currently:

TLS_RSA_WITH_AES_128_CBC_SHA256
TLS_RSA_WITH_AES_256_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
TLS_RSA_WITH_AES_128_GCM_SHA256
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_256_GCM_SHA384
TLS_RSA_WITH_AES_256_CBC_SHA

https://fedorahosted.org/freeipa/ticket/5589

Signed-off-by: Christian Heimes <chei...@redhat.com>
---
 contrib/nssciphersuite/README.txt        |  38 ++++++++
 contrib/nssciphersuite/nssciphersuite.py | 147 +++++++++++++++++++++++++++++++
 ipaserver/install/httpinstance.py        |  18 ++++
 ipaserver/install/server/upgrade.py      |  14 +++
 4 files changed, 217 insertions(+)
 create mode 100644 contrib/nssciphersuite/README.txt
 create mode 100755 contrib/nssciphersuite/nssciphersuite.py

diff --git a/contrib/nssciphersuite/README.txt b/contrib/nssciphersuite/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..89bafff560eb497089474e2d8a0b1b853d5c5bdf
--- /dev/null
+++ b/contrib/nssciphersuite/README.txt
@@ -0,0 +1,38 @@
+Cipher suite for mod_nss
+------------------------
+
+The nssciphersuite.py script parses mod_nss' nss_engine_cipher.c file and
+creates a list of secure cipher suites for TLS. The script filters out
+insecure, obsolete and slow ciphers according to some rules.
+
+As of January 2016 and mod_nss 1.0.12 the cipher suite list contains 14
+cipher suites for TLS 1.0, 1.1 and 1.2 for RSA and ECDSA certificates. The
+cipher suite list also supports Perfect Forward Secrecy with ephemeral ECDH
+key exchange. https://www.ssllabs.com/ gives a 'A' grade.
+
+Note:
+No suite is compatible with IE 8 and earlier on Windows XP. If you need IE 8
+support, append "+rsa_3des_sha" to enable TLS_RSA_WITH_3DES_EDE_CBC_SHA.
+
+# disabled cipher attributes: SSL_3DES, SSL_CAMELLIA, SSL_CAMELLIA128, SSL_CAMELLIA256, SSL_DES, SSL_DSS, SSL_MD5, SSL_RC2, SSL_RC4, SSL_aDSS, SSL_aNULL, SSL_eNULL, SSL_kECDHe, SSL_kECDHr, kECDH
+# weak strength: SSL_EXPORT40, SSL_EXPORT56, SSL_LOW, SSL_STRONG_NONE
+# enabled cipher suites:
+#   TLS_RSA_WITH_AES_128_CBC_SHA256
+#   TLS_RSA_WITH_AES_256_CBC_SHA256
+#   TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
+#   TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
+#   TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
+#   TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
+#   TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+#   TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
+#   TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+#   TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
+#   TLS_RSA_WITH_AES_128_GCM_SHA256
+#   TLS_RSA_WITH_AES_128_CBC_SHA
+#   TLS_RSA_WITH_AES_256_GCM_SHA384
+#   TLS_RSA_WITH_AES_256_CBC_SHA
+#
+
+NSSCipherSuite +aes_128_sha_256,+aes_256_sha_256,+ecdhe_ecdsa_aes_128_gcm_sha_256,+ecdhe_ecdsa_aes_128_sha,+ecdhe_ecdsa_aes_256_gcm_sha_384,+ecdhe_ecdsa_aes_256_sha,+ecdhe_rsa_aes_128_gcm_sha_256,+ecdhe_rsa_aes_128_sha,+ecdhe_rsa_aes_256_gcm_sha_384,+ecdhe_rsa_aes_256_sha,+rsa_aes_128_gcm_sha_256,+rsa_aes_128_sha,+rsa_aes_256_gcm_sha_384,+rsa_aes_256_sha
+
+
diff --git a/contrib/nssciphersuite/nssciphersuite.py b/contrib/nssciphersuite/nssciphersuite.py
new file mode 100755
index 0000000000000000000000000000000000000000..95252512a38d90bbcf12e5d362de8bf509c3e854
--- /dev/null
+++ b/contrib/nssciphersuite/nssciphersuite.py
@@ -0,0 +1,147 @@
+#!/usr/bin/python3
+#
+# Authors:
+#     Christian Heimes <chei...@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2016 Red Hat, Inc.
+# All rights reserved.
+#
+"""Generate safe NSSCipherSuite stanza for mod_nss
+"""
+from __future__ import print_function
+
+import operator
+import re
+import urllib.request  # pylint: disable=E0611
+
+SOURCE = "https://git.fedorahosted.org/cgit/mod_nss.git/plain/nss_engine_cipher.c";
+
+CIPHER_RE = re.compile(
+    r'\s*\{'
+    r'\"(?P<name>\w+)\",\s*'
+    r'(?P<num>(TLS|SSL)_\w+),\s*'
+    r'\"(?P<openssl_name>[\w-]+)\",\s*'
+    r'(?P<attr>[\w|]+),\s*'
+    r'(?P<version>\w+),\s*'
+    r'(?P<strength>\w+),\s*'
+    r'(?P<bits>\d+),\s*'
+    r'(?P<alg_bits>\d+)'
+)
+
+DISABLED_CIPHERS = {
+    # ciphers without encryption or authentication
+    'SSL_eNULL', 'SSL_aNULL',
+    # MD5 is broken
+    # SHA-1 is still required as PRF algorithm for TLSv1.0
+    'SSL_MD5',
+    # RC2 and RC4 stream ciphers are broken.
+    'SSL_RC2', 'SSL_RC4',
+    # DES is broken and Triple DES is too weak.
+    'SSL_DES', 'SSL_3DES',
+    # DSA is problematic.
+    'SSL_DSS', 'SSL_aDSS',
+    # prefer AES over Camellia.
+    'SSL_CAMELLIA128', 'SSL_CAMELLIA256', 'SSL_CAMELLIA',
+    # non-ephemeral EC Diffie-Hellmann with fixed parameters are not
+    # used by common browser and are therefore irrelevant for HTTPS.
+    'kECDH', 'SSL_kECDHr', 'SSL_kECDHe'
+}
+
+WEAK_STRENGTH = {
+    'SSL_STRONG_NONE',
+    'SSL_EXPORT40',
+    'SSL_EXPORT56',
+    'SSL_LOW'
+}
+
+
+def parse_nss_engine_cipher(lines, encoding='utf-8'):
+    """Parse nss_engine_cipher.c and get list of ciphers
+
+    :param lines: iterable or list of lines
+    :param encoding: default encoding
+    :return: list of cipher dicts
+    """
+    ciphers = []
+    start = False
+    for line in lines:
+        if not isinstance(line, str):
+            line = line.decode(encoding)
+
+        if line.startswith('cipher_properties'):
+            start = True
+        elif not start:
+            continue
+        elif line.startswith('};'):
+            break
+
+        mo = CIPHER_RE.match(line)
+        if not mo:
+            continue
+
+        match = mo.groupdict()
+        match['attr'] = set(match['attr'].split('|'))
+        match['bits'] = int(match['bits'])
+        match['alg_bits'] = int(match['alg_bits'])
+
+        # some cipher elemets aren't flagged
+        for algo in ['SHA256', 'SHA384']:
+            if match['num'].endswith(algo):
+                match['attr'].add('SSL_{}'.format(algo))
+
+        # cipher block chaining isn't tracked
+        if '_CBC' in match['num']:
+            match['attr'].add('SSL_CBC')
+
+        if match['attr'].intersection(DISABLED_CIPHERS):
+            match['enabled'] = False
+        elif match['strength'] in WEAK_STRENGTH:
+            match['enabled'] = False
+        else:
+            match['enabled'] = True
+
+        # EECDH + AES-CBC and large hash functions is slow and not more secure
+        if (match['attr'].issuperset({'SSL_CBC', 'SSL_kEECDH'}) and
+                match['attr'].intersection({'SSL_SHA256', 'SSL_SHA384'})):
+            match['enabled'] = False
+
+        ciphers.append(match)
+
+    ciphers.sort(key=operator.itemgetter('name'))
+    return ciphers
+
+
+def main():
+    with urllib.request.urlopen(SOURCE) as r:  # pylint: disable=E1101
+        ciphers = parse_nss_engine_cipher(r)
+    # with open('nss_engine_cipher.c') as f:
+    #     ciphers = parse_nss_engine_cipher(f)
+
+    print("# disabled cipher attributes: {}".format(
+        ', '.join(sorted(DISABLED_CIPHERS))))
+    print("# weak strength: {}".format(', '.join(sorted(WEAK_STRENGTH))))
+    print("# enabled cipher suites:")
+    suite = []
+    for cipher in ciphers:
+        if cipher['enabled']:
+            print("#   {:36}".format(cipher['num']))
+            suite.append('+{}'.format(cipher['name']))
+    print()
+    print("NSSCipherSuite {}".format(','.join(suite)))
+
+
+if __name__ == '__main__':
+    main()
diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py
index 3b46dce82cae017328c9555c543e78b64e642c89..ef339d9e91fa37184c865aed5975891b23793440 100644
--- a/ipaserver/install/httpinstance.py
+++ b/ipaserver/install/httpinstance.py
@@ -57,6 +57,18 @@ SELINUX_BOOLEAN_SETTINGS = dict(
 KDCPROXY_USER = 'kdcproxy'
 HTTPD_USER = constants.HTTPD_USER
 
+# See contrib/nsscipersuite/nssciphersuite.py
+NSS_CIPHER_SUITE = [
+    '+aes_128_sha_256', '+aes_256_sha_256',
+    '+ecdhe_ecdsa_aes_128_gcm_sha_256', '+ecdhe_ecdsa_aes_128_sha',
+    '+ecdhe_ecdsa_aes_256_gcm_sha_384', '+ecdhe_ecdsa_aes_256_sha',
+    '+ecdhe_rsa_aes_128_gcm_sha_256', '+ecdhe_rsa_aes_128_sha',
+    '+ecdhe_rsa_aes_256_gcm_sha_384', '+ecdhe_rsa_aes_256_sha',
+    '+rsa_aes_128_gcm_sha_256', '+rsa_aes_128_sha',
+    '+rsa_aes_256_gcm_sha_384', '+rsa_aes_256_sha'
+]
+
+
 def httpd_443_configured():
     """
     We now allow mod_ssl to be installed so don't automatically disable it.
@@ -146,6 +158,8 @@ class HTTPInstance(service.Service):
 
 
         self.step("setting mod_nss port to 443", self.__set_mod_nss_port)
+        self.step("setting mod_nss cipher suite",
+                  self.set_mod_nss_cipher_suite)
         self.step("setting mod_nss protocol list to TLSv1.0 - TLSv1.2",
                   self.set_mod_nss_protocol)
         self.step("setting mod_nss password file", self.__set_mod_nss_passwordfile)
@@ -255,6 +269,10 @@ class HTTPInstance(service.Service):
         installutils.set_directive(paths.HTTPD_NSS_CONF, 'NSSRenegotiation', 'on', False)
         installutils.set_directive(paths.HTTPD_NSS_CONF, 'NSSRequireSafeNegotiation', 'on', False)
 
+    def set_mod_nss_cipher_suite(self):
+        ciphers = ','.join(NSS_CIPHER_SUITE)
+        installutils.set_directive(paths.HTTPD_NSS_CONF, 'NSSCipherSuite', ciphers, False)
+
     def __set_mod_nss_passwordfile(self):
         installutils.set_directive(paths.HTTPD_NSS_CONF, 'NSSPassPhraseDialog', 'file:' + paths.HTTPD_PASSWORD_CONF)
 
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
index 20379f19c652cb0b5911a4c2f1c67eae7f763379..73f528e9a224441d35d7ce3cb88c1bf9f3895ffb 100644
--- a/ipaserver/install/server/upgrade.py
+++ b/ipaserver/install/server/upgrade.py
@@ -1361,6 +1361,19 @@ def update_mod_nss_protocol(http):
 
     sysupgrade.set_upgrade_state('nss.conf', 'protocol_updated_tls12', True)
 
+
+def update_mod_nss_cipher_suite(http):
+    root_logger.info('[Updating mod_nss cipher suite]')
+
+    if sysupgrade.get_upgrade_state('nss.conf', 'cipher_suite_updated'):
+        root_logger.info("Cipher suite already updated")
+        return
+
+    http.set_mod_nss_cipher_suite()
+
+    sysupgrade.set_upgrade_state('nss.conf', 'cipher_suite_updated', True)
+
+
 def ds_enable_sidgen_extdom_plugins(ds):
     """For AD trust agents, make sure we enable sidgen and extdom plugins
     """
@@ -1541,6 +1554,7 @@ def upgrade_configuration():
 
     http.stop()
     update_mod_nss_protocol(http)
+    update_mod_nss_cipher_suite(http)
     fix_trust_flags()
     export_kra_agent_pem()
     http.start()
-- 
2.5.0

Attachment: signature.asc
Description: OpenPGP digital signature

-- 
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