andykimpe / rpms / 389-ds-base

Forked from rpms/389-ds-base 5 months ago
Clone
dc8c34
From 6deda4f7c872efb052919b82320b3514fe8621ad Mon Sep 17 00:00:00 2001
dc8c34
From: William Brown <firstyear@redhat.com>
dc8c34
Date: Thu, 21 Apr 2016 13:36:28 +1000
dc8c34
Subject: [PATCH 398/404] Ticket 48798 - Enable DS to offer weaker DH params in
dc8c34
 NSS
dc8c34
dc8c34
Bug Description:  Java is unable to handle DH param's greater than 1024 bit.
dc8c34
As of NSS 2.20 and higher, nss defaults to params of 2048 bit. This breaks
dc8c34
all java clients.
dc8c34
dc8c34
Fix Description:  This adds a new option, allowWeakDHParams that allows
dc8c34
nss to generate and use insecure DH params that Java would be capable of
dc8c34
using.
dc8c34
dc8c34
This test case shows the ability to allow weak params, and
dc8c34
that they are indeed 1024 bits
dc8c34
dc8c34
https://fedorahosted.org/389/ticket/48798
dc8c34
dc8c34
Author: wibrown
dc8c34
dc8c34
Review by: nhosoi
dc8c34
dc8c34
(cherry picked from commit 50910ac7101e2ede6bf8211383dea8d5f00539bd)
dc8c34
---
dc8c34
 dirsrvtests/tests/tickets/ticket48798_test.py | 146 ++++++++++++++++++++++++++
dc8c34
 ldap/schema/01core389.ldif                    |   2 +-
dc8c34
 ldap/servers/slapd/ssl.c                      |  73 +++++++++++++
dc8c34
 lib/ldaputil/cert.c                           |   8 +-
dc8c34
 4 files changed, 227 insertions(+), 2 deletions(-)
dc8c34
 create mode 100644 dirsrvtests/tests/tickets/ticket48798_test.py
