diff options
15 files changed, 202 insertions, 163 deletions
diff --git a/src/cli/tls_server.cpp b/src/cli/tls_server.cpp index 82b2f5e2f..0ec45dc42 100644 --- a/src/cli/tls_server.cpp +++ b/src/cli/tls_server.cpp @@ -109,13 +109,10 @@ class TLS_Server final : public Command Basic_Credentials_Manager creds(rng(), server_crt, server_key); - auto protocol_chooser = [](const std::vector<std::string>& protocols) -> std::string + auto protocol_chooser = [](const std::vector<std::string>&) -> std::string { - for(size_t i = 0; i != protocols.size(); ++i) - { - std::cout << "Client offered protocol " << i << " = " << protocols[i] << std::endl; - } - return "echo/1.0"; // too bad + // we ignore whatever the client sends here + return "echo/1.0"; }; output() << "Listening for new connections on " << transport << " port " << port << std::endl; @@ -149,8 +146,9 @@ class TLS_Server final : public Command using namespace std::placeholders; - auto socket_write = is_tcp ? std::bind(&stream_socket_write, fd, _1, _2) : - std::bind(&dgram_socket_write, fd, _1, _2); + auto socket_write = is_tcp ? + std::bind(&TLS_Server::stream_socket_write, this, fd, _1, _2) : + std::bind(&TLS_Server::dgram_socket_write, this, fd, _1, _2); std::string s; std::list<std::string> pending_output; @@ -192,13 +190,13 @@ class TLS_Server final : public Command if(got == -1) { - std::cout << "Error in socket read - " << strerror(errno) << std::endl; + error_output() << "Error in socket read - " << strerror(errno) << std::endl; break; } if(got == 0) { - std::cout << "EOF on socket" << std::endl; + error_output() << "EOF on socket" << std::endl; break; } @@ -218,7 +216,7 @@ class TLS_Server final : public Command } catch(std::exception& e) { - std::cout << "Connection problem: " << e.what() << std::endl; + error_output() << "Connection problem: " << e.what() << std::endl; if(is_tcp) { ::close(fd); @@ -275,37 +273,37 @@ class TLS_Server final : public Command bool handshake_complete(const Botan::TLS::Session& session) { - std::cout << "Handshake complete, " << session.version().to_string() - << " using " << session.ciphersuite().to_string() << std::endl; + output() << "Handshake complete, " << session.version().to_string() + << " using " << session.ciphersuite().to_string() << std::endl; if(!session.session_id().empty()) { - std::cout << "Session ID " << Botan::hex_encode(session.session_id()) << std::endl; + output() << "Session ID " << Botan::hex_encode(session.session_id()) << std::endl; } if(!session.session_ticket().empty()) { - std::cout << "Session ticket " << Botan::hex_encode(session.session_ticket()) << std::endl; + output() << "Session ticket " << Botan::hex_encode(session.session_ticket()) << std::endl; } return true; } - static void dgram_socket_write(int sockfd, const uint8_t buf[], size_t length) + void dgram_socket_write(int sockfd, const uint8_t buf[], size_t length) { ssize_t sent = ::send(sockfd, buf, length, MSG_NOSIGNAL); if(sent == -1) { - std::cout << "Error writing to socket - " << strerror(errno) << std::endl; + error_output() << "Error writing to socket - " << strerror(errno) << std::endl; } else if(sent != static_cast<ssize_t>(length)) { - std::cout << "Packet of length " << length << " truncated to " << sent << std::endl; + error_output() << "Packet of length " << length << " truncated to " << sent << std::endl; } } - static void stream_socket_write(int sockfd, const uint8_t buf[], size_t length) + void stream_socket_write(int sockfd, const uint8_t buf[], size_t length) { while(length) { @@ -330,7 +328,7 @@ class TLS_Server final : public Command void alert_received(Botan::TLS::Alert alert, const uint8_t[], size_t) { - std::cout << "Alert: " << alert.type_string() << std::endl; + output() << "Alert: " << alert.type_string() << std::endl; } }; diff --git a/src/extra_tests/tls-attacker/README.md b/src/extra_tests/tls-attacker/README.md deleted file mode 100644 index abff9b2c3..000000000 --- a/src/extra_tests/tls-attacker/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# TLS-Attacker testsuite and fuzzing - -Extended Botan library tests with TLS-Attacker. https://github.com/RUB-NDS/TLS-Attacker - -## Testsuite -Contains a testsuite to validate correct TLS server behavior. - -Run -```bash -setup.sh -``` -to download and build the recent TLS-Attacker version, and generate RSA key pairs. - -Run -```bash -server_testsuite.sh -server_policytest.sh -``` -to run the tests. Testsuite executes specific TLS handshakes with the Botan server and verifies that the server correctly handles specific TLS versions and cipher suites. The policy test instantiates the Botan server with a specific policy and verifies that the server behaves according to this policy. - - -## Fuzzing -Starts the TLS-Attacker fuzzer against the Botan server. - -Run -```bash -setup.sh -``` -to download and build the recent TLS-Attacker version, generate RSA key pairs, and re-compile Botan with Address Sanitizer. - -Run -```bash -server_fuzzer.sh -``` -to start the fuzzer. The fuzzer config is located in `config.xml`. Per default, one Botan server is started on port 55020, with the generated RSA keys.`
\ No newline at end of file diff --git a/src/extra_tests/tls-attacker/fuzzing/config.xml b/src/extra_tests/tls-attacker/fuzzing/config.xml deleted file mode 100644 index 5ae1c829a..000000000 --- a/src/extra_tests/tls-attacker/fuzzing/config.xml +++ /dev/null @@ -1,14 +0,0 @@ -<startupCommandsHolder> - <serverCommand>../../../../botan </serverCommand> - <serverPort>55020</serverPort> - <workflowFolder>../TLS-Attacker/resources/fuzzing/workflows</workflowFolder> - <modifiedVariableTypes>TLS_CONSTANT,LENGTH,COUNT,PUBLIC_KEY,PADDING,SIGNATURE,PLAIN_PROTOCOL_MESSAGE</modifiedVariableTypes> - <outputFolder>output/</outputFolder> - <startupCommandsList> - <startupCommands> - <fuzzerCommand>simple_fuzzer -connect localhost:$PORT</fuzzerCommand> - <serverCommandParameters>tls_server ../rsa2048cert.pem ../rsa2048key.pem --port=$PORT </serverCommandParameters> - <shortName>botan-rsa</shortName> - </startupCommands> - </startupCommandsList> -</startupCommandsHolder>
\ No newline at end of file diff --git a/src/extra_tests/tls-attacker/fuzzing/server_fuzzer.sh b/src/extra_tests/tls-attacker/fuzzing/server_fuzzer.sh deleted file mode 100755 index 9e23aee89..000000000 --- a/src/extra_tests/tls-attacker/fuzzing/server_fuzzer.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -if [ -d tls-testsuite ] -then - cd tls-testsuite -fi - -java -jar ../TLS-Attacker/Runnable/target/TLS-Attacker-1.2.jar -loglevel ERROR multi_fuzzer -startup_command_file config.xml
\ No newline at end of file diff --git a/src/extra_tests/tls-attacker/fuzzing/setup.sh b/src/extra_tests/tls-attacker/fuzzing/setup.sh deleted file mode 100755 index 8c83f6eff..000000000 --- a/src/extra_tests/tls-attacker/fuzzing/setup.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh - -if [ ! -d output ] -then - mkdir output -fi - -cd .. - -openssl genpkey -algorithm RSA -out rsa2048key.pem -pkeyopt rsa_keygen_bits:2048 -openssl req -key rsa2048key.pem -new -x509 -days 365 -out rsa2048cert.pem -subj "/C=DE/ST=NRW/L=Bochum/O=TLS-Attacker/CN=tls-attacker.de" - -if [ ! -d TLS-Attacker ] -then - git clone https://github.com/RUB-NDS/TLS-Attacker.git -fi - -cd TLS-Attacker -git checkout . -git pull -./mvnw clean package -DskipTests=true - -cd ../../../../ -make clean -export ASAN_OPTIONS=check_initialization_order=true -if [ -n "$CC" ] - then ./configure.py --with-sanitizers --disable-shared --with-debug-info --with-bzip2 --with-lzma --with-sqlite --with-zlib --cc="$CC" --cc-bin="$CXX" - else ./configure.py --with-sanitizers --disable-shared --with-debug-info --with-bzip2 --with-lzma --with-sqlite --with-zlib -fi - -make -j4
\ No newline at end of file diff --git a/src/extra_tests/tls-attacker/testsuite/server_policytest.sh b/src/extra_tests/tls-attacker/testsuite/server_policytest.sh deleted file mode 100755 index 1237a2c1e..000000000 --- a/src/extra_tests/tls-attacker/testsuite/server_policytest.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -../../../../botan tls_server ../rsa2048cert.pem ../rsa2048key.pem --port=4434 --policy=../../../tests/data/tls-policy/bsi.txt > output/server_policytest.log 2>&1 & -botan_pid=$! - -java -jar ../TLS-Attacker/Runnable/target/TLS-Attacker-1.2.jar -loglevel INFO testtls_server -policy ../../../tests/data/tls-policy/bsi.txt -connect localhost:4434 -tls_timeout 1000 -rc=$? - -if [ $rc -eq 0 ]; then - echo Policy tests finished without failures -else - echo '\n\nPolicy tests failed. See the recent error and the server log output.' -# cat output/server_policytest.log -fi - -kill $botan_pid -exit $rc
\ No newline at end of file diff --git a/src/extra_tests/tls-attacker/testsuite/server_testsuite.sh b/src/extra_tests/tls-attacker/testsuite/server_testsuite.sh deleted file mode 100755 index e26d71e1a..000000000 --- a/src/extra_tests/tls-attacker/testsuite/server_testsuite.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -../../../../botan tls_server ../rsa2048cert.pem ../rsa2048key.pem --port=4433 > output/server_testsuite.log 2>&1 & -botan_pid=$! - -java -jar ../TLS-Attacker/Runnable/target/TLS-Attacker-1.2.jar -loglevel INFO testsuite_server -folder ../TLS-Attacker/resources/testsuite -tls_timeout 1000 -rc=$? - -if [ $rc -eq 0 ]; then - echo Tests finished without failures -else - echo '\n\nTests failed. See the recent error and the server log output.' -# cat output/server_testsuite.log -fi - -kill $botan_pid -exit $rc
\ No newline at end of file diff --git a/src/extra_tests/tls-attacker/testsuite/setup.sh b/src/extra_tests/tls-attacker/testsuite/setup.sh deleted file mode 100755 index f528cd1da..000000000 --- a/src/extra_tests/tls-attacker/testsuite/setup.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh - -if [ ! -d output ] -then - mkdir output -fi - -cd .. - -openssl genpkey -algorithm RSA -out rsa2048key.pem -pkeyopt rsa_keygen_bits:2048 -openssl req -key rsa2048key.pem -new -x509 -days 365 -out rsa2048cert.pem -subj "/C=DE/ST=NRW/L=Bochum/O=TLS-Attacker/CN=tls-attacker.de" - -if [ ! -d TLS-Attacker ] -then - git clone https://github.com/RUB-NDS/TLS-Attacker.git -fi - -cd TLS-Attacker -git checkout . -git pull -./mvnw clean package -DskipTests=true diff --git a/src/scripts/fuzzer.xml b/src/scripts/fuzzer.xml new file mode 100644 index 000000000..9c3d86551 --- /dev/null +++ b/src/scripts/fuzzer.xml @@ -0,0 +1,17 @@ +<!-- This is a template for a config file for fuzzing with TLS-Attacker --> + +<startupCommandsHolder> + <serverCommand>$botan_cli </serverCommand> + <serverPort>$tls_port</serverPort> + <workflowFolder>$workflow_dir</workflowFolder> + <modifiedVariableTypes>TLS_CONSTANT,LENGTH,COUNT,PUBLIC_KEY,PADDING,SIGNATURE,PLAIN_PROTOCOL_MESSAGE</modifiedVariableTypes> + <outputFolder>/tmp/</outputFolder> + <startupCommandsList> + <startupCommands> + <fuzzerCommand>simple_fuzzer -connect localhost:$PORT</fuzzerCommand> + <serverCommandParameters>tls_server $rsa_cert $rsa_key --port=$PORT --policy=$fuzz_policy --output=/tmp/botan_output.log --error-output=/tmp/botan_error_output.log </serverCommandParameters> + <shortName>botan-rsa</shortName> + </startupCommands> + <!-- TODO ECDSA --> + </startupCommandsList> +</startupCommandsHolder> diff --git a/src/scripts/run_tls_attacker.py b/src/scripts/run_tls_attacker.py new file mode 100755 index 000000000..a77364633 --- /dev/null +++ b/src/scripts/run_tls_attacker.py @@ -0,0 +1,138 @@ +#!/usr/bin/python + +import os +import sys +import subprocess +import tempfile +import time +import random +import optparse +import string + +def run_subprocess(cmd): + print("Running '%s'" % (' '.join(cmd))) + + proc = subprocess.Popen(cmd, bufsize=-1) + proc.communicate() + + if proc.returncode != 0: + print('Running "%s" failed rc %d' % (' '.join(cmd), proc.returncode)) + sys.exit(proc.returncode) + +def spawn_server(cmd): + print("Spawning '%s'" % (' '.join(cmd))) + return subprocess.Popen(cmd, bufsize=-1)#,stdout=subprocess.PIPE,stderr=subprocess.PIPE) + +def main(args=None): + if args is None: + args = sys.argv + + parser = optparse.OptionParser() + + parser.add_option('--type', default='tests', + help='Which TLS-Attacker tests to run (tests, policy, fuzzer)') + parser.add_option('--src-dir', metavar='DIR', default='./src', + help='Specify path to botan sources (default "%default")') + parser.add_option('--verbose', action='store_true', + help='Be noisy') + + (options, args) = parser.parse_args(args) + + if len(args) != 3: + print("Usage: %s botan_cli_exe botan_ci_tools" % (args[0])) + return 1 + + cli_exe = args[1] + ci_tools = args[2] + test_type = options.type + src_dir = options.src_dir + + if test_type not in ['tests', 'policy', 'fuzzer']: + print("Unknown --type %s" % (options.test_type)) + return 1 + + if os.access(cli_exe, os.X_OK) != True: + print("Unable to find CLI tool at %s" % (cli_exe)) + return 1 + + if os.access(src_dir, os.X_OK) != True: + print("Unable to find src dir at %s" % (src_dir)) + return 1 + + test_data_dir = os.path.join(src_dir, 'tests/data') + + lax_policy_txt = os.path.join(test_data_dir, 'tls-policy/compat.txt') + bsi_policy_txt = os.path.join(test_data_dir, 'tls-policy/bsi.txt') + + tls_attacker_dir = os.path.join(ci_tools, 'TLS-Attacker') + tls_attacker_jar = os.path.join(tls_attacker_dir, 'TLS-Attacker-1.2.jar') + tls_attacker_testsuites = os.path.join(tls_attacker_dir, 'resources/testsuite') + tls_fuzzer_workflows = os.path.join(tls_attacker_dir, 'resources/fuzzing/workflows') + + if os.access(tls_attacker_jar, os.R_OK) != True: + print("Unable to find TLS-Attacker jar at %s" % (tls_attacker_jar)) + return 1 + + rsa_key = tempfile.NamedTemporaryFile(prefix='rsa_key_') + rsa_crt = tempfile.NamedTemporaryFile(prefix='rsa_crt_') + + run_subprocess([cli_exe, 'keygen', '--algo=RSA', '--params=2048', '--output=%s' % (rsa_key.name)]) + run_subprocess([cli_exe, 'gen_self_signed', rsa_key.name, 'localhost', '--output=%s' % (rsa_crt.name)]) + + server_log = 'botan_log.txt' + server_err_log = 'botan_err_log.txt' + + tls_port = random.randint(50000, 60000) + + botan_server_cmd = [cli_exe, 'tls_server', rsa_crt.name, rsa_key.name, + '--port=%d' % (tls_port), + '--output='+server_log, + '--error-output='+server_err_log] + + java_tls_attacker = ['java', '-jar', tls_attacker_jar, + '-loglevel', 'DEBUG' if options.verbose else 'ERROR'] + tls_attacker_opts = ['-tls_timeout', '300', '-connect', 'localhost:%d' % (tls_port)] + + if test_type == 'tests': + try: + server_process = spawn_server(botan_server_cmd + + ['--policy=%s' % (lax_policy_txt)]) + time.sleep(1) + run_subprocess(java_tls_attacker + ['testsuite_server'] + tls_attacker_opts + + ['-folder', tls_attacker_testsuites]) + finally: + server_process.terminate() + elif test_type == 'policy': + try: + server_process = spawn_server(botan_server_cmd + + ['--policy=%s' % (bsi_policy_txt)]) + time.sleep(1) + run_subprocess(java_tls_attacker + ['testtls_server'] + tls_attacker_opts + + ['-policy', bsi_policy_txt]) + finally: + server_process.terminate() + elif test_type == 'fuzzer': + + template_mapping = { + 'rsa_key': rsa_key.name, + 'rsa_cert': rsa_crt.name, + 'botan_cli': cli_exe, + 'workflow_dir': tls_fuzzer_workflows, + 'fuzz_policy': lax_policy_txt, + 'tls_port': str(tls_port), + 'PORT': '$PORT' # this is a var for TLS-Attacker don't touch it + } + + template_txt = open(os.path.join(src_dir, 'scripts/fuzzer.xml')).read() + + config = string.Template(template_txt).substitute(template_mapping) + + fuzzer_config = tempfile.NamedTemporaryFile(prefix='fuzzer_cfg_', delete=False) + fuzzer_config.write(config.encode('ascii')) + fuzzer_config.close() + + run_subprocess(java_tls_attacker + ['multi_fuzzer'] + + ['-startup_command_file', fuzzer_config.name]) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/extra_tests/tls_scanner/policy.txt b/src/scripts/tls_scanner/policy.txt index a9854ee54..a9854ee54 100644 --- a/src/extra_tests/tls_scanner/policy.txt +++ b/src/scripts/tls_scanner/policy.txt diff --git a/src/extra_tests/tls_scanner/readme.txt b/src/scripts/tls_scanner/readme.txt index a4754b02d..a4754b02d 100644 --- a/src/extra_tests/tls_scanner/readme.txt +++ b/src/scripts/tls_scanner/readme.txt diff --git a/src/extra_tests/tls_scanner/tls_scanner.py b/src/scripts/tls_scanner/tls_scanner.py index 8fdf046ca..8fdf046ca 100755 --- a/src/extra_tests/tls_scanner/tls_scanner.py +++ b/src/scripts/tls_scanner/tls_scanner.py diff --git a/src/extra_tests/tls_scanner/urls.txt b/src/scripts/tls_scanner/urls.txt index a5bcf349e..a5bcf349e 100644 --- a/src/extra_tests/tls_scanner/urls.txt +++ b/src/scripts/tls_scanner/urls.txt diff --git a/src/tests/data/tls-policy/compat.txt b/src/tests/data/tls-policy/compat.txt new file mode 100644 index 000000000..1890b12b5 --- /dev/null +++ b/src/tests/data/tls-policy/compat.txt @@ -0,0 +1,29 @@ +# There is no cooresponding type for this text policy but it is useful +# for interop testing and fuzz testing + +# It is based on the default policy, but allows 3DES, SHA-1 signatures, +# static RSA, and reduces the ephemeral key sizes + +allow_tls10 = true +allow_tls11 = true +allow_tls12 = true +allow_dtls10 = false +allow_dtls12 = false +ciphers = ChaCha20Poly1305 AES-256/GCM AES-128/GCM AES-256 AES-128 3DES +macs = AEAD SHA-256 SHA-384 SHA-1 +signature_hashes = SHA-512 SHA-384 SHA-256 SHA-1 +signature_methods = ECDSA RSA +key_exchange_methods = CECPQ1 ECDH DH RSA +ecc_curves = x25519 secp256r1 secp521r1 secp384r1 brainpool256r1 brainpool384r1 brainpool512r1 +allow_insecure_renegotiation = false +include_time_in_hello_random = true +allow_server_initiated_renegotiation = false +hide_unknown_users = false +server_uses_own_ciphersuite_preferences = true +negotiate_encrypt_then_mac = true +session_ticket_lifetime = 86400 +dh_group = modp/ietf/1024 +minimum_dh_group_size = 1024 +minimum_ecdh_group_size = 255 +minimum_rsa_bits = 1024 +minimum_signature_strength = 80 |