From b2022ca83a5c56a525f4ebfad6b7f96debfad718 Mon Sep 17 00:00:00 2001 From: William Brown Date: Apr 26 2016 00:17:38 +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..7289453 --- /dev/null +++ b/dirsrvtests/tests/tickets/ticket48798_test.py @@ -0,0 +1,141 @@ +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 * + +from subprocess import check_output + +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=True) + 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) + 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 e620e74..35d7c4c 100644 --- a/ldap/schema/01core389.ldif +++ b/ldap/schema/01core389.ldif @@ -296,6 +296,7 @@ attributeTypes: ( 2.16.840.1.113730.3.1.2327 NAME 'nsslapd-auditfaillog' DESC 'N attributeTypes: ( 2.16.840.1.113730.3.1.2328 NAME 'nsslapd-auditfaillog-list' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape Directory Server' ) attributeTypes: ( 2.16.840.1.113730.3.1.2330 NAME 'nsslapd-logging-backend' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape Directory Server' ) attributeTypes: ( 2.16.840.1.113730.3.1.2331 NAME 'nsslapd-logging-hr-timestamps-enabled' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 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 # @@ -311,7 +312,7 @@ 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 ) MAY ( nsSaslMapPriority ) 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 $ sslVersionMin $ sslVersionMax $ nsSSLSessionTimeout $ nsSSL3SessionTimeout $ nsSSLClientAuth $ nsSSL2Ciphers $ nsSSL3Ciphers $ nsSSLSupportedCiphers $ allowWeakCipher $ CACertExtractFile ) X-ORIGIN 'Netscape' ) +objectClasses: ( nsEncryptionConfig-oid NAME 'nsEncryptionConfig' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsCertfile $ nsKeyfile $ nsSSL2 $ nsSSL3 $ nsTLS1 $ sslVersionMin $ sslVersionMax $ nsSSLSessionTimeout $ nsSSL3SessionTimeout $ nsSSLClientAuth $ nsSSL2Ciphers $ nsSSL3Ciphers $ nsSSLSupportedCiphers $ allowWeakCipher $ CACertExtractFile $ allowWeakDHParam ) X-ORIGIN 'Netscape' ) objectClasses: ( nsEncryptionModule-oid NAME 'nsEncryptionModule' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsSSLToken $ nsSSLPersonalityssl $ nsSSLActivation $ ServerKeyExtractFile $ ServerCertExtractFile ) X-ORIGIN 'Netscape' ) objectClasses: ( 2.16.840.1.113730.3.2.327 NAME 'rootDNPluginConfig' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( rootdn-open-time $ rootdn-close-time $ rootdn-days-allowed $ rootdn-allow-host $ rootdn-deny-host $ rootdn-allow-ip $ rootdn-deny-ip ) X-ORIGIN 'Netscape' ) objectClasses: ( 2.16.840.1.113730.3.2.328 NAME 'nsSchemaPolicy' DESC 'Netscape defined objectclass' SUP top MAY ( cn $ schemaUpdateObjectclassAccept $ schemaUpdateObjectclassReject $ schemaUpdateAttributeAccept $ schemaUpdateAttributeReject) X-ORIGIN 'Netscape Directory Server' ) diff --git a/ldap/servers/slapd/ssl.c b/ldap/servers/slapd/ssl.c index fd17c28..7da18f0 100644 --- a/ldap/servers/slapd/ssl.c +++ b/ldap/servers/slapd/ssl.c @@ -49,6 +49,10 @@ #define NSS_TLS10 1 #endif +#if NSS_VMAJOR * 100 + NSS_VMINOR >= 320 +#define HAVE_NSS_DHE 1 +#endif + /****************************************************************************** * Default SSL Version Rule * Old SSL version attributes: @@ -87,6 +91,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 @@ -103,6 +108,12 @@ static char * configDN = "cn=encryption,cn=config"; #define CIPHER_SET_ALLOWWEAKCIPHER 0x20 /* allowWeakCipher is on */ #define CIPHER_SET_DISALLOWWEAKCIPHER 0x40 /* allowWeakCipher is off */ +#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 + #define CIPHER_SET_ISDEFAULT(flag) \ (((flag)&CIPHER_SET_DEFAULT) ? PR_TRUE : PR_FALSE) #define CIPHER_SET_ISALL(flag) \ @@ -114,6 +125,7 @@ static char * configDN = "cn=encryption,cn=config"; (((flag)&CIPHER_SET_ALLOWWEAKCIPHER) ? PR_TRUE : PR_FALSE) #define ALLOWWEAK_ISOFF(flag) \ (((flag)&CIPHER_SET_DISALLOWWEAKCIPHER) ? PR_TRUE : PR_FALSE) + /* * If ISALL or ISDEFAULT, allowWeakCipher is true only if CIPHER_SET_ALLOWWEAKCIPHER. * Otherwise (user specified cipher list), allowWeakCipher is true @@ -132,6 +144,12 @@ static char * configDN = "cn=encryption,cn=config"; #define CIPHER_MUST_BE_DISABLED 0x2 #define CIPHER_IS_WEAK 0x4 #define CIPHER_IS_DEPRECATED 0x8 + +#ifdef HAVE_NSS_DHE +static int allowweakdhparam = CIPHER_SET_DEFAULTWEAKDHPARAM; +#endif + + static char **cipher_names = NULL; static char **enabled_cipher_names = NULL; typedef struct { @@ -302,6 +320,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() { @@ -1281,6 +1326,9 @@ slapd_ssl_init() char *val = NULL; PK11SlotInfo *slot; Slapi_Entry *entry = NULL; +#ifdef HAVE_NSS_DHE + SECStatus rv = SECFailure; +#endif /* Get general information */ @@ -1289,6 +1337,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"); + rv = SSL_EnableWeakDHEPrimeGroup(NULL, PR_TRUE); + if (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: @@ -1856,6 +1915,24 @@ 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); }