From 50910ac7101e2ede6bf8211383dea8d5f00539bd Mon Sep 17 00:00:00 2001 From: William Brown Date: Apr 26 2016 04:09:07 +0000 Subject: 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 --- diff --git a/dirsrvtests/tests/tickets/ticket48798_test.py b/dirsrvtests/tests/tickets/ticket48798_test.py new file mode 100644 index 0000000..6872552 --- /dev/null +++ b/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 + + +@pytest.fixture(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) diff --git a/ldap/schema/01core389.ldif b/ldap/schema/01core389.ldif index ba5b0aa..8f366a8 100644 --- a/ldap/schema/01core389.ldif +++ b/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' ) diff --git a/ldap/servers/slapd/ssl.c b/ldap/servers/slapd/ssl.c index 529dbc6..fcf7ba9 100644 --- a/ldap/servers/slapd/ssl.c +++ b/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); } diff --git a/lib/ldaputil/cert.c b/lib/ldaputil/cert.c index c26ff41..d617741 100644 --- a/lib/ldaputil/cert.c +++ b/lib/ldaputil/cert.c @@ -50,6 +50,7 @@ #include "prmem.h" #include "key.h" #include "cert.h" +#include #include #include #include @@ -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[] =