diff --git a/fuzz/CMakeLists.txt b/fuzz/CMakeLists.txt index 556514f7..5eff3d9b 100644 --- a/fuzz/CMakeLists.txt +++ b/fuzz/CMakeLists.txt @@ -58,6 +58,26 @@ target_link_libraries(client Fuzzer) target_link_libraries(client crypto) target_link_libraries(client ssl) +add_executable( + dtls_server + + dtls_server.cc +) + +target_link_libraries(dtls_server Fuzzer) +target_link_libraries(dtls_server crypto) +target_link_libraries(dtls_server ssl) + +add_executable( + dtls_client + + dtls_client.cc +) + +target_link_libraries(dtls_client Fuzzer) +target_link_libraries(dtls_client crypto) +target_link_libraries(dtls_client ssl) + add_executable( read_pem diff --git a/fuzz/client.cc b/fuzz/client.cc index 5f930b60..ad154865 100644 --- a/fuzz/client.cc +++ b/fuzz/client.cc @@ -15,7 +15,7 @@ #include "../ssl/test/fuzzer.h" -static TLSFuzzer g_fuzzer(TLSFuzzer::kClient); +static TLSFuzzer g_fuzzer(TLSFuzzer::kTLS, TLSFuzzer::kClient); extern "C" int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) { return g_fuzzer.TestOneInput(buf, len); diff --git a/fuzz/dtls_client.cc b/fuzz/dtls_client.cc new file mode 100644 index 00000000..5fb6b3bc --- /dev/null +++ b/fuzz/dtls_client.cc @@ -0,0 +1,22 @@ +/* Copyright (c) 2017, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include "../ssl/test/fuzzer.h" + + +static TLSFuzzer g_fuzzer(TLSFuzzer::kDTLS, TLSFuzzer::kClient); + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) { + return g_fuzzer.TestOneInput(buf, len); +} diff --git a/fuzz/dtls_server.cc b/fuzz/dtls_server.cc new file mode 100644 index 00000000..5a27915b --- /dev/null +++ b/fuzz/dtls_server.cc @@ -0,0 +1,22 @@ +/* Copyright (c) 2017, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include "../ssl/test/fuzzer.h" + + +static TLSFuzzer g_fuzzer(TLSFuzzer::kDTLS, TLSFuzzer::kServer); + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) { + return g_fuzzer.TestOneInput(buf, len); +} diff --git a/fuzz/refresh_ssl_corpora.sh b/fuzz/refresh_ssl_corpora.sh index bded4422..6db5562c 100755 --- a/fuzz/refresh_ssl_corpora.sh +++ b/fuzz/refresh_ssl_corpora.sh @@ -60,6 +60,8 @@ assert_directory client_corpus assert_directory client_corpus_no_fuzzer_mode assert_directory server_corpus assert_directory server_corpus_no_fuzzer_mode +assert_directory dtls_client_corpus +assert_directory dtls_server_corpus # Gather new transcripts. Ignore errors in running the tests. @@ -102,6 +104,8 @@ minimize_corpus "$fuzzer_mode_build_dir/fuzz/client" client_corpus minimize_corpus "$fuzzer_mode_build_dir/fuzz/server" server_corpus minimize_corpus "$no_fuzzer_mode_build_dir/fuzz/client" client_corpus_no_fuzzer_mode minimize_corpus "$no_fuzzer_mode_build_dir/fuzz/server" server_corpus_no_fuzzer_mode +minimize_corpus "$fuzzer_mode_build_dir/fuzz/dtls_client" dtls_client_corpus +minimize_corpus "$fuzzer_mode_build_dir/fuzz/dtls_server" dtls_server_corpus # Incorporate the new transcripts. @@ -110,3 +114,5 @@ minimize_corpus "$no_fuzzer_mode_build_dir/fuzz/server" server_corpus_no_fuzzer_ "$fuzzer_mode_build_dir/fuzz/server" -max_len=50000 -merge=1 server_corpus "${fuzzer_mode_transcripts}/tls/server" "$no_fuzzer_mode_build_dir/fuzz/client" -max_len=50000 -merge=1 client_corpus_no_fuzzer_mode "${no_fuzzer_mode_transcripts}/tls/client" "$no_fuzzer_mode_build_dir/fuzz/server" -max_len=50000 -merge=1 server_corpus_no_fuzzer_mode "${no_fuzzer_mode_transcripts}/tls/server" +"$fuzzer_mode_build_dir/fuzz/dtls_client" -max_len=50000 -merge=1 dtls_client_corpus "${fuzzer_mode_transcripts}/dtls/client" +"$fuzzer_mode_build_dir/fuzz/dtls_server" -max_len=50000 -merge=1 dtls_server_corpus "${fuzzer_mode_transcripts}/dtls/server" diff --git a/fuzz/server.cc b/fuzz/server.cc index 1d5c7b93..9f8cee26 100644 --- a/fuzz/server.cc +++ b/fuzz/server.cc @@ -15,7 +15,7 @@ #include "../ssl/test/fuzzer.h" -static TLSFuzzer g_fuzzer(TLSFuzzer::kServer); +static TLSFuzzer g_fuzzer(TLSFuzzer::kTLS, TLSFuzzer::kServer); extern "C" int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) { return g_fuzzer.TestOneInput(buf, len); diff --git a/ssl/test/fuzzer.h b/ssl/test/fuzzer.h index 76d52de4..c794c4ca 100644 --- a/ssl/test/fuzzer.h +++ b/ssl/test/fuzzer.h @@ -15,7 +15,11 @@ #ifndef HEADER_SSL_TEST_FUZZER #define HEADER_SSL_TEST_FUZZER +#include #include +#include + +#include #include #include @@ -253,13 +257,19 @@ int NPNAdvertiseCallback(SSL *ssl, const uint8_t **out, unsigned *out_len, class TLSFuzzer { public: + enum Protocol { + kTLS, + kDTLS, + }; + enum Role { kClient, kServer, }; - explicit TLSFuzzer(Role role) + TLSFuzzer(Protocol protocol, Role role) : debug_(getenv("BORINGSSL_FUZZER_DEBUG") != nullptr), + protocol_(protocol), role_(role) { if (!Init()) { abort(); @@ -284,11 +294,9 @@ class TLSFuzzer { SSL_set_tlsext_host_name(ssl.get(), "hostname"); } - BIO *in = BIO_new(BIO_s_mem()); - BIO *out = BIO_new(BIO_s_mem()); - SSL_set_bio(ssl.get(), in, out); // Takes ownership of |in| and |out|. + SSL_set0_rbio(ssl.get(), MakeBIO(CBS_data(&cbs), CBS_len(&cbs)).release()); + SSL_set0_wbio(ssl.get(), BIO_new(BIO_s_mem())); - BIO_write(in, CBS_data(&cbs), CBS_len(&cbs)); if (SSL_do_handshake(ssl.get()) == 1) { // Keep reading application data until error or EOF. uint8_t tmp[1024]; @@ -311,7 +319,7 @@ class TLSFuzzer { private: // Init initializes |ctx_| with settings common to all inputs. bool Init() { - ctx_.reset(SSL_CTX_new(TLS_method())); + ctx_.reset(SSL_CTX_new(protocol_ == kDTLS ? DTLS_method() : TLS_method())); bssl::UniquePtr pkey(EVP_PKEY_new()); bssl::UniquePtr privkey(RSA_private_key_from_bytes( kRSAPrivateKeyDER, sizeof(kRSAPrivateKeyDER))); @@ -341,11 +349,15 @@ class TLSFuzzer { SSL_CTX_enable_ocsp_stapling(ctx_.get()); // Enable versions and ciphers that are off by default. - if (!SSL_CTX_set_strict_cipher_list(ctx_.get(), "ALL:NULL-SHA") || - !SSL_CTX_set_max_proto_version(ctx_.get(), TLS1_3_VERSION) || - !SSL_CTX_set_min_proto_version(ctx_.get(), SSL3_VERSION)) { + if (!SSL_CTX_set_strict_cipher_list(ctx_.get(), "ALL:NULL-SHA")) { return false; } + if (protocol_ == kTLS) { + if (!SSL_CTX_set_max_proto_version(ctx_.get(), TLS1_3_VERSION) || + !SSL_CTX_set_min_proto_version(ctx_.get(), SSL3_VERSION)) { + return false; + } + } SSL_CTX_set_early_data_enabled(ctx_.get(), 1); @@ -436,11 +448,70 @@ class TLSFuzzer { } } + struct BIOData { + Protocol protocol; + CBS cbs; + }; + + bssl::UniquePtr MakeBIO(const uint8_t *in, size_t len) { + BIOData *b = new BIOData; + b->protocol = protocol_; + CBS_init(&b->cbs, in, len); + + bssl::UniquePtr bio(BIO_new(&kBIOMethod)); + bio->init = 1; + bio->ptr = b; + return bio; + } + + static int BIORead(BIO *bio, char *out, int len) { + assert(bio->method == &kBIOMethod); + BIOData *b = reinterpret_cast(bio->ptr); + if (b->protocol == kTLS) { + len = std::min(static_cast(len), CBS_len(&b->cbs)); + memcpy(out, CBS_data(&b->cbs), len); + CBS_skip(&b->cbs, len); + return len; + } + + // Preserve packet boundaries for DTLS. + CBS packet; + if (!CBS_get_u24_length_prefixed(&b->cbs, &packet)) { + return -1; + } + len = std::min(static_cast(len), CBS_len(&packet)); + memcpy(out, CBS_data(&packet), len); + return len; + } + + static int BIODestroy(BIO *bio) { + assert(bio->method == &kBIOMethod); + BIOData *b = reinterpret_cast(bio->ptr); + delete b; + return 1; + } + + static const BIO_METHOD kBIOMethod; + bool debug_; + Protocol protocol_; Role role_; bssl::UniquePtr ctx_; }; +const BIO_METHOD TLSFuzzer::kBIOMethod = { + 0, // type + nullptr, // name + nullptr, // bwrite + TLSFuzzer::BIORead, + nullptr, // bputs + nullptr, // bgets + nullptr, // ctrl + nullptr, // create + TLSFuzzer::BIODestroy, + nullptr, // callback_ctrl +}; + } // namespace diff --git a/ssl/test/runner/recordingconn.go b/ssl/test/runner/recordingconn.go index 4dae4357..427b2368 100644 --- a/ssl/test/runner/recordingconn.go +++ b/ssl/test/runner/recordingconn.go @@ -112,6 +112,10 @@ func (r *recordingConn) Transcript() []byte { if flow.flowType != writeFlow { continue } + if r.isDatagram { + // Prepend a length prefix to preserve packet boundaries. + ret = append(ret, byte(len(flow.data)>>16), byte(len(flow.data)>>8), byte(len(flow.data))) + } ret = append(ret, flow.data...) } return ret