From 05606f765fbd6b426e0be8edd18d2a5bef9cde73 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Feb 26 2019 16:09:20 +0000 Subject: [PATCH 1/5] Make TLSV1_3 flag bit-unique so it can be used in cipher comparisons --- diff --git a/nss_engine_cipher.h b/nss_engine_cipher.h index 83321c2..41b1e02 100644 --- a/nss_engine_cipher.h +++ b/nss_engine_cipher.h @@ -86,7 +86,7 @@ typedef struct #define SSLV3 0x00000002L #define TLSV1 SSLV3 #define TLSV1_2 0x00000004L -#define TLSV1_3 0x00000005L +#define TLSV1_3 0x00000008L /* the table itself is defined in nss_engine_cipher.c */ #if 0 From b41214b2253610fba72580461b8412f972cff245 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Feb 26 2019 16:09:23 +0000 Subject: [PATCH 2/5] Adapt to openssl 1.1.1a change to TLS 1.3 cipher handling Fedora system policy now disables RC4, ignore that Disable ARIA ciphers Disable TLSv1.3 ciphers in the output by default Add TLS1.3-specific test cas --- diff --git a/test/test_cipher.py b/test/test_cipher.py index 69de7dc..485d8fb 100644 --- a/test/test_cipher.py +++ b/test/test_cipher.py @@ -45,6 +45,7 @@ CIPHERS_NOT_IN_NSS = [ 'ECDHE-RSA-CAMELLIA128-SHA256', 'DHE-RSA-CAMELLIA128-SHA256', 'DHE-RSA-CAMELLIA256-SHA256', + 'TLS_AES_128_CCM_SHA256', ] CIPHERS_NOT_IN_OPENSSL = [ @@ -59,7 +60,7 @@ CIPHERS_NOT_IN_OPENSSL = [ ] OPENSSL_CIPHERS_IGNORE = ":-SSLv2:-KRB5:-PSK:-ADH:-DSS:-SEED:-IDEA" \ - ":-SRP:-AESCCM:-AESCCM8" + ":-SRP:-AESCCM:-AESCCM8:-RC4:-ARIA" if ENABLE_SERVER_DHE == 0: OPENSSL_CIPHERS_IGNORE += ':-DH' @@ -86,12 +87,21 @@ tls13_ciphers = [ ] -def assert_equal_openssl(ciphers): +def assert_equal_openssl(ciphers, tls13=False): nss_ciphers = ciphers + ":-EXP:-LOW:-RC4:-EDH" ossl_ciphers = ciphers + OPENSSL_CIPHERS_IGNORE + + if not tls13: + # Disable TLSv1.3 ciphers to match default output in openssl ciphers + nss_ciphers = nss_ciphers + ":-TLSv1.3" (nss, err, rc) = run([exe, "--o", nss_ciphers]) assert rc == 0 - (ossl, err, rc) = run([openssl, "ciphers", ossl_ciphers]) + if not tls13: + # Disable TLSv1.3 ciphers to match previous behavior + cmd = [openssl, "ciphers", "-ciphersuites", "", ossl_ciphers] + else: + cmd = [openssl, "ciphers", ossl_ciphers] + (ossl, err, rc) = run(cmd) assert rc == 0 nss_list = nss.strip().split(':') @@ -134,9 +144,9 @@ def assert_equal_openssl(ciphers): elif len(ossl_list) > len(nss_list): diff = set(ossl_list) - set(nss_list) else: - diff = '' + diff = None - assert nss_list == ossl_list, '%r != %r. Difference %r' % ( + assert diff is None, '%r != %r. Difference %r' % ( ':'.join(nss_list), ':'.join(ossl_list), diff) @@ -228,6 +238,9 @@ class test_ciphers(object): def test_TLSv12(self): assert_equal_openssl("TLSv1.2") + def test_TLSv13(self): + assert_equal_openssl("TLSv1.3", tls13=True) + def test_NULL(self): assert_equal_openssl("NULL") From 0017ebce4fd3e871d7cc85b332a6272c8a199e8e Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Feb 26 2019 16:26:16 +0000 Subject: [PATCH 3/5] Handle older openssl versions that lack the -ciphersuites option --- diff --git a/test/test_cipher.py b/test/test_cipher.py index 485d8fb..0e3c690 100644 --- a/test/test_cipher.py +++ b/test/test_cipher.py @@ -77,8 +77,13 @@ def openssl_tls13(): (out, err, rc) = run([openssl, 'ciphers', 'tls1_3']) return rc == 0 +def openssl_has_ciphersuites(): + (out, err, rc) = run(["openssl", "ciphers", "-ciphersuites", "", "AES"]) + return rc == 0 + OPENSSL_CHACHA20 = openssl_CHACHA20() OPENSSL_TLS13 = openssl_tls13() +OPENSSL_HAS_CIPHERSUITES = openssl_has_ciphersuites() tls13_ciphers = [ 'TLS-AES-128-GCM-SHA256', @@ -91,12 +96,12 @@ def assert_equal_openssl(ciphers, tls13=False): nss_ciphers = ciphers + ":-EXP:-LOW:-RC4:-EDH" ossl_ciphers = ciphers + OPENSSL_CIPHERS_IGNORE - if not tls13: + if not tls13 and OPENSSL_HAS_CIPHERSUITES: # Disable TLSv1.3 ciphers to match default output in openssl ciphers nss_ciphers = nss_ciphers + ":-TLSv1.3" (nss, err, rc) = run([exe, "--o", nss_ciphers]) assert rc == 0 - if not tls13: + if not tls13 and OPENSSL_HAS_CIPHERSUITES: # Disable TLSv1.3 ciphers to match previous behavior cmd = [openssl, "ciphers", "-ciphersuites", "", ossl_ciphers] else: @@ -239,7 +244,8 @@ class test_ciphers(object): assert_equal_openssl("TLSv1.2") def test_TLSv13(self): - assert_equal_openssl("TLSv1.3", tls13=True) + if OPENSSL_TLS13: + assert_equal_openssl("TLSv1.3", tls13=True) def test_NULL(self): assert_equal_openssl("NULL") From 78835ecd603e86460bf19272721723c84e6e993a Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Feb 26 2019 17:00:31 +0000 Subject: [PATCH 4/5] Use python to try to get the FQDN then fall back to older method My intention with this was to ensure that I got a FQDN. The thinking was that the "longest" hostname was probably the right one. This doesn't work in the case of localhost in Fedora because there is localhost.localdomain which is the default hostname but it also answers as localhost4.localdomain4 which is longer so gencert gets confused. --- diff --git a/gencert.in b/gencert.in index 0fd1c67..2b06ad0 100755 --- a/gencert.in +++ b/gencert.in @@ -38,6 +38,11 @@ getFQDN() { echo $maxhost return fi + hostname=$(python -c 'import socket; print(socket.getfqdn())') + if [ $? == 0 ]; then + echo $hostname + return + fi defhost=`hostname` if [ -e /usr/bin/host -o -e /bin/host ]; then hosthost=`host $defhost | grep -v "not found" | awk '{print $1}'` From 8c0cbd83ed894bcfddc497e44174bbfe24c6fbc7 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Feb 26 2019 17:00:31 +0000 Subject: [PATCH 5/5] Make the reverse proxy test optional since it relies on a host alias This is a bit of a hacky test for reverse proxy using an SNI name that relies on /etc/hosts to be tweaked in an odd way that doesn't work well in a CI system. --- diff --git a/test/test.py b/test/test.py index 7160a26..20fd3d2 100644 --- a/test/test.py +++ b/test/test.py @@ -1,5 +1,6 @@ from test_config import Declarative, write_template_file, restart_apache from test_config import stop_apache +from test_util import run from variable import ENABLE_SERVER_DHE import ssl import requests.exceptions @@ -17,6 +18,16 @@ except ImportError: from urllib3.packages.ssl_match_hostname import CertificateError +def www1_defined(): + """Dumb test to see if www1.example.com is a known host to see + whether the proxy tests should be executed or not. + """ + (out, err, rc) = run(["/usr/bin/ping", + "-w", "2", + "-c", "1", "www1.example.com"]) + return rc == 0 + + class test_suite1(Declarative): @classmethod def setUpClass(cls): @@ -232,21 +243,26 @@ class test_suite1(Declarative): expected=200, ), - dict( - desc='SNI request when SNI is disabled', - request=('/index.html', - {'host': 'www1.example.com', 'port': 8000}), - expected=requests.exceptions.SSLError(), - expected_str='doesn\'t match', - ), + ] - dict( - desc='Reverse proxy request when SNI is disabled', - request=('/proxy/index.html', {}), - expected=400, - ), + if www1_defined(): + tests.append( + dict( + desc='SNI request when SNI is disabled', + request=('/index.html', + {'host': 'www1.example.com', 'port': 8000}), + expected=requests.exceptions.SSLError(), + expected_str='doesn\'t match', + ), + ) - ] + tests.append( + dict( + desc='Reverse proxy request when SNI is disabled', + request=('/proxy/index.html', {}), + expected=400, + ), + ) if ENABLE_SERVER_DHE: tests.append(