Ver Fonte

Remove capability chain of custody stuff which was never used and was really overly complex and a potential source of security problems.

Adam Ierymenko há 5 anos atrás
pai
commit
0d05e4bcae
7 ficheiros alterados com 60 adições e 142 exclusões
  1. 0 5
      include/ZeroTierCore.h
  2. 1 1
      node/Address.hpp
  3. 0 8
      node/Buf.hpp
  4. 1 1
      node/C25519.hpp
  5. 44 41
      node/Capability.cpp
  6. 13 47
      node/Capability.hpp
  7. 1 39
      node/Credential.cpp

+ 0 - 5
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
-
 /* ----------------------------------------------------------------------------------------------------------------- */
 
 /**

+ 1 - 1
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; }

+ 0 - 8
node/Buf.hpp

@@ -234,14 +234,6 @@ public:
 		Utils::zero<ZT_BUF_MEM_SIZE>(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
 	 *

+ 1 - 1
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

+ 44 - 41
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<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH));++i) {
-			if (!(_custody[i].to)) {
-				_custody[i].to = to;
-				_custody[i].from = from.address();
-				_custody[i].signatureLength = from.sign(buf,(unsigned int)marshal(buf,true),_custody[i].signature,sizeof(_custody[i].signature));
-				return true;
-			}
-		}
-	} catch ( ... ) {}
-	return false;
+	_issuedTo = to;
+	_signedBy = from.address();
+	_signatureLength = from.sign(buf,(unsigned int)marshal(buf,true),_signature,sizeof(_signature));
+	return _signatureLength > 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<uint64_t>(data + p,_nwid); p += 8;
 	Utils::storeBigEndian<uint64_t>(data + p,(uint64_t)_ts); p += 8;
 	Utils::storeBigEndian<uint32_t>(data + p,_id); p += 4;
+
 	Utils::storeBigEndian<uint16_t>(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<uint16_t>(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<ZT_ADDRESS_LENGTH;++k)
-					data[p++] = 0;
-				break;
-			}
-		}
+		_issuedTo.copyTo(data + p); p += ZT_ADDRESS_LENGTH;
+		_signedBy.copyTo(data + 0); p += ZT_ADDRESS_LENGTH;
+		data[p++] = 1; // LEGACY: old versions require a reserved byte here
+		Utils::storeBigEndian<uint16_t>(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<ZT_ADDRESS_LENGTH;++k)
+			data[p++] = 0;
 	}
+
 	data[p++] = 0;
 	data[p++] = 0; // uint16_t size of additional fields, currently 0
+
 	if (forSign) {
 		for(int k=0;k<8;++k)
 			data[p++] = 0x7f;
 	}
+
 	return p;
 }
 
@@ -81,7 +79,7 @@ int Capability::unmarshal(const uint8_t *data,int len) noexcept
 	_ts = (int64_t)Utils::loadBigEndian<uint64_t>(data + 8);
 	_id = Utils::loadBigEndian<uint32_t>(data + 16);
 
-	const unsigned int rc = Utils::loadBigEndian<uint16_t>(data + 20);;
+	const unsigned int rc = Utils::loadBigEndian<uint16_t>(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<uint16_t>(data + p); p += 2;
-		_custody[i].signatureLength = sl;
-		if ((sl > sizeof(_custody[i].signature))||((p + (int)sl) > len))
+		_signatureLength = Utils::loadBigEndian<uint16_t>(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<uint16_t>(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:

+ 13 - 47
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;i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH;++i) {
-			if (!_custody[i].to)
-				return i2;
-			else i2 = _custody[i].to;
-		}
-		return i2;
-	}
+	ZT_INLINE const Address &issuedTo() const noexcept { return _issuedTo; }
+	ZT_INLINE const Address &signer() const noexcept { return _signedBy; }
+	ZT_INLINE const uint8_t *signature() const noexcept { return _signature; }
+	ZT_INLINE unsigned int signatureLength() const noexcept { return _signatureLength; }
 
 	/**
 	 * Sign this capability and add signature to its chain of custody
@@ -182,18 +154,12 @@ private:
 	uint64_t _nwid;
 	int64_t _ts;
 	uint32_t _id;
-
-	unsigned int _maxCustodyChainLength;
-
 	unsigned int _ruleCount;
 	ZT_VirtualNetworkRule _rules[ZT_MAX_CAPABILITY_RULES];
-
-	struct {
-		Address to;
-		Address from;
-		unsigned int signatureLength;
-		uint8_t signature[ZT_SIGNATURE_BUFFER_SIZE];
-	} _custody[ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH];
+	Address _issuedTo;
+	Address _signedBy;
+	unsigned int _signatureLength;
+	uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE];
 };
 
 } // namespace ZeroTier

+ 1 - 39
node/Credential.cpp

@@ -68,6 +68,7 @@ static ZT_INLINE Credential::VerifyResult _credVerify(const RuntimeEnvironment *
 
 Credential::VerifyResult Credential::_verify(const RuntimeEnvironment *const RR,void *tPtr,const Revocation &credential) const { return _credVerify(RR,tPtr,credential); }
 Credential::VerifyResult Credential::_verify(const RuntimeEnvironment *const RR,void *tPtr,const Tag &credential) const { return _credVerify(RR,tPtr,credential); }
+Credential::VerifyResult Credential::_verify(const RuntimeEnvironment *const RR,void *tPtr,const Capability &credential) const { return _credVerify(RR,tPtr,credential); }
 Credential::VerifyResult Credential::_verify(const RuntimeEnvironment *const RR,void *tPtr,const CertificateOfOwnership &credential) const { return _credVerify(RR,tPtr,credential); }
 
 Credential::VerifyResult Credential::_verify(const RuntimeEnvironment *const RR,void *tPtr,const CertificateOfMembership &credential) const
@@ -87,43 +88,4 @@ Credential::VerifyResult Credential::_verify(const RuntimeEnvironment *const RR,
 	return peer->identity().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<credential._maxCustodyChainLength;++c) {
-			if (c == 0) {
-				if ((!credential._custody[c].to)||(!credential._custody[c].from)||(credential._custody[c].from != Network::controllerFor(credential._nwid)))
-					return Credential::VERIFY_BAD_SIGNATURE; // the first entry must be present and from the network's controller
-			} else {
-				if (!credential._custody[c].to)
-					return Credential::VERIFY_OK; // all previous entries were valid, so we are valid
-				else if ((!credential._custody[c].from)||(credential._custody[c].from != credential._custody[c-1].to))
-					return Credential::VERIFY_BAD_SIGNATURE; // otherwise if we have another entry it must be from the previous holder in the chain
-			}
-
-			const SharedPtr<Peer> 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