dc8c34
dc8c34
diff --git a/dirsrvtests/tests/tickets/ticket48798_test.py b/dirsrvtests/tests/tickets/ticket48798_test.py
dc8c34
new file mode 100644
dc8c34
index 0000000..6872552
dc8c34
--- /dev/null
dc8c34
+++ b/dirsrvtests/tests/tickets/ticket48798_test.py
dc8c34
@@ -0,0 +1,146 @@
dc8c34
+import os
dc8c34
+import sys
dc8c34
+import time
dc8c34
+import ldap
dc8c34
+import logging
dc8c34
+import pytest
dc8c34
+
dc8c34
+import nss
dc8c34
+
dc8c34
+from lib389 import DirSrv, Entry, tools, tasks
dc8c34
+from lib389.tools import DirSrvTools
dc8c34
+from lib389._constants import *
dc8c34
+from lib389.properties import *
dc8c34
+from lib389.tasks import *
dc8c34
+from lib389.utils import *
dc8c34
+
dc8c34
+# Only works in py2.7
dc8c34
+# from subprocess import check_output
dc8c34
+from subprocess import Popen
dc8c34
+
dc8c34
+logging.getLogger(__name__).setLevel(logging.DEBUG)
dc8c34
+log = logging.getLogger(__name__)
dc8c34
+
dc8c34
+
dc8c34
+class TopologyStandalone(object):
dc8c34
+    def __init__(self, standalone):
dc8c34
+        standalone.open()
dc8c34
+        self.standalone = standalone
dc8c34
+
dc8c34
+
dc8c34
+@pytest.fixture(scope="module")
dc8c34
+def topology(request):
dc8c34
+    # Creating standalone instance ...
dc8c34
+    standalone = DirSrv(verbose=False)
dc8c34
+    args_instance[SER_HOST] = HOST_STANDALONE
dc8c34
+    args_instance[SER_PORT] = PORT_STANDALONE
dc8c34
+    args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
dc8c34
+    args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
dc8c34
+    args_standalone = args_instance.copy()
dc8c34
+    standalone.allocate(args_standalone)
dc8c34
+    instance_standalone = standalone.exists()
dc8c34
+    if instance_standalone:
dc8c34
+        standalone.delete()
dc8c34
+    standalone.create()
dc8c34
+    standalone.open()
dc8c34
+
dc8c34
+    # Delete each instance in the end
dc8c34
+    def fin():
dc8c34
+        pass
dc8c34
+        #standalone.delete()
dc8c34
+    request.addfinalizer(fin)
dc8c34
+
dc8c34
+    # Clear out the tmp dir
dc8c34
+    #standalone.clearTmpDir(__file__)
dc8c34
+
dc8c34
+    return TopologyStandalone(standalone)
dc8c34
+
dc8c34
+def check_socket_dh_param_size(hostname, port):
dc8c34
+    ### You know why we have to do this? 
dc8c34
+    # Because TLS and SSL suck. Hard. They are impossible. It's all terrible, burn it all down.
dc8c34
+    cmd = "echo quit | openssl s_client -connect {HOSTNAME}:{PORT} -msg -cipher DH | grep -A 1 ServerKeyExchange".format(
dc8c34
+        HOSTNAME=hostname,
dc8c34
+        PORT=port)
dc8c34
+    #output = check_output(cmd, shell=True)
dc8c34
+    p = Popen(cmd, shell=True, stdout=PIPE)
dc8c34
+    (output, _) = p.communicate()
dc8c34
+    
dc8c34
+    dhheader = output.split('\n')[1]
dc8c34
+    # Get rid of all the other whitespace.
dc8c34
+    dhheader = dhheader.replace(' ', '')
dc8c34
+    # Example is 0c00040b0100ffffffffffffffffadf8
dc8c34
+    # We need the bits 0100 here. Which means 256 bytes aka 256 * 8, for 2048 bit.
dc8c34
+    dhheader = dhheader[8:12]
dc8c34
+    # make it an int, and times 8
dc8c34
+    i = int(dhheader, 16) * 8
dc8c34
+    return i
dc8c34
+
dc8c34
+
dc8c34
+def test_ticket48798(topology):
dc8c34
+    """
dc8c34
+    Test DH param sizes offered by DS.
dc8c34
+
dc8c34
+    """
dc8c34
+
dc8c34
+    # Create a CA
dc8c34
+    # This is a trick. The nss db that ships with DS is broken fundamentally.
dc8c34
+    ## THIS ASSUMES old nss format. SQLite will bite us!
dc8c34
+    for f in ('key3.db', 'cert8.db', 'key4.db', 'cert9.db', 'secmod.db', 'pkcs11.txt'):
dc8c34
+        try:
dc8c34
+            os.remove("%s/%s" % (topology.standalone.confdir, f ))
dc8c34
+        except:
dc8c34
+            pass
dc8c34
+
dc8c34
+    # Check if the db exists. Should be false.
dc8c34
+    assert(topology.standalone.nss_ssl._db_exists() is False)
dc8c34
+    # Create it. Should work.
dc8c34
+    assert(topology.standalone.nss_ssl.reinit() is True)
dc8c34
+    # Check if the db exists. Should be true
dc8c34
+    assert(topology.standalone.nss_ssl._db_exists() is True)
dc8c34
+
dc8c34
+    # Check if ca exists. Should be false.
dc8c34
+    assert(topology.standalone.nss_ssl._rsa_ca_exists() is False)
dc8c34
+    # Create it. Should work.
dc8c34
+    assert(topology.standalone.nss_ssl.create_rsa_ca() is True)
dc8c34
+    # Check if ca exists. Should be true
dc8c34
+    assert(topology.standalone.nss_ssl._rsa_ca_exists() is True)
dc8c34
+
dc8c34
+    # Check if we have a server cert / key. Should be false.
dc8c34
+    assert(topology.standalone.nss_ssl._rsa_key_and_cert_exists() is False)
dc8c34
+    # Create it. Should work.
dc8c34
+    assert(topology.standalone.nss_ssl.create_rsa_key_and_cert() is True)
dc8c34
+    # Check if server cert and key exist. Should be true.
dc8c34
+    assert(topology.standalone.nss_ssl._rsa_key_and_cert_exists() is True)
dc8c34
+
dc8c34
+    topology.standalone.config.enable_ssl(secport=DEFAULT_SECURE_PORT, secargs={'nsSSL3Ciphers': '+all'} )
dc8c34
+
dc8c34
+    topology.standalone.restart(30)
dc8c34
+
dc8c34
+    # Confirm that we have a connection, and that it has DH
dc8c34
+
dc8c34
+    # Open a socket to the port.
dc8c34
+    # Check the security settings.
dc8c34
+    size = check_socket_dh_param_size(topology.standalone.host, DEFAULT_SECURE_PORT)
dc8c34
+
dc8c34
+    assert(size == 2048)
dc8c34
+
dc8c34
+    # Now toggle the settings.
dc8c34
+    mod = [(ldap.MOD_REPLACE, 'allowWeakDHParam', 'on')]
dc8c34
+    dn_enc = 'cn=encryption,cn=config'
dc8c34
+    topology.standalone.modify_s(dn_enc, mod)
dc8c34
+
dc8c34
+    topology.standalone.restart(30)
dc8c34
+
dc8c34
+    # Check the DH params are less than 1024.
dc8c34
+    size = check_socket_dh_param_size(topology.standalone.host, DEFAULT_SECURE_PORT)
dc8c34
+
dc8c34
+    assert(size == 1024)
dc8c34
+
dc8c34
+    log.info('Test complete')
dc8c34
+
dc8c34
+
dc8c34
+if __name__ == '__main__':
dc8c34
+    # Run isolated
dc8c34
+    # -s for DEBUG mode
dc8c34
+    CURRENT_FILE = os.path.realpath(__file__)
dc8c34
+    pytest.main("-s %s" % CURRENT_FILE)
dc8c34
diff --git a/ldap/schema/01core389.ldif b/ldap/schema/01core389.ldif
dc8c34
index 79cefea..c962dc0 100644
dc8c34
--- a/ldap/schema/01core389.ldif
dc8c34
+++ b/ldap/schema/01core389.ldif
dc8c34
@@ -172,5 +172,5 @@ objectClasses: ( 2.16.840.1.113730.3.2.103 NAME 'nsDS5ReplicationAgreement' DESC
dc8c34
 objectClasses: ( 2.16.840.1.113730.3.2.39 NAME 'nsslapdConfig' DESC 'Netscape defined objectclass' SUP top MAY ( cn ) X-ORIGIN 'Netscape Directory Server' )
dc8c34
 objectClasses: ( 2.16.840.1.113730.3.2.317 NAME 'nsSaslMapping' DESC 'Netscape defined objectclass' SUP top MUST ( cn $ nsSaslMapRegexString $ nsSaslMapBaseDNTemplate $ nsSaslMapFilterTemplate ) X-ORIGIN 'Netscape Directory Server' )
dc8c34
 objectClasses: ( 2.16.840.1.113730.3.2.43 NAME 'nsSNMP' DESC 'Netscape defined objectclass' SUP top MUST ( cn $ nsSNMPEnabled ) MAY ( nsSNMPOrganization $ nsSNMPLocation $ nsSNMPContact $ nsSNMPDescription $ nsSNMPName $ nsSNMPMasterHost $ nsSNMPMasterPort ) X-ORIGIN 'Netscape Directory Server' )
dc8c34
-objectClasses: ( nsEncryptionConfig-oid NAME 'nsEncryptionConfig' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsCertfile $ nsKeyfile $ nsSSL2 $ nsSSL3 $ nsTLS1 $ nsSSLSessionTimeout $ nsSSL3SessionTimeout $ nsSSLClientAuth $ nsSSL2Ciphers $ nsSSL3Ciphers $ nsSSLSupportedCiphers) X-ORIGIN 'Netscape' )
dc8c34
+objectClasses: ( nsEncryptionConfig-oid NAME 'nsEncryptionConfig' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsCertfile $ nsKeyfile $ nsSSL2 $ nsSSL3 $ nsTLS1 $ nsSSLSessionTimeout $ nsSSL3SessionTimeout $ nsSSLClientAuth $ nsSSL2Ciphers $ nsSSL3Ciphers $ nsSSLSupportedCiphers $ allowWeakDHParam ) X-ORIGIN 'Netscape' )
dc8c34
 objectClasses: ( nsEncryptionModule-oid NAME 'nsEncryptionModule' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsSSLToken $ nsSSLPersonalityssl $ nsSSLActivation ) X-ORIGIN 'Netscape' )
