diff --git a/include/ZeroTierCore.h b/include/ZeroTierCore.h index d32f691b5..230275e48 100644 --- a/include/ZeroTierCore.h +++ b/include/ZeroTierCore.h @@ -189,11 +189,6 @@ extern "C" { */ #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/Address.hpp b/node/Address.hpp index efd5bb0f1..57feadd9f 100644 --- a/node/Address.hpp +++ b/node/Address.hpp @@ -31,7 +31,7 @@ class Address : public TriviallyCopyable { public: ZT_INLINE Address() noexcept : _a(0) {} - ZT_INLINE Address(const uint64_t a) noexcept : _a(a) {} + explicit ZT_INLINE Address(const uint64_t a) noexcept : _a(a) {} explicit ZT_INLINE Address(const uint8_t b[5]) noexcept : _a(((uint64_t)b[0] << 32U) | ((uint64_t)b[1] << 24U) | ((uint64_t)b[2] << 16U) | ((uint64_t)b[3] << 8U) | (uint64_t)b[4]) {} ZT_INLINE Address &operator=(const Address &a) noexcept { _a = a._a; return *this; } diff --git a/node/Buf.hpp b/node/Buf.hpp index be83e376f..4d9186964 100644 --- a/node/Buf.hpp +++ b/node/Buf.hpp @@ -234,14 +234,6 @@ public: Utils::zero(unsafeData); } - /** - * Zero security critical data using Utils::burn() to ensure it's never optimized out. - */ - ZT_INLINE void burn() noexcept - { - Utils::burn(unsafeData,ZT_BUF_MEM_SIZE); - } - /** * Read a byte * diff --git a/node/C25519.hpp b/node/C25519.hpp index a47271823..062c7077c 100644 --- a/node/C25519.hpp +++ b/node/C25519.hpp @@ -101,7 +101,7 @@ public: * @param siglen Length of signature in bytes * @return True if signature is valid and the message is authentic and unmodified */ - static bool verify(const uint8_t their[ZT_C25519_PUBLIC_KEY_LEN],const void *msg,unsigned int len,const void *signature,const unsigned int siglen); + static bool verify(const uint8_t their[ZT_C25519_PUBLIC_KEY_LEN],const void *msg,unsigned int len,const void *signature,unsigned int siglen); private: // derive first 32 bytes of kp.pub from first 32 bytes of kp.priv diff --git a/node/Capability.cpp b/node/Capability.cpp index efea90df6..661a8ea33 100644 --- a/node/Capability.cpp +++ b/node/Capability.cpp @@ -21,54 +21,52 @@ namespace ZeroTier { bool Capability::sign(const Identity &from,const Address &to) noexcept { uint8_t buf[ZT_CAPABILITY_MARSHAL_SIZE_MAX + 16]; - try { - for(unsigned int i=0;((i<_maxCustodyChainLength)&&(i 0; } int Capability::marshal(uint8_t data[ZT_CAPABILITY_MARSHAL_SIZE_MAX],const bool forSign) const noexcept { int p = 0; + if (forSign) { for(int k=0;k<8;++k) data[p++] = 0x7f; } + Utils::storeBigEndian(data + p,_nwid); p += 8; Utils::storeBigEndian(data + p,(uint64_t)_ts); p += 8; Utils::storeBigEndian(data + p,_id); p += 4; + Utils::storeBigEndian(data + p,(uint16_t)_ruleCount); p += 2; - p += Capability::marshalVirtualNetworkRules(data + 22,_rules,_ruleCount); - data[p++] = (uint8_t)_maxCustodyChainLength; + p += Capability::marshalVirtualNetworkRules(data + p,_rules,_ruleCount); + + // LEGACY: older versions supported multiple records with this being a maximum custody + // chain length. This is deprecated so set the max chain length to one. + data[p++] = (uint8_t)1; + if (!forSign) { - for(unsigned int i=0;;++i) { - if ((i < _maxCustodyChainLength)&&(i < ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)&&(_custody[i].to)) { - _custody[i].to.copyTo(data + p); p += ZT_ADDRESS_LENGTH; - _custody[i].from.copyTo(data + p); p += ZT_ADDRESS_LENGTH; - data[p++] = 1; - Utils::storeBigEndian(data + p,(uint16_t)_custody[i].signatureLength); p += 2; - for(unsigned int k=0;k<_custody[i].signatureLength;++k) - data[p++] = _custody[i].signature[k]; - } else { - for(int k=0;k(data + p,(uint16_t)_signatureLength); p += 2; + Utils::copy(data + p,_signature,_signatureLength); p += (int)_signatureLength; + + // LEGACY: older versions supported more than one record terminated by a zero address. + for(int k=0;k(data + 8); _id = Utils::loadBigEndian(data + 16); - const unsigned int rc = Utils::loadBigEndian(data + 20);; + const unsigned int rc = Utils::loadBigEndian(data + 20); if (rc > ZT_MAX_CAPABILITY_RULES) return -1; const int rulesLen = unmarshalVirtualNetworkRules(data + 22,len - 22,_rules,_ruleCount,rc); @@ -91,31 +89,37 @@ int Capability::unmarshal(const uint8_t *data,int len) noexcept if (p >= len) return -1; - _maxCustodyChainLength = data[p++]; + ++p; // LEGACY: skip old max record count + // LEGACY: since it was once supported to have multiple records, scan them all. Since + // this feature was never used, just set the signature and issued to and other related + // fields each time and we should only ever see one. If there's more than one and the + // last is not the controller, this credential will just fail validity check. for(unsigned int i=0;;++i) { if ((p + ZT_ADDRESS_LENGTH) > len) return -1; const Address to(data + p); p += ZT_ADDRESS_LENGTH; - if (!to) break; - if ((i >= _maxCustodyChainLength)||(i >= ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)) - return -1; - _custody[i].to = to; + + if (!to) + break; + + _issuedTo = to; if ((p + ZT_ADDRESS_LENGTH) > len) return -1; - _custody[i].from.setTo(data + p); p += ZT_ADDRESS_LENGTH + 1; + _signedBy.setTo(data + p); p += ZT_ADDRESS_LENGTH + 1; // LEGACY: +1 to skip reserved field + if ((p + 2) > len) return -1; - const unsigned int sl = Utils::loadBigEndian(data + p); p += 2; - _custody[i].signatureLength = sl; - if ((sl > sizeof(_custody[i].signature))||((p + (int)sl) > len)) + _signatureLength = Utils::loadBigEndian(data + p); p += 2; + if ((_signatureLength > sizeof(_signature))||((p + (int)_signatureLength) > len)) return -1; - Utils::copy(_custody[i].signature,data + p,sl); p += (int)sl; + Utils::copy(_signature,data + p,_signatureLength); p += (int)_signatureLength; } if ((p + 2) > len) return -1; p += 2 + Utils::loadBigEndian(data + p); + if (p > len) return -1; @@ -173,8 +177,7 @@ int Capability::marshalVirtualNetworkRules(uint8_t *data,const ZT_VirtualNetwork case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE: case ZT_NETWORK_RULE_MATCH_IPV6_DEST: data[p++] = 17; - for(int k=0;k<16;++k) - data[p++] = rules[i].v.ipv6.ip[k]; + Utils::copy<16>(data + p,rules[i].v.ipv6.ip); p += 16; data[p++] = rules[i].v.ipv6.mask; break; case ZT_NETWORK_RULE_MATCH_IP_TOS: @@ -250,7 +253,7 @@ int Capability::unmarshalVirtualNetworkRules(const uint8_t *const data,const int const int fieldLen = (int)data[p++]; if ((p + fieldLen) > len) return -1; - switch((ZT_VirtualNetworkRuleType)(rules[ruleCount].t & 0x3f)) { + switch((ZT_VirtualNetworkRuleType)(rules[ruleCount].t & 0x3fU)) { default: break; case ZT_NETWORK_RULE_ACTION_TEE: diff --git a/node/Capability.hpp b/node/Capability.hpp index 276b96e50..ded1addb1 100644 --- a/node/Capability.hpp +++ b/node/Capability.hpp @@ -26,16 +26,14 @@ #include "Identity.hpp" #define ZT_VIRTUALNETWORKRULE_MARSHAL_SIZE_MAX 21 - -#define ZT_CAPABILITY__CUSTODY_CHAIN_ITEM_MARSHAL_SIZE_MAX (5 + 5 + 2 + ZT_SIGNATURE_BUFFER_SIZE) -#define ZT_CAPABILITY_MARSHAL_SIZE_MAX (8 + 8 + 4 + 1 + 2 + (ZT_VIRTUALNETWORKRULE_MARSHAL_SIZE_MAX * ZT_MAX_CAPABILITY_RULES) + 2 + (ZT_CAPABILITY__CUSTODY_CHAIN_ITEM_MARSHAL_SIZE_MAX * ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)) +#define ZT_CAPABILITY_MARSHAL_SIZE_MAX (8 + 8 + 4 + 1 + 2 + (ZT_VIRTUALNETWORKRULE_MARSHAL_SIZE_MAX * ZT_MAX_CAPABILITY_RULES) + 2 + (5 + 5 + 1 + 2 + ZT_SIGNATURE_BUFFER_SIZE)) namespace ZeroTier { class RuntimeEnvironment; /** - * A set of grouped and signed network flow rules + * A set of grouped and signed network flow rules for a specific member. * * On the sending side the sender does the following for each packet: * @@ -51,11 +49,6 @@ class RuntimeEnvironment; * * Note that this is after evaluation of network scope rules and only if * network scope rules do not deliver an explicit match. - * - * Capabilities support a chain of custody. This is currently unused but - * in the future would allow the publication of capabilities that can be - * handed off between nodes. Limited transferability of capabilities is - * a feature of true capability based security. */ class Capability : public Credential { @@ -74,12 +67,12 @@ public: * @param rules Network flow rules for this capability * @param ruleCount Number of flow rules */ - ZT_INLINE Capability(const uint32_t id,const uint64_t nwid,const int64_t ts,const unsigned int mccl,const ZT_VirtualNetworkRule *const rules,const unsigned int ruleCount) noexcept : + ZT_INLINE Capability(const uint32_t id,const uint64_t nwid,const int64_t ts,const ZT_VirtualNetworkRule *const rules,const unsigned int ruleCount) noexcept : _nwid(nwid), _ts(ts), _id(id), - _maxCustodyChainLength((mccl > 0) ? ((mccl < ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH) ? mccl : (unsigned int)ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH) : 1), - _ruleCount((ruleCount < ZT_MAX_CAPABILITY_RULES) ? ruleCount : ZT_MAX_CAPABILITY_RULES) + _ruleCount((ruleCount < ZT_MAX_CAPABILITY_RULES) ? ruleCount : ZT_MAX_CAPABILITY_RULES), + _signatureLength(0) { if (_ruleCount > 0) Utils::copy(_rules,rules,sizeof(ZT_VirtualNetworkRule) * _ruleCount); @@ -95,34 +88,13 @@ public: */ ZT_INLINE unsigned int ruleCount() const noexcept { return _ruleCount; } - /** - * @return ID and evaluation order of this capability in network - */ ZT_INLINE uint32_t id() const noexcept { return _id; } - - /** - * @return Network ID for which this capability was issued - */ ZT_INLINE uint64_t networkId() const noexcept { return _nwid; } - - /** - * @return Timestamp - */ ZT_INLINE int64_t timestamp() const noexcept { return _ts; } - - /** - * @return Last 'to' address in chain of custody - */ - ZT_INLINE Address issuedTo() const noexcept - { - Address i2; - for(int i=0;iidentity().verify(buf,bufSize,credential._signature,credential._signatureLength) ? Credential::VERIFY_OK : Credential::VERIFY_BAD_SIGNATURE; } -Credential::VerifyResult Credential::_verify(const RuntimeEnvironment *RR,void *tPtr,const Capability &credential) const -{ - uint8_t tmp[ZT_CAPABILITY_MARSHAL_SIZE_MAX + 16]; - try { - // There must be at least one entry, and sanity check for bad chain max length - if ((credential._maxCustodyChainLength < 1)||(credential._maxCustodyChainLength > ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)) - return Credential::VERIFY_BAD_SIGNATURE; - - int l = credential.marshal(tmp,true); - if (l <= 0) - return Credential::VERIFY_BAD_SIGNATURE; - - // Validate all entries in chain of custody - for(unsigned int c=0;c peer(RR->topology->peer(tPtr,credential._custody[c].from)); - if (peer) { - if (!peer->identity().verify(tmp,(unsigned int)l,credential._custody[c].signature,credential._custody[c].signatureLength)) - return Credential::VERIFY_BAD_SIGNATURE; - } else { - return Credential::VERIFY_NEED_IDENTITY; - } - } - - // We reached max custody chain length and everything was valid - return Credential::VERIFY_OK; - } catch ( ... ) {} - return Credential::VERIFY_BAD_SIGNATURE; -} - } // namespace ZeroTier