Bug 1338620 - Derive tls-client fuzzer configuration from input data r=franziskus
authorTim Taubert <ttaubert@mozilla.com>
Mon, 13 Feb 2017 20:31:22 +0100
changeset 13124 9296c4353e76d02a361c7269542d332430154144
parent 13123 58536cade197d9d6b1699624181d8bc603eb588f
child 13125 ae2f0b34ba5d6c5d02e0250b20e5c12cc5721bad
push id2009
push userttaubert@mozilla.com
push dateMon, 13 Feb 2017 19:32:37 +0000
reviewersfranziskus
bugs1338620
Bug 1338620 - Derive tls-client fuzzer configuration from input data r=franziskus Differential Revision: https://nss-review.dev.mozaws.net/D205
fuzz/fuzz.gyp
fuzz/tls_client_config.cc
fuzz/tls_client_config.h
fuzz/tls_client_target.cc
--- a/fuzz/fuzz.gyp
+++ b/fuzz/fuzz.gyp
@@ -243,16 +243,17 @@
         '<(DEPTH)/exports.gyp:nss_exports',
         'nssfuzz-mpi-base',
       ],
     },
     {
       'target_name': 'nssfuzz-tls-client',
       'type': 'executable',
       'sources': [
+        'tls_client_config.cc',
         'tls_client_socket.cc',
         'tls_client_target.cc',
       ],
       'dependencies': [
         '<(DEPTH)/cpputil/cpputil.gyp:cpputil',
         '<(DEPTH)/exports.gyp:nss_exports',
         'fuzz_base',
       ],
new file mode 100644
--- /dev/null
+++ b/fuzz/tls_client_config.cc
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "tls_client_config.h"
+
+const uint64_t CONFIG_FAIL_CERT_AUTH = 1;
+const uint64_t CONFIG_ENABLE_EXTENDED_MS = 2;
+const uint64_t CONFIG_REQUIRE_DH_NAMED_GROUPS = 4;
+const uint64_t CONFIG_ENABLE_FALSE_START = 8;
+
+// XOR 64-bit chunks of data to build a bitmap of config options derived from
+// the fuzzing input. This seems the only way to fuzz various options while
+// still maintaining compatibility with BoringSSL or OpenSSL fuzzers.
+ClientConfig::ClientConfig(const uint8_t* data, size_t len) {
+  for (size_t i = 0; i < len; i++) {
+    config_ ^= static_cast<uint64_t>(data[i]) << (8 * (i % 8));
+  }
+}
+
+bool ClientConfig::FailCertificateAuthentication() {
+  return config_ & CONFIG_FAIL_CERT_AUTH;
+}
+
+bool ClientConfig::EnableExtendedMasterSecret() {
+  return config_ & CONFIG_ENABLE_EXTENDED_MS;
+}
+
+bool ClientConfig::RequireDhNamedGroups() {
+  return config_ & CONFIG_REQUIRE_DH_NAMED_GROUPS;
+}
+
+bool ClientConfig::EnableFalseStart() {
+  return config_ & CONFIG_ENABLE_FALSE_START;
+}
new file mode 100644
--- /dev/null
+++ b/fuzz/tls_client_config.h
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef tls_client_config_h__
+#define tls_client_config_h__
+
+#include <stdint.h>
+#include <cstddef>
+
+class ClientConfig {
+ public:
+  ClientConfig(const uint8_t* data, size_t len);
+
+  bool FailCertificateAuthentication();
+  bool EnableExtendedMasterSecret();
+  bool RequireDhNamedGroups();
+  bool EnableFalseStart();
+
+ private:
+  uint64_t config_;
+};
+
+#endif  // tls_client_config_h__
--- a/fuzz/tls_client_target.cc
+++ b/fuzz/tls_client_target.cc
@@ -6,66 +6,80 @@
 #include <stdint.h>
 #include <memory>
 
 #include "blapi.h"
 #include "prinit.h"
 #include "ssl.h"
 
 #include "shared.h"
+#include "tls_client_config.h"
 #include "tls_client_socket.h"
 
 static PRStatus EnableAllProtocolVersions() {
   SSLVersionRange supported;
 
   SECStatus rv = SSL_VersionRangeGetSupported(ssl_variant_stream, &supported);
   assert(rv == SECSuccess);
 
   rv = SSL_VersionRangeSetDefault(ssl_variant_stream, &supported);
   assert(rv == SECSuccess);
 
   return PR_SUCCESS;
 }
 
 static SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checksig,
                                      PRBool isServer) {
-  return SECSuccess;
+  assert(!isServer);
+  auto config = reinterpret_cast<ClientConfig*>(arg);
+  return config->FailCertificateAuthentication() ? SECFailure : SECSuccess;
 }
 
