mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-01-18 02:40:13 +00:00
Backport only the COM mitigation instead of everything from 1.8
This commit is contained in:
parent
75a45eeb27
commit
eabe091038
@ -1730,7 +1730,7 @@ void EmbeddedNetworkController::_request(
|
||||
nc->certificateOfOwnershipCount = 1;
|
||||
}
|
||||
|
||||
CertificateOfMembership com(now,credentialtmd,nwid,identity.address());
|
||||
CertificateOfMembership com(now,credentialtmd,nwid,identity);
|
||||
if (com.sign(_signingId)) {
|
||||
nc->com = com;
|
||||
} else {
|
||||
|
@ -20,165 +20,67 @@
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
void CertificateOfMembership::setQualifier(uint64_t id,uint64_t value,uint64_t maxDelta)
|
||||
CertificateOfMembership::CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Identity &issuedTo)
|
||||
{
|
||||
_signedBy.zero();
|
||||
_qualifiers[0].id = COM_RESERVED_ID_TIMESTAMP;
|
||||
_qualifiers[0].value = timestamp;
|
||||
_qualifiers[0].maxDelta = timestampMaxDelta;
|
||||
_qualifiers[1].id = COM_RESERVED_ID_NETWORK_ID;
|
||||
_qualifiers[1].value = nwid;
|
||||
_qualifiers[1].maxDelta = 0;
|
||||
_qualifiers[2].id = COM_RESERVED_ID_ISSUED_TO;
|
||||
_qualifiers[2].value = issuedTo.address().toInt();
|
||||
_qualifiers[2].maxDelta = 0xffffffffffffffffULL;
|
||||
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
if (_qualifiers[i].id == id) {
|
||||
_qualifiers[i].value = value;
|
||||
_qualifiers[i].maxDelta = maxDelta;
|
||||
return;
|
||||
}
|
||||
// Include hash of full identity public key in COM for hardening purposes. Pack it in
|
||||
// using the original COM format. Format may be revised in the future to make this cleaner.
|
||||
uint64_t idHash[6];
|
||||
issuedTo.publicKeyHash(idHash);
|
||||
for(unsigned long i=0;i<4;++i) {
|
||||
_qualifiers[i + 3].id = (uint64_t)(i + 3);
|
||||
_qualifiers[i + 3].value = Utils::ntoh(idHash[i]);
|
||||
_qualifiers[i + 3].maxDelta = 0xffffffffffffffffULL;
|
||||
}
|
||||
|
||||
if (_qualifierCount < ZT_NETWORK_COM_MAX_QUALIFIERS) {
|
||||
_qualifiers[_qualifierCount].id = id;
|
||||
_qualifiers[_qualifierCount].value = value;
|
||||
_qualifiers[_qualifierCount].maxDelta = maxDelta;
|
||||
++_qualifierCount;
|
||||
std::sort(&(_qualifiers[0]),&(_qualifiers[_qualifierCount]));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
|
||||
std::string CertificateOfMembership::toString() const
|
||||
{
|
||||
char tmp[ZT_NETWORK_COM_MAX_QUALIFIERS * 32];
|
||||
std::string s;
|
||||
|
||||
s.append("1:"); // COM_UINT64_ED25519
|
||||
|
||||
uint64_t *const buf = new uint64_t[_qualifierCount * 3];
|
||||
try {
|
||||
unsigned int ptr = 0;
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
buf[ptr++] = Utils::hton(_qualifiers[i].id);
|
||||
buf[ptr++] = Utils::hton(_qualifiers[i].value);
|
||||
buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
|
||||
}
|
||||
s.append(Utils::hex(buf,ptr * sizeof(uint64_t),tmp));
|
||||
delete [] buf;
|
||||
} catch ( ... ) {
|
||||
delete [] buf;
|
||||
throw;
|
||||
}
|
||||
|
||||
s.push_back(':');
|
||||
|
||||
s.append(_signedBy.toString(tmp));
|
||||
|
||||
if (_signedBy) {
|
||||
s.push_back(':');
|
||||
s.append(Utils::hex(_signature.data,ZT_C25519_SIGNATURE_LEN,tmp));
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void CertificateOfMembership::fromString(const char *s)
|
||||
{
|
||||
_qualifierCount = 0;
|
||||
_signedBy.zero();
|
||||
_qualifierCount = 7;
|
||||
memset(_signature.data,0,ZT_C25519_SIGNATURE_LEN);
|
||||
|
||||
if (!*s)
|
||||
return;
|
||||
|
||||
unsigned int colonAt = 0;
|
||||
while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
|
||||
|
||||
if (!((colonAt == 1)&&(s[0] == '1'))) // COM_UINT64_ED25519?
|
||||
return;
|
||||
|
||||
s += colonAt + 1;
|
||||
colonAt = 0;
|
||||
while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
|
||||
|
||||
if (colonAt) {
|
||||
const unsigned int buflen = colonAt / 2;
|
||||
char *const buf = new char[buflen];
|
||||
unsigned int bufactual = Utils::unhex(s,colonAt,buf,buflen);
|
||||
char *bufptr = buf;
|
||||
try {
|
||||
while (bufactual >= 24) {
|
||||
if (_qualifierCount < ZT_NETWORK_COM_MAX_QUALIFIERS) {
|
||||
_qualifiers[_qualifierCount].id = Utils::ntoh(*((uint64_t *)bufptr)); bufptr += 8;
|
||||
_qualifiers[_qualifierCount].value = Utils::ntoh(*((uint64_t *)bufptr)); bufptr += 8;
|
||||
_qualifiers[_qualifierCount].maxDelta = Utils::ntoh(*((uint64_t *)bufptr)); bufptr += 8;
|
||||
++_qualifierCount;
|
||||
} else {
|
||||
bufptr += 24;
|
||||
}
|
||||
bufactual -= 24;
|
||||
}
|
||||
} catch ( ... ) {}
|
||||
delete [] buf;
|
||||
}
|
||||
|
||||
if (s[colonAt]) {
|
||||
s += colonAt + 1;
|
||||
colonAt = 0;
|
||||
while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
|
||||
|
||||
if (colonAt) {
|
||||
char addrbuf[ZT_ADDRESS_LENGTH];
|
||||
if (Utils::unhex(s,colonAt,addrbuf,sizeof(addrbuf)) == ZT_ADDRESS_LENGTH)
|
||||
_signedBy.setTo(addrbuf,ZT_ADDRESS_LENGTH);
|
||||
|
||||
if ((_signedBy)&&(s[colonAt])) {
|
||||
s += colonAt + 1;
|
||||
colonAt = 0;
|
||||
while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
|
||||
if (colonAt) {
|
||||
if (Utils::unhex(s,colonAt,_signature.data,ZT_C25519_SIGNATURE_LEN) != ZT_C25519_SIGNATURE_LEN)
|
||||
_signedBy.zero();
|
||||
} else {
|
||||
_signedBy.zero();
|
||||
}
|
||||
} else {
|
||||
_signedBy.zero();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(&(_qualifiers[0]),&(_qualifiers[_qualifierCount]));
|
||||
}
|
||||
|
||||
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
|
||||
bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other) const
|
||||
bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other, const Identity &otherIdentity) const
|
||||
{
|
||||
unsigned int myidx = 0;
|
||||
unsigned int otheridx = 0;
|
||||
|
||||
if ((_qualifierCount == 0)||(other._qualifierCount == 0))
|
||||
return false;
|
||||
|
||||
while (myidx < _qualifierCount) {
|
||||
// Fail if we're at the end of other, since this means the field is
|
||||
// missing.
|
||||
if (otheridx >= other._qualifierCount)
|
||||
return false;
|
||||
std::map< uint64_t, uint64_t > otherFields;
|
||||
for(unsigned int i=0;i<other._qualifierCount;++i)
|
||||
otherFields[other._qualifiers[i].id] = other._qualifiers[i].value;
|
||||
|
||||
// Seek to corresponding tuple in other, ignoring tuples that
|
||||
// we may not have. If we run off the end of other, the tuple is
|
||||
// missing. This works because tuples are sorted by ID.
|
||||
while (other._qualifiers[otheridx].id != _qualifiers[myidx].id) {
|
||||
++otheridx;
|
||||
if (otheridx >= other._qualifierCount)
|
||||
bool fullIdentityVerification = false;
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
const uint64_t qid = _qualifiers[i].id;
|
||||
if ((qid >= 3)&&(qid <= 6))
|
||||
fullIdentityVerification = true;
|
||||
std::map< uint64_t, uint64_t >::iterator otherQ(otherFields.find(qid));
|
||||
if (otherQ == otherFields.end())
|
||||
return false;
|
||||
const uint64_t a = _qualifiers[i].value;
|
||||
const uint64_t b = otherQ->second;
|
||||
if (((a >= b) ? (a - b) : (b - a)) > _qualifiers[i].maxDelta)
|
||||
return false;
|
||||
}
|
||||
|
||||
// If this COM has a full hash of its identity, assume the other must have this as well.
|
||||
// Otherwise we are on a controller that does not incorporate these.
|
||||
if (fullIdentityVerification) {
|
||||
uint64_t idHash[6];
|
||||
otherIdentity.publicKeyHash(idHash);
|
||||
for(unsigned long i=0;i<4;++i) {
|
||||
std::map< uint64_t, uint64_t >::iterator otherQ(otherFields.find((uint64_t)(i + 3)));
|
||||
if (otherQ == otherFields.end())
|
||||
return false;
|
||||
if (otherQ->second != Utils::ntoh(idHash[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare to determine if the absolute value of the difference
|
||||
// between these two parameters is within our maxDelta.
|
||||
const uint64_t a = _qualifiers[myidx].value;
|
||||
const uint64_t b = other._qualifiers[myidx].value;
|
||||
if (((a >= b) ? (a - b) : (b - a)) > _qualifiers[myidx].maxDelta)
|
||||
return false;
|
||||
|
||||
++myidx;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -94,6 +94,8 @@ public:
|
||||
* ZeroTier address to whom certificate was issued
|
||||
*/
|
||||
COM_RESERVED_ID_ISSUED_TO = 2
|
||||
|
||||
// IDs 3-6 reserved for full hash of identity to which this COM was issued.
|
||||
};
|
||||
|
||||
/**
|
||||
@ -110,20 +112,7 @@ public:
|
||||
* @param nwid Network ID
|
||||
* @param issuedTo Certificate recipient
|
||||
*/
|
||||
CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo)
|
||||
{
|
||||
_qualifiers[0].id = COM_RESERVED_ID_TIMESTAMP;
|
||||
_qualifiers[0].value = timestamp;
|
||||
_qualifiers[0].maxDelta = timestampMaxDelta;
|
||||
_qualifiers[1].id = COM_RESERVED_ID_NETWORK_ID;
|
||||
_qualifiers[1].value = nwid;
|
||||
_qualifiers[1].maxDelta = 0;
|
||||
_qualifiers[2].id = COM_RESERVED_ID_ISSUED_TO;
|
||||
_qualifiers[2].value = issuedTo.toInt();
|
||||
_qualifiers[2].maxDelta = 0xffffffffffffffffULL;
|
||||
_qualifierCount = 3;
|
||||
memset(_signature.data,0,ZT_C25519_SIGNATURE_LEN);
|
||||
}
|
||||
CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Identity &issuedTo);
|
||||
|
||||
/**
|
||||
* Create from binary-serialized COM in buffer
|
||||
@ -183,36 +172,6 @@ public:
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or update a qualifier in this certificate
|
||||
*
|
||||
* Any signature is invalidated and signedBy is set to null.
|
||||
*
|
||||
* @param id Qualifier ID
|
||||
* @param value Qualifier value
|
||||
* @param maxDelta Qualifier maximum allowed difference (absolute value of difference)
|
||||
*/
|
||||
void setQualifier(uint64_t id,uint64_t value,uint64_t maxDelta);
|
||||
inline void setQualifier(ReservedId id,uint64_t value,uint64_t maxDelta) { setQualifier((uint64_t)id,value,maxDelta); }
|
||||
|
||||
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
/**
|
||||
* @return String-serialized representation of this certificate
|
||||
*/
|
||||
std::string toString() const;
|
||||
|
||||
/**
|
||||
* Set this certificate equal to the hex-serialized string
|
||||
*
|
||||
* Invalid strings will result in invalid or undefined certificate
|
||||
* contents. These will subsequently fail validation and comparison.
|
||||
* Empty strings will result in an empty certificate.
|
||||
*
|
||||
* @param s String to deserialize
|
||||
*/
|
||||
void fromString(const char *s);
|
||||
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
|
||||
/**
|
||||
* Compare two certificates for parameter agreement
|
||||
*
|
||||
@ -224,9 +183,10 @@ public:
|
||||
* tuples present in this cert but not in other result in 'false'.
|
||||
*
|
||||
* @param other Cert to compare with
|
||||
* @param otherIdentity Identity of other node
|
||||
* @return True if certs agree and 'other' may be communicated with
|
||||
*/
|
||||
bool agreesWith(const CertificateOfMembership &other) const;
|
||||
bool agreesWith(const CertificateOfMembership &other, const Identity &otherIdentity) const;
|
||||
|
||||
/**
|
||||
* Sign this certificate
|
||||
|
@ -666,11 +666,6 @@
|
||||
*/
|
||||
#define ZT_TRUST_EXPIRATION 600000
|
||||
|
||||
/**
|
||||
* Enable support for older network configurations from older (pre-1.1.6) controllers
|
||||
*/
|
||||
#define ZT_SUPPORT_OLD_STYLE_NETCONF 1
|
||||
|
||||
/**
|
||||
* Desired buffer size for UDP sockets (used in service and osdep but defined here)
|
||||
*/
|
||||
|
@ -109,6 +109,18 @@ public:
|
||||
*/
|
||||
inline bool hasPrivate() const { return (_privateKey != (C25519::Private *)0); }
|
||||
|
||||
/**
|
||||
* Compute a SHA384 hash of this identity's address and public key(s).
|
||||
*
|
||||
* @param sha384buf Buffer with 48 bytes of space to receive hash
|
||||
*/
|
||||
inline void publicKeyHash(void *sha384buf) const
|
||||
{
|
||||
uint8_t address[ZT_ADDRESS_LENGTH];
|
||||
_address.copyTo(address, ZT_ADDRESS_LENGTH);
|
||||
SHA384(sha384buf, address, ZT_ADDRESS_LENGTH, _publicKey.data, ZT_C25519_PUBLIC_KEY_LEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the SHA512 hash of our private key (if we have one)
|
||||
*
|
||||
|
@ -91,13 +91,14 @@ public:
|
||||
* Check whether the peer represented by this Membership should be allowed on this network at all
|
||||
*
|
||||
* @param nconf Our network config
|
||||
* @param otherNodeIdentity Identity of remote node
|
||||
* @return True if this peer is allowed on this network at all
|
||||
*/
|
||||
inline bool isAllowedOnNetwork(const NetworkConfig &nconf) const
|
||||
inline bool isAllowedOnNetwork(const NetworkConfig &thisNodeNetworkConfig, const Identity &otherNodeIdentity) const
|
||||
{
|
||||
if (nconf.isPublic()) return true;
|
||||
if (thisNodeNetworkConfig.isPublic()) return true;
|
||||
if (_com.timestamp() <= _comRevocationThreshold) return false;
|
||||
return nconf.com.agreesWith(_com);
|
||||
return thisNodeNetworkConfig.com.agreesWith(_com, otherNodeIdentity);
|
||||
}
|
||||
|
||||
inline bool recentlyAssociated(const int64_t now) const
|
||||
|
@ -1226,7 +1226,7 @@ bool Network::gate(void *tPtr,const SharedPtr<Peer> &peer)
|
||||
try {
|
||||
if (_config) {
|
||||
Membership *m = _memberships.get(peer->address());
|
||||
if ( (_config.isPublic()) || ((m)&&(m->isAllowedOnNetwork(_config))) ) {
|
||||
if ( (_config.isPublic()) || ((m)&&(m->isAllowedOnNetwork(_config, peer->identity()))) ) {
|
||||
if (!m)
|
||||
m = &(_membership(peer->address()));
|
||||
if (m->multicastLikeGate(now)) {
|
||||
@ -1480,8 +1480,11 @@ void Network::_sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMu
|
||||
Membership *m = (Membership *)0;
|
||||
Hashtable<Address,Membership>::Iterator i(_memberships);
|
||||
while (i.next(a,m)) {
|
||||
if ( ( m->multicastLikeGate(now) || (newMulticastGroup) ) && (m->isAllowedOnNetwork(_config)) && (!std::binary_search(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end(),*a)) )
|
||||
_announceMulticastGroupsTo(tPtr,*a,groups);
|
||||
Identity aid(RR->topology->getIdentity(tPtr, *a));
|
||||
if (aid) {
|
||||
if ( ( m->multicastLikeGate(now) || (newMulticastGroup) ) && (m->isAllowedOnNetwork(_config, aid)) && (!std::binary_search(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end(),*a)) )
|
||||
_announceMulticastGroupsTo(tPtr,*a,groups);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user