mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-05 20:13:44 +02:00
Certificate work, and add an Endpoint attributes field to Locator for future use.
This commit is contained in:
parent
85ef9535d5
commit
189dea7c96
10 changed files with 289 additions and 105 deletions
|
@ -28,21 +28,35 @@ Locator::Locator(const char *const str) noexcept
|
|||
}
|
||||
}
|
||||
|
||||
bool Locator::add(const Endpoint &ep)
|
||||
bool Locator::add(const Endpoint &ep, const EndpointAttributes &a)
|
||||
{
|
||||
for (Vector< std::pair< Endpoint, EndpointAttributes > >::iterator i(m_endpoints.begin());i!=m_endpoints.end();++i) {
|
||||
if (i->first == ep) {
|
||||
i->second = a;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (m_endpoints.size() < ZT_LOCATOR_MAX_ENDPOINTS) {
|
||||
if (std::find(m_endpoints.begin(), m_endpoints.end(), ep) == m_endpoints.end())
|
||||
m_endpoints.push_back(ep);
|
||||
m_endpoints.push_back(std::pair<Endpoint, EndpointAttributes>(ep, a));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct p_SortByEndpoint
|
||||
{
|
||||
// There can't be more than one of the same endpoint, so only need to sort
|
||||
// by endpoint.
|
||||
ZT_INLINE bool operator()(const std::pair< Endpoint, Locator::EndpointAttributes > &a,const std::pair< Endpoint, Locator::EndpointAttributes > &b) const noexcept
|
||||
{ return a.first < b.first; }
|
||||
};
|
||||
|
||||
bool Locator::sign(const int64_t ts, const Identity &id) noexcept
|
||||
{
|
||||
m_ts = ts;
|
||||
m_signer = id.fingerprint();
|
||||
std::sort(m_endpoints.begin(), m_endpoints.end());
|
||||
|
||||
std::sort(m_endpoints.begin(), m_endpoints.end(), p_SortByEndpoint());
|
||||
|
||||
uint8_t signdata[ZT_LOCATOR_MARSHAL_SIZE_MAX];
|
||||
const unsigned int signlen = marshal(signdata, true);
|
||||
|
@ -102,11 +116,15 @@ int Locator::marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX], const bool exclu
|
|||
|
||||
Utils::storeBigEndian<uint16_t>(data + p, (uint16_t) m_endpoints.size());
|
||||
p += 2;
|
||||
for (Vector<Endpoint>::const_iterator e(m_endpoints.begin());e != m_endpoints.end();++e) {
|
||||
l = e->marshal(data + p);
|
||||
for (Vector< std::pair< Endpoint, EndpointAttributes> >::const_iterator e(m_endpoints.begin());e != m_endpoints.end();++e) {
|
||||
l = e->first.marshal(data + p);
|
||||
if (l <= 0)
|
||||
return -1;
|
||||
p += l;
|
||||
|
||||
l = (int)e->second.data[0] + 1;
|
||||
Utils::copy(data + p, e->second.data, (unsigned int)l);
|
||||
p += l;
|
||||
}
|
||||
|
||||
Utils::storeMachineEndian< uint16_t >(data + p, 0); // length of meta-data, currently always 0
|
||||
|
@ -143,10 +161,14 @@ int Locator::unmarshal(const uint8_t *data, const int len) noexcept
|
|||
m_endpoints.resize(endpointCount);
|
||||
m_endpoints.shrink_to_fit();
|
||||
for (unsigned int i = 0;i < endpointCount;++i) {
|
||||
l = m_endpoints[i].unmarshal(data + p, len - p);
|
||||
l = m_endpoints[i].first.unmarshal(data + p, len - p);
|
||||
if (l <= 0)
|
||||
return -1;
|
||||
p += l;
|
||||
|
||||
l = (int)data[p] + 1;
|
||||
Utils::copy(m_endpoints[i].second.data, data + p, (unsigned int)l);
|
||||
p += l;
|
||||
}
|
||||
|
||||
if (unlikely((p + 2) > len))
|
||||
|
@ -175,15 +197,17 @@ extern "C" {
|
|||
ZT_Locator *ZT_Locator_create(
|
||||
int64_t ts,
|
||||
const ZT_Endpoint *endpoints,
|
||||
const ZT_EndpointAttributes *endpointAttributes, // TODO: not used yet
|
||||
unsigned int endpointCount,
|
||||
const ZT_Identity *signer)
|
||||
{
|
||||
try {
|
||||
if ((ts <= 0) || (!endpoints) || (endpointCount == 0) || (!signer))
|
||||
return nullptr;
|
||||
ZeroTier::Locator::EndpointAttributes emptyAttributes;
|
||||
ZeroTier::Locator *loc = new ZeroTier::Locator();
|
||||
for (unsigned int i = 0;i < endpointCount;++i)
|
||||
loc->add(reinterpret_cast<const ZeroTier::Endpoint *>(endpoints)[i]);
|
||||
loc->add(reinterpret_cast<const ZeroTier::Endpoint *>(endpoints)[i], emptyAttributes);
|
||||
if (!loc->sign(ts, *reinterpret_cast<const ZeroTier::Identity *>(signer))) {
|
||||
delete loc;
|
||||
return nullptr;
|
||||
|
|
|
@ -21,10 +21,26 @@
|
|||
#include "SharedPtr.hpp"
|
||||
#include "FCV.hpp"
|
||||
#include "Containers.hpp"
|
||||
#include "Dictionary.hpp"
|
||||
|
||||
#define ZT_LOCATOR_MAX_ENDPOINTS 8
|
||||
#define ZT_LOCATOR_MARSHAL_SIZE_MAX (8 + ZT_FINGERPRINT_MARSHAL_SIZE + 2 + (ZT_LOCATOR_MAX_ENDPOINTS * ZT_ENDPOINT_MARSHAL_SIZE_MAX) + 2 + 2 + ZT_SIGNATURE_BUFFER_SIZE)
|
||||
#define ZT_LOCATOR_STRING_SIZE_MAX 4096
|
||||
/**
|
||||
* Maximum size of endpoint attributes dictionary plus one byte for size.
|
||||
*
|
||||
* This cannot be (easily) changed.
|
||||
*/
|
||||
#define ZT_LOCATOR_MAX_ENDPOINT_ATTRIBUTES_SIZE 256
|
||||
|
||||
/**
|
||||
* Maximum number of endpoints, which can be increased.
|
||||
*/
|
||||
#define ZT_LOCATOR_MAX_ENDPOINTS 16
|
||||
|
||||
#define ZT_LOCATOR_MARSHAL_SIZE_MAX (8 + ZT_FINGERPRINT_MARSHAL_SIZE + 2 + (ZT_LOCATOR_MAX_ENDPOINTS * (ZT_ENDPOINT_MARSHAL_SIZE_MAX + ZT_LOCATOR_MAX_ENDPOINT_ATTRIBUTES_SIZE)) + 2 + 2 + ZT_SIGNATURE_BUFFER_SIZE)
|
||||
|
||||
/**
|
||||
* Maximum size of a string format Locator (this is way larger than needed)
|
||||
*/
|
||||
#define ZT_LOCATOR_STRING_SIZE_MAX 16384
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
|
@ -36,17 +52,56 @@ namespace ZeroTier {
|
|||
*/
|
||||
class Locator
|
||||
{
|
||||
friend class SharedPtr<Locator>;
|
||||
friend class SharedPtr<const Locator>;
|
||||
friend class SharedPtr< Locator >;
|
||||
|
||||
friend class SharedPtr< const Locator >;
|
||||
|
||||
public:
|
||||
ZT_INLINE Locator() noexcept :
|
||||
/**
|
||||
* Attributes of an endpoint in this locator
|
||||
*
|
||||
* This is specified for future use, but there are currently no attributes
|
||||
* defined. A Dictionary is used for serialization for extensibility.
|
||||
*/
|
||||
struct EndpointAttributes
|
||||
{
|
||||
/**
|
||||
* Raw attributes data in the form of a dictionary prefixed by its size.
|
||||
*
|
||||
* The maximum size of attributes is 256, which is more than enough for
|
||||
* tiny things like bandwidth and priority.
|
||||
*/
|
||||
uint8_t data[ZT_LOCATOR_MAX_ENDPOINT_ATTRIBUTES_SIZE];
|
||||
|
||||
ZT_INLINE EndpointAttributes() noexcept
|
||||
{ Utils::zero< ZT_LOCATOR_MAX_ENDPOINT_ATTRIBUTES_SIZE >(data); }
|
||||
|
||||
ZT_INLINE bool operator==(const EndpointAttributes &a) const noexcept
|
||||
{ return ((data[0] == a.data[0]) && (memcmp(data, a.data, data[0]) == 0)); }
|
||||
|
||||
ZT_INLINE bool operator<(const EndpointAttributes &a) const noexcept
|
||||
{ return ((data[0] < a.data[0]) || ((data[0] == a.data[0]) && (memcmp(data, a.data, data[0]) < 0))); }
|
||||
|
||||
ZT_INLINE bool operator!=(const EndpointAttributes &a) const noexcept
|
||||
{ return !(*this == a); }
|
||||
|
||||
ZT_INLINE bool operator>(const EndpointAttributes &a) const noexcept
|
||||
{ return (a < *this); }
|
||||
|
||||
ZT_INLINE bool operator<=(const EndpointAttributes &a) const noexcept
|
||||
{ return !(a < *this); }
|
||||
|
||||
ZT_INLINE bool operator>=(const EndpointAttributes &a) const noexcept
|
||||
{ return !(*this < a); }
|
||||
};
|
||||
|
||||
ZT_INLINE Locator() noexcept:
|
||||
m_ts(0)
|
||||
{}
|
||||
|
||||
explicit Locator(const char *const str) noexcept;
|
||||
|
||||
ZT_INLINE Locator(const Locator &loc) noexcept :
|
||||
ZT_INLINE Locator(const Locator &loc) noexcept:
|
||||
m_ts(loc.m_ts),
|
||||
m_signer(loc.m_signer),
|
||||
m_endpoints(loc.m_endpoints),
|
||||
|
@ -69,13 +124,13 @@ public:
|
|||
/**
|
||||
* @return Endpoints specified in locator
|
||||
*/
|
||||
ZT_INLINE const Vector<Endpoint> &endpoints() const noexcept
|
||||
ZT_INLINE const Vector< std::pair< Endpoint, EndpointAttributes > > &endpoints() const noexcept
|
||||
{ return m_endpoints; }
|
||||
|
||||
/**
|
||||
* @return Signature data
|
||||
*/
|
||||
ZT_INLINE const FCV<uint8_t, ZT_SIGNATURE_BUFFER_SIZE> &signature() const noexcept
|
||||
ZT_INLINE const FCV< uint8_t, ZT_SIGNATURE_BUFFER_SIZE > &signature() const noexcept
|
||||
{ return m_signature; }
|
||||
|
||||
/**
|
||||
|
@ -85,9 +140,10 @@ public:
|
|||
* care not to add duplicates.
|
||||
*
|
||||
* @param ep Endpoint to add
|
||||
* @param a Endpoint attributes
|
||||
* @return True if endpoint was added (or already present), false if locator is full
|
||||
*/
|
||||
bool add(const Endpoint &ep);
|
||||
bool add(const Endpoint &ep, const EndpointAttributes &a);
|
||||
|
||||
/**
|
||||
* Sign this locator
|
||||
|
@ -117,7 +173,10 @@ public:
|
|||
char *toString(char s[ZT_LOCATOR_STRING_SIZE_MAX]) const noexcept;
|
||||
|
||||
ZT_INLINE String toString() const
|
||||
{ char tmp[ZT_LOCATOR_STRING_SIZE_MAX]; return String(toString(tmp)); }
|
||||
{
|
||||
char tmp[ZT_LOCATOR_STRING_SIZE_MAX];
|
||||
return String(toString(tmp));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a string format locator
|
||||
|
@ -130,8 +189,11 @@ public:
|
|||
explicit ZT_INLINE operator bool() const noexcept
|
||||
{ return m_ts > 0; }
|
||||
|
||||
static constexpr int marshalSizeMax() noexcept { return ZT_LOCATOR_MARSHAL_SIZE_MAX; }
|
||||
static constexpr int marshalSizeMax() noexcept
|
||||
{ return ZT_LOCATOR_MARSHAL_SIZE_MAX; }
|
||||
|
||||
int marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX], bool excludeSignature = false) const noexcept;
|
||||
|
||||
int unmarshal(const uint8_t *data, int len) noexcept;
|
||||
|
||||
ZT_INLINE bool operator==(const Locator &l) const noexcept
|
||||
|
@ -142,15 +204,16 @@ public:
|
|||
(m_endpoints == l.m_endpoints) &&
|
||||
(m_signature == l.m_signature));
|
||||
}
|
||||
|
||||
ZT_INLINE bool operator!=(const Locator &l) const noexcept
|
||||
{ return !(*this == l); }
|
||||
|
||||
private:
|
||||
int64_t m_ts;
|
||||
Fingerprint m_signer;
|
||||
Vector<Endpoint> m_endpoints;
|
||||
FCV<uint8_t, ZT_SIGNATURE_BUFFER_SIZE> m_signature;
|
||||
std::atomic<int> __refCount;
|
||||
Vector< std::pair< Endpoint, EndpointAttributes > > m_endpoints;
|
||||
FCV< uint8_t, ZT_SIGNATURE_BUFFER_SIZE > m_signature;
|
||||
std::atomic< int > __refCount;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
|
|
@ -564,6 +564,26 @@ int Node::tryPeer(
|
|||
return 0;
|
||||
}
|
||||
|
||||
ZT_CertificateError Node::addCertificate(
|
||||
void *tptr,
|
||||
int64_t now,
|
||||
unsigned int localTrust,
|
||||
const ZT_Certificate *cert,
|
||||
const void *certData,
|
||||
unsigned int certSize)
|
||||
{
|
||||
Certificate c;
|
||||
if (cert) {
|
||||
c = *cert;
|
||||
} else {
|
||||
if ((!certData) || (!certSize))
|
||||
return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
|
||||
if (!c.decode(certData, certSize))
|
||||
return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
return RR->topology->addCertificate(tptr, c, now, localTrust, true, true, true);
|
||||
}
|
||||
|
||||
int Node::sendUserMessage(
|
||||
void *tptr,
|
||||
uint64_t dest,
|
||||
|
@ -1030,6 +1050,22 @@ int ZT_Node_tryPeer(
|
|||
}
|
||||
}
|
||||
|
||||
enum ZT_CertificateError ZT_Node_addCertificate(
|
||||
ZT_Node *node,
|
||||
void *tptr,
|
||||
int64_t now,
|
||||
unsigned int localTrust,
|
||||
const ZT_Certificate *cert,
|
||||
const void *certData,
|
||||
unsigned int certSize)
|
||||
{
|
||||
try {
|
||||
return reinterpret_cast<ZeroTier::Node *>(node)->addCertificate(tptr, now, localTrust, cert, certData, certSize);
|
||||
} catch (...) {
|
||||
return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
}
|
||||
|
||||
void ZT_Node_setNetworkUserPtr(ZT_Node *node, uint64_t nwid, void *ptr)
|
||||
{
|
||||
try {
|
||||
|
|
|
@ -139,6 +139,14 @@ public:
|
|||
const ZT_Endpoint *endpoint,
|
||||
int retries);
|
||||
|
||||
ZT_CertificateError addCertificate(
|
||||
void *tptr,
|
||||
int64_t now,
|
||||
unsigned int localTrust,
|
||||
const ZT_Certificate *cert,
|
||||
const void *certData,
|
||||
unsigned int certSize);
|
||||
|
||||
int sendUserMessage(
|
||||
void *tptr,
|
||||
uint64_t dest,
|
||||
|
|
|
@ -254,14 +254,14 @@ void Peer::pulse(void *const tPtr, const int64_t now, const bool isRoot)
|
|||
// callback (if one was supplied).
|
||||
|
||||
if (m_locator) {
|
||||
for (Vector< Endpoint >::const_iterator ep(m_locator->endpoints().begin()); ep != m_locator->endpoints().end(); ++ep) {
|
||||
if (ep->type == ZT_ENDPOINT_TYPE_IP_UDP) {
|
||||
if (RR->node->shouldUsePathForZeroTierTraffic(tPtr, m_id, -1, ep->ip())) {
|
||||
int64_t < = m_lastTried[*ep];
|
||||
for (Vector< std::pair<Endpoint, Locator::EndpointAttributes > >::const_iterator ep(m_locator->endpoints().begin()); ep != m_locator->endpoints().end(); ++ep) {
|
||||
if (ep->first.type == ZT_ENDPOINT_TYPE_IP_UDP) {
|
||||
if (RR->node->shouldUsePathForZeroTierTraffic(tPtr, m_id, -1, ep->first.ip())) {
|
||||
int64_t < = m_lastTried[ep->first];
|
||||
if ((now - lt) > ZT_PATH_MIN_TRY_INTERVAL) {
|
||||
lt = now;
|
||||
RR->t->tryingNewPath(tPtr, 0x84b22322, m_id, ep->ip(), InetAddress::NIL, 0, 0, Identity::NIL);
|
||||
sent(now, m_sendProbe(tPtr, -1, ep->ip(), nullptr, 0, now));
|
||||
RR->t->tryingNewPath(tPtr, 0x84b22322, m_id, ep->first.ip(), InetAddress::NIL, 0, 0, Identity::NIL);
|
||||
sent(now, m_sendProbe(tPtr, -1, ep->first.ip(), nullptr, 0, now));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -898,8 +898,8 @@ extern "C" const char *ZTT_general()
|
|||
Endpoint ep0(InetAddress::LO4);
|
||||
Endpoint ep1(InetAddress::LO6);
|
||||
Locator loc;
|
||||
loc.add(ep0);
|
||||
loc.add(ep1);
|
||||
loc.add(ep0, Locator::EndpointAttributes());
|
||||
loc.add(ep1, Locator::EndpointAttributes());
|
||||
loc.sign(now(), v1id);
|
||||
String locStr(loc.toString());
|
||||
//ZT_T_PRINTF("%s %s %s ",locStr.c_str(),loc.endpoints()[0].toString().c_str(),loc.endpoints()[1].toString().c_str());
|
||||
|
|
|
@ -77,13 +77,16 @@ void Topology::allPeers(Vector< SharedPtr< Peer > > &allPeers, Vector< SharedPtr
|
|||
|
||||
void Topology::doPeriodicTasks(void *tPtr, const int64_t now)
|
||||
{
|
||||
// Clean any expired certificates
|
||||
// Clean any expired certificates, updating roots if they have changed.
|
||||
{
|
||||
Mutex::Lock l1(m_certs_l);
|
||||
if (m_cleanCertificates(tPtr, now)) {
|
||||
RWMutex::Lock l3(m_peers_l);
|
||||
RWMutex::Lock l2(m_roots_l);
|
||||
m_updateRootPeers(tPtr, now);
|
||||
m_writeTrustStore(tPtr);
|
||||
{
|
||||
RWMutex::Lock l3(m_peers_l);
|
||||
RWMutex::Lock l2(m_roots_l);
|
||||
m_updateRootPeers(tPtr, now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,19 +112,24 @@ void Topology::doPeriodicTasks(void *tPtr, const int64_t now)
|
|||
for (Map< Address, SharedPtr< Peer > >::iterator i(m_peers.begin()); i != m_peers.end(); ++i) {
|
||||
// TODO: also delete if the peer has not exchanged meaningful communication in a while, such as
|
||||
// a network frame or non-trivial control packet.
|
||||
if (((now - i->second->lastReceive()) > ZT_PEER_ALIVE_TIMEOUT) && (std::find(rootLookup.begin(), rootLookup.end(), (uintptr_t)i->second.ptr()) == rootLookup.end()))
|
||||
if (((now - i->second->lastReceive()) > ZT_PEER_ALIVE_TIMEOUT) && (std::find(rootLookup.begin(), rootLookup.end(), (uintptr_t)(i->second.ptr())) == rootLookup.end()))
|
||||
toDelete.push_back(i->first);
|
||||
}
|
||||
}
|
||||
if (!toDelete.empty()) {
|
||||
ZT_SPEW("garbage collecting %u offline or stale peer objects", (unsigned int)toDelete.size());
|
||||
for (Vector< Address >::iterator i(toDelete.begin()); i != toDelete.end(); ++i) {
|
||||
RWMutex::Lock l1(m_peers_l);
|
||||
const Map< Address, SharedPtr< Peer > >::iterator p(m_peers.find(*i));
|
||||
if (likely(p != m_peers.end())) {
|
||||
p->second->save(tPtr);
|
||||
m_peers.erase(p);
|
||||
SharedPtr< Peer > toSave;
|
||||
{
|
||||
RWMutex::Lock l1(m_peers_l);
|
||||
const Map< Address, SharedPtr< Peer > >::iterator p(m_peers.find(*i));
|
||||
if (p != m_peers.end()) {
|
||||
p->second.swap(toSave);
|
||||
m_peers.erase(p);
|
||||
}
|
||||
}
|
||||
if (toSave)
|
||||
toSave->save(tPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,97 +164,75 @@ void Topology::saveAll(void *tPtr)
|
|||
{
|
||||
{
|
||||
RWMutex::RLock l(m_peers_l);
|
||||
for (Map< Address, SharedPtr< Peer > >::iterator i(m_peers.begin()); i != m_peers.end(); ++i) {
|
||||
for (Map< Address, SharedPtr< Peer > >::iterator i(m_peers.begin()); i != m_peers.end(); ++i)
|
||||
i->second->save(tPtr);
|
||||
}
|
||||
}
|
||||
{
|
||||
char tmp[32];
|
||||
Dictionary d;
|
||||
{
|
||||
Mutex::Lock l(m_certs_l);
|
||||
unsigned long idx = 0;
|
||||
d.add("c$", (uint64_t)m_certs.size());
|
||||
for (Map< SHA384Hash, std::pair< SharedPtr< const Certificate >, unsigned int > >::const_iterator c(m_certs.begin()); c != m_certs.end(); ++c) {
|
||||
d[Dictionary::arraySubscript(tmp, sizeof(tmp), "c$.s", idx)].assign(c->first.data, c->first.data + ZT_SHA384_DIGEST_SIZE);
|
||||
d.add(Dictionary::arraySubscript(tmp, sizeof(tmp), "c$.lt", idx), (uint64_t)c->second.second);
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
Vector< uint8_t > trustStore;
|
||||
d.encode(trustStore);
|
||||
RR->node->stateObjectPut(tPtr, ZT_STATE_OBJECT_TRUST_STORE, Utils::ZERO256, trustStore.data(), (unsigned int)trustStore.size());
|
||||
Mutex::Lock l(m_certs_l);
|
||||
m_writeTrustStore(tPtr);
|
||||
}
|
||||
}
|
||||
|
||||
ZT_CertificateError Topology::addCertificate(void *tPtr, const Certificate &cert, const int64_t now, const unsigned int localTrust, const bool writeToLocalStore, const bool refreshRootSets, const bool verify)
|
||||
{
|
||||
{
|
||||
const SHA384Hash serial(cert.serialNo);
|
||||
p_CertEntry certEntry;
|
||||
Mutex::Lock l1(m_certs_l);
|
||||
|
||||
// Check to see if we already have this specific certificate.
|
||||
const SHA384Hash serial(cert.serialNo);
|
||||
if (m_certs.find(serial) != m_certs.end())
|
||||
return ZT_CERTIFICATE_ERROR_NONE;
|
||||
|
||||
// Verify certificate all the way to a trusted root. This also verifies inner
|
||||
// signatures such as those of locators or the subject unique ID.
|
||||
if (verify) {
|
||||
const ZT_CertificateError err = m_verifyCertificate(cert, now, localTrust, false);
|
||||
if (err != ZT_CERTIFICATE_ERROR_NONE)
|
||||
return err;
|
||||
{
|
||||
Map< SHA384Hash, p_CertEntry >::iterator c(m_certs.find(serial));
|
||||
if (c != m_certs.end()) {
|
||||
if (c->second.localTrust == localTrust)
|
||||
return ZT_CERTIFICATE_ERROR_NONE;
|
||||
certEntry.certificate = c->second.certificate;
|
||||
}
|
||||
}
|
||||
if (!certEntry.certificate) {
|
||||
certEntry.certificate.set(new Certificate(cert));
|
||||
if (verify) {
|
||||
m_cleanCertificates(tPtr, now);
|
||||
const ZT_CertificateError err = m_verifyCertificate(cert, now, localTrust, false);
|
||||
if (err != ZT_CERTIFICATE_ERROR_NONE)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// Create entry containing copy of certificate and trust flags.
|
||||
const std::pair< SharedPtr< const Certificate >, unsigned int > certEntry(SharedPtr< const Certificate >(new Certificate(cert)), localTrust);
|
||||
certEntry.localTrust = localTrust;
|
||||
|
||||
// If the subject contains a unique ID, check if we already have a cert for the
|
||||
// same uniquely identified subject. If so, check its subject timestamp and keep
|
||||
// the one we have if newer. Otherwise replace it. Note that the verification
|
||||
// function will have checked the unique ID proof signature already if a unique
|
||||
// ID was present.
|
||||
if ((cert.subject.uniqueId) && (cert.subject.uniqueIdSize > 0)) {
|
||||
SHA384Hash uniqueIdHash;
|
||||
SHA384(uniqueIdHash.data, cert.subject.uniqueId, cert.subject.uniqueIdSize);
|
||||
std::pair< SharedPtr< const Certificate >, unsigned int > &bySubjectUniqueId = m_certsBySubjectUniqueId[uniqueIdHash];
|
||||
if (bySubjectUniqueId.first) {
|
||||
if (bySubjectUniqueId.first->subject.timestamp >= cert.subject.timestamp)
|
||||
p_CertEntry &bySubjectUniqueId = m_certsBySubjectUniqueID[uniqueIdHash];
|
||||
if (bySubjectUniqueId.certificate) {
|
||||
if (bySubjectUniqueId.certificate->subject.timestamp >= cert.subject.timestamp)
|
||||
return ZT_CERTIFICATE_ERROR_HAVE_NEWER_CERT;
|
||||
m_eraseCertificate(tPtr, bySubjectUniqueId.first, &uniqueIdHash);
|
||||
m_certsBySubjectUniqueId[uniqueIdHash] = certEntry;
|
||||
m_eraseCertificate(tPtr, bySubjectUniqueId.certificate, &uniqueIdHash);
|
||||
m_certsBySubjectUniqueID[uniqueIdHash] = certEntry;
|
||||
} else {
|
||||
bySubjectUniqueId = certEntry;
|
||||
}
|
||||
}
|
||||
|
||||
// Save certificate by serial number.
|
||||
m_certs[serial] = certEntry;
|
||||
|
||||
// Add certificate to sets of certificates whose subject references a given identity.
|
||||
for (unsigned int i = 0; i < cert.subject.identityCount; ++i) {
|
||||
const Identity *const ii = reinterpret_cast<const Identity *>(cert.subject.identities[i].identity);
|
||||
if (ii)
|
||||
m_certsBySubjectIdentity[ii->fingerprint()].insert(certEntry);
|
||||
m_certsBySubjectIdentity[ii->fingerprint()][certEntry.certificate] = localTrust;
|
||||
}
|
||||
|
||||
// Clean any certificates whose chains are now broken, which can happen if there was
|
||||
// an update that replaced an old cert with a given unique ID. Otherwise this generally
|
||||
// does nothing here. Skip if verify is false since this means we're mindlessly loading
|
||||
// certificates, which right now only happens on startup when they're loaded from the
|
||||
// local certificate cache.
|
||||
if (verify)
|
||||
m_cleanCertificates(tPtr, now);
|
||||
m_certs[serial] = certEntry;
|
||||
|
||||
// Refresh the root peers lists, since certs may enumerate roots.
|
||||
if (refreshRootSets) {
|
||||
RWMutex::Lock l3(m_peers_l);
|
||||
RWMutex::Lock l2(m_roots_l);
|
||||
m_updateRootPeers(tPtr, now);
|
||||
}
|
||||
|
||||
if (writeToLocalStore)
|
||||
m_writeTrustStore(tPtr);
|
||||
}
|
||||
|
||||
if (writeToLocalStore) {
|
||||
// Write certificate data prefixed by local trust flags as a 32-bit integer.
|
||||
Vector< uint8_t > certData(cert.encode());
|
||||
uint64_t id[6];
|
||||
Utils::copy< 48 >(id, cert.serialNo);
|
||||
|
@ -293,10 +279,8 @@ void Topology::m_eraseCertificate(void *tPtr, const SharedPtr< const Certificate
|
|||
const SHA384Hash serialNo(cert->serialNo);
|
||||
m_certs.erase(serialNo);
|
||||
|
||||
RR->node->stateObjectDelete(tPtr, ZT_STATE_OBJECT_CERT, serialNo.data);
|
||||
|
||||
if (uniqueIdHash)
|
||||
m_certsBySubjectUniqueId.erase(*uniqueIdHash);
|
||||
m_certsBySubjectUniqueID.erase(*uniqueIdHash);
|
||||
|
||||
for (unsigned int i = 0; i < cert->subject.identityCount; ++i) {
|
||||
const Identity *const ii = reinterpret_cast<const Identity *>(cert->subject.identities[i].identity);
|
||||
|
@ -308,6 +292,8 @@ void Topology::m_eraseCertificate(void *tPtr, const SharedPtr< const Certificate
|
|||
m_certsBySubjectIdentity.erase(bySubjectIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
RR->node->stateObjectDelete(tPtr, ZT_STATE_OBJECT_CERT, serialNo.data);
|
||||
}
|
||||
|
||||
bool Topology::m_cleanCertificates(void *tPtr, int64_t now)
|
||||
|
@ -317,13 +303,13 @@ bool Topology::m_cleanCertificates(void *tPtr, int64_t now)
|
|||
bool deleted = false;
|
||||
Vector< SharedPtr< const Certificate >> toDelete;
|
||||
for (;;) {
|
||||
for (Map< SHA384Hash, std::pair< SharedPtr< const Certificate >, unsigned int > >::iterator c(m_certs.begin()); c != m_certs.end(); ++c) {
|
||||
for (Map< SHA384Hash, p_CertEntry >::iterator c(m_certs.begin()); c != m_certs.end(); ++c) {
|
||||
// Verify, but the last boolean option tells it to skip signature checks as this would
|
||||
// already have been done. This will therefore just check the path and validity times
|
||||
// of the certificate.
|
||||
const ZT_CertificateError err = m_verifyCertificate(*(c->second.first), now, c->second.second, true);
|
||||
const ZT_CertificateError err = m_verifyCertificate(*(c->second.certificate), now, c->second.localTrust, true);
|
||||
if (err != ZT_CERTIFICATE_ERROR_NONE)
|
||||
toDelete.push_back(c->second.first);
|
||||
toDelete.push_back(c->second.certificate);
|
||||
}
|
||||
|
||||
if (toDelete.empty())
|
||||
|
@ -481,4 +467,24 @@ void Topology::m_updateRootPeers(void *tPtr, const int64_t now)
|
|||
m_rankRoots(now);
|
||||
}
|
||||
|
||||
void Topology::m_writeTrustStore(void *tPtr)
|
||||
{
|
||||
// assumes m_certs is locked
|
||||
|
||||
char tmp[32];
|
||||
Dictionary d;
|
||||
|
||||
unsigned long idx = 0;
|
||||
d.add("c$", (uint64_t)m_certs.size());
|
||||
for (Map< SHA384Hash, p_CertEntry >::const_iterator c(m_certs.begin()); c != m_certs.end(); ++c) {
|
||||
d[Dictionary::arraySubscript(tmp, sizeof(tmp), "c$.s", idx)].assign(c->first.data, c->first.data + ZT_SHA384_DIGEST_SIZE);
|
||||
d.add(Dictionary::arraySubscript(tmp, sizeof(tmp), "c$.lt", idx), (uint64_t)c->second.localTrust);
|
||||
++idx;
|
||||
}
|
||||
|
||||
Vector< uint8_t > trustStore;
|
||||
d.encode(trustStore);
|
||||
RR->node->stateObjectPut(tPtr, ZT_STATE_OBJECT_TRUST_STORE, Utils::ZERO256, trustStore.data(), (unsigned int)trustStore.size());
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
|
|
@ -152,6 +152,7 @@ private:
|
|||
SharedPtr< Peer > m_peerFromCached(void *tPtr, const Address &zta);
|
||||
SharedPtr< Path > m_newPath(const int64_t l, const InetAddress &r, const UniqueID &k);
|
||||
void m_updateRootPeers(void *tPtr, int64_t now);
|
||||
void m_writeTrustStore(void *tPtr);
|
||||
|
||||
const RuntimeEnvironment *const RR;
|
||||
|
||||
|
@ -160,9 +161,19 @@ private:
|
|||
Map< Address, SharedPtr< Peer > > m_peers;
|
||||
Map< UniqueID, SharedPtr< Path > > m_paths;
|
||||
|
||||
Map< SHA384Hash, std::pair< SharedPtr< const Certificate >, unsigned int > > m_certs;
|
||||
struct p_CertEntry
|
||||
{
|
||||
ZT_INLINE p_CertEntry() :
|
||||
certificate(),
|
||||
localTrust(0)
|
||||
{}
|
||||
SharedPtr< const Certificate > certificate;
|
||||
unsigned int localTrust;
|
||||
};
|
||||
|
||||
Map< SHA384Hash, p_CertEntry > m_certs;
|
||||
Map< SHA384Hash, p_CertEntry > m_certsBySubjectUniqueID;
|
||||
Map< Fingerprint, Map< SharedPtr< const Certificate >, unsigned int > > m_certsBySubjectIdentity;
|
||||
Map< SHA384Hash, std::pair< SharedPtr< const Certificate >, unsigned int > > m_certsBySubjectUniqueId;
|
||||
|
||||
RWMutex m_paths_l; // m_paths
|
||||
RWMutex m_peers_l; // m_peers
|
||||
|
|
|
@ -1492,6 +1492,14 @@ typedef struct
|
|||
} value;
|
||||
} ZT_Endpoint;
|
||||
|
||||
/**
|
||||
* Endpoint attributes
|
||||
*
|
||||
* Right now this is typedef'd to void because there are none. It will become
|
||||
* a struct once there's something to specify.
|
||||
*/
|
||||
typedef void ZT_EndpointAttributes;
|
||||
|
||||
/**
|
||||
* Network path to a peer
|
||||
*/
|
||||
|
@ -2361,6 +2369,29 @@ ZT_SDK_API int ZT_Node_tryPeer(
|
|||
const ZT_Endpoint *endpoint,
|
||||
int retries);
|
||||
|
||||
/**
|
||||
* Add a certificate to this node's certificate store
|
||||
*
|
||||
* This supports adding of certificates as expanded ZT_Certificate structures
|
||||
* or as raw data. If 'cert' is NULL then certData/certSize must be set.
|
||||
*
|
||||
* @param node Node instance
|
||||
* @param tptr Thread pointer to pass to functions/callbacks resulting from this call
|
||||
* @param localTrust Local trust flags (ORed together)
|
||||
* @param cert Certificate object, or set to NULL if certData and certSize are to be used
|
||||
* @param certData Certificate binary data if 'cert' is NULL, NULL otherwise
|
||||
* @param certSize Size of certificate binary data, 0 if none
|
||||
* @return
|
||||
*/
|
||||
ZT_SDK_API enum ZT_CertificateError ZT_Node_addCertificate(
|
||||
ZT_Node *node,
|
||||
void *tptr,
|
||||
int64_t now,
|
||||
unsigned int localTrust,
|
||||
const ZT_Certificate *cert,
|
||||
const void *certData,
|
||||
unsigned int certSize);
|
||||
|
||||
/**
|
||||
* Send a VERB_USER_MESSAGE to another ZeroTier node
|
||||
*
|
||||
|
@ -2561,8 +2592,12 @@ ZT_SDK_API int ZT_Endpoint_fromString(
|
|||
/**
|
||||
* Create and sign a new locator
|
||||
*
|
||||
* Note that attributes must be either NULL to use defaults for all or there
|
||||
* must be an attributes object for each endpoint.
|
||||
*
|
||||
* @param ts Locator timestamp
|
||||
* @param endpoints List of endpoints to store in locator
|
||||
* @param endpointAttributes Array of ZT_EndpointAttributes objects or NULL to use defaults
|
||||
* @param endpointCount Number of endpoints (maximum: 8)
|
||||
* @param signer Identity to sign locator (must include private key)
|
||||
* @return Locator or NULL on error (too many endpoints or identity does not have private key)
|
||||
|
@ -2570,6 +2605,7 @@ ZT_SDK_API int ZT_Endpoint_fromString(
|
|||
ZT_SDK_API ZT_Locator *ZT_Locator_create(
|
||||
int64_t ts,
|
||||
const ZT_Endpoint *endpoints,
|
||||
const ZT_EndpointAttributes *endpointAttributes,
|
||||
unsigned int endpointCount,
|
||||
const ZT_Identity *signer);
|
||||
|
||||
|
@ -2807,7 +2843,7 @@ ZT_SDK_API enum ZT_CertificateError ZT_Certificate_verify(const ZT_Certificate *
|
|||
ZT_SDK_API const ZT_Certificate *ZT_Certificate_clone(const ZT_Certificate *cert);
|
||||
|
||||
/**
|
||||
* Free a certificate created with ZT_Certificate_decode()
|
||||
* Free a certificate created with ZT_Certificate_decode() or ZT_Certificate_clone()
|
||||
*
|
||||
* @param cert Certificate to free
|
||||
*/
|
||||
|
|
|
@ -48,7 +48,7 @@ func NewLocator(ts int64, endpoints []Endpoint, signer *Identity) (*Locator, err
|
|||
for _, e := range endpoints {
|
||||
eps = append(eps, e.cep)
|
||||
}
|
||||
loc := C.ZT_Locator_create(C.int64_t(ts), &eps[0], C.uint(len(eps)), signer.cIdentity())
|
||||
loc := C.ZT_Locator_create(C.int64_t(ts), &eps[0], nil, C.uint(len(eps)), signer.cIdentity())
|
||||
if uintptr(loc) == 0 {
|
||||
return nil, ErrInvalidParameter
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue