From 1ac9c020b5865bd1422a253a41deef2778481670 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sat, 9 Feb 2019 21:07:24 +0200 Subject: [PATCH] tests: TLS fuzzing tool Add test-tls program that can be used for fuzzing the internal TLS client and server implementations. This tool can write client or server messages into a file as an initialization step and for the fuzzing step, that file (with potential modifications) can be used to replace the internally generated message contents. The TEST_FUZZ=y build parameter is used to make a special build where a hardcoded random number generator and hardcoded timestamp are used to force deterministic behavior for the TLS operations. Signed-off-by: Jouni Malinen --- src/crypto/Makefile | 2 + src/lib.rules | 5 + src/tls/tlsv1_client_write.c | 3 + src/tls/tlsv1_server_write.c | 3 + src/utils/os_unix.c | 8 ++ tests/Makefile | 9 ++ tests/test-tls.c | 243 +++++++++++++++++++++++++++++++++++ 7 files changed, 273 insertions(+) create mode 100644 tests/test-tls.c diff --git a/src/crypto/Makefile b/src/crypto/Makefile index ee93e41fb..ab108daac 100644 --- a/src/crypto/Makefile +++ b/src/crypto/Makefile @@ -62,7 +62,9 @@ LIB_OBJS += crypto_internal-modexp.o LIB_OBJS += crypto_internal-rsa.o LIB_OBJS += tls_internal.o LIB_OBJS += fips_prf_internal.o +ifndef TEST_FUZZ LIB_OBJS += random.o +endif libcrypto.a: $(LIB_OBJS) diff --git a/src/lib.rules b/src/lib.rules index 0c79d992a..4ec4711e3 100644 --- a/src/lib.rules +++ b/src/lib.rules @@ -6,6 +6,11 @@ ifndef CFLAGS CFLAGS = -MMD -O2 -Wall -g endif +ifdef TEST_FUZZ +CFLAGS += -DCONFIG_NO_RANDOM_POOL +CFLAGS += -DTEST_FUZZ +endif + CFLAGS += -I.. -I../utils diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c index 04d895e61..4a1147b69 100644 --- a/src/tls/tlsv1_client_write.c +++ b/src/tls/tlsv1_client_write.c @@ -72,6 +72,9 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) *out_len = 0; os_get_time(&now); +#ifdef TEST_FUZZ + now.sec = 0xfffefdfc; +#endif /* TEST_FUZZ */ WPA_PUT_BE32(conn->client_random, now.sec); if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c index 892b74573..8d36cf135 100644 --- a/src/tls/tlsv1_server_write.c +++ b/src/tls/tlsv1_server_write.c @@ -53,6 +53,9 @@ static int tls_write_server_hello(struct tlsv1_server *conn, pos += TLS_RECORD_HEADER_LEN; os_get_time(&now); +#ifdef TEST_FUZZ + now.sec = 0xfffefdfc; +#endif /* TEST_FUZZ */ WPA_PUT_BE32(conn->server_random, now.sec); if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c index 1894fcdb0..acc697558 100644 --- a/src/utils/os_unix.c +++ b/src/utils/os_unix.c @@ -250,6 +250,13 @@ void os_daemonize_terminate(const char *pid_file) int os_get_random(unsigned char *buf, size_t len) { +#ifdef TEST_FUZZ + size_t i; + + for (i = 0; i < len; i++) + buf[i] = i & 0xff; + return 0; +#else /* TEST_FUZZ */ FILE *f; size_t rc; @@ -266,6 +273,7 @@ int os_get_random(unsigned char *buf, size_t len) fclose(f); return rc != len ? -1 : 0; +#endif /* TEST_FUZZ */ } diff --git a/tests/Makefile b/tests/Makefile index ba5d94bae..93a844b47 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -17,6 +17,11 @@ ifndef CFLAGS CFLAGS = -MMD -O2 -Wall -g endif +ifdef TEST_FUZZ +CFLAGS += -DCONFIG_NO_RANDOM_POOL +CFLAGS += -DTEST_FUZZ +endif + CFLAGS += -I../src CFLAGS += -I../src/utils @@ -77,6 +82,9 @@ test-sha1: test-sha1.o $(LIBS) test-sha256: test-sha256.o $(LIBS) $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS) +test-tls: test-tls.o $(LIBS) + $(LDO) $(LDFLAGS) -o $@ $< $(LLIBS) + test-x509: test-x509.o $(LIBS) $(LDO) $(LDFLAGS) -o $@ $< $(LLIBS) @@ -99,6 +107,7 @@ clean: $(MAKE) -C ../src clean rm -f $(TESTS) *~ *.o *.d rm -f test-https + rm -f test-tls rm -f test_x509v3_nist.out.* rm -f test_x509v3_nist2.out.* diff --git a/tests/test-tls.c b/tests/test-tls.c new file mode 100644 index 000000000..9941fb5fa --- /dev/null +++ b/tests/test-tls.c @@ -0,0 +1,243 @@ +/* + * Testing tool for TLSv1 client/server routines + * Copyright (c) 2019, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/tls.h" + + +static void usage(void) { + wpa_printf(MSG_INFO, + "usage: test-tls "); + exit(-1); +} + + +static void write_msg(FILE *f, struct wpabuf *msg) +{ + u8 len[2]; + + wpa_printf(MSG_DEBUG, "TEST: Write message to file (msg_len=%u)", + (unsigned int) wpabuf_len(msg)); + WPA_PUT_BE16(len, wpabuf_len(msg)); + fwrite(len, 2, 1, f); + fwrite(wpabuf_head(msg), wpabuf_len(msg), 1, f); +} + + +static struct wpabuf * read_msg(FILE *f) +{ + u8 len[2]; + u16 msg_len; + struct wpabuf *msg; + + if (fread(len, 2, 1, f) != 1) { + wpa_printf(MSG_ERROR, "TEST-ERROR: Could not read msg len"); + return NULL; + } + msg_len = WPA_GET_BE16(len); + + msg = wpabuf_alloc(msg_len); + if (!msg) + return NULL; + if (msg_len > 0 && + fread(wpabuf_put(msg, msg_len), msg_len, 1, f) != 1) { + wpa_printf(MSG_ERROR, "TEST-ERROR: Truncated msg (msg_len=%u)", + msg_len); + wpabuf_free(msg); + return NULL; + } + wpa_hexdump_buf(MSG_DEBUG, "TEST: Read message from file", msg); + + return msg; +} + + +int main(int argc, char *argv[]) +{ + struct tls_config conf; + void *tls_server, *tls_client; + struct tls_connection_params params; + struct tls_connection *conn_server = NULL, *conn_client = NULL; + int ret = -1; + struct wpabuf *in = NULL, *out = NULL, *appl; + enum { SERVER, CLIENT } test_peer; + enum { READ, WRITE } test_oper; + const char *file; + FILE *f; + + wpa_debug_level = 0; + wpa_debug_show_keys = 1; + + if (argc < 4) + usage(); + + if (os_strcmp(argv[1], "server") == 0) + test_peer = SERVER; + else if (os_strcmp(argv[1], "client") == 0) + test_peer = CLIENT; + else + usage(); + + if (os_strcmp(argv[2], "read") == 0) + test_oper = READ; + else if (os_strcmp(argv[2], "write") == 0) + test_oper = WRITE; + else + usage(); + + file = argv[3]; + + f = fopen(file, test_oper == READ ? "r" : "w"); + if (!f) + return -1; + + os_memset(&conf, 0, sizeof(conf)); + tls_server = tls_init(&conf); + tls_client = tls_init(&conf); + if (!tls_server || !tls_client) + goto fail; + + os_memset(¶ms, 0, sizeof(params)); + params.ca_cert = "hwsim/auth_serv/ca.pem"; + params.client_cert = "hwsim/auth_serv/server.pem"; + params.private_key = "hwsim/auth_serv/server.key"; + params.dh_file = "hwsim/auth_serv/dh.conf"; + + if (tls_global_set_params(tls_server, ¶ms)) { + wpa_printf(MSG_ERROR, "Failed to set TLS parameters"); + goto fail; + } + + conn_server = tls_connection_init(tls_server); + conn_client = tls_connection_init(tls_client); + if (!conn_server || !conn_client) + goto fail; + + in = NULL; + for (;;) { + appl = NULL; + if (test_peer == CLIENT && test_oper == READ) + out = read_msg(f); + else + out = tls_connection_handshake(tls_client, conn_client, + in, &appl); + wpabuf_free(in); + in = NULL; + if (!out) + goto fail; + if (test_peer == CLIENT && test_oper == WRITE && + wpabuf_len(out) > 0) + write_msg(f, out); + if (!(test_peer == CLIENT && test_oper == READ) && + tls_connection_get_failed(tls_client, conn_client)) { + wpa_printf(MSG_ERROR, "TLS handshake failed"); + goto fail; + } + if (((test_peer == CLIENT && test_oper == READ) || + tls_connection_established(tls_client, conn_client)) && + ((test_peer == SERVER && test_oper == READ) || + tls_connection_established(tls_server, conn_server))) + break; + + appl = NULL; + if (test_peer == SERVER && test_oper == READ) + in = read_msg(f); + else + in = tls_connection_server_handshake(tls_server, + conn_server, + out, &appl); + wpabuf_free(out); + out = NULL; + if (!in) + goto fail; + if (test_peer == SERVER && test_oper == WRITE) + write_msg(f, in); + if (!(test_peer == SERVER && test_oper == READ) && + tls_connection_get_failed(tls_server, conn_server)) { + wpa_printf(MSG_ERROR, "TLS handshake failed"); + goto fail; + } + if (((test_peer == CLIENT && test_oper == READ) || + tls_connection_established(tls_client, conn_client)) && + ((test_peer == SERVER && test_oper == READ) || + tls_connection_established(tls_server, conn_server))) + break; + } + + wpabuf_free(in); + in = wpabuf_alloc(100); + if (!in) + goto fail; + wpabuf_put_str(in, "PING"); + wpabuf_free(out); + if (test_peer == CLIENT && test_oper == READ) + out = read_msg(f); + else + out = tls_connection_encrypt(tls_client, conn_client, in); + wpabuf_free(in); + in = NULL; + if (!out) + goto fail; + if (test_peer == CLIENT && test_oper == WRITE) + write_msg(f, out); + + if (!(test_peer == SERVER && test_oper == READ)) { + in = tls_connection_decrypt(tls_server, conn_server, out); + wpabuf_free(out); + out = NULL; + if (!in) + goto fail; + wpa_hexdump_buf(MSG_DEBUG, "Server decrypted ApplData", in); + } + + wpabuf_free(in); + in = wpabuf_alloc(100); + if (!in) + goto fail; + wpabuf_put_str(in, "PONG"); + wpabuf_free(out); + if (test_peer == SERVER && test_oper == READ) + out = read_msg(f); + else + out = tls_connection_encrypt(tls_server, conn_server, in); + wpabuf_free(in); + in = NULL; + if (!out) + goto fail; + if (test_peer == SERVER && test_oper == WRITE) + write_msg(f, out); + + if (!(test_peer == CLIENT && test_oper == READ)) { + in = tls_connection_decrypt(tls_client, conn_client, out); + wpabuf_free(out); + out = NULL; + if (!in) + goto fail; + wpa_hexdump_buf(MSG_DEBUG, "Client decrypted ApplData", in); + } + + ret = 0; +fail: + if (tls_server) { + if (conn_server) + tls_connection_deinit(tls_server, conn_server); + tls_deinit(tls_server); + } + if (tls_client) { + if (conn_client) + tls_connection_deinit(tls_server, conn_client); + tls_deinit(tls_client); + } + wpabuf_free(in); + wpabuf_free(out); + fclose(f); + + return ret; +}