瀏覽代碼

Ticket 48798 - Enable DS to offer weaker DH params in NSS

Bug Description:  Java is unable to handle DH param's greater than 1024 bit.
As of NSS 2.20 and higher, nss defaults to params of 2048 bit. This breaks
all java clients.

Fix Description:  This adds a new option, allowWeakDHParams that allows
nss to generate and use insecure DH params that Java would be capable of
using.

This test case shows the ability to allow weak params, and
that they are indeed 1024 bits

https://fedorahosted.org/389/ticket/48798

Author: wibrown

Review by: nhosoi
William Brown 9 年之前
父節點
當前提交
50910ac710
共有 4 個文件被更改,包括 228 次插入2 次删除
  1. 146 0
      dirsrvtests/tests/tickets/ticket48798_test.py
  2. 2 1
      ldap/schema/01core389.ldif
  3. 73 0
      ldap/servers/slapd/ssl.c
  4. 7 1
      lib/ldaputil/cert.c

+ 146 - 0
dirsrvtests/tests/tickets/ticket48798_test.py

@@ -0,0 +1,146 @@
+import os
+import sys
+import time
+import ldap
+import logging
+import pytest
+
+import nss
+
+from lib389 import DirSrv, Entry, tools, tasks
+from lib389.tools import DirSrvTools
+from lib389._constants import *
+from lib389.properties import *
+from lib389.tasks import *
+from lib389.utils import *
+
+# Only works in py2.7
+# from subprocess import check_output
+from subprocess import Popen
+
+logging.getLogger(__name__).setLevel(logging.DEBUG)
+log = logging.getLogger(__name__)
+
+
+class TopologyStandalone(object):
+    def __init__(self, standalone):
+        standalone.open()
+        self.standalone = standalone
+
+
[email protected](scope="module")
+def topology(request):
+    # Creating standalone instance ...
+    standalone = DirSrv(verbose=False)
+    args_instance[SER_HOST] = HOST_STANDALONE
+    args_instance[SER_PORT] = PORT_STANDALONE
+    args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
+    args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
+    args_standalone = args_instance.copy()
+    standalone.allocate(args_standalone)
+    instance_standalone = standalone.exists()
+    if instance_standalone:
+        standalone.delete()
+    standalone.create()
+    standalone.open()
+
+    # Delete each instance in the end
+    def fin():
+        pass
+        #standalone.delete()
+    request.addfinalizer(fin)
+
+    # Clear out the tmp dir
+    #standalone.clearTmpDir(__file__)
+
+    return TopologyStandalone(standalone)
+
+def check_socket_dh_param_size(hostname, port):
+    ### You know why we have to do this? 
+    # Because TLS and SSL suck. Hard. They are impossible. It's all terrible, burn it all down.
+    cmd = "echo quit | openssl s_client -connect {HOSTNAME}:{PORT} -msg -cipher DH | grep -A 1 ServerKeyExchange".format(
+        HOSTNAME=hostname,
+        PORT=port)
+    #output = check_output(cmd, shell=True)
+    p = Popen(cmd, shell=True, stdout=PIPE)
+    (output, _) = p.communicate()
+    
+    dhheader = output.split('\n')[1]
+    # Get rid of all the other whitespace.
+    dhheader = dhheader.replace(' ', '')
+    # Example is 0c00040b0100ffffffffffffffffadf8
+    # We need the bits 0100 here. Which means 256 bytes aka 256 * 8, for 2048 bit.
+    dhheader = dhheader[8:12]
+    # make it an int, and times 8
+    i = int(dhheader, 16) * 8
+    return i
+
+
+def test_ticket48798(topology):
+    """
+    Test DH param sizes offered by DS.
+
+    """
+
+    # Create a CA
+    # This is a trick. The nss db that ships with DS is broken fundamentally.
+    ## THIS ASSUMES old nss format. SQLite will bite us!
+    for f in ('key3.db', 'cert8.db', 'key4.db', 'cert9.db', 'secmod.db', 'pkcs11.txt'):
+        try:
+            os.remove("%s/%s" % (topology.standalone.confdir, f ))
+        except:
+            pass
+
+    # Check if the db exists. Should be false.
+    assert(topology.standalone.nss_ssl._db_exists() is False)
+    # Create it. Should work.
+    assert(topology.standalone.nss_ssl.reinit() is True)
+    # Check if the db exists. Should be true
+    assert(topology.standalone.nss_ssl._db_exists() is True)
+
+    # Check if ca exists. Should be false.
+    assert(topology.standalone.nss_ssl._rsa_ca_exists() is False)
+    # Create it. Should work.
+    assert(topology.standalone.nss_ssl.create_rsa_ca() is True)
+    # Check if ca exists. Should be true
+    assert(topology.standalone.nss_ssl._rsa_ca_exists() is True)
+
+    # Check if we have a server cert / key. Should be false.
+    assert(topology.standalone.nss_ssl._rsa_key_and_cert_exists() is False)
+    # Create it. Should work.
+    assert(topology.standalone.nss_ssl.create_rsa_key_and_cert() is True)
+    # Check if server cert and key exist. Should be true.
+    assert(topology.standalone.nss_ssl._rsa_key_and_cert_exists() is True)
+
+    topology.standalone.config.enable_ssl(secport=DEFAULT_SECURE_PORT, secargs={'nsSSL3Ciphers': '+all'} )
+
+    topology.standalone.restart(30)
+
+    # Confirm that we have a connection, and that it has DH
+
+    # Open a socket to the port.
+    # Check the security settings.
+    size = check_socket_dh_param_size(topology.standalone.host, DEFAULT_SECURE_PORT)
+
+    assert(size == 2048)
+
+    # Now toggle the settings.
+    mod = [(ldap.MOD_REPLACE, 'allowWeakDHParam', 'on')]
+    dn_enc = 'cn=encryption,cn=config'
+    topology.standalone.modify_s(dn_enc, mod)
+
+    topology.standalone.restart(30)
+
+    # Check the DH params are less than 1024.
+    size = check_socket_dh_param_size(topology.standalone.host, DEFAULT_SECURE_PORT)
+
+    assert(size == 1024)
+
+    log.info('Test complete')
+
+
+if __name__ == '__main__':
+    # Run isolated
+    # -s for DEBUG mode
+    CURRENT_FILE = os.path.realpath(__file__)
+    pytest.main("-s %s" % CURRENT_FILE)