dc8c34
diff --git a/ldap/servers/slapd/ssl.c b/ldap/servers/slapd/ssl.c
dc8c34
index 1f64be0..090c328 100644
dc8c34
--- a/ldap/servers/slapd/ssl.c
dc8c34
+++ b/ldap/servers/slapd/ssl.c
dc8c34
@@ -89,6 +89,10 @@
dc8c34
 #define NSS_TLS10 1
dc8c34
 #endif
dc8c34
 
dc8c34
+#if NSS_VMAJOR * 100 + NSS_VMINOR >= 320
dc8c34
+#define HAVE_NSS_DHE 1
dc8c34
+#endif
dc8c34
+
dc8c34
 #if !defined(NSS_TLS10) /* NSS_TLS11 or newer */
dc8c34
 static SSLVersionRange enabledNSSVersions;
dc8c34
 static SSLVersionRange slapdNSSVersions;
dc8c34
@@ -117,6 +121,7 @@ static int stimeout;
dc8c34
 static char *ciphers = NULL;
dc8c34
 static char * configDN = "cn=encryption,cn=config";
dc8c34
 
dc8c34
+
dc8c34
 /* Copied from libadmin/libadmin.h public/nsapi.h */
dc8c34
 #define SERVER_KEY_NAME "Server-Key"
dc8c34
 #define MAGNUS_ERROR_LEN 1024
dc8c34
@@ -125,6 +130,15 @@ static char * configDN = "cn=encryption,cn=config";
dc8c34
 #define FILE_PATHSEP '/'
dc8c34
 
dc8c34
 /* ----------------------- Multiple cipher support ------------------------ */
dc8c34
+#ifdef HAVE_NSS_DHE
dc8c34
+#define CIPHER_SET_DEFAULTWEAKDHPARAM 0x100 /* allowWeakDhParam is not set in cn=encryption */
dc8c34
+#define CIPHER_SET_ALLOWWEAKDHPARAM   0x200 /* allowWeakDhParam is on */
dc8c34
+#define CIPHER_SET_DISALLOWWEAKDHPARAM   0x400 /* allowWeakDhParam is off */
dc8c34
+#endif
dc8c34
+
dc8c34
+#ifdef HAVE_NSS_DHE
dc8c34
+static int allowweakdhparam = CIPHER_SET_DEFAULTWEAKDHPARAM;
dc8c34
+#endif
dc8c34
 
dc8c34
 
dc8c34
 static char **cipher_names = NULL;
dc8c34
@@ -244,6 +258,33 @@ getSupportedCiphers()
dc8c34
 	return cipher_names;
dc8c34
 }
dc8c34
 
