From 0f4d18c4ed3c2e57e35a334b72a9cfe5a03d185a Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 2 Apr 2021 18:12:30 -0400 Subject: [PATCH] Add serialize and deserialize functions to TrustStore. --- core/LZ4.cpp | 2 - core/LZ4.hpp | 3 ++ core/Node.cpp | 42 +++++++++---------- core/Node.hpp | 6 +-- core/TrustStore.cpp | 100 ++++++++++++++++++++++++++++++++++++++++++++ core/TrustStore.hpp | 18 ++++++++ 6 files changed, 145 insertions(+), 26 deletions(-) diff --git a/core/LZ4.cpp b/core/LZ4.cpp index 66407c645..8d33f8646 100644 --- a/core/LZ4.cpp +++ b/core/LZ4.cpp @@ -60,8 +60,6 @@ namespace { // #define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */ // #define LZ4_VERSION_RELEASE 5 /* for tweaks, bug-fixes, or development */ #define LZ4_MEMORY_USAGE 14 -#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ -#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */ diff --git a/core/LZ4.hpp b/core/LZ4.hpp index f80270966..e2dfad89d 100644 --- a/core/LZ4.hpp +++ b/core/LZ4.hpp @@ -18,6 +18,9 @@ namespace ZeroTier { +#define LZ4_MAX_INPUT_SIZE 0x7E000000 +#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) + int LZ4_compress_fast(const char *source,char *dest,int inputSize,int maxOutputSize,int acceleration = 1) noexcept; int LZ4_decompress_safe(const char *source,char *dest,int compressedSize,int maxDecompressedSize) noexcept; diff --git a/core/Node.cpp b/core/Node.cpp index 6ec96d045..1cff6dfb7 100644 --- a/core/Node.cpp +++ b/core/Node.cpp @@ -143,10 +143,10 @@ Node::~Node() { ZT_SPEW("Node shutting down (in destructor)."); - m_networks_l.lock(); + m_allNetworks_l.lock(); RR->networks->clear(); - m_networks.clear(); - m_networks_l.unlock(); + m_allNetworks.clear(); + m_allNetworks_l.unlock(); delete reinterpret_cast<_NodeObjects *>(m_objects); @@ -159,10 +159,10 @@ Node::~Node() void Node::shutdown(void *tPtr) { - m_networks_l.lock(); + m_allNetworks_l.lock(); RR->networks->clear(); - m_networks.clear(); - m_networks_l.unlock(); + m_allNetworks.clear(); + m_allNetworks_l.unlock(); postEvent(tPtr, ZT_EVENT_DOWN); if (RR->topology) RR->topology->saveAll(tPtr); @@ -201,8 +201,8 @@ ZT_ResultCode Node::processBackgroundTasks( if ((now - m_lastNetworkHousekeepingRun) >= ZT_NETWORK_HOUSEKEEPING_PERIOD) { m_lastHousekeepingRun = now; ZT_SPEW("running networking housekeeping..."); - Mutex::Lock l(m_networks_l); - for (Vector< SharedPtr< Network > >::const_iterator i(m_networks.begin()); i != m_networks.end(); ++i) { + Mutex::Lock l(m_allNetworks_l); + for (Vector< SharedPtr< Network > >::const_iterator i(m_allNetworks.begin()); i != m_allNetworks.end(); ++i) { (*i)->doPeriodicTasks(tPtr, now); } } @@ -235,7 +235,7 @@ ZT_ResultCode Node::join( void *uptr, void *tptr) { - Mutex::Lock l(m_networks_l); + Mutex::Lock l(m_allNetworks_l); Fingerprint fp; if (controllerFingerprint) { @@ -245,12 +245,12 @@ ZT_ResultCode Node::join( ZT_SPEW("joining network %.16llx", nwid); } - for (Vector< SharedPtr< Network > >::iterator n(m_networks.begin()); n != m_networks.end(); ++n) { + for (Vector< SharedPtr< Network > >::iterator n(m_allNetworks.begin()); n != m_allNetworks.end(); ++n) { if ((*n)->id() == nwid) return ZT_RESULT_OK; } SharedPtr< Network > network(new Network(RR, tptr, nwid, fp, uptr, nullptr)); - m_networks.push_back(network); + m_allNetworks.push_back(network); RR->networks->set(nwid, network); return ZT_RESULT_OK; @@ -261,17 +261,17 @@ ZT_ResultCode Node::leave( void **uptr, void *tptr) { - Mutex::Lock l(m_networks_l); + Mutex::Lock l(m_allNetworks_l); ZT_SPEW("leaving network %.16llx", nwid); ZT_VirtualNetworkConfig ctmp; SharedPtr< Network > network; RR->networks->erase(nwid); - for (Vector< SharedPtr< Network > >::iterator n(m_networks.begin()); n != m_networks.end(); ++n) { + for (Vector< SharedPtr< Network > >::iterator n(m_allNetworks.begin()); n != m_allNetworks.end(); ++n) { if ((*n)->id() == nwid) { network.move(*n); - m_networks.erase(n); + m_allNetworks.erase(n); break; } } @@ -452,9 +452,9 @@ ZT_VirtualNetworkConfig *Node::networkConfig(uint64_t nwid) const ZT_VirtualNetworkList *Node::networks() const { - Mutex::Lock l(m_networks_l); + Mutex::Lock l(m_allNetworks_l); - char *const buf = (char *)::malloc(sizeof(ZT_VirtualNetworkList) + (sizeof(ZT_VirtualNetworkConfig) * m_networks.size())); + char *const buf = (char *)::malloc(sizeof(ZT_VirtualNetworkList) + (sizeof(ZT_VirtualNetworkConfig) * m_allNetworks.size())); if (!buf) return nullptr; ZT_VirtualNetworkList *nl = (ZT_VirtualNetworkList *)buf; @@ -462,7 +462,7 @@ ZT_VirtualNetworkList *Node::networks() const nl->networks = (ZT_VirtualNetworkConfig *)(buf + sizeof(ZT_VirtualNetworkList)); nl->networkCount = 0; - for (Vector< SharedPtr< Network > >::const_iterator i(m_networks.begin()); i != m_networks.end(); ++i) + for (Vector< SharedPtr< Network > >::const_iterator i(m_allNetworks.begin()); i != m_allNetworks.end(); ++i) (*i)->externalConfig(&(nl->networks[nl->networkCount++])); return nl; @@ -474,9 +474,9 @@ void Node::setNetworkUserPtr( { SharedPtr< Network > nw(RR->networks->get(nwid)); if (nw) { - m_networks_l.lock(); // ensure no concurrent modification of user PTR in network + m_allNetworks_l.lock(); // ensure no concurrent modification of user PTR in network *(nw->userPtr()) = ptr; - m_networks_l.unlock(); + m_allNetworks_l.unlock(); } } @@ -639,8 +639,8 @@ void Node::setController(void *networkControllerInstance) bool Node::shouldUsePathForZeroTierTraffic(void *tPtr, const Identity &id, const int64_t localSocket, const InetAddress &remoteAddress) { { - Mutex::Lock l(m_networks_l); - for (Vector< SharedPtr< Network > >::iterator i(m_networks.begin()); i != m_networks.end(); ++i) { + Mutex::Lock l(m_allNetworks_l); + for (Vector< SharedPtr< Network > >::iterator i(m_allNetworks.begin()); i != m_allNetworks.end(); ++i) { for (unsigned int k = 0, j = (*i)->config().staticIpCount; k < j; ++k) { if ((*i)->config().staticIps[k].containsAddress(remoteAddress)) return false; diff --git a/core/Node.hpp b/core/Node.hpp index 3891a5c59..828cec94c 100644 --- a/core/Node.hpp +++ b/core/Node.hpp @@ -218,9 +218,9 @@ private: // Pointer to a struct defined in Node that holds instances of core objects. void *m_objects; - // This isn't the primary network lookup but holds a vector of networks for rapid iteration through all of them. - Vector< SharedPtr< Network > > m_networks; - Mutex m_networks_l; + // This stores networks for rapid iteration, while RR->networks is the primary lookup. + Vector< SharedPtr< Network > > m_allNetworks; + Mutex m_allNetworks_l; // These are local interface addresses that have been configured via the API // and can be pushed to other nodes. diff --git a/core/TrustStore.cpp b/core/TrustStore.cpp index d33616a5d..e090399d7 100644 --- a/core/TrustStore.cpp +++ b/core/TrustStore.cpp @@ -12,6 +12,7 @@ /****/ #include "TrustStore.hpp" +#include "LZ4.hpp" namespace ZeroTier { @@ -223,4 +224,103 @@ void TrustStore::update(const int64_t clock, Vector< SharedPtr< Entry > > *const } } +Vector< uint8_t > TrustStore::save() const +{ + Vector< uint8_t> comp; + + int compSize; + { + Vector< uint8_t > b; + b.reserve(65536); + + RWMutex::RLock l(m_lock); + + b.push_back(0); // version byte + + for (Map< H384, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end(); ++c) { + const Vector< uint8_t > cdata(c->second->certificate().encode()); + if (!cdata.empty()) { + const uint32_t localTrust = (uint32_t)c->second->localTrust(); + b.push_back((uint8_t)(localTrust >> 24U)); + b.push_back((uint8_t)(localTrust >> 16U)); + b.push_back((uint8_t)(localTrust >> 8U)); + b.push_back((uint8_t)localTrust); + const uint32_t size = (uint32_t)cdata.size(); + b.push_back((uint8_t)(size >> 24U)); + b.push_back((uint8_t)(size >> 16U)); + b.push_back((uint8_t)(size >> 8U)); + b.push_back((uint8_t)size); + b.insert(b.end(), cdata.begin(), cdata.end()); + } + } + + comp.resize((unsigned long)LZ4_COMPRESSBOUND(b.size()) + 8); + compSize = LZ4_compress_fast(reinterpret_cast(b.data()), reinterpret_cast(comp.data() + 8), (int)b.size(), (int)(comp.size() - 8)); + if (unlikely(compSize <= 0)) // shouldn't be possible + return Vector< uint8_t >(); + + const uint32_t uncompSize = (uint32_t)b.size(); + const uint32_t cksum = Utils::fnv1a32(b.data(), (unsigned int)uncompSize); + comp[0] = (uint8_t)(uncompSize >> 24); + comp[1] = (uint8_t)(uncompSize >> 16); + comp[2] = (uint8_t)(uncompSize >> 8); + comp[3] = (uint8_t)uncompSize; + comp[4] = (uint8_t)(cksum >> 24); + comp[5] = (uint8_t)(cksum >> 16); + comp[6] = (uint8_t)(cksum >> 8); + comp[7] = (uint8_t)cksum; + compSize += 8; + } + + comp.resize((unsigned long)compSize); + comp.shrink_to_fit(); + + return comp; +} + +int TrustStore::load(const Vector< uint8_t > &data) +{ + if (data.size() < 8) + return -1; + + const unsigned long uncompSize = Utils::loadBigEndian(data.data()); + if (uncompSize > (data.size() * 256)) // sanity check + return -1; + if (uncompSize < 1) // no room for at least version and count + return -1; + + Vector< uint8_t > uncomp; + uncomp.resize(uncompSize); + + if (LZ4_decompress_safe(reinterpret_cast(data.data() + 8), reinterpret_cast(uncomp.data()), (int)(data.size() - 8), (int)uncompSize) != (int)uncompSize) + return -1; + const uint8_t *b = uncomp.data(); + if (Utils::fnv1a32(b, (unsigned int)uncompSize) != Utils::loadBigEndian(data.data() + 4)) + return -1; + const uint8_t *const eof = b + uncompSize; + + if (*(b++) != 0) // unrecognized version + return -1; + + int readCount = 0; + for(;;) { + if ((b + 8) > eof) + break; + const uint32_t localTrust = Utils::loadBigEndian(b); + b += 4; + const uint32_t cdataSize = Utils::loadBigEndian(b); + b += 4; + + if ((b + cdataSize) > eof) + break; + Certificate c; + if (c.decode(b, (unsigned int)cdataSize)) { + this->add(c, (unsigned int)localTrust); + ++readCount; + } + b += cdataSize; + } + return readCount; +} + } // namespace ZeroTier diff --git a/core/TrustStore.hpp b/core/TrustStore.hpp index c2f40624f..a4977d5ec 100644 --- a/core/TrustStore.hpp +++ b/core/TrustStore.hpp @@ -164,6 +164,24 @@ public: */ void update(int64_t clock, Vector< SharedPtr< Entry > > *purge); + /** + * Create a compressed binary version of certificates and their local trust + * + * @return Binary compressed certificates and local trust info + */ + Vector< uint8_t > save() const; + + /** + * Decode a saved trust store + * + * Decoded certificates are added to the add queue, so update() must be + * called after this to actually apply them. + * + * @param data Data to decode + * @return Number of certificates or -1 if input is invalid + */ + int load(const Vector< uint8_t > &data); + private: Map< H384, SharedPtr< Entry > > m_bySerial; // all certificates Map< Vector< uint8_t >, SharedPtr< Entry > > m_bySubjectUniqueId; // non-rejected certificates only