summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Ierymenko <adam.ierymenko@gmail.com>2017-02-23 11:47:36 -0800
committerAdam Ierymenko <adam.ierymenko@gmail.com>2017-02-23 11:47:36 -0800
commit10185e92faa77a4b032a27a7c01b4186727b91b9 (patch)
tree5ce223c4cbeda31551d80ee3f42445924902f2ad
parent33b94e8478e99d1e185041fa522b01de25fe53d4 (diff)
downloadinfinitytier-10185e92faa77a4b032a27a7c01b4186727b91b9.tar.gz
infinitytier-10185e92faa77a4b032a27a7c01b4186727b91b9.zip
Certificate of ownership -- used to secure against IP address spoofing, especially for IPv4 and regular IPv6.
-rw-r--r--controller/EmbeddedNetworkController.cpp9
-rw-r--r--include/ZeroTierOne.h5
-rw-r--r--node/Capability.hpp1
-rw-r--r--node/CertificateOfOwnership.cpp46
-rw-r--r--node/CertificateOfOwnership.hpp251
-rw-r--r--node/IncomingPacket.cpp19
-rw-r--r--node/Membership.cpp164
-rw-r--r--node/Membership.hpp81
-rw-r--r--node/Network.hpp11
-rw-r--r--node/NetworkConfig.cpp25
-rw-r--r--node/NetworkConfig.hpp21
-rw-r--r--node/Packet.hpp2
-rw-r--r--node/Revocation.hpp5
-rw-r--r--node/Tag.hpp3
-rw-r--r--objects.mk1
15 files changed, 550 insertions, 94 deletions
diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp
index ca548fd4..78a9b7c7 100644
--- a/controller/EmbeddedNetworkController.cpp
+++ b/controller/EmbeddedNetworkController.cpp
@@ -1706,6 +1706,15 @@ void EmbeddedNetworkController::_request(
}
}
+ // Issue a certificate of ownership for all static IPs
+ if (nc.staticIpCount) {
+ nc.certificatesOfOwnership[0] = CertificateOfOwnership(nwid,now,identity.address(),1);
+ for(unsigned int i=0;i<nc.staticIpCount;++i)
+ nc.certificatesOfOwnership[0].addThing(nc.staticIps[i]);
+ nc.certificatesOfOwnership[0].sign(_signingId);
+ nc.certificateOfOwnershipCount = 1;
+ }
+
CertificateOfMembership com(now,credentialtmd,nwid,identity.address());
if (com.sign(_signingId)) {
nc.com = com;
diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h
index e2380a7b..c1dbd8f8 100644
--- a/include/ZeroTierOne.h
+++ b/include/ZeroTierOne.h
@@ -137,6 +137,11 @@ extern "C" {
#define ZT_MAX_CAPABILITY_RULES 64
/**
+ * Maximum number of certificates of ownership to assign to a single network member
+ */
+#define ZT_MAX_CERTIFICATES_OF_OWNERSHIP 4
+
+/**
* Global maximum length for capability chain of custody (including initial issue)
*/
#define ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH 7
diff --git a/node/Capability.hpp b/node/Capability.hpp
index 08714038..d884625e 100644
--- a/node/Capability.hpp
+++ b/node/Capability.hpp
@@ -396,7 +396,6 @@ public:
unsigned int p = startAt;
- // These are the same between Tag and Capability
_nwid = b.template at<uint64_t>(p); p += 8;
_ts = b.template at<uint64_t>(p); p += 8;
_id = b.template at<uint32_t>(p); p += 4;
diff --git a/node/CertificateOfOwnership.cpp b/node/CertificateOfOwnership.cpp
new file mode 100644
index 00000000..8305c489
--- /dev/null
+++ b/node/CertificateOfOwnership.cpp
@@ -0,0 +1,46 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "CertificateOfOwnership.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Identity.hpp"
+#include "Topology.hpp"
+#include "Switch.hpp"
+#include "Network.hpp"
+
+namespace ZeroTier {
+
+int CertificateOfOwnership::verify(const RuntimeEnvironment *RR) const
+{
+ if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
+ return -1;
+ const Identity id(RR->topology->getIdentity(_signedBy));
+ if (!id) {
+ RR->sw->requestWhois(_signedBy);
+ return 1;
+ }
+ try {
+ Buffer<(sizeof(CertificateOfOwnership) + 64)> tmp;
+ this->serialize(tmp,true);
+ return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
+ } catch ( ... ) {
+ return -1;
+ }
+}
+
+} // namespace ZeroTier
diff --git a/node/CertificateOfOwnership.hpp b/node/CertificateOfOwnership.hpp
new file mode 100644
index 00000000..69b26aec
--- /dev/null
+++ b/node/CertificateOfOwnership.hpp
@@ -0,0 +1,251 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ZT_CERTIFICATEOFOWNERSHIP_HPP
+#define ZT_CERTIFICATEOFOWNERSHIP_HPP
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "Constants.hpp"
+#include "C25519.hpp"
+#include "Address.hpp"
+#include "Identity.hpp"
+#include "Buffer.hpp"
+#include "InetAddress.hpp"
+#include "MAC.hpp"
+
+// Max things per CertificateOfOwnership
+#define ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS 16
+
+// Maximum size of a thing's value field in bytes
+#define ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE 16
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+
+/**
+ * Certificate indicating ownership of a network identifier
+ */
+class CertificateOfOwnership
+{
+public:
+ enum Thing
+ {
+ THING_NULL = 0,
+ THING_MAC_ADDRESS = 1,
+ THING_IPV4_ADDRESS = 2,
+ THING_IPV6_ADDRESS = 3
+ };
+
+ CertificateOfOwnership() :
+ _networkId(0),
+ _ts(0),
+ _id(0),
+ _thingCount(0)
+ {
+ }
+
+ CertificateOfOwnership(const uint64_t nwid,const uint64_t ts,const Address &issuedTo,const uint32_t id) :
+ _networkId(nwid),
+ _ts(ts),
+ _flags(0),
+ _id(id),
+ _thingCount(0),
+ _issuedTo(issuedTo)
+ {
+ }
+
+ inline uint64_t networkId() const { return _networkId; }
+ inline uint64_t timestamp() const { return _ts; }
+ inline uint32_t id() const { return _id; }
+ inline unsigned int thingCount() const { return (unsigned int)_thingCount; }
+
+ inline Thing thingType(const unsigned int i) const { return (Thing)_thingTypes[i]; }
+ inline const uint8_t *thingValue(const unsigned int i) const { return _thingValues[i]; }
+
+ inline const Address &issuedTo() const { return _issuedTo; }
+
+ inline bool owns(const Thing &t,const void *v,unsigned int l)
+ {
+ for(unsigned int i=0,j=_thingCount;i<j;++i) {
+ if (_thingTypes[i] == (uint8_t)t) {
+ unsigned int k = 0;
+ while (k < l) {
+ if (reinterpret_cast<const uint8_t *>(v)[k] != _thingValues[i][k])
+ break;
+ ++k;
+ }
+ if (k == l)
+ return true;
+ }
+ }
+ return false;
+ }
+
+ inline bool owns(const InetAddress &ip)
+ {
+ if (ip.ss_family == AF_INET)
+ return this->owns(THING_IPV4_ADDRESS,&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
+ if (ip.ss_family == AF_INET6)
+ return this->owns(THING_IPV6_ADDRESS,reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
+ return false;
+ }
+
+ inline bool owns(const MAC &mac)
+ {
+ uint8_t tmp[6];
+ mac.copyTo(tmp,6);
+ return this->owns(THING_MAC_ADDRESS,tmp,6);
+ }
+
+ inline void addThing(const InetAddress &ip)
+ {
+ if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) return;
+ if (ip.ss_family == AF_INET) {
+ _thingTypes[_thingCount] = THING_IPV4_ADDRESS;
+ memcpy(_thingValues[_thingCount],&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
+ ++_thingCount;
+ } else if (ip.ss_family == AF_INET6) {
+ _thingTypes[_thingCount] = THING_IPV6_ADDRESS;
+ memcpy(_thingValues[_thingCount],reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
+ ++_thingCount;
+ }
+ }
+
+ inline void addThing(const MAC &mac)
+ {
+ if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) return;
+ _thingTypes[_thingCount] = THING_MAC_ADDRESS;
+ mac.copyTo(_thingValues[_thingCount],6);
+ ++_thingCount;
+ }
+
+ /**
+ * @param signer Signing identity, must have private key
+ * @return True if signature was successful
+ */
+ inline bool sign(const Identity &signer)
+ {
+ if (signer.hasPrivate()) {
+ Buffer<sizeof(CertificateOfOwnership) + 64> tmp;
+ _signedBy = signer.address();
+ this->serialize(tmp,true);
+ _signature = signer.sign(tmp.data(),tmp.size());
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param RR Runtime environment to allow identity lookup for signedBy
+ * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature
+ */
+ int verify(const RuntimeEnvironment *RR) const;
+
+ template<unsigned int C>
+ inline void serialize(Buffer<C> &b,const bool forSign = false) const
+ {
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+
+ b.append(_networkId);
+ b.append(_ts);
+ b.append(_flags);
+ b.append(_id);
+ b.append((uint16_t)_thingCount);
+ for(unsigned int i=0,j=_thingCount;i<j;++i) {
+ b.append((uint8_t)_thingTypes[i]);
+ b.append(_thingValues[i],ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE);
+ }
+
+ _issuedTo.appendTo(b);
+ _signedBy.appendTo(b);
+ if (!forSign) {
+ b.append((uint8_t)1); // 1 == Ed25519
+ b.append((uint16_t)ZT_C25519_SIGNATURE_LEN); // length of signature
+ b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
+ }
+
+ b.append((uint16_t)0); // length of additional fields, currently 0
+
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+ }
+
+ template<unsigned int C>
+ inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+ {
+ unsigned int p = startAt;
+
+ memset(this,0,sizeof(CertificateOfOwnership));
+
+ _networkId = b.template at<uint64_t>(p); p += 8;
+ _ts = b.template at<uint64_t>(p); p += 8;
+ _flags = b.template at<uint64_t>(p); p += 8;
+ _id = b.template at<uint32_t>(p); p += 4;
+ _thingCount = b.template at<uint16_t>(p); p += 2;
+ for(unsigned int i=0,j=_thingCount;i<j;++i) {
+ if (i < ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) {
+ _thingTypes[i] = (uint8_t)b[p++];
+ memcpy(_thingValues[i],b.field(p,ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE),ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE);
+ p += ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE;
+ }
+ }
+
+ _issuedTo.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ _signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ if (b[p++] == 1) {
+ if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
+ throw std::runtime_error("invalid signature length");
+ p += 2;
+ memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
+ } else {
+ p += 2 + b.template at<uint16_t>(p);
+ }
+
+ p += 2 + b.template at<uint16_t>(p);
+ if (p > b.size())
+ throw std::runtime_error("extended field overflow");
+
+ return (p - startAt);
+ }
+
+ // Provides natural sort order by ID
+ inline bool operator<(const CertificateOfOwnership &coo) const { return (_id < coo._id); }
+
+ inline bool operator==(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) == 0); }
+ inline bool operator!=(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) != 0); }
+
+private:
+ uint64_t _networkId;
+ uint64_t _ts;
+ uint64_t _flags;
+ uint32_t _id;
+ uint16_t _thingCount;
+ uint8_t _thingTypes[ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS];
+ uint8_t _thingValues[ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS][ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE];
+ Address _issuedTo;
+ Address _signedBy;
+ C25519::Signature _signature;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp
index b077f7e2..b5b2bcb3 100644
--- a/node/IncomingPacket.cpp
+++ b/node/IncomingPacket.cpp
@@ -832,6 +832,7 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
Capability cap;
Tag tag;
Revocation revocation;
+ CertificateOfOwnership coo;
bool trustEstablished = false;
unsigned int p = ZT_PACKET_IDX_PAYLOAD;
@@ -909,6 +910,24 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
}
}
}
+
+ const unsigned int numCoos = at<uint16_t>(p); p += 2;
+ for(unsigned int i=0;i<numCoos;++i) {
+ p += coo.deserialize(*this,p);
+ const SharedPtr<Network> network(RR->node->network(coo.networkId()));
+ if (network) {
+ switch(network->addCredential(coo)) {
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
+ }
+ }
+ }
}
peer->received(_path,hops(),packetId(),Packet::VERB_NETWORK_CREDENTIALS,0,Packet::VERB_NOP,trustEstablished);
diff --git a/node/Membership.cpp b/node/Membership.cpp
index 8c6dab64..1eacb93d 100644
--- a/node/Membership.cpp
+++ b/node/Membership.cpp
@@ -37,6 +37,7 @@ Membership::Membership() :
{
for(unsigned int i=0;i<ZT_MAX_NETWORK_TAGS;++i) _remoteTags[i] = &(_tagMem[i]);
for(unsigned int i=0;i<ZT_MAX_NETWORK_CAPABILITIES;++i) _remoteCaps[i] = &(_capMem[i]);
+ for(unsigned int i=0;i<ZT_MAX_CERTIFICATES_OF_OWNERSHIP;++i) _remoteCoos[i] = &(_cooMem[i]);
}
void Membership::pushCredentials(const RuntimeEnvironment *RR,const uint64_t now,const Address &peerAddress,const NetworkConfig &nconf,int localCapabilityIndex,const bool force)
@@ -62,8 +63,19 @@ void Membership::pushCredentials(const RuntimeEnvironment *RR,const uint64_t now
}
}
+ const CertificateOfOwnership *sendCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP];
+ unsigned int sendCooCount = 0;
+ for(unsigned int c=0;c<nconf.certificateOfOwnershipCount;++c) {
+ if ( (_localCoos[c].id != nconf.certificatesOfOwnership[c].id()) || ((now - _localCoos[c].lastPushed) >= ZT_CREDENTIAL_PUSH_EVERY) || (force) ) {
+ _localCoos[c].lastPushed = now;
+ _localCoos[c].id = nconf.certificatesOfOwnership[c].id();
+ sendCoos[sendCooCount++] = &(nconf.certificatesOfOwnership[c]);
+ }
+ }
+
unsigned int tagPtr = 0;
- while ((tagPtr < sendTagCount)||(sendCom)||(sendCap)) {
+ unsigned int cooPtr = 0;
+ while ((tagPtr < sendTagCount)||(cooPtr < sendCooCount)||(sendCom)||(sendCap)) {
Packet outp(peerAddress,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
if (sendCom) {
@@ -82,7 +94,7 @@ void Membership::pushCredentials(const RuntimeEnvironment *RR,const uint64_t now
const unsigned int tagCountAt = outp.size();
outp.addSize(2);
unsigned int thisPacketTagCount = 0;
- while ((tagPtr < sendTagCount)&&((outp.size() + sizeof(Tag) + 32) < ZT_PROTO_MAX_PACKET_LENGTH)) {
+ while ((tagPtr < sendTagCount)&&((outp.size() + sizeof(Tag) + 16) < ZT_PROTO_MAX_PACKET_LENGTH)) {
sendTags[tagPtr++]->serialize(outp);
++thisPacketTagCount;
}
@@ -91,6 +103,15 @@ void Membership::pushCredentials(const RuntimeEnvironment *RR,const uint64_t now
// No revocations, these propagate differently
outp.append((uint16_t)0);
+ const unsigned int cooCountAt = outp.size();
+ outp.addSize(2);
+ unsigned int thisPacketCooCount = 0;
+ while ((cooPtr < sendCooCount)&&((outp.size() + sizeof(CertificateOfOwnership) + 16) < ZT_PROTO_MAX_PACKET_LENGTH)) {
+ sendCoos[cooPtr++]->serialize(outp);
+ ++thisPacketCooCount;
+ }
+ outp.setAt(cooCountAt,(uint16_t)thisPacketCooCount);
+
outp.compress();
RR->sw->send(outp,true);
}
@@ -98,14 +119,14 @@ void Membership::pushCredentials(const RuntimeEnvironment *RR,const uint64_t now
const Capability *Membership::getCapability(const NetworkConfig &nconf,const uint32_t id) const
{
- const _RemoteCapability *const *c = std::lower_bound(&(_remoteCaps[0]),&(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]),(uint64_t)id,_RemoteCredentialSorter<_RemoteCapability>());
- return ( ((c != &(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]))&&((*c)->id == (uint64_t)id)) ? ((((*c)->lastReceived)&&(_isCredentialTimestampValid(nconf,(*c)->cap,**c))) ? &((*c)->cap) : (const Capability *)0) : (const Capability *)0);
+ const _RemoteCredential<Capability> *const *c = std::lower_bound(&(_remoteCaps[0]),&(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]),(uint64_t)id,_RemoteCredentialComp<Capability>());
+ return ( ((c != &(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]))&&((*c)->id == (uint64_t)id)) ? ((((*c)->lastReceived)&&(_isCredentialTimestampValid(nconf,(*c)->credential,**c))) ? &((*c)->credential) : (const Capability *)0) : (const Capability *)0);
}
const Tag *Membership::getTag(const NetworkConfig &nconf,const uint32_t id) const
{
- const _RemoteTag *const *t = std::lower_bound(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]),(uint64_t)id,_RemoteCredentialSorter<_RemoteTag>());
- return ( ((t != &(_remoteTags[ZT_MAX_NETWORK_CAPABILITIES]))&&((*t)->id == (uint64_t)id)) ? ((((*t)->lastReceived)&&(_isCredentialTimestampValid(nconf,(*t)->tag,**t))) ? &((*t)->tag) : (const Tag *)0) : (const Tag *)0);
+ const _RemoteCredential<Tag> *const *t = std::lower_bound(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]),(uint64_t)id,_RemoteCredentialComp<Tag>());
+ return ( ((t != &(_remoteTags[ZT_MAX_NETWORK_CAPABILITIES]))&&((*t)->id == (uint64_t)id)) ? ((((*t)->lastReceived)&&(_isCredentialTimestampValid(nconf,(*t)->credential,**t))) ? &((*t)->credential) : (const Tag *)0) : (const Tag *)0);
}
Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const CertificateOfMembership &com)
@@ -141,14 +162,14 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Tag &tag)
{
- _RemoteTag *const *htmp = std::lower_bound(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]),(uint64_t)tag.id(),_RemoteCredentialSorter<_RemoteTag>());
- _RemoteTag *have = ((htmp != &(_remoteTags[ZT_MAX_NETWORK_TAGS]))&&((*htmp)->id == (uint64_t)tag.id())) ? *htmp : (_RemoteTag *)0;
+ _RemoteCredential<Tag> *const *htmp = std::lower_bound(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]),(uint64_t)tag.id(),_RemoteCredentialComp<Tag>());
+ _RemoteCredential<Tag> *have = ((htmp != &(_remoteTags[ZT_MAX_NETWORK_TAGS]))&&((*htmp)->id == (uint64_t)tag.id())) ? *htmp : (_RemoteCredential<Tag> *)0;
if (have) {
- if ( (!_isCredentialTimestampValid(nconf,tag,*have)) || (have->tag.timestamp() > tag.timestamp()) ) {
+ if ( (!_isCredentialTimestampValid(nconf,tag,*have)) || (have->credential.timestamp() > tag.timestamp()) ) {
TRACE("addCredential(Tag) for %s on %.16llx REJECTED (revoked or too old)",tag.issuedTo().toString().c_str(),tag.networkId());
return ADD_REJECTED;
}
- if (have->tag == tag) {
+ if (have->credential == tag) {
TRACE("addCredential(Tag) for %s on %.16llx ACCEPTED (redundant)",tag.issuedTo().toString().c_str(),tag.networkId());
return ADD_ACCEPTED_REDUNDANT;
}
@@ -162,7 +183,7 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
TRACE("addCredential(Tag) for %s on %.16llx ACCEPTED (new)",tag.issuedTo().toString().c_str(),tag.networkId());
if (!have) have = _newTag(tag.id());
have->lastReceived = RR->node->now();
- have->tag = tag;
+ have->credential = tag;
return ADD_ACCEPTED_NEW;
case 1:
return ADD_DEFERRED_FOR_WHOIS;
@@ -171,14 +192,14 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Capability &cap)
{
- _RemoteCapability *const *htmp = std::lower_bound(&(_remoteCaps[0]),&(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]),(uint64_t)cap.id(),_RemoteCredentialSorter<_RemoteCapability>());
- _RemoteCapability *have = ((htmp != &(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]))&&((*htmp)->id == (uint64_t)cap.id())) ? *htmp : (_RemoteCapability *)0;
+ _RemoteCredential<Capability> *const *htmp = std::lower_bound(&(_remoteCaps[0]),&(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]),(uint64_t)cap.id(),_RemoteCredentialComp<Capability>());
+ _RemoteCredential<Capability> *have = ((htmp != &(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]))&&((*htmp)->id == (uint64_t)cap.id())) ? *htmp : (_RemoteCredential<Capability> *)0;
if (have) {
- if ( (!_isCredentialTimestampValid(nconf,cap,*have)) || (have->cap.timestamp() > cap.timestamp()) ) {
+ if ( (!_isCredentialTimestampValid(nconf,cap,*have)) || (have->credential.timestamp() > cap.timestamp()) ) {
TRACE("addCredential(Capability) for %s on %.16llx REJECTED (revoked or too old)",cap.issuedTo().toString().c_str(),cap.networkId());
return ADD_REJECTED;
}
- if (have->cap == cap) {
+ if (have->credential == cap) {
TRACE("addCredential(Capability) for %s on %.16llx ACCEPTED (redundant)",cap.issuedTo().toString().c_str(),cap.networkId());
return ADD_ACCEPTED_REDUNDANT;
}
@@ -192,7 +213,7 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
TRACE("addCredential(Capability) for %s on %.16llx ACCEPTED (new)",cap.issuedTo().toString().c_str(),cap.networkId());
if (!have) have = _newCapability(cap.id());
have->lastReceived = RR->node->now();
- have->cap = cap;
+ have->credential = cap;
return ADD_ACCEPTED_NEW;
case 1:
return ADD_DEFERRED_FOR_WHOIS;
@@ -209,13 +230,15 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
switch(rev.type()) {
default:
//case Revocation::CREDENTIAL_TYPE_ALL:
- return ( (_revokeCom(rev)||_revokeCap(rev,now)||_revokeTag(rev,now)) ? ADD_ACCEPTED_NEW : ADD_ACCEPTED_REDUNDANT );
+ return ( (_revokeCom(rev)||_revokeCap(rev,now)||_revokeTag(rev,now)||_revokeCoo(rev,now)) ? ADD_ACCEPTED_NEW : ADD_ACCEPTED_REDUNDANT );
case Revocation::CREDENTIAL_TYPE_COM:
return (_revokeCom(rev) ? ADD_ACCEPTED_NEW : ADD_ACCEPTED_REDUNDANT);
case Revocation::CREDENTIAL_TYPE_CAPABILITY:
return (_revokeCap(rev,now) ? ADD_ACCEPTED_NEW : ADD_ACCEPTED_REDUNDANT);
case Revocation::CREDENTIAL_TYPE_TAG:
return (_revokeTag(rev,now) ? ADD_ACCEPTED_NEW : ADD_ACCEPTED_REDUNDANT);
+ case Revocation::CREDENTIAL_TYPE_COO:
+ return (_revokeCoo(rev,now) ? ADD_ACCEPTED_NEW : ADD_ACCEPTED_REDUNDANT);
}
}
case 1:
@@ -223,9 +246,40 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
}
}
-Membership::_RemoteTag *Membership::_newTag(const uint64_t id)
+
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const CertificateOfOwnership &coo)
+{
+ _RemoteCredential<CertificateOfOwnership> *const *htmp = std::lower_bound(&(_remoteCoos[0]),&(_remoteCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP]),(uint64_t)coo.id(),_RemoteCredentialComp<CertificateOfOwnership>());
+ _RemoteCredential<CertificateOfOwnership> *have = ((htmp != &(_remoteCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP]))&&((*htmp)->id == (uint64_t)coo.id())) ? *htmp : (_RemoteCredential<CertificateOfOwnership> *)0;
+ if (have) {
+ if ( (!_isCredentialTimestampValid(nconf,coo,*have)) || (have->credential.timestamp() > coo.timestamp()) ) {
+ TRACE("addCredential(CertificateOfOwnership) for %s on %.16llx REJECTED (revoked or too old)",cap.issuedTo().toString().c_str(),cap.networkId());
+ return ADD_REJECTED;
+ }
+ if (have->credential == coo) {
+ TRACE("addCredential(CertificateOfOwnership) for %s on %.16llx ACCEPTED (redundant)",cap.issuedTo().toString().c_str(),cap.networkId());
+ return ADD_ACCEPTED_REDUNDANT;
+ }
+ }
+
+ switch(coo.verify(RR)) {
+ default:
+ TRACE("addCredential(CertificateOfOwnership) for %s on %.16llx REJECTED (invalid)",cap.issuedTo().toString().c_str(),cap.networkId());
+ return ADD_REJECTED;
+ case 0:
+ TRACE("addCredential(CertificateOfOwnership) for %s on %.16llx ACCEPTED (new)",cap.issuedTo().toString().c_str(),cap.networkId());
+ if (!have) have = _newCoo(coo.id());
+ have->lastReceived = RR->node->now();
+ have->credential = coo;
+ return ADD_ACCEPTED_NEW;
+ case 1:
+ return ADD_DEFERRED_FOR_WHOIS;
+ }
+}
+
+Membership::_RemoteCredential<Tag> *Membership::_newTag(const uint64_t id)
{
- _RemoteTag *t = NULL;
+ _RemoteCredential<Tag> *t = NULL;
uint64_t minlr = 0xffffffffffffffffULL;
for(unsigned int i=0;i<ZT_MAX_NETWORK_TAGS;++i) {
if (_remoteTags[i]->id == ZT_MEMBERSHIP_CRED_ID_UNUSED) {
@@ -236,21 +290,21 @@ Membership::_RemoteTag *Membership::_newTag(const uint64_t id)
minlr = _remoteTags[i]->lastReceived;
}
}
-
- if (t) {
- t->id = id;
- t->lastReceived = 0;
- t->revocationThreshold = 0;
- t->tag = Tag();
- }
-
- std::sort(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]),_RemoteCredentialSorter<_RemoteTag>());
+
+ if (t) {
+ t->id = id;
+ t->lastReceived = 0;
+ t->revocationThreshold = 0;
+ t->credential = Tag();
+ }
+
+ std::sort(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]));
return t;
}
-Membership::_RemoteCapability *Membership::_newCapability(const uint64_t id)
+Membership::_RemoteCredential<Capability> *Membership::_newCapability(const uint64_t id)
{
- _RemoteCapability *c = NULL;
+ _RemoteCredential<Capability> *c = NULL;
uint64_t minlr = 0xffffffffffffffffULL;
for(unsigned int i=0;i<ZT_MAX_NETWORK_CAPABILITIES;++i) {
if (_remoteCaps[i]->id == ZT_MEMBERSHIP_CRED_ID_UNUSED) {
@@ -266,10 +320,35 @@ Membership::_RemoteCapability *Membership::_newCapability(const uint64_t id)
c->id = id;
c->lastReceived = 0;
c->revocationThreshold = 0;
- c->cap = Capability();
+ c->credential = Capability();
+ }
+
+ std::sort(&(_remoteCaps[0]),&(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]));
+ return c;
+}
+
+Membership::_RemoteCredential<CertificateOfOwnership> *Membership::_newCoo(const uint64_t id)
+{
+ _RemoteCredential<CertificateOfOwnership> *c = NULL;
+ uint64_t minlr = 0xffffffffffffffffULL;
+ for(unsigned int i=0;i<ZT_MAX_CERTIFICATES_OF_OWNERSHIP;++i) {
+ if (_remoteCoos[i]->id == ZT_MEMBERSHIP_CRED_ID_UNUSED) {
+ c = _remoteCoos[i];
+ break;
+ } else if (_remoteCoos[i]->lastReceived <= minlr) {
+ c = _remoteCoos[i];
+ minlr = _remoteCoos[i]->lastReceived;
+ }
+ }
+
+ if (c) {
+ c->id = id;
+ c->lastReceived = 0;
+ c->revocationThreshold = 0;
+ c->credential = CertificateOfOwnership();
}
- std::sort(&(_remoteCaps[0]),&(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]),_RemoteCredentialSorter<_RemoteCapability>());
+ std::sort(&(_remoteCoos[0]),&(_remoteCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP]));
return c;
}
@@ -284,8 +363,8 @@ bool Membership::_revokeCom(const Revocation &rev)
bool Membership::_revokeCap(const Revocation &rev,const uint64_t now)
{
- _RemoteCapability *const *htmp = std::lower_bound(&(_remoteCaps[0]),&(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]),(uint64_t)rev.credentialId(),_RemoteCredentialSorter<_RemoteCapability>());
- _RemoteCapability *have = ((htmp != &(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]))&&((*htmp)->id == (uint64_t)rev.credentialId())) ? *htmp : (_RemoteCapability *)0;
+ _RemoteCredential<Capability> *const *htmp = std::lower_bound(&(_remoteCaps[0]),&(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]),(uint64_t)rev.credentialId(),_RemoteCredentialComp<Capability>());
+ _RemoteCredential<Capability> *have = ((htmp != &(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]))&&((*htmp)->id == (uint64_t)rev.credentialId())) ? *htmp : (_RemoteCredential<Capability> *)0;
if (!have) have = _newCapability(rev.credentialId());
if (rev.threshold() > have->revocationThreshold) {
have->lastReceived = now;
@@ -297,8 +376,8 @@ bool Membership::_revokeCap(const Revocation &rev,const uint64_t now)
bool Membership::_revokeTag(const Revocation &rev,const uint64_t now)
{
- _RemoteTag *const *htmp = std::lower_bound(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]),(uint64_t)rev.credentialId(),_RemoteCredentialSorter<_RemoteTag>());
- _RemoteTag *have = ((htmp != &(_remoteTags[ZT_MAX_NETWORK_TAGS]))&&((*htmp)->id == (uint64_t)rev.credentialId())) ? *htmp : (_RemoteTag *)0;
+ _RemoteCredential<Tag> *const *htmp = std::lower_bound(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]),(uint64_t)rev.credentialId(),_RemoteCredentialComp<Tag>());
+ _RemoteCredential<Tag> *have = ((htmp != &(_remoteTags[ZT_MAX_NETWORK_TAGS]))&&((*htmp)->id == (uint64_t)rev.credentialId())) ? *htmp : (_RemoteCredential<Tag> *)0;
if (!have) have = _newTag(rev.credentialId());
if (rev.threshold() > have->revocationThreshold) {
have->lastReceived = now;
@@ -308,4 +387,17 @@ bool Membership::_revokeTag(const Revocation &rev,const uint64_t now)
return false;
}
+bool Membership::_revokeCoo(const Revocation &rev,const uint64_t now)
+{
+ _RemoteCredential<CertificateOfOwnership> *const *htmp = std::lower_bound(&(_remoteCoos[0]),&(_remoteCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP]),(uint64_t)rev.credentialId(),_RemoteCredentialComp<CertificateOfOwnership>());
+ _RemoteCredential<CertificateOfOwnership> *have = ((htmp != &(_remoteCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP]))&&((*htmp)->id == (uint64_t)rev.credentialId())) ? *htmp : (_RemoteCredential<CertificateOfOwnership> *)0;
+ if (!have) have = _newCoo(rev.credentialId());
+ if (rev.threshold() > have->revocationThreshold) {
+ have->lastReceived = now;
+ have->revocationThreshold = rev.threshold();
+ return true;
+ }
+ return false;
+}
+
} // namespace ZeroTier
diff --git a/node/Membership.hpp b/node/Membership.hpp
index 9814dce8..4e9d7769 100644
--- a/node/Membership.hpp
+++ b/node/Membership.hpp
@@ -39,49 +39,30 @@ class Network;
/**
* A container for certificates of membership and other network credentials
*
- * This is kind of analogous to a join table between Peer and Network. It is
- * held by the Network object for each participating Peer.
+ * This is essentially a relational join between Peer and Network.
*
* This class is not thread safe. It must be locked externally.
*/
class Membership
{
private:
- // Tags and related state
- struct _RemoteTag
- {
- _RemoteTag() : id(ZT_MEMBERSHIP_CRED_ID_UNUSED),lastReceived(0),revocationThreshold(0) {}
- // Tag ID (last 32 bits, first 32 bits are set in unused entries to sort them to end)
- uint64_t id;
- // Last time we received THEIR tag (with this ID)
- uint64_t lastReceived;
- // Revocation blacklist threshold or 0 if none
- uint64_t revocationThreshold;
- // THEIR tag
- Tag tag;
- };
-
- // Credentials and related state
- struct _RemoteCapability
+ template<typename T>
+ struct _RemoteCredential
{
- _RemoteCapability() : id(ZT_MEMBERSHIP_CRED_ID_UNUSED),lastReceived(0),revocationThreshold(0) {}
- // Capability ID (last 32 bits, first 32 bits are set in unused entries to sort them to end)
+ _RemoteCredential() : id(ZT_MEMBERSHIP_CRED_ID_UNUSED),lastReceived(0),revocationThreshold(0) {}
uint64_t id;
- // Last time we received THEIR capability (with this ID)
- uint64_t lastReceived;
- // Revocation blacklist threshold or 0 if none
- uint64_t revocationThreshold;
- // THEIR capability
- Capability cap;
+ uint64_t lastReceived; // last time we got this credential
+ uint64_t revocationThreshold; // credentials before this time are invalid
+ T credential;
+ inline bool operator<(const _RemoteCredential &c) const { return (id < c.id); }
};
- // Comparison operator for remote credential entries
template<typename T>
- struct _RemoteCredentialSorter
+ struct _RemoteCredentialComp
{
- inline bool operator()(const T *a,const T *b) const { return (a->id < b->id); }
- inline bool operator()(const uint64_t a,const T *b) const { return (a < b->id); }
- inline bool operator()(const T *a,const uint64_t b) const { return (a->id < b); }
+ inline bool operator()(const _RemoteCredential<T> *a,const _RemoteCredential<T> *b) const { return (a->id < b->id); }
+ inline bool operator()(const uint64_t a,const _RemoteCredential<T> *b) const { return (a < b->id); }
+ inline bool operator()(const _RemoteCredential<T> *a,const uint64_t b) const { return (a->id < b); }
inline bool operator()(const uint64_t a,const uint64_t b) const { return (a < b); }
};
@@ -89,8 +70,8 @@ private:
struct _LocalCredentialPushState
{
_LocalCredentialPushState() : lastPushed(0),id(0) {}
- uint64_t lastPushed;
- uint32_t id;
+ uint64_t lastPushed; // last time we sent our own copy of this credential
+ uint64_t id;
};
public:
@@ -117,7 +98,7 @@ public:
{
for(;;) {
if ((_i != &(_m->_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]))&&((*_i)->id != ZT_MEMBERSHIP_CRED_ID_UNUSED)) {
- const Capability *tmp = &((*_i)->cap);
+ const Capability *tmp = &((*_i)->credential);
if (_m->_isCredentialTimestampValid(*_c,*tmp,**_i)) {
++_i;
return tmp;
@@ -131,7 +112,7 @@ public:
private:
const Membership *_m;
const NetworkConfig *_c;
- const _RemoteCapability *const *_i;
+ const _RemoteCredential<Capability> *const *_i;
};
friend class CapabilityIterator;
@@ -150,7 +131,7 @@ public:
{
for(;;) {
if ((_i != &(_m->_remoteTags[ZT_MAX_NETWORK_TAGS]))&&((*_i)->id != ZT_MEMBERSHIP_CRED_ID_UNUSED)) {
- const Tag *tmp = &((*_i)->tag);
+ const Tag *tmp = &((*_i)->credential);
if (_m->_isCredentialTimestampValid(*_c,*tmp,**_i)) {
++_i;
return tmp;
@@ -164,7 +145,7 @@ public:
private:
const Membership *_m;
const NetworkConfig *_c;
- const _RemoteTag *const *_i;
+ const _RemoteCredential<Tag> *const *_i;
};
friend class TagIterator;
@@ -249,12 +230,19 @@ public:
*/
AddCredentialResult addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Revocation &rev);
+ /**
+ * Validate and add a credential if signature is okay and it's otherwise good
+ */
+ AddCredentialResult addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const CertificateOfOwnership &coo);
+
private:
- _RemoteTag *_newTag(const uint64_t id);
- _RemoteCapability *_newCapability(const uint64_t id);
+ _RemoteCredential<Tag> *_newTag(const uint64_t id);
+ _RemoteCredential<Capability> *_newCapability(const uint64_t id);
+ _RemoteCredential<CertificateOfOwnership> *_newCoo(const uint64_t id);
bool _revokeCom(const Revocation &rev);
bool _revokeCap(const Revocation &rev,const uint64_t now);
bool _revokeTag(const Revocation &rev,const uint64_t now);
+ bool _revokeCoo(const Revocation &rev,const uint64_t now);
template<typename C,typename CS>
inline bool _isCredentialTimestampValid(const NetworkConfig &nconf,const C &cred,const CS &state) const
@@ -275,17 +263,20 @@ private:
// Remote member's latest network COM
CertificateOfMembership _com;
- // Sorted (in ascending order of ID) arrays of pointers to remote tags and capabilities
- _RemoteTag *_remoteTags[ZT_MAX_NETWORK_TAGS];
- _RemoteCapability *_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES];
+ // Sorted (in ascending order of ID) arrays of pointers to remote credentials
+ _RemoteCredential<Tag> *_remoteTags[ZT_MAX_NETWORK_TAGS];
+ _RemoteCredential<Capability> *_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES];
+ _RemoteCredential<CertificateOfOwnership> *_remoteCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP];
- // This is the RAM allocated for remote tags and capabilities from which the sorted arrays are populated
- _RemoteTag _tagMem[ZT_MAX_NETWORK_TAGS];
- _RemoteCapability _capMem[ZT_MAX_NETWORK_CAPABILITIES];
+ // This is the RAM allocated for remote credential cache objects
+ _RemoteCredential<Tag> _tagMem[ZT_MAX_NETWORK_TAGS];
+ _RemoteCredential<Capability> _capMem[ZT_MAX_NETWORK_CAPABILITIES];
+ _RemoteCredential<CertificateOfOwnership> _cooMem[ZT_MAX_CERTIFICATES_OF_OWNERSHIP];
// Local credential push state tracking
_LocalCredentialPushState _localTags[ZT_MAX_NETWORK_TAGS];
_LocalCredentialPushState _localCaps[ZT_MAX_NETWORK_CAPABILITIES];
+ _LocalCredentialPushState _localCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP];
};
} // namespace ZeroTier
diff --git a/node/Network.hpp b/node/Network.hpp
index 85ee6e9a..56c7fc60 100644
--- a/node/Network.hpp
+++ b/node/Network.hpp
@@ -302,6 +302,17 @@ public:
Membership::AddCredentialResult addCredential(const Address &sentFrom,const Revocation &rev);
/**
+ * Validate a credential and learn it if it passes certificate and other checks
+ */
+ inline Membership::AddCredentialResult addCredential(const CertificateOfOwnership &coo)
+ {
+ if (coo.networkId() != _id)
+ return Membership::ADD_REJECTED;
+ Mutex::Lock _l(_lock);
+ return _membership(coo.issuedTo()).addCredential(RR,_config,coo);
+ }
+
+ /**
* Force push credentials (COM, etc.) to a peer now
*
* @param to Destination peer address
diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp
index 2f356b15..fe7393e8 100644
--- a/node/NetworkConfig.cpp
+++ b/node/NetworkConfig.cpp
@@ -21,7 +21,6 @@
#include <algorithm>
#include "NetworkConfig.hpp"
-#include "Utils.hpp"
namespace ZeroTier {
@@ -138,6 +137,13 @@ bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,b
}
tmp->clear();
+ for(unsigned int i=0;i<this->certificateOfOwnershipCount;++i)
+ this->certificatesOfOwnership[i].serialize(*tmp);
+ if (tmp->size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP,*tmp)) return false;
+ }
+
+ tmp->clear();
for(unsigned int i=0;i<this->specialistCount;++i)
tmp->append((uint64_t)this->specialists[i]);
if (tmp->size()) {
@@ -297,10 +303,23 @@ bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACI
std::sort(&(this->tags[0]),&(this->tags[this->tagCount]));
}
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP,*tmp)) {
+ unsigned int p = 0;
+ while (p < tmp->size()) {
+ if (certificateOfOwnershipCount < ZT_MAX_CERTIFICATES_OF_OWNERSHIP)
+ p += certificatesOfOwnership[certificateOfOwnershipCount++].deserialize(*tmp,p);
+ else {
+ CertificateOfOwnership foo;
+ p += foo.deserialize(*tmp,p);
+ }
+ }
+ }
+
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,*tmp)) {
unsigned int p = 0;
- while (((p + 8) <= tmp->size())&&(specialistCount < ZT_MAX_NETWORK_SPECIALISTS)) {
- this->specialists[this->specialistCount++] = tmp->at<uint64_t>(p);
+ while ((p + 8) <= tmp->size()) {
+ if (specialistCount < ZT_MAX_NETWORK_SPECIALISTS)
+ this->specialists[this->specialistCount++] = tmp->at<uint64_t>(p);
p += 8;
}
}
diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp
index 39087395..85c24090 100644
--- a/node/NetworkConfig.hpp
+++ b/node/NetworkConfig.hpp
@@ -35,10 +35,12 @@
#include "MulticastGroup.hpp"
#include "Address.hpp"
#include "CertificateOfMembership.hpp"
+#include "CertificateOfOwnership.hpp"
#include "Capability.hpp"
#include "Tag.hpp"
#include "Dictionary.hpp"
#include "Identity.hpp"
+#include "Utils.hpp"
/**
* Default maximum time delta for COMs, tags, and capabilities
@@ -99,7 +101,7 @@
namespace ZeroTier {
// Dictionary capacity needed for max size network config
-#define ZT_NETWORKCONFIG_DICT_CAPACITY (4096 + (sizeof(ZT_VirtualNetworkRule) * ZT_MAX_NETWORK_RULES) + (sizeof(Capability) * ZT_MAX_NETWORK_CAPABILITIES) + (sizeof(Tag) * ZT_MAX_NETWORK_TAGS))
+#define ZT_NETWORKCONFIG_DICT_CAPACITY (1024 + (sizeof(ZT_VirtualNetworkRule) * ZT_MAX_NETWORK_RULES) + (sizeof(Capability) * ZT_MAX_NETWORK_CAPABILITIES) + (sizeof(Tag) * ZT_MAX_NETWORK_TAGS) + (sizeof(CertificateOfOwnership) * ZT_MAX_CERTIFICATES_OF_OWNERSHIP))
// Dictionary capacity needed for max size network meta-data
#define ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY 1024
@@ -173,6 +175,8 @@ namespace ZeroTier {
#define ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES "CAP"
// tags (binary blobs)
#define ZT_NETWORKCONFIG_DICT_KEY_TAGS "TAG"
+// tags (binary blobs)
+#define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP "COO"
// curve25519 signature
#define ZT_NETWORKCONFIG_DICT_KEY_SIGNATURE "C25519"
@@ -474,11 +478,6 @@ public:
unsigned int staticIpCount;
/**
- * Number of pinned devices (devices with physical address hints)
- */
- unsigned int pinnedCount;
-
- /**
* Number of rule table entries
*/
unsigned int ruleCount;
@@ -494,6 +493,11 @@ public:
unsigned int tagCount;
/**
+ * Number of certificates of ownership
+ */
+ unsigned int certificateOfOwnershipCount;
+
+ /**
* Specialist devices
*
* For each entry the least significant 40 bits are the device's ZeroTier
@@ -527,6 +531,11 @@ public:
Tag tags[ZT_MAX_NETWORK_TAGS];
/**
+ * Certificates of ownership for this network member
+ */
+ CertificateOfOwnership certificatesOfOwnership[ZT_MAX_CERTIFICATES_OF_OWNERSHIP];
+
+ /**
* Network type (currently just public or private)
*/
ZT_VirtualNetworkType type;
diff --git a/node/Packet.hpp b/node/Packet.hpp
index b736b84a..6482356a 100644
--- a/node/Packet.hpp
+++ b/node/Packet.hpp
@@ -730,6 +730,8 @@ public:
* <[...] one or more serialized Tags>
* <[2] 16-bit number of revocations>
* <[...] one or more serialized Revocations>
+ * <[2] 16-bit number of certificates of ownership>
+ * <[...] one or more serialized CertificateOfOwnership>
*
* This can be sent by anyone at any time to push network credentials.
* These will of course only be accepted if they are properly signed.
diff --git a/node/Revocation.hpp b/node/Revocation.hpp
index bc290e75..3903f440 100644
--- a/node/Revocation.hpp
+++ b/node/Revocation.hpp
@@ -50,9 +50,10 @@ public:
enum CredentialType
{
CREDENTIAL_TYPE_ALL = 0,
- CREDENTIAL_TYPE_COM = 1,
+ CREDENTIAL_TYPE_COM = 1, // CertificateOfMembership
CREDENTIAL_TYPE_CAPABILITY = 2,
- CREDENTIAL_TYPE_TAG = 3
+ CREDENTIAL_TYPE_TAG = 3,
+ CREDENTIAL_TYPE_COO = 4 // CertificateOfOwnership
};
Revocation()
diff --git a/node/Tag.hpp b/node/Tag.hpp
index 65348200..146e8da9 100644
--- a/node/Tag.hpp
+++ b/node/Tag.hpp
@@ -139,7 +139,8 @@ public:
{
unsigned int p = startAt;
- // These are the same between Tag and Capability
+ memset(this,0,sizeof(Tag));
+
_networkId = b.template at<uint64_t>(p); p += 8;
_ts = b.template at<uint64_t>(p); p += 8;
_id = b.template at<uint32_t>(p); p += 4;
diff --git a/objects.mk b/objects.mk
index 31498b72..427024eb 100644
--- a/objects.mk
+++ b/objects.mk
@@ -4,6 +4,7 @@ OBJS=\
node/C25519.o \
node/Capability.o \
node/CertificateOfMembership.o \
+ node/CertificateOfOwnership.o \
node/Cluster.o \
node/Identity.o \
node/IncomingPacket.o \