dc8c34
+#ifdef HAVE_NSS_DHE
dc8c34
+int
dc8c34
+get_allow_weak_dh_param(Slapi_Entry *e)
dc8c34
+{
dc8c34
+    /* Check if the user wants weak params */
dc8c34
+    int allow = CIPHER_SET_DEFAULTWEAKDHPARAM;
dc8c34
+    char *val;
dc8c34
+    val = slapi_entry_attr_get_charptr(e, "allowWeakDHParam");
dc8c34
+    if (val) {
dc8c34
+        if (!PL_strcasecmp(val, "off") || !PL_strcasecmp(val, "false") || 
dc8c34
+                !PL_strcmp(val, "0") || !PL_strcasecmp(val, "no")) {
dc8c34
+            allow = CIPHER_SET_DISALLOWWEAKDHPARAM;
dc8c34
+        } else if (!PL_strcasecmp(val, "on") || !PL_strcasecmp(val, "true") || 
dc8c34
+                !PL_strcmp(val, "1") || !PL_strcasecmp(val, "yes")) {
dc8c34
+            allow = CIPHER_SET_ALLOWWEAKDHPARAM;
dc8c34
+            slapd_SSL_warn("The value of allowWeakDHParam is set to %s. THIS EXPOSES YOU TO CVE-2015-4000.", val);
dc8c34
+        } else {
dc8c34
+            slapd_SSL_warn("The value of allowWeakDHParam \"%s\" is invalid.",
dc8c34
+                           "Ignoring it and set it to default.", val);
dc8c34
+        }
dc8c34
+    }
dc8c34
+    slapi_ch_free((void **) &val;;
dc8c34
+    return allow;
dc8c34
+}
dc8c34
+#endif
dc8c34
+
dc8c34
+
dc8c34
 char **
dc8c34
 getEnabledCiphers()
dc8c34
 {
dc8c34
@@ -841,6 +882,9 @@ slapd_ssl_init() {
dc8c34
     int rv = 0;
dc8c34
     PK11SlotInfo *slot;
dc8c34
     Slapi_Entry *entry = NULL;
dc8c34
+#ifdef HAVE_NSS_DHE
dc8c34
+    SECStatus  nss_rv = SECFailure;
dc8c34
+#endif
dc8c34
 
dc8c34
     /* Get general information */
dc8c34
 
dc8c34
@@ -849,6 +893,17 @@ slapd_ssl_init() {
dc8c34
     val = slapi_entry_attr_get_charptr( entry, "nssslSessionTimeout" );
dc8c34
     ciphers = slapi_entry_attr_get_charptr( entry, "nsssl3ciphers" );
dc8c34
 
dc8c34
+#ifdef HAVE_NSS_DHE
dc8c34
+    allowweakdhparam = get_allow_weak_dh_param(entry);
dc8c34
+    if (allowweakdhparam & CIPHER_SET_ALLOWWEAKDHPARAM) {
dc8c34
+        slapd_SSL_warn("notice, generating new WEAK DH param");
dc8c34
+        nss_rv = SSL_EnableWeakDHEPrimeGroup(NULL, PR_TRUE);
dc8c34
+        if (nss_rv != SECSuccess) {
dc8c34
+            slapd_SSL_warn("Warning, unable to generate weak dh parameters");
dc8c34
+        }
dc8c34
+    }
dc8c34
+#endif
dc8c34
+
dc8c34
     /* We are currently using the value of sslSessionTimeout
dc8c34
 	   for ssl3SessionTimeout, see SSL_ConfigServerSessionIDCache() */
dc8c34
     /* Note from Tom Weinstein on the meaning of the timeout:
dc8c34
@@ -1192,6 +1247,24 @@ int slapd_ssl_init2(PRFileDesc **fd, int startTLS)
dc8c34
                 }
dc8c34
 
dc8c34
                 if (SECSuccess == rv) {
dc8c34
+
dc8c34
+#ifdef HAVE_NSS_DHE
dc8c34
+                    /* Step If we want weak dh params, flag it on the socket now! */
dc8c34
+
dc8c34
+                    rv = SSL_OptionSet(*fd, SSL_ENABLE_SERVER_DHE, PR_TRUE);
dc8c34
+                    if (rv != SECSuccess) {
dc8c34
+                        slapd_SSL_warn("Warning, unable to start DHE");
dc8c34
+                    }
dc8c34
+
dc8c34
+                    if (allowweakdhparam & CIPHER_SET_ALLOWWEAKDHPARAM) {
dc8c34
+                        slapd_SSL_warn("notice, allowing weak parameters on socket.");
dc8c34
+                        rv = SSL_EnableWeakDHEPrimeGroup(*fd, PR_TRUE);
dc8c34
+                        if (rv != SECSuccess) {
dc8c34
+                            slapd_SSL_warn("Warning, unable to allow weak DH params on socket.");
dc8c34
+                        }
dc8c34
+                    }
dc8c34
+#endif
dc8c34
+
dc8c34
                     if( slapd_pk11_fortezzaHasKEA(cert) == PR_TRUE ) {
dc8c34
                         rv = SSL_ConfigSecureServer(*fd, cert, key, kt_fortezza);
dc8c34
                     }
dc8c34
diff --git a/lib/ldaputil/cert.c b/lib/ldaputil/cert.c
dc8c34
index c26ff41..d617741 100644
dc8c34
--- a/lib/ldaputil/cert.c
dc8c34
+++ b/lib/ldaputil/cert.c
dc8c34
@@ -50,6 +50,7 @@
dc8c34
 #include "prmem.h"
dc8c34
 #include "key.h"
dc8c34
 #include "cert.h"
dc8c34
+#include <nss.h>
dc8c34
 #include <ldaputil/certmap.h>
dc8c34
 #include <ldaputil/errors.h>
dc8c34
 #include <ldaputil/cert.h>
dc8c34
@@ -285,7 +286,12 @@ _replaceAVA (char* attr, char** avas)
dc8c34
 }
dc8c34
 
dc8c34
 struct _attr_getter_pair {
dc8c34
-    char* (*getter) (CERTName* dn);
dc8c34
+#if NSS_VMAJOR < 3 || (NSS_VMAJOR == 3 && NSS_VMINOR < 15)
dc8c34
+    char* (*getter) ( CERTName* dn);
dc8c34
+#else
dc8c34
+    /* in 3.15.x "const" was added to the declarations */
dc8c34
+    char* (*getter) (const CERTName* dn);
dc8c34
+#endif
dc8c34
     const char* name1;
dc8c34
     const char* name2;
dc8c34
 } _attr_getter_table[] =
dc8c34
-- 
dc8c34
2.4.11
dc8c34