Add serialize and deserialize functions to TrustStore.

This commit is contained in:
Adam Ierymenko 2021-04-02 18:12:30 -04:00
parent 38fcefceee
commit 0f4d18c4ed
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
6 changed files with 145 additions and 26 deletions

View file

@ -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) */

View file

@ -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;

View file

@ -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;

View file

@ -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.

View file

@ -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<const char *>(b.data()), reinterpret_cast<char *>(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<uint32_t>(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<const char *>(data.data() + 8), reinterpret_cast<char *>(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<uint32_t>(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<uint32_t>(b);
b += 4;
const uint32_t cdataSize = Utils::loadBigEndian<uint32_t>(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

View file

@ -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