diff options
author | Tim Taubert <ttaubert@mozilla.com> | 2017-03-24 11:18:52 +0100 |
---|---|---|
committer | Tim Taubert <ttaubert@mozilla.com> | 2017-03-24 11:18:52 +0100 |
commit | 14dce5c17c2b2993ffd4fa456524b781f46a2dc6 (patch) | |
tree | 31724255437e0c38c5aba50459f21ea0d797129f /cpputil | |
parent | 81311a26f78c579d457a1d0e04f7e7e382e93235 (diff) | |
download | nss-hg-14dce5c17c2b2993ffd4fa456524b781f46a2dc6.tar.gz |
Bug 1348775 - Add custom TLS mutators for libFuzzer fuzzing targets r=franziskus
Differential Revision: https://nss-review.dev.mozaws.net/D261
Diffstat (limited to 'cpputil')
-rw-r--r-- | cpputil/cpputil.gyp | 1 | ||||
-rw-r--r-- | cpputil/databuffer.h | 191 | ||||
-rw-r--r-- | cpputil/tls_parser.cc | 73 | ||||
-rw-r--r-- | cpputil/tls_parser.h | 138 |
4 files changed, 403 insertions, 0 deletions
diff --git a/cpputil/cpputil.gyp b/cpputil/cpputil.gyp index cf666fd3c..82183f241 100644 --- a/cpputil/cpputil.gyp +++ b/cpputil/cpputil.gyp @@ -12,6 +12,7 @@ 'sources': [ 'dummy_io.cc', 'dummy_io_fwd.cc', + 'tls_parser.cc', ], 'dependencies': [ '<(DEPTH)/exports.gyp:nss_exports', diff --git a/cpputil/databuffer.h b/cpputil/databuffer.h new file mode 100644 index 000000000..e7236d4e9 --- /dev/null +++ b/cpputil/databuffer.h @@ -0,0 +1,191 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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 databuffer_h__ +#define databuffer_h__ + +#include <algorithm> +#include <cassert> +#include <cstring> +#include <iomanip> +#include <iostream> +#if defined(WIN32) || defined(WIN64) +#include <winsock2.h> +#else +#include <arpa/inet.h> +#endif + +extern bool g_ssl_gtest_verbose; + +namespace nss_test { + +class DataBuffer { + public: + DataBuffer() : data_(nullptr), len_(0) {} + DataBuffer(const uint8_t* data, size_t len) : data_(nullptr), len_(0) { + Assign(data, len); + } + DataBuffer(const DataBuffer& other) : data_(nullptr), len_(0) { + Assign(other); + } + ~DataBuffer() { delete[] data_; } + + DataBuffer& operator=(const DataBuffer& other) { + if (&other != this) { + Assign(other); + } + return *this; + } + + void Allocate(size_t len) { + delete[] data_; + data_ = new uint8_t[len ? len : 1]; // Don't depend on new [0]. + len_ = len; + } + + void Truncate(size_t len) { len_ = std::min(len_, len); } + + void Assign(const DataBuffer& other) { Assign(other.data(), other.len()); } + + void Assign(const uint8_t* data, size_t len) { + if (data) { + Allocate(len); + memcpy(static_cast<void*>(data_), static_cast<const void*>(data), len); + } else { + assert(len == 0); + data_ = nullptr; + len_ = 0; + } + } + + // Write will do a new allocation and expand the size of the buffer if needed. + // Returns the offset of the end of the write. + size_t Write(size_t index, const uint8_t* val, size_t count) { + assert(val); + if (index + count > len_) { + size_t newlen = index + count; + uint8_t* tmp = new uint8_t[newlen]; // Always > 0. + if (data_) { + memcpy(static_cast<void*>(tmp), static_cast<const void*>(data_), len_); + } + if (index > len_) { + memset(static_cast<void*>(tmp + len_), 0, index - len_); + } + delete[] data_; + data_ = tmp; + len_ = newlen; + } + if (data_) { + memcpy(static_cast<void*>(data_ + index), static_cast<const void*>(val), + count); + } + return index + count; + } + + size_t Write(size_t index, const DataBuffer& buf) { + return Write(index, buf.data(), buf.len()); + } + + // Write an integer, also performing host-to-network order conversion. + // Returns the offset of the end of the write. + size_t Write(size_t index, uint32_t val, size_t count) { + assert(count <= sizeof(uint32_t)); + uint32_t nvalue = htonl(val); + auto* addr = reinterpret_cast<const uint8_t*>(&nvalue); + return Write(index, addr + sizeof(uint32_t) - count, count); + } + + // This can't use the same trick as Write(), since we might be reading from a + // smaller data source. + bool Read(size_t index, size_t count, uint32_t* val) const { + assert(count < sizeof(uint32_t)); + assert(val); + if ((index > len()) || (count > (len() - index))) { + return false; + } + *val = 0; + for (size_t i = 0; i < count; ++i) { + *val = (*val << 8) | data()[index + i]; + } + return true; + } + + // Starting at |index|, remove |remove| bytes and replace them with the + // contents of |buf|. + void Splice(const DataBuffer& buf, size_t index, size_t remove = 0) { + Splice(buf.data(), buf.len(), index, remove); + } + + void Splice(const uint8_t* ins, size_t ins_len, size_t index, + size_t remove = 0) { + assert(ins); + uint8_t* old_value = data_; + size_t old_len = len_; + + // The amount of stuff remaining from the tail of the old. + size_t tail_len = old_len - std::min(old_len, index + remove); + // The new length: the head of the old, the new, and the tail of the old. + len_ = index + ins_len + tail_len; + data_ = new uint8_t[len_ ? len_ : 1]; + + // The head of the old. + if (old_value) { + Write(0, old_value, std::min(old_len, index)); + } + // Maybe a gap. + if (old_value && index > old_len) { + memset(old_value + index, 0, index - old_len); + } + // The new. + Write(index, ins, ins_len); + // The tail of the old. + if (tail_len > 0) { + Write(index + ins_len, old_value + index + remove, tail_len); + } + + delete[] old_value; + } + + void Append(const DataBuffer& buf) { Splice(buf, len_); } + + const uint8_t* data() const { return data_; } + uint8_t* data() { return data_; } + size_t len() const { return len_; } + bool empty() const { return len_ == 0; } + + private: + uint8_t* data_; + size_t len_; +}; + +static const size_t kMaxBufferPrint = 32; + +inline std::ostream& operator<<(std::ostream& stream, const DataBuffer& buf) { + stream << "[" << buf.len() << "] "; + for (size_t i = 0; i < buf.len(); ++i) { + if (!g_ssl_gtest_verbose && i >= kMaxBufferPrint) { + stream << "..."; + break; + } + stream << std::hex << std::setfill('0') << std::setw(2) + << static_cast<unsigned>(buf.data()[i]); + } + stream << std::dec; + return stream; +} + +inline bool operator==(const DataBuffer& a, const DataBuffer& b) { + return (a.empty() && b.empty()) || + (a.len() == b.len() && 0 == memcmp(a.data(), b.data(), a.len())); +} + +inline bool operator!=(const DataBuffer& a, const DataBuffer& b) { + return !(a == b); +} + +} // namespace nss_test + +#endif diff --git a/cpputil/tls_parser.cc b/cpputil/tls_parser.cc new file mode 100644 index 000000000..e4c06aa91 --- /dev/null +++ b/cpputil/tls_parser.cc @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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_parser.h" + +namespace nss_test { + +bool TlsParser::Read(uint8_t* val) { + if (remaining() < 1) { + return false; + } + *val = *ptr(); + consume(1); + return true; +} + +bool TlsParser::Read(uint32_t* val, size_t size) { + if (size > sizeof(uint32_t)) { + return false; + } + + uint32_t v = 0; + for (size_t i = 0; i < size; ++i) { + uint8_t tmp; + if (!Read(&tmp)) { + return false; + } + + v = (v << 8) | tmp; + } + + *val = v; + return true; +} + +bool TlsParser::Read(DataBuffer* val, size_t len) { + if (remaining() < len) { + return false; + } + + val->Assign(ptr(), len); + consume(len); + return true; +} + +bool TlsParser::ReadVariable(DataBuffer* val, size_t len_size) { + uint32_t len; + if (!Read(&len, len_size)) { + return false; + } + return Read(val, len); +} + +bool TlsParser::Skip(size_t len) { + if (len > remaining()) { + return false; + } + consume(len); + return true; +} + +bool TlsParser::SkipVariable(size_t len_size) { + uint32_t len; + if (!Read(&len, len_size)) { + return false; + } + return Skip(len); +} + +} // namespace nss_test diff --git a/cpputil/tls_parser.h b/cpputil/tls_parser.h new file mode 100644 index 000000000..c9495b583 --- /dev/null +++ b/cpputil/tls_parser.h @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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_parser_h_ +#define tls_parser_h_ + +#include <cstdint> +#include <cstring> +#include <memory> +#if defined(WIN32) || defined(WIN64) +#include <winsock2.h> +#else +#include <arpa/inet.h> +#endif +#include "databuffer.h" + +namespace nss_test { + +const uint8_t kTlsChangeCipherSpecType = 20; +const uint8_t kTlsAlertType = 21; +const uint8_t kTlsHandshakeType = 22; +const uint8_t kTlsApplicationDataType = 23; + +const uint8_t kTlsHandshakeClientHello = 1; +const uint8_t kTlsHandshakeServerHello = 2; +const uint8_t kTlsHandshakeNewSessionTicket = 4; +const uint8_t kTlsHandshakeHelloRetryRequest = 6; +const uint8_t kTlsHandshakeEncryptedExtensions = 8; +const uint8_t kTlsHandshakeCertificate = 11; +const uint8_t kTlsHandshakeServerKeyExchange = 12; +const uint8_t kTlsHandshakeCertificateRequest = 13; +const uint8_t kTlsHandshakeCertificateVerify = 15; +const uint8_t kTlsHandshakeClientKeyExchange = 16; +const uint8_t kTlsHandshakeFinished = 20; + +const uint8_t kTlsAlertWarning = 1; +const uint8_t kTlsAlertFatal = 2; + +const uint8_t kTlsAlertCloseNotify = 0; +const uint8_t kTlsAlertEndOfEarlyData = 1; +const uint8_t kTlsAlertUnexpectedMessage = 10; +const uint8_t kTlsAlertBadRecordMac = 20; +const uint8_t kTlsAlertRecordOverflow = 22; +const uint8_t kTlsAlertHandshakeFailure = 40; +const uint8_t kTlsAlertIllegalParameter = 47; +const uint8_t kTlsAlertDecodeError = 50; +const uint8_t kTlsAlertDecryptError = 51; +const uint8_t kTlsAlertProtocolVersion = 70; +const uint8_t kTlsAlertInappropriateFallback = 86; +const uint8_t kTlsAlertMissingExtension = 109; +const uint8_t kTlsAlertUnsupportedExtension = 110; +const uint8_t kTlsAlertUnrecognizedName = 112; +const uint8_t kTlsAlertNoApplicationProtocol = 120; + +const uint8_t kTlsFakeChangeCipherSpec[] = { + kTlsChangeCipherSpecType, // Type + 0xfe, + 0xff, // Version + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x10, // Fictitious sequence # + 0x00, + 0x01, // Length + 0x01 // Value +}; + +static const uint8_t kTls13PskKe = 0; +static const uint8_t kTls13PskDhKe = 1; +static const uint8_t kTls13PskAuth = 0; +static const uint8_t kTls13PskSignAuth = 1; + +inline bool IsDtls(uint16_t version) { return (version & 0x8000) == 0x8000; } + +inline uint16_t NormalizeTlsVersion(uint16_t version) { + if (version == 0xfeff) { + return 0x0302; // special: DTLS 1.0 == TLS 1.1 + } + if (IsDtls(version)) { + return (version ^ 0xffff) + 0x0201; + } + return version; +} + +inline uint16_t TlsVersionToDtlsVersion(uint16_t version) { + if (version == 0x0302) { + return 0xfeff; + } + if (version == 0x0304) { + return version; + } + return 0xffff - version + 0x0201; +} + +inline size_t WriteVariable(DataBuffer* target, size_t index, + const DataBuffer& buf, size_t len_size) { + index = target->Write(index, static_cast<uint32_t>(buf.len()), len_size); + return target->Write(index, buf.data(), buf.len()); +} + +class TlsParser { + public: + TlsParser(const uint8_t* data, size_t len) : buffer_(data, len), offset_(0) {} + explicit TlsParser(const DataBuffer& buf) : buffer_(buf), offset_(0) {} + + bool Read(uint8_t* val); + // Read an integral type of specified width. + bool Read(uint32_t* val, size_t size); + // Reads len bytes into dest buffer, overwriting it. + bool Read(DataBuffer* dest, size_t len); + // Reads bytes into dest buffer, overwriting it. The number of bytes is + // determined by reading from len_size bytes from the stream first. + bool ReadVariable(DataBuffer* dest, size_t len_size); + + bool Skip(size_t len); + bool SkipVariable(size_t len_size); + + size_t consumed() const { return offset_; } + size_t remaining() const { return buffer_.len() - offset_; } + + private: + void consume(size_t len) { offset_ += len; } + const uint8_t* ptr() const { return buffer_.data() + offset_; } + + DataBuffer buffer_; + size_t offset_; +}; + +} // namespace nss_test + +#endif |