+ 2 - 1
ldap/schema/01core389.ldif

@@ -155,6 +155,7 @@ attributeTypes: ( 2.16.840.1.113730.3.1.2155 NAME 'nsds5ReplicaBackoffMax' DESC
 attributeTypes: ( 2.16.840.1.113730.3.1.2156 NAME 'nsslapd-sasl-max-buffer-size' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
 attributeTypes: ( 2.16.840.1.113730.3.1.2310 NAME 'nsds5ReplicaFlowControlWindow' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' ) 
 attributeTypes: ( 2.16.840.1.113730.3.1.2311 NAME 'nsds5ReplicaFlowControlPause' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' ) 
+attributeTypes: ( 2.16.840.1.113730.3.1.2332 NAME 'allowWeakDHParam' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape Directory Server' )
 #
 # objectclasses
 #
@@ -170,5 +171,5 @@ objectClasses: ( 2.16.840.1.113730.3.2.103 NAME 'nsDS5ReplicationAgreement' DESC
 objectClasses: ( 2.16.840.1.113730.3.2.39 NAME 'nsslapdConfig' DESC 'Netscape defined objectclass' SUP top MAY ( cn ) X-ORIGIN 'Netscape Directory Server' )
 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' )
 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' )
-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' )
+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' )
 objectClasses: ( nsEncryptionModule-oid NAME 'nsEncryptionModule' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsSSLToken $ nsSSLPersonalityssl $ nsSSLActivation ) X-ORIGIN 'Netscape' )

+ 73 - 0
ldap/servers/slapd/ssl.c

@@ -89,6 +89,10 @@
 #define NSS_TLS10 1
 #endif
 
+#if NSS_VMAJOR * 100 + NSS_VMINOR >= 320
+#define HAVE_NSS_DHE 1
+#endif
+
 #if !defined(NSS_TLS10) /* NSS_TLS11 or newer */
 static SSLVersionRange enabledNSSVersions;
 static SSLVersionRange slapdNSSVersions;
@@ -117,6 +121,7 @@ static int stimeout;
 static char *ciphers = NULL;
 static char * configDN = "cn=encryption,cn=config";
 
+
 /* Copied from libadmin/libadmin.h public/nsapi.h */
 #define SERVER_KEY_NAME "Server-Key"
 #define MAGNUS_ERROR_LEN 1024
@@ -125,6 +130,15 @@ static char * configDN = "cn=encryption,cn=config";
 #define FILE_PATHSEP '/'
 
 /* ----------------------- Multiple cipher support ------------------------ */
+#ifdef HAVE_NSS_DHE
+#define CIPHER_SET_DEFAULTWEAKDHPARAM 0x100 /* allowWeakDhParam is not set in cn=encryption */
+#define CIPHER_SET_ALLOWWEAKDHPARAM   0x200 /* allowWeakDhParam is on */
+#define CIPHER_SET_DISALLOWWEAKDHPARAM   0x400 /* allowWeakDhParam is off */
+#endif
+
+#ifdef HAVE_NSS_DHE
+static int allowweakdhparam = CIPHER_SET_DEFAULTWEAKDHPARAM;
+#endif
 
 
 static char **cipher_names = NULL;
@@ -244,6 +258,33 @@ getSupportedCiphers()
 	return cipher_names;
 }
 
+#ifdef HAVE_NSS_DHE
+int
+get_allow_weak_dh_param(Slapi_Entry *e)
+{
+    /* Check if the user wants weak params */
+    int allow = CIPHER_SET_DEFAULTWEAKDHPARAM;
+    char *val;
+    val = slapi_entry_attr_get_charptr(e, "allowWeakDHParam");
+    if (val) {
+        if (!PL_strcasecmp(val, "off") || !PL_strcasecmp(val, "false") || 
+                !PL_strcmp(val, "0") || !PL_strcasecmp(val, "no")) {
+            allow = CIPHER_SET_DISALLOWWEAKDHPARAM;
+        } else if (!PL_strcasecmp(val, "on") || !PL_strcasecmp(val, "true") || 
+                !PL_strcmp(val, "1") || !PL_strcasecmp(val, "yes")) {
+            allow = CIPHER_SET_ALLOWWEAKDHPARAM;
+            slapd_SSL_warn("The value of allowWeakDHParam is set to %s. THIS EXPOSES YOU TO CVE-2015-4000.", val);
+        } else {
+            slapd_SSL_warn("The value of allowWeakDHParam \"%s\" is invalid.",
+                           "Ignoring it and set it to default.", val);
+        }
+    }
+    slapi_ch_free((void **) &val);
+    return allow;
+}
+#endif
+
+
 char **
 getEnabledCiphers()
 {
@@ -841,6 +882,9 @@ slapd_ssl_init() {
     int rv = 0;
     PK11SlotInfo *slot;
     Slapi_Entry *entry = NULL;
+#ifdef HAVE_NSS_DHE
+    SECStatus  nss_rv = SECFailure;
+#endif
 
     /* Get general information */
 
@@ -849,6 +893,17 @@ slapd_ssl_init() {
     val = slapi_entry_attr_get_charptr( entry, "nssslSessionTimeout" );
     ciphers = slapi_entry_attr_get_charptr( entry, "nsssl3ciphers" );
 
+#ifdef HAVE_NSS_DHE
+    allowweakdhparam = get_allow_weak_dh_param(entry);
+    if (allowweakdhparam & CIPHER_SET_ALLOWWEAKDHPARAM) {
+        slapd_SSL_warn("notice, generating new WEAK DH param");
+        nss_rv = SSL_EnableWeakDHEPrimeGroup(NULL, PR_TRUE);
+        if (nss_rv != SECSuccess) {
+            slapd_SSL_warn("Warning, unable to generate weak dh parameters");
+        }
+    }
+#endif
+
     /* We are currently using the value of sslSessionTimeout
 	   for ssl3SessionTimeout, see SSL_ConfigServerSessionIDCache() */
     /* Note from Tom Weinstein on the meaning of the timeout:
@@ -1192,6 +1247,24 @@ int slapd_ssl_init2(PRFileDesc **fd, int startTLS)
                 }
 
                 if (SECSuccess == rv) {
+
+#ifdef HAVE_NSS_DHE
+                    /* Step If we want weak dh params, flag it on the socket now! */
+
+                    rv = SSL_OptionSet(*fd, SSL_ENABLE_SERVER_DHE, PR_TRUE);
+                    if (rv != SECSuccess) {
+                        slapd_SSL_warn("Warning, unable to start DHE");
+                    }
+
+                    if (allowweakdhparam & CIPHER_SET_ALLOWWEAKDHPARAM) {
+                        slapd_SSL_warn("notice, allowing weak parameters on socket.");
+                        rv = SSL_EnableWeakDHEPrimeGroup(*fd, PR_TRUE);
+                        if (rv != SECSuccess) {
+                            slapd_SSL_warn("Warning, unable to allow weak DH params on socket.");
+                        }
+                    }
+#endif
+
                     if( slapd_pk11_fortezzaHasKEA(cert) == PR_TRUE ) {
                         rv = SSL_ConfigSecureServer(*fd, cert, key, kt_fortezza);
                     }

+ 7 - 1
lib/ldaputil/cert.c

@@ -50,6 +50,7 @@
 #include "prmem.h"
 #include "key.h"
 #include "cert.h"
+#include <nss.h>
 #include <ldaputil/certmap.h>
 #include <ldaputil/errors.h>
 #include <ldaputil/cert.h>
@@ -285,7 +286,12 @@ _replaceAVA (char* attr, char** avas)
 }
 
 struct _attr_getter_pair {
-    char* (*getter) (CERTName* dn);
+#if NSS_VMAJOR < 3 || (NSS_VMAJOR == 3 && NSS_VMINOR < 15)
+    char* (*getter) ( CERTName* dn);
+#else
+    /* in 3.15.x "const" was added to the declarations */
+    char* (*getter) (const CERTName* dn);
+#endif
     const char* name1;
     const char* name2;
 } _attr_getter_table[] =