From b5be989573ec139bb5cc72959492e25b25426577 Mon Sep 17 00:00:00 2001 From: Noriko Hosoi Date: Mon, 21 Jul 2014 16:04:37 -0700 Subject: [PATCH 2/2] Ticket 47838 - CI test: add test case for ticket 47838 Description: harden the list of ciphers available by default Test Description: 0) Generate self signed cert and import it to the server Enable SSL. 1) Set nsSSL3Ciphers: +all Check all ciphers are eneabled except null ciphers. 2) Set nsSSL3Ciphers: +rsa_aes_128_sha,+rsa_aes_256_sha Check rsa_aes_128_sha, tls_rsa_aes_128_sha, rsa_aes_256_sha, tls_rsa_aes_256_sha are enabled. 3) Set nsSSL3Ciphers: -all Check all ciphers are disabled. 4) Delete nsSSL3Chiphers Check all ciphers are disabled. 5) Set nssSSL3Chiphers: +all,-rsa_rc4_128_md5 Check the enabled cipher count is +all count - 1. 6) Set nssSSL3Chiphers: -all,+rsa_rc4_128_md5 Check the enabled cipher count is -all count + 1. --- dirsrvtests/tickets/ticket47838_test.py | 440 ++++++++++++++++++++++++++++++++ 1 file changed, 440 insertions(+) create mode 100644 dirsrvtests/tickets/ticket47838_test.py diff --git a/dirsrvtests/tickets/ticket47838_test.py b/dirsrvtests/tickets/ticket47838_test.py new file mode 100644 index 0000000..4c4f4bf --- /dev/null +++ b/dirsrvtests/tickets/ticket47838_test.py @@ -0,0 +1,440 @@ +import os +import sys +import time +import ldap +import logging +import socket +import pytest +import shutil +from lib389 import DirSrv, Entry, tools +from lib389 import DirSrvTools +from lib389.tools import DirSrvTools +from lib389._constants import * +from lib389.properties import * +from constants import * + +log = logging.getLogger(__name__) + +installation_prefix = None + +CONFIG_DN = 'cn=config' +ENCRYPTION_DN = 'cn=encryption,%s' % CONFIG_DN +RSA = 'RSA' +RSA_DN = 'cn=%s,%s' % (RSA, ENCRYPTION_DN) +LDAPSPORT = '10636' +SERVERCERT = 'Server-Cert' +plus_all_ecount = 0 +plus_all_dcount = 0 + +class TopologyStandalone(object): + def __init__(self, standalone): + standalone.open() + self.standalone = standalone + +@pytest.fixture(scope="module") +def topology(request): + ''' + This fixture is used to standalone topology for the 'module'. + At the beginning, It may exists a standalone instance. + It may also exists a backup for the standalone instance. + + Principle: + If standalone instance exists: + restart it + If backup of standalone exists: + create/rebind to standalone + + restore standalone instance from backup + else: + Cleanup everything + remove instance + remove backup + Create instance + Create backup + ''' + global installation_prefix + + if installation_prefix: + args_instance[SER_DEPLOYED_DIR] = installation_prefix + + standalone = DirSrv(verbose=False) + + # Args for the standalone instance + args_instance[SER_HOST] = HOST_STANDALONE + args_instance[SER_PORT] = PORT_STANDALONE + args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE + args_standalone = args_instance.copy() + standalone.allocate(args_standalone) + + # Get the status of the backups + backup_standalone = standalone.checkBackupFS() + + # Get the status of the instance and restart it if it exists + instance_standalone = standalone.exists() + if instance_standalone: + # assuming the instance is already stopped, just wait 5 sec max + standalone.stop(timeout=5) + try: + standalone.start(timeout=10) + except ldap.SERVER_DOWN: + pass + + if backup_standalone: + # The backup exist, assuming it is correct + # we just re-init the instance with it + if not instance_standalone: + standalone.create() + # Used to retrieve configuration information (dbdir, confdir...) + standalone.open() + + # restore standalone instance from backup + standalone.stop(timeout=10) + standalone.restoreFS(backup_standalone) + standalone.start(timeout=10) + + else: + # We should be here only in two conditions + # - This is the first time a test involve standalone instance + # - Something weird happened (instance/backup destroyed) + # so we discard everything and recreate all + + # Remove the backup. So even if we have a specific backup file + # (e.g backup_standalone) we clear backup that an instance may have created + if backup_standalone: + standalone.clearBackupFS() + + # Remove the instance + if instance_standalone: + standalone.delete() + + # Create the instance + standalone.create() + + # Used to retrieve configuration information (dbdir, confdir...) + standalone.open() + + # Time to create the backups + standalone.stop(timeout=10) + standalone.backupfile = standalone.backupFS() + standalone.start(timeout=10) + + # clear the tmp directory + standalone.clearTmpDir(__file__) + + # + # Here we have standalone instance up and running + # Either coming from a backup recovery + # or from a fresh (re)init + # Time to return the topology + return TopologyStandalone(standalone) + +def _header(topology, label): + topology.standalone.log.info("\n\n###############################################") + topology.standalone.log.info("#######") + topology.standalone.log.info("####### %s" % label) + topology.standalone.log.info("#######") + topology.standalone.log.info("###############################################") + +def test_ticket47838_init(topology): + """ + Generate self signed cert and import it to the DS cert db. + Enable SSL + """ + _header(topology, 'Testing Ticket 47838 - harden the list of ciphers available by default') + + conf_dir = topology.standalone.confdir + + log.info("\n######################### Checking existing certs ######################\n") + os.system('certutil -L -d %s -n "CA certificate"' % conf_dir) + os.system('certutil -L -d %s -n "%s"' % (conf_dir, SERVERCERT)) + + log.info("\n######################### Create a password file ######################\n") + pwdfile = '%s/pwdfile.txt' % (conf_dir) + opasswd = os.popen("(ps -ef ; w ) | sha1sum | awk '{print $1}'", "r") + passwd = opasswd.readline() + pwdfd = open(pwdfile, "w") + pwdfd.write(passwd) + pwdfd.close() + + log.info("\n######################### Create a noise file ######################\n") + noisefile = '%s/noise.txt' % (conf_dir) + noise = os.popen("(w ; ps -ef ; date ) | sha1sum | awk '{print $1}'", "r") + noisewdfd = open(noisefile, "w") + noisewdfd.write(noise.readline()) + noisewdfd.close() + + log.info("\n######################### Create key3.db and cert8.db database ######################\n") + os.system("ls %s" % pwdfile) + os.system("cat %s" % pwdfile) + os.system('certutil -N -d %s -f %s' % (conf_dir, pwdfile)) + + log.info("\n######################### Creating encryption key for CA ######################\n") + os.system('certutil -G -d %s -z %s -f %s' % (conf_dir, noisefile, pwdfile)) + + log.info("\n######################### Creating self-signed CA certificate ######################\n") + os.system('( echo y ; echo ; echo y ) | certutil -S -n "CA certificate" -s "cn=CAcert" -x -t "CT,," -m 1000 -v 120 -d %s -z %s -f %s -2' % (conf_dir, noisefile, pwdfile)) + + log.info("\n######################### Exporting the CA certificate to cacert.asc ######################\n") + cafile = '%s/cacert.asc' % conf_dir + catxt = os.popen('certutil -L -d %s -n "CA certificate" -a' % conf_dir) + cafd = open(cafile, "w") + while True: + line = catxt.readline() + if (line == ''): + break + cafd.write(line) + cafd.close() + + log.info("\n######################### Generate the server certificate ######################\n") + ohostname = os.popen('hostname --fqdn', "r") + myhostname = ohostname.readline() + os.system('certutil -S -n "%s" -s "cn=%s,ou=389 Directory Server" -c "CA certificate" -t "u,u,u" -m 1001 -v 120 -d %s -z %s -f %s' % (SERVERCERT, myhostname.rstrip(), conf_dir, noisefile, pwdfile)) + + log.info("\n######################### create the pin file ######################\n") + pinfile = '%s/pin.txt' % (conf_dir) + pintxt = 'Internal (Software) Token:%s' % passwd + pinfd = open(pinfile, "w") + pinfd.write(pintxt) + pinfd.close() + + log.info("\n######################### enable SSL in the directory server with all ciphers ######################\n") + topology.standalone.simple_bind_s(DN_DM, PASSWORD) + topology.standalone.modify_s(ENCRYPTION_DN, [(ldap.MOD_REPLACE, 'nsSSL3', 'on'), + (ldap.MOD_REPLACE, 'nsSSLClientAuth', 'allowed'), + (ldap.MOD_REPLACE, 'nsSSL3Ciphers', '+all')]) + + topology.standalone.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, 'nsslapd-security', 'on'), + (ldap.MOD_REPLACE, 'nsslapd-ssl-check-hostname', 'off'), + (ldap.MOD_REPLACE, 'nsslapd-secureport', LDAPSPORT)]) + + topology.standalone.add_s(Entry((RSA_DN, {'objectclass': "top nsEncryptionModule".split(), + 'cn': RSA, + 'nsSSLPersonalitySSL': SERVERCERT, + 'nsSSLToken': 'internal (software)', + 'nsSSLActivation': 'on'}))) + +def test_ticket47838_run_0(topology): + """ + Check nsSSL3Ciphers: +all + All ciphers are enabled except null. + """ + _header(topology, 'Test Case 1 - Check the ciphers availability for "+all"') + + topology.standalone.simple_bind_s(DN_DM, PASSWORD) + topology.standalone.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', '64')]) + + log.info("\n######################### Restarting the server ######################\n") + topology.standalone.restart(timeout=120) + + enabled = os.popen('egrep "SSL alert:" %s | egrep enabled | wc -l' % topology.standalone.errlog) + disabled = os.popen('egrep "SSL alert:" %s | egrep disabled | wc -l' % topology.standalone.errlog) + ecount = int(enabled.readline().rstrip()) + dcount = int(disabled.readline().rstrip()) + + log.info("Enabled ciphers: %d" % ecount) + log.info("Disabled ciphers: %d" % dcount) + assert ecount >= 36 + assert dcount <= 6 + global plus_all_ecount + global plus_all_dcount + plus_all_ecount = ecount + plus_all_dcount = dcount + +def test_ticket47838_run_1(topology): + """ + Check nsSSL3Ciphers: +rsa_aes_128_sha,+rsa_aes_256_sha + rsa_aes_128_sha, tls_rsa_aes_128_sha, rsa_aes_256_sha, tls_rsa_aes_256_sha are enabled. + """ + _header(topology, 'Test Case 2 - Check the ciphers availability for "+rsa_aes_128_sha,+rsa_aes_256_sha"') + + topology.standalone.simple_bind_s(DN_DM, PASSWORD) + topology.standalone.modify_s(ENCRYPTION_DN, [(ldap.MOD_REPLACE, 'nsSSL3Ciphers', '+rsa_aes_128_sha,+rsa_aes_256_sha')]) + + log.info("\n######################### Restarting the server ######################\n") + topology.standalone.stop(timeout=10) + os.system('mv %s %s.47838_0' % (topology.standalone.errlog, topology.standalone.errlog)) + os.system('touch %s' % (topology.standalone.errlog)) + topology.standalone.start(timeout=120) + + enabled = os.popen('egrep "SSL alert:" %s | egrep enabled | wc -l' % topology.standalone.errlog) + disabled = os.popen('egrep "SSL alert:" %s | egrep disabled | wc -l' % topology.standalone.errlog) + ecount = int(enabled.readline().rstrip()) + dcount = int(disabled.readline().rstrip()) + + log.info("Enabled ciphers: %d" % ecount) + log.info("Disabled ciphers: %d" % dcount) + global plus_all_ecount + global plus_all_dcount + assert ecount == 4 + assert dcount == (plus_all_ecount + plus_all_dcount - ecount) + +def test_ticket47838_run_2(topology): + """ + Check nsSSL3Ciphers: -all + All ciphers are disabled. + """ + _header(topology, 'Test Case 3 - Check the ciphers availability for "-all"') + + topology.standalone.simple_bind_s(DN_DM, PASSWORD) + topology.standalone.modify_s(ENCRYPTION_DN, [(ldap.MOD_REPLACE, 'nsSSL3Ciphers', '-all')]) + + log.info("\n######################### Restarting the server ######################\n") + topology.standalone.stop(timeout=10) + os.system('mv %s %s.47838_1' % (topology.standalone.errlog, topology.standalone.errlog)) + os.system('touch %s' % (topology.standalone.errlog)) + topology.standalone.start(timeout=120) + + enabled = os.popen('egrep "SSL alert:" %s | egrep enabled | wc -l' % topology.standalone.errlog) + disabled = os.popen('egrep "SSL alert:" %s | egrep disabled | wc -l' % topology.standalone.errlog) + ecount = int(enabled.readline().rstrip()) + dcount = int(disabled.readline().rstrip()) + + log.info("Enabled ciphers: %d" % ecount) + log.info("Disabled ciphers: %d" % dcount) + global plus_all_ecount + global plus_all_dcount + assert ecount == 0 + assert dcount == (plus_all_ecount + plus_all_dcount) + +def test_ticket47838_run_3(topology): + """ + Check no nsSSL3Ciphers + All ciphers are disabled. + """ + _header(topology, 'Test Case 4 - Check no nssSSL3Chiphers') + + topology.standalone.simple_bind_s(DN_DM, PASSWORD) + topology.standalone.modify_s(ENCRYPTION_DN, [(ldap.MOD_DELETE, 'nsSSL3Ciphers', '-all')]) + + log.info("\n######################### Restarting the server ######################\n") + topology.standalone.stop(timeout=10) + os.system('mv %s %s.47838_2' % (topology.standalone.errlog, topology.standalone.errlog)) + os.system('touch %s' % (topology.standalone.errlog)) + topology.standalone.start(timeout=120) + + enabled = os.popen('egrep "SSL alert:" %s | egrep enabled | wc -l' % topology.standalone.errlog) + disabled = os.popen('egrep "SSL alert:" %s | egrep disabled | wc -l' % topology.standalone.errlog) + ecount = int(enabled.readline().rstrip()) + dcount = int(disabled.readline().rstrip()) + + log.info("Enabled ciphers: %d" % ecount) + log.info("Disabled ciphers: %d" % dcount) + global plus_all_ecount + global plus_all_dcount + assert ecount == 0 + assert dcount == (plus_all_ecount + plus_all_dcount) + +def test_ticket47838_run_4(topology): + """ + Check nssSSL3Chiphers: +all,-rsa_rc4_128_md5 + All ciphers are disabled. + """ + _header(topology, 'Test Case 5 - Check nssSSL3Chiphers: +all,-rsa_rc4_128_md5') + + topology.standalone.simple_bind_s(DN_DM, PASSWORD) + topology.standalone.modify_s(ENCRYPTION_DN, [(ldap.MOD_REPLACE, 'nsSSL3Ciphers', '+all,-rsa_rc4_128_md5')]) + + log.info("\n######################### Restarting the server ######################\n") + topology.standalone.stop(timeout=10) + os.system('mv %s %s.47838_3' % (topology.standalone.errlog, topology.standalone.errlog)) + os.system('touch %s' % (topology.standalone.errlog)) + topology.standalone.start(timeout=120) + + enabled = os.popen('egrep "SSL alert:" %s | egrep enabled | wc -l' % topology.standalone.errlog) + disabled = os.popen('egrep "SSL alert:" %s | egrep disabled | wc -l' % topology.standalone.errlog) + ecount = int(enabled.readline().rstrip()) + dcount = int(disabled.readline().rstrip()) + + log.info("Enabled ciphers: %d" % ecount) + log.info("Disabled ciphers: %d" % dcount) + global plus_all_ecount + global plus_all_dcount + assert ecount == (plus_all_ecount - 1) + assert dcount == (plus_all_dcount + 1) + +def test_ticket47838_run_5(topology): + """ + Check nssSSL3Chiphers: -all,+rsa_rc4_128_md5 + All ciphers are disabled. + """ + _header(topology, 'Test Case 6 - Check nssSSL3Chiphers: -all,+rsa_rc4_128_md5') + + topology.standalone.simple_bind_s(DN_DM, PASSWORD) + topology.standalone.modify_s(ENCRYPTION_DN, [(ldap.MOD_REPLACE, 'nsSSL3Ciphers', '-all,+rsa_rc4_128_md5')]) + + log.info("\n######################### Restarting the server ######################\n") + topology.standalone.stop(timeout=10) + os.system('mv %s %s.47838_4' % (topology.standalone.errlog, topology.standalone.errlog)) + os.system('touch %s' % (topology.standalone.errlog)) + topology.standalone.start(timeout=120) + + enabled = os.popen('egrep "SSL alert:" %s | egrep enabled | wc -l' % topology.standalone.errlog) + disabled = os.popen('egrep "SSL alert:" %s | egrep disabled | wc -l' % topology.standalone.errlog) + ecount = int(enabled.readline().rstrip()) + dcount = int(disabled.readline().rstrip()) + + log.info("Enabled ciphers: %d" % ecount) + log.info("Disabled ciphers: %d" % dcount) + global plus_all_ecount + global plus_all_dcount + assert ecount == 1 + assert dcount == (plus_all_ecount + plus_all_dcount - ecount) + +def test_ticket47838_run_6(topology): + """ + NOTE: Currently, this test case is commented out since if the server fails to start, + it repeatedly restarted. + Check nssSSL3Chiphers: all <== invalid value + All ciphers are disabled. + """ + _header(topology, 'Test Case 7 - Check nssSSL3Chiphers: all, which is invalid') + + topology.standalone.simple_bind_s(DN_DM, PASSWORD) + topology.standalone.modify_s(ENCRYPTION_DN, [(ldap.MOD_REPLACE, 'nsSSL3Ciphers', 'all')]) + + log.info("\n######################### Restarting the server ######################\n") + topology.standalone.stop(timeout=10) + os.system('mv %s %s.47838_5' % (topology.standalone.errlog, topology.standalone.errlog)) + os.system('touch %s' % (topology.standalone.errlog)) + topology.standalone.start(timeout=120) + + errmsg = os.popen('egrep "SSL alert:" %s | egrep "invalid ciphers"' % topology.standalone.errlog) + if errmsg != "": + log.info("Expected error message:") + log.info("%s" % errmsg) + else: + log.info("Expected error message was not found") + assert False + + topology.standalone.log.info("ticket47838 was successfully verified."); + +def test_ticket47838_final(topology): + topology.standalone.simple_bind_s(DN_DM, PASSWORD) + topology.standalone.stop(timeout=10) + +def run_isolated(): + ''' + run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..) + To run isolated without py.test, you need to + - edit this file and comment '@pytest.fixture' line before 'topology' function. + - set the installation prefix + - run this program + ''' + global installation_prefix + installation_prefix = None + + topo = topology(True) + test_ticket47838_init(topo) + + test_ticket47838_run_0(topo) + test_ticket47838_run_1(topo) + test_ticket47838_run_2(topo) + test_ticket47838_run_3(topo) + test_ticket47838_run_4(topo) + test_ticket47838_run_5(topo) + # test_ticket47838_run_6(topo) + + test_ticket47838_final(topo) + +if __name__ == '__main__': + run_isolated() -- 1.8.1.4