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

This commit is contained in:
Adam Ierymenko 2020-03-28 12:06:10 -07:00
parent 87da45b3f5
commit 0d05e4bcae
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
7 changed files with 60 additions and 142 deletions

View file

@ -189,11 +189,6 @@ extern "C" {
*/ */
#define ZT_MAX_CERTIFICATES_OF_OWNERSHIP 4 #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
/* ----------------------------------------------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------------------------------------------- */
/** /**

View file

@ -31,7 +31,7 @@ class Address : public TriviallyCopyable
{ {
public: public:
ZT_INLINE Address() noexcept : _a(0) {} 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]) {} 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; } ZT_INLINE Address &operator=(const Address &a) noexcept { _a = a._a; return *this; }

View file

@ -234,14 +234,6 @@ public:
Utils::zero<ZT_BUF_MEM_SIZE>(unsafeData); 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 * Read a byte
* *

View file

@ -101,7 +101,7 @@ public:
* @param siglen Length of signature in bytes * @param siglen Length of signature in bytes
* @return True if signature is valid and the message is authentic and unmodified * @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: private:
// derive first 32 bytes of kp.pub from first 32 bytes of kp.priv // derive first 32 bytes of kp.pub from first 32 bytes of kp.priv

View file

@ -21,54 +21,52 @@ namespace ZeroTier {
bool Capability::sign(const Identity &from,const Address &to) noexcept bool Capability::sign(const Identity &from,const Address &to) noexcept
{ {
uint8_t buf[ZT_CAPABILITY_MARSHAL_SIZE_MAX + 16]; uint8_t buf[ZT_CAPABILITY_MARSHAL_SIZE_MAX + 16];
try { _issuedTo = to;
for(unsigned int i=0;((i<_maxCustodyChainLength)&&(i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH));++i) { _signedBy = from.address();
if (!(_custody[i].to)) { _signatureLength = from.sign(buf,(unsigned int)marshal(buf,true),_signature,sizeof(_signature));
_custody[i].to = to; return _signatureLength > 0;
_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;
} }
int Capability::marshal(uint8_t data[ZT_CAPABILITY_MARSHAL_SIZE_MAX],const bool forSign) const noexcept int Capability::marshal(uint8_t data[ZT_CAPABILITY_MARSHAL_SIZE_MAX],const bool forSign) const noexcept
{ {
int p = 0; int p = 0;
if (forSign) { if (forSign) {
for(int k=0;k<8;++k) for(int k=0;k<8;++k)
data[p++] = 0x7f; data[p++] = 0x7f;
} }
Utils::storeBigEndian<uint64_t>(data + p,_nwid); p += 8; Utils::storeBigEndian<uint64_t>(data + p,_nwid); p += 8;
Utils::storeBigEndian<uint64_t>(data + p,(uint64_t)_ts); p += 8; Utils::storeBigEndian<uint64_t>(data + p,(uint64_t)_ts); p += 8;
Utils::storeBigEndian<uint32_t>(data + p,_id); p += 4; Utils::storeBigEndian<uint32_t>(data + p,_id); p += 4;
Utils::storeBigEndian<uint16_t>(data + p,(uint16_t)_ruleCount); p += 2; Utils::storeBigEndian<uint16_t>(data + p,(uint16_t)_ruleCount); p += 2;
p += Capability::marshalVirtualNetworkRules(data + 22,_rules,_ruleCount); p += Capability::marshalVirtualNetworkRules(data + p,_rules,_ruleCount);
data[p++] = (uint8_t)_maxCustodyChainLength;
// 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) { if (!forSign) {
for(unsigned int i=0;;++i) { _issuedTo.copyTo(data + p); p += ZT_ADDRESS_LENGTH;
if ((i < _maxCustodyChainLength)&&(i < ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)&&(_custody[i].to)) { _signedBy.copyTo(data + 0); p += ZT_ADDRESS_LENGTH;
_custody[i].to.copyTo(data + p); p += ZT_ADDRESS_LENGTH; data[p++] = 1; // LEGACY: old versions require a reserved byte here
_custody[i].from.copyTo(data + p); p += ZT_ADDRESS_LENGTH; Utils::storeBigEndian<uint16_t>(data + p,(uint16_t)_signatureLength); p += 2;
data[p++] = 1; Utils::copy(data + p,_signature,_signatureLength); p += (int)_signatureLength;
Utils::storeBigEndian<uint16_t>(data + p,(uint16_t)_custody[i].signatureLength); p += 2;
for(unsigned int k=0;k<_custody[i].signatureLength;++k) // LEGACY: older versions supported more than one record terminated by a zero address.
data[p++] = _custody[i].signature[k]; for(int k=0;k<ZT_ADDRESS_LENGTH;++k)
} else { data[p++] = 0;
for(int k=0;k<ZT_ADDRESS_LENGTH;++k)
data[p++] = 0;
break;
}
}
} }
data[p++] = 0; data[p++] = 0;
data[p++] = 0; // uint16_t size of additional fields, currently 0 data[p++] = 0; // uint16_t size of additional fields, currently 0
if (forSign) { if (forSign) {
for(int k=0;k<8;++k) for(int k=0;k<8;++k)
data[p++] = 0x7f; data[p++] = 0x7f;
} }
return p; 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); _ts = (int64_t)Utils::loadBigEndian<uint64_t>(data + 8);
_id = Utils::loadBigEndian<uint32_t>(data + 16); _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) if (rc > ZT_MAX_CAPABILITY_RULES)
return -1; return -1;
const int rulesLen = unmarshalVirtualNetworkRules(data + 22,len - 22,_rules,_ruleCount,rc); 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) if (p >= len)
return -1; 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) { for(unsigned int i=0;;++i) {
if ((p + ZT_ADDRESS_LENGTH) > len) if ((p + ZT_ADDRESS_LENGTH) > len)
return -1; return -1;
const Address to(data + p); p += ZT_ADDRESS_LENGTH; const Address to(data + p); p += ZT_ADDRESS_LENGTH;
if (!to) break;
if ((i >= _maxCustodyChainLength)||(i >= ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)) if (!to)
return -1; break;
_custody[i].to = to;
_issuedTo = to;
if ((p + ZT_ADDRESS_LENGTH) > len) if ((p + ZT_ADDRESS_LENGTH) > len)
return -1; 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) if ((p + 2) > len)
return -1; return -1;
const unsigned int sl = Utils::loadBigEndian<uint16_t>(data + p); p += 2; _signatureLength = Utils::loadBigEndian<uint16_t>(data + p); p += 2;
_custody[i].signatureLength = sl; if ((_signatureLength > sizeof(_signature))||((p + (int)_signatureLength) > len))
if ((sl > sizeof(_custody[i].signature))||((p + (int)sl) > len))
return -1; 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) if ((p + 2) > len)
return -1; return -1;
p += 2 + Utils::loadBigEndian<uint16_t>(data + p); p += 2 + Utils::loadBigEndian<uint16_t>(data + p);
if (p > len) if (p > len)
return -1; 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_SOURCE:
case ZT_NETWORK_RULE_MATCH_IPV6_DEST: case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
data[p++] = 17; data[p++] = 17;
for(int k=0;k<16;++k) Utils::copy<16>(data + p,rules[i].v.ipv6.ip); p += 16;
data[p++] = rules[i].v.ipv6.ip[k];
data[p++] = rules[i].v.ipv6.mask; data[p++] = rules[i].v.ipv6.mask;
break; break;
case ZT_NETWORK_RULE_MATCH_IP_TOS: 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++]; const int fieldLen = (int)data[p++];
if ((p + fieldLen) > len) if ((p + fieldLen) > len)
return -1; return -1;
switch((ZT_VirtualNetworkRuleType)(rules[ruleCount].t & 0x3f)) { switch((ZT_VirtualNetworkRuleType)(rules[ruleCount].t & 0x3fU)) {
default: default:
break; break;
case ZT_NETWORK_RULE_ACTION_TEE: case ZT_NETWORK_RULE_ACTION_TEE:

View file

@ -26,16 +26,14 @@
#include "Identity.hpp" #include "Identity.hpp"
#define ZT_VIRTUALNETWORKRULE_MARSHAL_SIZE_MAX 21 #define ZT_VIRTUALNETWORKRULE_MARSHAL_SIZE_MAX 21
#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))
#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))
namespace ZeroTier { namespace ZeroTier {
class RuntimeEnvironment; 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: * 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 * Note that this is after evaluation of network scope rules and only if
* network scope rules do not deliver an explicit match. * 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 class Capability : public Credential
{ {
@ -74,12 +67,12 @@ public:
* @param rules Network flow rules for this capability * @param rules Network flow rules for this capability
* @param ruleCount Number of flow rules * @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), _nwid(nwid),
_ts(ts), _ts(ts),
_id(id), _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) if (_ruleCount > 0)
Utils::copy(_rules,rules,sizeof(ZT_VirtualNetworkRule) * _ruleCount); Utils::copy(_rules,rules,sizeof(ZT_VirtualNetworkRule) * _ruleCount);
@ -95,34 +88,13 @@ public:
*/ */
ZT_INLINE unsigned int ruleCount() const noexcept { return _ruleCount; } 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; } 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; } ZT_INLINE uint64_t networkId() const noexcept { return _nwid; }
/**
* @return Timestamp
*/
ZT_INLINE int64_t timestamp() const noexcept { return _ts; } ZT_INLINE int64_t timestamp() const noexcept { return _ts; }
ZT_INLINE const Address &issuedTo() const noexcept { return _issuedTo; }
/** ZT_INLINE const Address &signer() const noexcept { return _signedBy; }
* @return Last 'to' address in chain of custody ZT_INLINE const uint8_t *signature() const noexcept { return _signature; }
*/ ZT_INLINE unsigned int signatureLength() const noexcept { return _signatureLength; }
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;
}
/** /**
* Sign this capability and add signature to its chain of custody * Sign this capability and add signature to its chain of custody
@ -182,18 +154,12 @@ private:
uint64_t _nwid; uint64_t _nwid;
int64_t _ts; int64_t _ts;
uint32_t _id; uint32_t _id;
unsigned int _maxCustodyChainLength;
unsigned int _ruleCount; unsigned int _ruleCount;
ZT_VirtualNetworkRule _rules[ZT_MAX_CAPABILITY_RULES]; ZT_VirtualNetworkRule _rules[ZT_MAX_CAPABILITY_RULES];
Address _issuedTo;
struct { Address _signedBy;
Address to; unsigned int _signatureLength;
Address from; uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE];
unsigned int signatureLength;
uint8_t signature[ZT_SIGNATURE_BUFFER_SIZE];
} _custody[ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH];
}; };
} // namespace ZeroTier } // namespace ZeroTier

View file

@ -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 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 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 CertificateOfOwnership &credential) const { return _credVerify(RR,tPtr,credential); }
Credential::VerifyResult Credential::_verify(const RuntimeEnvironment *const RR,void *tPtr,const CertificateOfMembership &credential) const 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; 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 } // namespace ZeroTier