-static void SetSocketOptions(PRFileDesc* fd) {
+static void SetSocketOptions(PRFileDesc* fd,
+                             std::unique_ptr<ClientConfig>& config) {
   // Disable session cache for now.
   SECStatus rv = SSL_OptionSet(fd, SSL_NO_CACHE, true);
   assert(rv == SECSuccess);
 
-  rv = SSL_OptionSet(fd, SSL_ENABLE_EXTENDED_MASTER_SECRET, true);
+  rv = SSL_OptionSet(fd, SSL_ENABLE_EXTENDED_MASTER_SECRET,
+                     config->EnableExtendedMasterSecret());
   assert(rv == SECSuccess);
 
-  rv = SSL_OptionSet(fd, SSL_ENABLE_SIGNED_CERT_TIMESTAMPS, true);
+  rv = SSL_OptionSet(fd, SSL_REQUIRE_DH_NAMED_GROUPS,
+                     config->RequireDhNamedGroups());
   assert(rv == SECSuccess);
 
-  rv = SSL_OptionSet(fd, SSL_ENABLE_FALLBACK_SCSV, true);
-  assert(rv == SECSuccess);
-
-  rv = SSL_OptionSet(fd, SSL_ENABLE_ALPN, true);
+  rv = SSL_OptionSet(fd, SSL_ENABLE_FALSE_START, config->EnableFalseStart());
   assert(rv == SECSuccess);
 
   rv =
       SSL_OptionSet(fd, SSL_ENABLE_RENEGOTIATION, SSL_RENEGOTIATE_UNRESTRICTED);
   assert(rv == SECSuccess);
 }
 
 static void EnableAllCipherSuites(PRFileDesc* fd) {
   for (uint16_t i = 0; i < SSL_NumImplementedCiphers; ++i) {
     SECStatus rv = SSL_CipherPrefSet(fd, SSL_ImplementedCiphers[i], true);
     assert(rv == SECSuccess);
   }
 }
 
-static void SetupAuthCertificateHook(PRFileDesc* fd) {
-  SECStatus rv = SSL_AuthCertificateHook(fd, AuthCertificateHook, nullptr);
+// This is only called when we set SSL_ENABLE_FALSE_START=1,
+// so we can always just set *canFalseStart=true.
+static SECStatus CanFalseStartCallback(PRFileDesc* fd, void* arg,
+                                       PRBool* canFalseStart) {
+  *canFalseStart = true;
+  return SECSuccess;
+}
+
+static void SetupCallbacks(PRFileDesc* fd, ClientConfig* config) {
+  SECStatus rv = SSL_AuthCertificateHook(fd, AuthCertificateHook, config);
+  assert(rv == SECSuccess);
+
+  rv = SSL_SetCanFalseStartCallback(fd, CanFalseStartCallback, nullptr);
   assert(rv == SECSuccess);
 }
 
 static void DoHandshake(PRFileDesc* fd) {
   SECStatus rv = SSL_ResetHandshake(fd, false /* asServer */);
   assert(rv == SECSuccess);
 
   do {
@@ -84,30 +98,31 @@ static void DoHandshake(PRFileDesc* fd) 
   }
 }
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t len) {
   static std::unique_ptr<NSSDatabase> db(new NSSDatabase());
   assert(db != nullptr);
 
   EnableAllProtocolVersions();
+  std::unique_ptr<ClientConfig> config(new ClientConfig(data, len));
 
   // Reset the RNG state.
   SECStatus rv = RNG_ResetForFuzzing();
   assert(rv == SECSuccess);
 
   // Create and import dummy socket.
   std::unique_ptr<DummyPrSocket> socket(new DummyPrSocket(data, len));
   static PRDescIdentity id = PR_GetUniqueIdentity("fuzz-client");
   ScopedPRFileDesc fd(DummyIOLayerMethods::CreateFD(id, socket.get()));
   PRFileDesc* ssl_fd = SSL_ImportFD(nullptr, fd.get());
   assert(ssl_fd == fd.get());
 
   // Probably not too important for clients.
   SSL_SetURL(ssl_fd, "server");
 
-  SetSocketOptions(ssl_fd);
+  SetSocketOptions(ssl_fd, config);
   EnableAllCipherSuites(ssl_fd);
-  SetupAuthCertificateHook(ssl_fd);
+  SetupCallbacks(ssl_fd, config.get());
   DoHandshake(ssl_fd);
 
   return 0;
 }