From c9a37255db2a2b176772bcda6ef3b1660960678c Mon Sep 17 00:00:00 2001 From: Howard Johnson Date: Thu, 29 Jun 2017 19:49:53 +0100 Subject: [PATCH] Ticket 49303 - Add option to disable TLS client-initiated renegotiation Bug Description: TLS renegotiation is a CPU-intensive process, which a malicious client could use to consume server resources and perform a denial of service attack. NSS defaults to allowing client-initiated renegotiation, but has an option to disable it. It would be useful to expose this as a DS configuration option. Fix Description: Added a new 'nsTLSAllowClientRenegotiation' attribute to the cn=encryption,cn=config object. This takes two values 'yes', and 'no'. If the value is 'no', renegotiation is disabled. If the value is 'yes', is not set, or is set to an invalid value, renegotiation is enabled. https://pagure.io/389-ds-base/issue/49303 Author: Howard Johnson Review by: ??? --- dirsrvtests/tests/tickets/ticket49303_test.py | 101 ++++++++++++++++++++++++++ ldap/schema/01core389.ldif | 3 +- ldap/servers/slapd/ssl.c | 28 +++++++ 3 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 dirsrvtests/tests/tickets/ticket49303_test.py diff --git a/dirsrvtests/tests/tickets/ticket49303_test.py b/dirsrvtests/tests/tickets/ticket49303_test.py new file mode 100644 index 0000000..507cc45 --- /dev/null +++ b/dirsrvtests/tests/tickets/ticket49303_test.py @@ -0,0 +1,101 @@ +import time +import logging +import os +import subprocess +import pytest +from lib389.topologies import topology_st as topo + +DEBUGGING = os.getenv("DEBUGGING", default=False) +if DEBUGGING: + logging.getLogger(__name__).setLevel(logging.DEBUG) +else: + logging.getLogger(__name__).setLevel(logging.INFO) +log = logging.getLogger(__name__) + + +def try_reneg(host, port): + """ + Connect to the specified host and port with openssl, and attempt to + initiate a renegotiation. Returns true if successful, false if not. + """ + + cmd = [ + '/usr/bin/openssl', + 's_client', + '-connect', + '%s:%d' % (host, port), + ] + + try: + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + stderr=subprocess.PIPE) + except ValueError, e: + log.info("openssl failed: %s", e) + proc.kill() + + # This 'R' command is intercepted by openssl and triggers a renegotiation + proc.communicate('R\n') + + # We rely on openssl returning 0 if no errors occured, and 1 if any did + # (for example, the server rejecting renegotiation and terminating the + # connection) + return proc.returncode == 0 + + +def enable_ssl(server, ldapsport): + server.stop() + server.nss_ssl.reinit() + server.nss_ssl.create_rsa_ca() + server.nss_ssl.create_rsa_key_and_cert() + server.start() + server.rsa.create() + server.config.set('nsslapd-secureport', '%s' % ldapsport) + server.config.set('nsslapd-security', 'on') + + time.sleep(1) + server.restart() + + +def set_reneg(server, state): + server.encryption.set('nsTLSAllowClientRenegotiation', state) + time.sleep(1) + server.restart() + + +def test_ticket49303(topo): + """ + Test the nsTLSAllowClientRenegotiation setting. + """ + sslport = 63601 + + log.info("Ticket 49303 - Allow disabling of SSL renegotiation") + + # No value set, defaults to reneg allowed + enable_ssl(topo.standalone, sslport) + assert try_reneg('127.0.0.1', sslport) is True + log.info("Renegotiation allowed by default - OK") + + # Turn reneg off + set_reneg(topo.standalone, 'no') + assert try_reneg('127.0.0.1', sslport) is False + log.info("Renegotiation disallowed - OK") + + # Explicitly enable + set_reneg(topo.standalone, 'yes') + assert try_reneg('127.0.0.1', sslport) is True + log.info("Renegotiation explicitly allowed - OK") + + # Set to an invalid value, defaults to allowed + set_reneg(topo.standalone, 'invalid') + assert try_reneg('127.0.0.1', sslport) is True + log.info("Renegotiation allowed when option is invalid - OK") + + log.info("Ticket 49303 - PASSED") + + +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 5e5f69f..836b953 100644 --- a/ldap/schema/01core389.ldif +++ b/ldap/schema/01core389.ldif @@ -107,6 +107,7 @@ attributeTypes: ( nsSSLToken-oid NAME 'nsSSLToken' DESC 'Netscape defined attrib attributeTypes: ( nsSSLPersonalitySSL-oid NAME 'nsSSLPersonalitySSL' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape' ) attributeTypes: ( nsSSLActivation-oid NAME 'nsSSLActivation' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape' ) attributeTypes: ( CACertExtractFile-oid NAME 'CACertExtractFile' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape' ) +attributeTypes: ( nsTLSAllowClientRenegotiation-oid NAME 'nsTLSAllowClientRenegotiation' DESC 'Allow clients to renegotiate open TLS connections using RFC 5746 secure renegotiation' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape' ) attributeTypes: ( ServerKeyExtractFile-oid NAME 'ServerKeyExtractFile' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape' ) attributeTypes: ( ServerCertExtractFile-oid NAME 'ServerCertExtractFile' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape' ) attributeTypes: ( 2.16.840.1.113730.3.1.2091 NAME 'nsslapd-suffix' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'Netscape' ) @@ -317,7 +318,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 $ nsTLS10 $ nsTLS11 $ nsTLS12 $ sslVersionMin $ sslVersionMax $ nsSSLSessionTimeout $ nsSSL3SessionTimeout $ nsSSLClientAuth $ nsSSL2Ciphers $ nsSSL3Ciphers $ nsSSLSupportedCiphers $ allowWeakCipher $ CACertExtractFile $ allowWeakDHParam ) X-ORIGIN 'Netscape' ) +objectClasses: ( nsEncryptionConfig-oid NAME 'nsEncryptionConfig' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsCertfile $ nsKeyfile $ nsSSL2 $ nsSSL3 $ nsTLS1 $ nsTLS10 $ nsTLS11 $ nsTLS12 $ sslVersionMin $ sslVersionMax $ nsSSLSessionTimeout $ nsSSL3SessionTimeout $ nsSSLClientAuth $ nsSSL2Ciphers $ nsSSL3Ciphers $ nsSSLSupportedCiphers $ allowWeakCipher $ CACertExtractFile $ allowWeakDHParam $ nsTLSAllowClientRenegotiation ) 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 01b708d..f551bc1 100644 --- a/ldap/servers/slapd/ssl.c +++ b/ldap/servers/slapd/ssl.c @@ -1753,6 +1753,7 @@ slapd_ssl_init2(PRFileDesc **fd, int startTLS) #endif char cipher_string[1024]; int allowweakcipher = CIPHER_SET_DEFAULTWEAKCIPHER; + int_fast16_t renegotiation = (int_fast16_t) SSL_RENEGOTIATE_REQUIRES_XTN; /* turn off the PKCS11 pin interactive mode */ /* wibrown 2016 */ @@ -2218,6 +2219,33 @@ slapd_ssl_init2(PRFileDesc **fd, int startTLS) #if !defined(NSS_TLS10) /* NSS_TLS11 or newer */ } #endif + + val = NULL; + if (e != NULL) { + val = slapi_entry_attr_get_charptr(e, "nsTLSAllowClientRenegotiation"); + } + if( val ) { + /* We default to allowing reneg. If the option is "no", + * disable reneg. Else if the option isn't "yes", complain + * and do the default (allow reneg). */ + if (!PL_strcasecmp(val, "no")) { + renegotiation = SSL_RENEGOTIATE_NEVER; + } else if (PL_strcasecmp(val, "yes")) { + slapd_SSL_warn("The value of nsTLSAllowClientRenegotiation (\"%s\") is invalid." + " Ignoring it and setting it to default.", val); + } + } + slapi_ch_free_string(&val); + + sslStatus = SSL_OptionSet(pr_sock, SSL_ENABLE_RENEGOTIATION, (PRBool) renegotiation); + if (sslStatus != SECSuccess) { + errorCode = PR_GetError(); + slapd_SSL_error("Failed to set SSL renegotiation on the imported " + "socket (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)", + errorCode, slapd_pr_strerror(errorCode)); + return -1; + } + freeConfigEntry( &e ); if(( slapd_SSLclientAuth = config_get_SSLclientAuth()) != SLAPD_SSLCLIENTAUTH_OFF ) { -- 2.9.4