diff options
Diffstat (limited to 'security/nss/cpputil')
-rw-r--r-- | security/nss/cpputil/.clang-format | 4 | ||||
-rw-r--r-- | security/nss/cpputil/Makefile | 49 | ||||
-rw-r--r-- | security/nss/cpputil/README | 11 | ||||
-rw-r--r-- | security/nss/cpputil/config.mk | 15 | ||||
-rw-r--r-- | security/nss/cpputil/cpputil.gyp | 29 | ||||
-rw-r--r-- | security/nss/cpputil/cpputil.h | 12 | ||||
-rw-r--r-- | security/nss/cpputil/databuffer.cc | 127 | ||||
-rw-r--r-- | security/nss/cpputil/databuffer.h | 110 | ||||
-rw-r--r-- | security/nss/cpputil/dummy_io.cc | 225 | ||||
-rw-r--r-- | security/nss/cpputil/dummy_io.h | 62 | ||||
-rw-r--r-- | security/nss/cpputil/dummy_io_fwd.cc | 162 | ||||
-rw-r--r-- | security/nss/cpputil/manifest.mn | 24 | ||||
-rw-r--r-- | security/nss/cpputil/scoped_ptrs.h | 74 | ||||
-rw-r--r-- | security/nss/cpputil/scoped_ptrs_util.h | 39 | ||||
-rw-r--r-- | security/nss/cpputil/tls_parser.cc | 73 | ||||
-rw-r--r-- | security/nss/cpputil/tls_parser.h | 145 |
16 files changed, 1161 insertions, 0 deletions
diff --git a/security/nss/cpputil/.clang-format b/security/nss/cpputil/.clang-format new file mode 100644 index 000000000..06e3c5115 --- /dev/null +++ b/security/nss/cpputil/.clang-format @@ -0,0 +1,4 @@ +--- +Language: Cpp +BasedOnStyle: Google +... diff --git a/security/nss/cpputil/Makefile b/security/nss/cpputil/Makefile new file mode 100644 index 000000000..7adfc6117 --- /dev/null +++ b/security/nss/cpputil/Makefile @@ -0,0 +1,49 @@ +#! gmake +# +# 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/. + +####################################################################### +# (1) Include initial platform-independent assignments (MANDATORY). # +####################################################################### + +include manifest.mn + +####################################################################### +# (2) Include "global" configuration information. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/config.mk + +####################################################################### +# (3) Include "component" configuration information. (OPTIONAL) # +####################################################################### + +ifeq (WINNT,$(OS_ARCH)) +OS_CFLAGS += -EHsc +else +CXXFLAGS += -std=c++0x +endif + +####################################################################### +# (4) Include "local" platform-dependent assignments (OPTIONAL). # +####################################################################### + +include config.mk + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### diff --git a/security/nss/cpputil/README b/security/nss/cpputil/README new file mode 100644 index 000000000..22297dd33 --- /dev/null +++ b/security/nss/cpputil/README @@ -0,0 +1,11 @@ +###################################### +## PLEASE READ BEFORE USING CPPUTIL ## +###################################### + +This is a static library supposed to be mainly used by NSS internally. We use +it for testing, fuzzing, and a few new tools written in C++ that we're +experimenting with. + +You might find it handy to use for your own projects but please be aware that +we will make no promises your application won't break in the future. We will +provide no support if you decide to link against it. diff --git a/security/nss/cpputil/config.mk b/security/nss/cpputil/config.mk new file mode 100644 index 000000000..b8c03de79 --- /dev/null +++ b/security/nss/cpputil/config.mk @@ -0,0 +1,15 @@ +# +# 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/. + +# +# Override TARGETS variable so that only static libraries +# are specifed as dependencies within rules.mk. +# + +TARGETS = $(LIBRARY) +SHARED_LIBRARY = +IMPORT_LIBRARY = +PROGRAM = + diff --git a/security/nss/cpputil/cpputil.gyp b/security/nss/cpputil/cpputil.gyp new file mode 100644 index 000000000..5042acf5c --- /dev/null +++ b/security/nss/cpputil/cpputil.gyp @@ -0,0 +1,29 @@ +# 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/. +{ + 'includes': [ + '../coreconf/config.gypi', + ], + 'targets': [ + { + 'target_name': 'cpputil', + 'type': 'static_library', + 'sources': [ + 'databuffer.cc', + 'dummy_io.cc', + 'dummy_io_fwd.cc', + 'tls_parser.cc', + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_exports', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(DEPTH)/cpputil', + ], + }, + }, + ], +} + diff --git a/security/nss/cpputil/cpputil.h b/security/nss/cpputil/cpputil.h new file mode 100644 index 000000000..017ce9bfc --- /dev/null +++ b/security/nss/cpputil/cpputil.h @@ -0,0 +1,12 @@ +/* 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 cpputil_h__ +#define cpputil_h__ + +static unsigned char* toUcharPtr(const uint8_t* v) { + return const_cast<unsigned char*>(static_cast<const unsigned char*>(v)); +} + +#endif // cpputil_h__ diff --git a/security/nss/cpputil/databuffer.cc b/security/nss/cpputil/databuffer.cc new file mode 100644 index 000000000..d60ebccb3 --- /dev/null +++ b/security/nss/cpputil/databuffer.cc @@ -0,0 +1,127 @@ +/* -*- 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 "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 + +namespace nss_test { + +void DataBuffer::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 DataBuffer::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; +} + +// Write an integer, also performing host-to-network order conversion. +// Returns the offset of the end of the write. +size_t DataBuffer::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); +} + +void DataBuffer::Splice(const uint8_t* ins, size_t ins_len, size_t index, + size_t remove) { + 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; +} + +// This can't use the same trick as Write(), since we might be reading from a +// smaller data source. +bool DataBuffer::Read(size_t index, size_t count, uint64_t* val) const { + assert(count <= sizeof(uint64_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; +} + +bool DataBuffer::Read(size_t index, size_t count, uint32_t* val) const { + assert(count <= sizeof(uint32_t)); + uint64_t tmp; + + if (!Read(index, count, &tmp)) { + return false; + } + *val = tmp & 0xffffffff; + return true; +} + +size_t DataBuffer::logging_limit = 32; + +/* static */ void DataBuffer::SetLogLimit(size_t limit) { + DataBuffer::logging_limit = limit; +} + +} // namespace nss_test diff --git a/security/nss/cpputil/databuffer.h b/security/nss/cpputil/databuffer.h new file mode 100644 index 000000000..58e07efe1 --- /dev/null +++ b/security/nss/cpputil/databuffer.h @@ -0,0 +1,110 @@ +/* -*- 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 <cstring> +#include <iomanip> +#include <iostream> + +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); + + // 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); + 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); + + // 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); + void Append(const DataBuffer& buf) { Splice(buf, len_); } + + bool Read(size_t index, size_t count, uint64_t* val) const; + bool Read(size_t index, size_t count, uint32_t* val) const; + + const uint8_t* data() const { return data_; } + uint8_t* data() { return data_; } + size_t len() const { return len_; } + bool empty() const { return len_ == 0; } + + static void SetLogLimit(size_t limit); + friend std::ostream& operator<<(std::ostream& stream, const DataBuffer& buf); + + private: + static size_t logging_limit; + uint8_t* data_; + size_t len_; +}; + +inline std::ostream& operator<<(std::ostream& stream, const DataBuffer& buf) { + stream << "[" << buf.len() << "] "; + for (size_t i = 0; i < buf.len(); ++i) { + if (i >= DataBuffer::logging_limit) { + 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/security/nss/cpputil/dummy_io.cc b/security/nss/cpputil/dummy_io.cc new file mode 100644 index 000000000..ef45db833 --- /dev/null +++ b/security/nss/cpputil/dummy_io.cc @@ -0,0 +1,225 @@ +/* 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 <assert.h> +#include <iostream> + +#include "prerror.h" +#include "prio.h" + +#include "dummy_io.h" + +#define UNIMPLEMENTED() \ + std::cerr << "Unimplemented: " << __FUNCTION__ << std::endl; \ + assert(false); + +extern const struct PRIOMethods DummyMethodsForward; + +ScopedPRFileDesc DummyIOLayerMethods::CreateFD(PRDescIdentity id, + DummyIOLayerMethods *methods) { + ScopedPRFileDesc fd(PR_CreateIOLayerStub(id, &DummyMethodsForward)); + assert(fd); + if (!fd) { + return nullptr; + } + fd->secret = reinterpret_cast<PRFilePrivate *>(methods); + return fd; +} + +PRStatus DummyIOLayerMethods::Close(PRFileDesc *f) { + f->secret = nullptr; + f->dtor(f); + return PR_SUCCESS; +} + +int32_t DummyIOLayerMethods::Read(PRFileDesc *f, void *buf, int32_t length) { + UNIMPLEMENTED(); + return -1; +} + +int32_t DummyIOLayerMethods::Write(PRFileDesc *f, const void *buf, + int32_t length) { + UNIMPLEMENTED(); + return -1; +} + +int32_t DummyIOLayerMethods::Available(PRFileDesc *f) { + UNIMPLEMENTED(); + return -1; +} + +int64_t DummyIOLayerMethods::Available64(PRFileDesc *f) { + UNIMPLEMENTED(); + return -1; +} + +PRStatus DummyIOLayerMethods::Sync(PRFileDesc *f) { + UNIMPLEMENTED(); + return PR_FAILURE; +} + +int32_t DummyIOLayerMethods::Seek(PRFileDesc *f, int32_t offset, + PRSeekWhence how) { + UNIMPLEMENTED(); + return -1; +} + +int64_t DummyIOLayerMethods::Seek64(PRFileDesc *f, int64_t offset, + PRSeekWhence how) { + UNIMPLEMENTED(); + return -1; +} + +PRStatus DummyIOLayerMethods::FileInfo(PRFileDesc *f, PRFileInfo *info) { + UNIMPLEMENTED(); + return PR_FAILURE; +} + +PRStatus DummyIOLayerMethods::FileInfo64(PRFileDesc *f, PRFileInfo64 *info) { + UNIMPLEMENTED(); + return PR_FAILURE; +} + +int32_t DummyIOLayerMethods::Writev(PRFileDesc *f, const PRIOVec *iov, + int32_t iov_size, PRIntervalTime to) { + UNIMPLEMENTED(); + return -1; +} + +PRStatus DummyIOLayerMethods::Connect(PRFileDesc *f, const PRNetAddr *addr, + PRIntervalTime to) { + UNIMPLEMENTED(); + return PR_FAILURE; +} + +PRFileDesc *DummyIOLayerMethods::Accept(PRFileDesc *sd, PRNetAddr *addr, + PRIntervalTime to) { + UNIMPLEMENTED(); + return nullptr; +} + +PRStatus DummyIOLayerMethods::Bind(PRFileDesc *f, const PRNetAddr *addr) { + UNIMPLEMENTED(); + return PR_FAILURE; +} + +PRStatus DummyIOLayerMethods::Listen(PRFileDesc *f, int32_t depth) { + UNIMPLEMENTED(); + return PR_FAILURE; +} + +PRStatus DummyIOLayerMethods::Shutdown(PRFileDesc *f, int32_t how) { + return PR_SUCCESS; +} + +int32_t DummyIOLayerMethods::Recv(PRFileDesc *f, void *buf, int32_t buflen, + int32_t flags, PRIntervalTime to) { + UNIMPLEMENTED(); + return -1; +} + +// Note: this is always nonblocking and assumes a zero timeout. +int32_t DummyIOLayerMethods::Send(PRFileDesc *f, const void *buf, + int32_t amount, int32_t flags, + PRIntervalTime to) { + return Write(f, buf, amount); +} + +int32_t DummyIOLayerMethods::Recvfrom(PRFileDesc *f, void *buf, int32_t amount, + int32_t flags, PRNetAddr *addr, + PRIntervalTime to) { + UNIMPLEMENTED(); + return -1; +} + +int32_t DummyIOLayerMethods::Sendto(PRFileDesc *f, const void *buf, + int32_t amount, int32_t flags, + const PRNetAddr *addr, PRIntervalTime to) { + UNIMPLEMENTED(); + return -1; +} + +int16_t DummyIOLayerMethods::Poll(PRFileDesc *f, int16_t in_flags, + int16_t *out_flags) { + UNIMPLEMENTED(); + return -1; +} + +int32_t DummyIOLayerMethods::AcceptRead(PRFileDesc *sd, PRFileDesc **nd, + PRNetAddr **raddr, void *buf, + int32_t amount, PRIntervalTime t) { + UNIMPLEMENTED(); + return -1; +} + +int32_t DummyIOLayerMethods::TransmitFile(PRFileDesc *sd, PRFileDesc *f, + const void *headers, int32_t hlen, + PRTransmitFileFlags flags, + PRIntervalTime t) { + UNIMPLEMENTED(); + return -1; +} + +// TODO: Modify to return unique names for each channel +// somehow, as opposed to always the same static address. The current +// implementation messes up the session cache, which is why it's off +// elsewhere +PRStatus DummyIOLayerMethods::Getpeername(PRFileDesc *f, PRNetAddr *addr) { + addr->inet.family = PR_AF_INET; + addr->inet.port = 0; + addr->inet.ip = 0; + + return PR_SUCCESS; +} + +PRStatus DummyIOLayerMethods::Getsockname(PRFileDesc *f, PRNetAddr *addr) { + UNIMPLEMENTED(); + return PR_FAILURE; +} + +PRStatus DummyIOLayerMethods::Getsockoption(PRFileDesc *f, + PRSocketOptionData *opt) { + switch (opt->option) { + case PR_SockOpt_Nonblocking: + opt->value.non_blocking = PR_TRUE; + return PR_SUCCESS; + default: + UNIMPLEMENTED(); + break; + } + + return PR_FAILURE; +} + +PRStatus DummyIOLayerMethods::Setsockoption(PRFileDesc *f, + const PRSocketOptionData *opt) { + switch (opt->option) { + case PR_SockOpt_Nonblocking: + return PR_SUCCESS; + case PR_SockOpt_NoDelay: + return PR_SUCCESS; + default: + UNIMPLEMENTED(); + break; + } + + return PR_FAILURE; +} + +int32_t DummyIOLayerMethods::Sendfile(PRFileDesc *out, PRSendFileData *in, + PRTransmitFileFlags flags, + PRIntervalTime to) { + UNIMPLEMENTED(); + return -1; +} + +PRStatus DummyIOLayerMethods::ConnectContinue(PRFileDesc *f, int16_t flags) { + UNIMPLEMENTED(); + return PR_FAILURE; +} + +int32_t DummyIOLayerMethods::Reserved(PRFileDesc *f) { + UNIMPLEMENTED(); + return -1; +} diff --git a/security/nss/cpputil/dummy_io.h b/security/nss/cpputil/dummy_io.h new file mode 100644 index 000000000..797ac6113 --- /dev/null +++ b/security/nss/cpputil/dummy_io.h @@ -0,0 +1,62 @@ +/* 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 dummy_io_h__ +#define dummy_io_h__ + +#include "prerror.h" +#include "prio.h" + +#include "scoped_ptrs.h" + +class DummyIOLayerMethods { + public: + static ScopedPRFileDesc CreateFD(PRDescIdentity id, + DummyIOLayerMethods *methods); + + virtual PRStatus Close(PRFileDesc *f); + virtual int32_t Read(PRFileDesc *f, void *buf, int32_t length); + virtual int32_t Write(PRFileDesc *f, const void *buf, int32_t length); + virtual int32_t Available(PRFileDesc *f); + virtual int64_t Available64(PRFileDesc *f); + virtual PRStatus Sync(PRFileDesc *f); + virtual int32_t Seek(PRFileDesc *f, int32_t offset, PRSeekWhence how); + virtual int64_t Seek64(PRFileDesc *f, int64_t offset, PRSeekWhence how); + virtual PRStatus FileInfo(PRFileDesc *f, PRFileInfo *info); + virtual PRStatus FileInfo64(PRFileDesc *f, PRFileInfo64 *info); + virtual int32_t Writev(PRFileDesc *f, const PRIOVec *iov, int32_t iov_size, + PRIntervalTime to); + virtual PRStatus Connect(PRFileDesc *f, const PRNetAddr *addr, + PRIntervalTime to); + virtual PRFileDesc *Accept(PRFileDesc *sd, PRNetAddr *addr, + PRIntervalTime to); + virtual PRStatus Bind(PRFileDesc *f, const PRNetAddr *addr); + virtual PRStatus Listen(PRFileDesc *f, int32_t depth); + virtual PRStatus Shutdown(PRFileDesc *f, int32_t how); + virtual int32_t Recv(PRFileDesc *f, void *buf, int32_t buflen, int32_t flags, + PRIntervalTime to); + virtual int32_t Send(PRFileDesc *f, const void *buf, int32_t amount, + int32_t flags, PRIntervalTime to); + virtual int32_t Recvfrom(PRFileDesc *f, void *buf, int32_t amount, + int32_t flags, PRNetAddr *addr, PRIntervalTime to); + virtual int32_t Sendto(PRFileDesc *f, const void *buf, int32_t amount, + int32_t flags, const PRNetAddr *addr, + PRIntervalTime to); + virtual int16_t Poll(PRFileDesc *f, int16_t in_flags, int16_t *out_flags); + virtual int32_t AcceptRead(PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, + void *buf, int32_t amount, PRIntervalTime t); + virtual int32_t TransmitFile(PRFileDesc *sd, PRFileDesc *f, + const void *headers, int32_t hlen, + PRTransmitFileFlags flags, PRIntervalTime t); + virtual PRStatus Getpeername(PRFileDesc *f, PRNetAddr *addr); + virtual PRStatus Getsockname(PRFileDesc *f, PRNetAddr *addr); + virtual PRStatus Getsockoption(PRFileDesc *f, PRSocketOptionData *opt); + virtual PRStatus Setsockoption(PRFileDesc *f, const PRSocketOptionData *opt); + virtual int32_t Sendfile(PRFileDesc *out, PRSendFileData *in, + PRTransmitFileFlags flags, PRIntervalTime to); + virtual PRStatus ConnectContinue(PRFileDesc *f, int16_t flags); + virtual int32_t Reserved(PRFileDesc *f); +}; + +#endif // dummy_io_h__ diff --git a/security/nss/cpputil/dummy_io_fwd.cc b/security/nss/cpputil/dummy_io_fwd.cc new file mode 100644 index 000000000..5e53d9e1b --- /dev/null +++ b/security/nss/cpputil/dummy_io_fwd.cc @@ -0,0 +1,162 @@ +/* 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 "prio.h" + +#include "dummy_io.h" + +static DummyIOLayerMethods *ToMethods(PRFileDesc *f) { + return reinterpret_cast<DummyIOLayerMethods *>(f->secret); +} + +static PRStatus DummyClose(PRFileDesc *f) { return ToMethods(f)->Close(f); } + +static int32_t DummyRead(PRFileDesc *f, void *buf, int32_t length) { + return ToMethods(f)->Read(f, buf, length); +} + +static int32_t DummyWrite(PRFileDesc *f, const void *buf, int32_t length) { + return ToMethods(f)->Write(f, buf, length); +} + +static int32_t DummyAvailable(PRFileDesc *f) { + return ToMethods(f)->Available(f); +} + +static int64_t DummyAvailable64(PRFileDesc *f) { + return ToMethods(f)->Available64(f); +} + +static PRStatus DummySync(PRFileDesc *f) { return ToMethods(f)->Sync(f); } + +static int32_t DummySeek(PRFileDesc *f, int32_t offset, PRSeekWhence how) { + return ToMethods(f)->Seek(f, offset, how); +} + +static int64_t DummySeek64(PRFileDesc *f, int64_t offset, PRSeekWhence how) { + return ToMethods(f)->Seek64(f, offset, how); +} + +static PRStatus DummyFileInfo(PRFileDesc *f, PRFileInfo *info) { + return ToMethods(f)->FileInfo(f, info); +} + +static PRStatus DummyFileInfo64(PRFileDesc *f, PRFileInfo64 *info) { + return ToMethods(f)->FileInfo64(f, info); +} + +static int32_t DummyWritev(PRFileDesc *f, const PRIOVec *iov, int32_t iov_size, + PRIntervalTime to) { + return ToMethods(f)->Writev(f, iov, iov_size, to); +} + +static PRStatus DummyConnect(PRFileDesc *f, const PRNetAddr *addr, + PRIntervalTime to) { + return ToMethods(f)->Connect(f, addr, to); +} + +static PRFileDesc *DummyAccept(PRFileDesc *f, PRNetAddr *addr, + PRIntervalTime to) { + return ToMethods(f)->Accept(f, addr, to); +} + +static PRStatus DummyBind(PRFileDesc *f, const PRNetAddr *addr) { + return ToMethods(f)->Bind(f, addr); +} + +static PRStatus DummyListen(PRFileDesc *f, int32_t depth) { + return ToMethods(f)->Listen(f, depth); +} + +static PRStatus DummyShutdown(PRFileDesc *f, int32_t how) { + return ToMethods(f)->Shutdown(f, how); +} + +static int32_t DummyRecv(PRFileDesc *f, void *buf, int32_t buflen, + int32_t flags, PRIntervalTime to) { + return ToMethods(f)->Recv(f, buf, buflen, flags, to); +} + +static int32_t DummySend(PRFileDesc *f, const void *buf, int32_t amount, + int32_t flags, PRIntervalTime to) { + return ToMethods(f)->Send(f, buf, amount, flags, to); +} + +static int32_t DummyRecvfrom(PRFileDesc *f, void *buf, int32_t amount, + int32_t flags, PRNetAddr *addr, + PRIntervalTime to) { + return ToMethods(f)->Recvfrom(f, buf, amount, flags, addr, to); +} + +static int32_t DummySendto(PRFileDesc *f, const void *buf, int32_t amount, + int32_t flags, const PRNetAddr *addr, + PRIntervalTime to) { + return ToMethods(f)->Sendto(f, buf, amount, flags, addr, to); +} + +static int16_t DummyPoll(PRFileDesc *f, int16_t in_flags, int16_t *out_flags) { + return ToMethods(f)->Poll(f, in_flags, out_flags); +} + +static int32_t DummyAcceptRead(PRFileDesc *f, PRFileDesc **nd, + PRNetAddr **raddr, void *buf, int32_t amount, + PRIntervalTime t) { + return ToMethods(f)->AcceptRead(f, nd, raddr, buf, amount, t); +} + +static int32_t DummyTransmitFile(PRFileDesc *sd, PRFileDesc *f, + const void *headers, int32_t hlen, + PRTransmitFileFlags flags, PRIntervalTime t) { + return ToMethods(f)->TransmitFile(sd, f, headers, hlen, flags, t); +} + +static PRStatus DummyGetpeername(PRFileDesc *f, PRNetAddr *addr) { + return ToMethods(f)->Getpeername(f, addr); +} + +static PRStatus DummyGetsockname(PRFileDesc *f, PRNetAddr *addr) { + return ToMethods(f)->Getsockname(f, addr); +} + +static PRStatus DummyGetsockoption(PRFileDesc *f, PRSocketOptionData *opt) { + return ToMethods(f)->Getsockoption(f, opt); +} + +static PRStatus DummySetsockoption(PRFileDesc *f, + const PRSocketOptionData *opt) { + return ToMethods(f)->Setsockoption(f, opt); +} + +static int32_t DummySendfile(PRFileDesc *f, PRSendFileData *in, + PRTransmitFileFlags flags, PRIntervalTime to) { + return ToMethods(f)->Sendfile(f, in, flags, to); +} + +static PRStatus DummyConnectContinue(PRFileDesc *f, int16_t flags) { + return ToMethods(f)->ConnectContinue(f, flags); +} + +static int32_t DummyReserved(PRFileDesc *f) { + return ToMethods(f)->Reserved(f); +} + +extern const struct PRIOMethods DummyMethodsForward = { + PR_DESC_LAYERED, DummyClose, + DummyRead, DummyWrite, + DummyAvailable, DummyAvailable64, + DummySync, DummySeek, + DummySeek64, DummyFileInfo, + DummyFileInfo64, DummyWritev, + DummyConnect, DummyAccept, + DummyBind, DummyListen, + DummyShutdown, DummyRecv, + DummySend, DummyRecvfrom, + DummySendto, DummyPoll, + DummyAcceptRead, DummyTransmitFile, + DummyGetsockname, DummyGetpeername, + DummyReserved, DummyReserved, + DummyGetsockoption, DummySetsockoption, + DummySendfile, DummyConnectContinue, + DummyReserved, DummyReserved, + DummyReserved, DummyReserved}; diff --git a/security/nss/cpputil/manifest.mn b/security/nss/cpputil/manifest.mn new file mode 100644 index 000000000..b3ccad8b5 --- /dev/null +++ b/security/nss/cpputil/manifest.mn @@ -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/. +CORE_DEPTH = .. +DEPTH = .. + +MODULE = nss +LIBRARY_NAME = cpputil + +ifeq ($(NSS_BUILD_UTIL_ONLY),1) +CPPSRCS = \ + $(NULL) +else +CPPSRCS = \ + databuffer.cc \ + dummy_io.cc \ + dummy_io_fwd.cc \ + tls_parser.cc \ + $(NULL) +endif + +EXPORTS = \ + $(NULL) diff --git a/security/nss/cpputil/scoped_ptrs.h b/security/nss/cpputil/scoped_ptrs.h new file mode 100644 index 000000000..b92b8132b --- /dev/null +++ b/security/nss/cpputil/scoped_ptrs.h @@ -0,0 +1,74 @@ +/* -*- 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 scoped_ptrs_h__ +#define scoped_ptrs_h__ + +#include <memory> +#include "cert.h" +#include "keyhi.h" +#include "pk11pub.h" +#include "pkcs11uri.h" + +struct ScopedDelete { + void operator()(CERTCertificate* cert) { CERT_DestroyCertificate(cert); } + void operator()(CERTCertificateList* list) { + CERT_DestroyCertificateList(list); + } + void operator()(CERTName* name) { CERT_DestroyName(name); } + void operator()(CERTCertList* list) { CERT_DestroyCertList(list); } + void operator()(CERTSubjectPublicKeyInfo* spki) { + SECKEY_DestroySubjectPublicKeyInfo(spki); + } + void operator()(PK11SlotInfo* slot) { PK11_FreeSlot(slot); } + void operator()(PK11SymKey* key) { PK11_FreeSymKey(key); } + void operator()(PRFileDesc* fd) { PR_Close(fd); } + void operator()(SECAlgorithmID* id) { SECOID_DestroyAlgorithmID(id, true); } + void operator()(SECItem* item) { SECITEM_FreeItem(item, true); } + void operator()(SECKEYPublicKey* key) { SECKEY_DestroyPublicKey(key); } + void operator()(SECKEYPrivateKey* key) { SECKEY_DestroyPrivateKey(key); } + void operator()(SECKEYPrivateKeyList* list) { + SECKEY_DestroyPrivateKeyList(list); + } + void operator()(PK11URI* uri) { PK11URI_DestroyURI(uri); } + void operator()(PLArenaPool* arena) { PORT_FreeArena(arena, PR_FALSE); } + void operator()(PK11Context* context) { PK11_DestroyContext(context, true); } + void operator()(PK11GenericObject* obj) { PK11_DestroyGenericObject(obj); } +}; + +template <class T> +struct ScopedMaybeDelete { + void operator()(T* ptr) { + if (ptr) { + ScopedDelete del; + del(ptr); + } + } +}; + +#define SCOPED(x) typedef std::unique_ptr<x, ScopedMaybeDelete<x> > Scoped##x + +SCOPED(CERTCertificate); +SCOPED(CERTCertificateList); +SCOPED(CERTCertList); +SCOPED(CERTName); +SCOPED(CERTSubjectPublicKeyInfo); +SCOPED(PK11SlotInfo); +SCOPED(PK11SymKey); +SCOPED(PRFileDesc); +SCOPED(SECAlgorithmID); +SCOPED(SECItem); +SCOPED(SECKEYPublicKey); +SCOPED(SECKEYPrivateKey); +SCOPED(SECKEYPrivateKeyList); +SCOPED(PK11URI); +SCOPED(PLArenaPool); +SCOPED(PK11Context); +SCOPED(PK11GenericObject); + +#undef SCOPED + +#endif // scoped_ptrs_h__ diff --git a/security/nss/cpputil/scoped_ptrs_util.h b/security/nss/cpputil/scoped_ptrs_util.h new file mode 100644 index 000000000..2dbf34e1d --- /dev/null +++ b/security/nss/cpputil/scoped_ptrs_util.h @@ -0,0 +1,39 @@ +/* -*- 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 scoped_ptrs_util_h__ +#define scoped_ptrs_util_h__ + +#include <memory> +#include "pkcs11uri.h" +#include "secoid.h" + +struct ScopedDelete { + void operator()(SECAlgorithmID* id) { SECOID_DestroyAlgorithmID(id, true); } + void operator()(SECItem* item) { SECITEM_FreeItem(item, true); } + void operator()(PK11URI* uri) { PK11URI_DestroyURI(uri); } + void operator()(PLArenaPool* arena) { PORT_FreeArena(arena, PR_FALSE); } +}; + +template <class T> +struct ScopedMaybeDelete { + void operator()(T* ptr) { + if (ptr) { + ScopedDelete del; + del(ptr); + } + } +}; + +#define SCOPED(x) typedef std::unique_ptr<x, ScopedMaybeDelete<x> > Scoped##x + +SCOPED(SECAlgorithmID); +SCOPED(SECItem); +SCOPED(PK11URI); + +#undef SCOPED + +#endif // scoped_ptrs_util_h__ diff --git a/security/nss/cpputil/tls_parser.cc b/security/nss/cpputil/tls_parser.cc new file mode 100644 index 000000000..e4c06aa91 --- /dev/null +++ b/security/nss/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/security/nss/cpputil/tls_parser.h b/security/nss/cpputil/tls_parser.h new file mode 100644 index 000000000..a5f5771d5 --- /dev/null +++ b/security/nss/cpputil/tls_parser.h @@ -0,0 +1,145 @@ +/* -*- 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" +#include "sslt.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 kTlsAltHandshakeType = 24; +const uint8_t kTlsAckType = 25; + +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 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 kTlsAlertInternalError = 80; +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 std::ostream& operator<<(std::ostream& os, SSLProtocolVariant v) { + return os << ((v == ssl_variant_stream) ? "TLS" : "DTLS"); +} + +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 |