Fuzz SSL_serialize_handoff() and SSL_serialize_handback().
This is done by adding two new tagged data types to the shim's
transcript: one for the serialized handoff, and another for the
serialized handback.
Then, the handshake driver in |TLSFuzzer| is modified to be able to
drive a handoff+handback sequence in the same way as was done for
testing: by swapping |BIO|s into additional |SSL| objects. (If a
particular transcript does not contain a serialized handoff, this is a
no-op.)
Change-Id: Iab23e4dc27959ffd3d444adc41d40a4274e83653
Reviewed-on: https://boringssl-review.googlesource.com/27204
Commit-Queue: Matt Braithwaite <mab@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 03a89fb..5fe394d 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -4544,6 +4544,7 @@
// WARNING: |SSL_apply_handoff| may trigger “msg” callback calls.
OPENSSL_EXPORT void SSL_CTX_set_handoff_mode(SSL_CTX *ctx, bool on);
+OPENSSL_EXPORT void SSL_set_handoff_mode(SSL *SSL, bool on);
OPENSSL_EXPORT bool SSL_serialize_handoff(const SSL *ssl, CBB *out);
OPENSSL_EXPORT bool SSL_decline_handoff(SSL *ssl);
OPENSSL_EXPORT bool SSL_apply_handoff(SSL *ssl, Span<const uint8_t> handoff);
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc
index f67961d..8fb9ada 100644
--- a/ssl/ssl_lib.cc
+++ b/ssl/ssl_lib.cc
@@ -503,6 +503,13 @@
ssl->config = nullptr;
}
+void SSL_set_handoff_mode(SSL *ssl, bool on) {
+ if (!ssl->config) {
+ return;
+ }
+ ssl->config->handoff = on;
+}
+
} // namespace bssl
using namespace bssl;
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 1b37a8d..ae0d2a1 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -163,6 +163,7 @@
return true;
}
+// MoveBIOs moves the |BIO|s of |src| to |dst|. It is used for handoff.
static void MoveBIOs(SSL *dest, SSL *src) {
BIO *rbio = SSL_get_rbio(src);
BIO_up_ref(rbio);
@@ -1924,6 +1925,36 @@
return fwrite(settings, settings_len, 1, file.get()) == 1;
}
+ bool WriteHandoff(const bssl::Array<uint8_t> &handoff) {
+ if (path_.empty()) {
+ return true;
+ }
+
+ CBB child;
+ if (!CBB_add_u16(cbb_.get(), kHandoffTag) ||
+ !CBB_add_u24_length_prefixed(cbb_.get(), &child) ||
+ !CBB_add_bytes(&child, handoff.data(), handoff.size()) ||
+ !CBB_flush(cbb_.get())) {
+ return false;
+ }
+ return true;
+ }
+
+ bool WriteHandback(const bssl::Array<uint8_t> &handback) {
+ if (path_.empty()) {
+ return true;
+ }
+
+ CBB child;
+ if (!CBB_add_u16(cbb_.get(), kHandbackTag) ||
+ !CBB_add_u24_length_prefixed(cbb_.get(), &child) ||
+ !CBB_add_bytes(&child, handback.data(), handback.size()) ||
+ !CBB_flush(cbb_.get())) {
+ return false;
+ }
+ return true;
+ }
+
private:
std::string path_;
bssl::ScopedCBB cbb_;
@@ -2147,7 +2178,8 @@
static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session,
bssl::UniquePtr<SSL> *ssl_uniqueptr,
- const TestConfig *config, bool is_resume, bool is_retry);
+ const TestConfig *config, bool is_resume, bool is_retry,
+ SettingsWriter *writer);
// DoConnection tests an SSL connection against the peer. On success, it returns
// true and sets |*out_session| to the negotiated SSL session. If the test is a
@@ -2156,7 +2188,7 @@
static bool DoConnection(bssl::UniquePtr<SSL_SESSION> *out_session,
SSL_CTX *ssl_ctx, const TestConfig *config,
const TestConfig *retry_config, bool is_resume,
- SSL_SESSION *session) {
+ SSL_SESSION *session, SettingsWriter *writer) {
bssl::UniquePtr<SSL> ssl = NewSSL(ssl_ctx, config, session, is_resume,
std::unique_ptr<TestState>(new TestState));
if (!ssl) {
@@ -2201,7 +2233,7 @@
SSL_set_bio(ssl.get(), bio.get(), bio.get());
bio.release(); // SSL_set_bio takes ownership.
- bool ret = DoExchange(out_session, &ssl, config, is_resume, false);
+ bool ret = DoExchange(out_session, &ssl, config, is_resume, false, writer);
if (!config->is_server && is_resume && config->expect_reject_early_data) {
// We must have failed due to an early data rejection.
if (ret) {
@@ -2236,7 +2268,7 @@
}
assert(!config->handoff);
- ret = DoExchange(out_session, &ssl, retry_config, is_resume, true);
+ ret = DoExchange(out_session, &ssl, retry_config, is_resume, true, writer);
}
if (!ret) {
@@ -2269,8 +2301,8 @@
static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session,
bssl::UniquePtr<SSL> *ssl_uniqueptr,
- const TestConfig *config, bool is_resume,
- bool is_retry) {
+ const TestConfig *config, bool is_resume, bool is_retry,
+ SettingsWriter *writer) {
int ret;
SSL *ssl = ssl_uniqueptr->get();
SSL_CTX *session_ctx = ssl->ctx;
@@ -2312,7 +2344,8 @@
bssl::Array<uint8_t> handoff;
if (!CBB_init(cbb.get(), 512) ||
!SSL_serialize_handoff(ssl_handoff.get(), cbb.get()) ||
- !CBBFinishArray(cbb.get(), &handoff)) {
+ !CBBFinishArray(cbb.get(), &handoff) ||
+ !writer->WriteHandoff(handoff)) {
fprintf(stderr, "Handoff serialisation failed.\n");
return false;
}
@@ -2344,7 +2377,8 @@
bssl::Array<uint8_t> handback;
if (!CBB_init(cbb.get(), 512) ||
!SSL_serialize_handback(ssl, cbb.get()) ||
- !CBBFinishArray(cbb.get(), &handback)) {
+ !CBBFinishArray(cbb.get(), &handback) ||
+ !writer->WriteHandback(handback)) {
fprintf(stderr, "Handback serialisation failed.\n");
return false;
}
@@ -2740,7 +2774,7 @@
return 1;
}
bool ok = DoConnection(&session, ssl_ctx.get(), config, &retry_config,
- is_resume, offer_session.get());
+ is_resume, offer_session.get(), &writer);
if (!writer.Commit()) {
fprintf(stderr, "Error writing settings.\n");
return 1;
diff --git a/ssl/test/fuzzer.h b/ssl/test/fuzzer.h
index c794c4c..b240630 100644
--- a/ssl/test/fuzzer.h
+++ b/ssl/test/fuzzer.h
@@ -30,9 +30,9 @@
#include <openssl/ssl.h>
#include <openssl/x509.h>
+#include "../internal.h"
#include "./fuzzer_tags.h"
-
namespace {
const uint8_t kP256KeyPKCS8[] = {
@@ -276,6 +276,19 @@
}
}
+ static void MoveBIOs(SSL *dest, SSL *src) {
+ BIO *rbio = SSL_get_rbio(src);
+ BIO_up_ref(rbio);
+ SSL_set0_rbio(dest, rbio);
+
+ BIO *wbio = SSL_get_wbio(src);
+ BIO_up_ref(wbio);
+ SSL_set0_wbio(dest, wbio);
+
+ SSL_set0_rbio(src, nullptr);
+ SSL_set0_wbio(src, nullptr);
+ }
+
int TestOneInput(const uint8_t *buf, size_t len) {
RAND_reset_for_fuzzing();
@@ -294,19 +307,63 @@
SSL_set_tlsext_host_name(ssl.get(), "hostname");
}
+ // ssl_handoff may or may not be used.
+ bssl::UniquePtr<SSL> ssl_handoff(SSL_new(ctx_.get()));
+ bssl::UniquePtr<SSL> ssl_handback(SSL_new(ctx_.get()));
+ SSL_set_accept_state(ssl_handoff.get());
+
SSL_set0_rbio(ssl.get(), MakeBIO(CBS_data(&cbs), CBS_len(&cbs)).release());
SSL_set0_wbio(ssl.get(), BIO_new(BIO_s_mem()));
- if (SSL_do_handshake(ssl.get()) == 1) {
+ SSL *ssl_handshake = ssl.get();
+ bool handshake_successful = false;
+ bool handback_successful = false;
+ for (;;) {
+ int ret = SSL_do_handshake(ssl_handshake);
+ if (ret < 0 && SSL_get_error(ssl_handshake, ret) == SSL_ERROR_HANDOFF) {
+ MoveBIOs(ssl_handoff.get(), ssl.get());
+ // Ordinarily we would call SSL_serialize_handoff(ssl.get(). But for
+ // fuzzing, use the serialized handoff that's getting fuzzed.
+ if (!SSL_apply_handoff(ssl_handoff.get(), handoff_)) {
+ if (debug_) {
+ fprintf(stderr, "Handoff failed.\n");
+ }
+ break;
+ }
+ ssl_handshake = ssl_handoff.get();
+ } else if (ret < 0 &&
+ SSL_get_error(ssl_handshake, ret) == SSL_ERROR_HANDBACK) {
+ MoveBIOs(ssl_handback.get(), ssl_handoff.get());
+ if (!SSL_apply_handback(ssl_handback.get(), handback_)) {
+ if (debug_) {
+ fprintf(stderr, "Handback failed.\n");
+ }
+ break;
+ }
+ handback_successful = true;
+ ssl_handshake = ssl_handback.get();
+ } else {
+ handshake_successful = ret == 1;
+ break;
+ }
+ }
+
+ if (debug_) {
+ if (!handshake_successful) {
+ fprintf(stderr, "Handshake failed.\n");
+ } else if (handback_successful) {
+ fprintf(stderr, "Handback successful.\n");
+ }
+ }
+
+ if (handshake_successful) {
// Keep reading application data until error or EOF.
uint8_t tmp[1024];
for (;;) {
- if (SSL_read(ssl.get(), tmp, sizeof(tmp)) <= 0) {
+ if (SSL_read(ssl_handshake, tmp, sizeof(tmp)) <= 0) {
break;
}
}
- } else if (debug_) {
- fprintf(stderr, "Handshake failed.\n");
}
if (debug_) {
@@ -389,6 +446,8 @@
// |ctx| is shared between runs, so we must clear any modifications to it
// made later on in this function.
SSL_CTX_flush_sessions(ctx_.get(), 0);
+ handoff_ = {};
+ handback_ = {};
bssl::UniquePtr<SSL> ssl(SSL_new(ctx_.get()));
if (role_ == kServer) {
@@ -442,6 +501,26 @@
break;
}
+ case kHandoffTag: {
+ CBS handoff;
+ if (!CBS_get_u24_length_prefixed(cbs, &handoff)) {
+ return nullptr;
+ }
+ handoff_.CopyFrom(handoff);
+ bssl::SSL_set_handoff_mode(ssl.get(), 1);
+ break;
+ }
+
+ case kHandbackTag: {
+ CBS handback;
+ if (!CBS_get_u24_length_prefixed(cbs, &handback)) {
+ return nullptr;
+ }
+ handback_.CopyFrom(handback);
+ bssl::SSL_set_handoff_mode(ssl.get(), 1);
+ break;
+ }
+
default:
return nullptr;
}
@@ -497,6 +576,7 @@
Protocol protocol_;
Role role_;
bssl::UniquePtr<SSL_CTX> ctx_;
+ bssl::Array<uint8_t> handoff_, handback_;
};
const BIO_METHOD TLSFuzzer::kBIOMethod = {
diff --git a/ssl/test/fuzzer_tags.h b/ssl/test/fuzzer_tags.h
index b161d80..c21aca3 100644
--- a/ssl/test/fuzzer_tags.h
+++ b/ssl/test/fuzzer_tags.h
@@ -42,4 +42,10 @@
// kTLS13Variant is followed by a u8 denoting the TLS 1.3 variant to configure.
static const uint16_t kTLS13Variant = 3;
+// kHandoffTag is followed by the output of |SSL_serialize_handoff|.
+static const uint16_t kHandoffTag = 4;
+
+// kHandbackTag is followed by te output of |SSL_serialize_handback|.
+static const uint16_t kHandbackTag = 5;
+
#endif // HEADER_SSL_TEST_FUZZER_TAGS