Bläddra i källkod

Instantaneous blacklisting and credential revocation.

Adam Ierymenko 9 år sedan
förälder
incheckning
0a7a33ef8f
6 ändrade filer med 96 tillägg och 43 borttagningar
  1. 16 7
      node/IncomingPacket.cpp
  2. 48 8
      node/Membership.hpp
  3. 4 8
      node/Network.cpp
  4. 11 0
      node/Network.hpp
  5. 0 12
      node/NetworkConfig.hpp
  6. 17 8
      node/Packet.hpp

+ 16 - 7
node/IncomingPacket.cpp

@@ -829,13 +829,22 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
 bool IncomingPacket::_doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
 {
 	try {
-		unsigned int p = ZT_PACKET_IDX_PAYLOAD;
-		while ((p + 8) <= size()) {
-			const uint64_t nwid = at<uint64_t>(p); p += 8;
-			if (Network::controllerFor(nwid) == peer->address()) {
-				SharedPtr<Network> network(RR->node->network(nwid));
-				if (network)
-					network->requestConfiguration();
+		const uint64_t nwid = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
+
+		if (Network::controllerFor(nwid) == peer->address()) {
+			SharedPtr<Network> network(RR->node->network(nwid));
+			if (network) {
+				network->requestConfiguration();
+			} else {
+				TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): not a member of %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),nwid);
+				return true;
+			}
+
+			const unsigned int blacklistCount = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 8);
+			unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 10;
+			for(unsigned int i=0;i<blacklistCount;++i) {
+				network->blacklistBefore(Address(field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH),at<uint64_t>(ptr + 5));
+				ptr += 13;
 			}
 		}
 	} catch ( ... ) {

+ 48 - 8
node/Membership.hpp

@@ -42,7 +42,10 @@ namespace ZeroTier {
 class RuntimeEnvironment;
 
 /**
- * A container for certificates of membership and other credentials for peer participation on networks
+ * 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
+ * presently held by the Network object for each participating Peer.
  */
 class Membership
 {
@@ -73,12 +76,13 @@ private:
 
 public:
 	/**
-	 * A wrapper to iterate through capabilities in ascending order of capability ID
+	 * A wrapper to iterate through member capabilities in ascending order of capability ID and return only valid ones
 	 */
 	class CapabilityIterator
 	{
 	public:
 		CapabilityIterator(const Membership &m) :
+			_m(m),
 			_i(m._caps.begin()),
 			_e(m._caps.end())
 		{
@@ -87,7 +91,7 @@ public:
 		inline const Capability *next(const NetworkConfig &nconf)
 		{
 			while (_i != _e) {
-				if ((_i->second.lastReceived)&&(nconf.isCredentialTimestampValid(_i->second.cap)))
+				if ((_i->second.lastReceived)&&(_m.isCredentialTimestampValid(nconf,_i->second.cap)))
 					return &((_i++)->second.cap);
 				else ++_i;
 			}
@@ -95,12 +99,14 @@ public:
 		}
 
 	private:
+		const Membership &_m;
 		std::map<uint32_t,CState>::const_iterator _i,_e;
 	};
 	friend class CapabilityIterator;
 
 	Membership() :
 		_lastPushedCom(0),
+		_blacklistBefore(0),
 		_com(),
 		_caps(),
 		_tags(8)
@@ -125,9 +131,30 @@ public:
 	bool sendCredentialsIfNeeded(const RuntimeEnvironment *RR,const uint64_t now,const Address &peerAddress,const CertificateOfMembership &com,const Capability *cap,const Tag **tags,const unsigned int tagCount);
 
 	/**
-	 * @return This peer's COM if they have sent one
+	 * @param nconf Our network config
+	 * @return True if this peer is allowed on this network at all
+	 */
+	inline bool isAllowedOnNetwork(const NetworkConfig &nconf) const
+	{
+		if (nconf.isPublic())
+			return true;
+		if ((_blacklistBefore)&&(_com.timestamp().first <= _blacklistBefore))
+			return false;
+		return nconf.com.agreesWith(_com);
+	}
+
+	/**
+	 * Check whether a capability or tag is expired
+	 *
+	 * @param cred Credential to check -- must have timestamp() accessor method
+	 * @return True if credential is NOT expired
 	 */
-	inline const CertificateOfMembership &com() const { return _com; }
+	template<typename C>
+	inline bool isCredentialTimestampValid(const NetworkConfig &nconf,const C &cred) const
+	{
+		const uint64_t ts = cred.timestamp();
+		return ( ( (ts >= nconf.timestamp) || ((nconf.timestamp - ts) <= nconf.credentialTimeToLive) ) && (ts > _blacklistBefore) );
+	}
 
 	/**
 	 * @param nconf Network configuration
@@ -137,7 +164,7 @@ public:
 	inline const Tag *getTag(const NetworkConfig &nconf,const uint32_t id) const
 	{
 		const TState *t = _tags.get(id);
-		return ((t) ? (((t->lastReceived != 0)&&(nconf.isCredentialTimestampValid(t->tag))) ? &(t->tag) : (const Tag *)0) : (const Tag *)0);
+		return ((t) ? (((t->lastReceived != 0)&&(isCredentialTimestampValid(nconf,t->tag))) ? &(t->tag) : (const Tag *)0) : (const Tag *)0);
 	}
 
 	/**
@@ -154,7 +181,7 @@ public:
 		TState *ts = (TState *)0;
 		Hashtable<uint32_t,TState>::Iterator i(const_cast<Membership *>(this)->_tags);
 		while (i.next(id,ts)) {
-			if ((ts->lastReceived)&&(nconf.isCredentialTimestampValid(ts->tag))) {
+			if ((ts->lastReceived)&&(isCredentialTimestampValid(nconf,ts->tag))) {
 				if (n >= maxTags)
 					return n;
 				ids[n] = *id;
@@ -172,7 +199,7 @@ public:
 	inline const Capability *getCapability(const NetworkConfig &nconf,const uint32_t id) const
 	{
 		std::map<uint32_t,CState>::const_iterator c(_caps.find(id));
-		return ((c != _caps.end()) ? (((c->second.lastReceived != 0)&&(nconf.isCredentialTimestampValid(c->second.cap))) ? &(c->second.cap) : (const Capability *)0) : (const Capability *)0);
+		return ((c != _caps.end()) ? (((c->second.lastReceived != 0)&&(isCredentialTimestampValid(nconf,c->second.cap))) ? &(c->second.cap) : (const Capability *)0) : (const Capability *)0);
 	}
 
 	/**
@@ -196,6 +223,16 @@ public:
 	 */
 	int addCredential(const RuntimeEnvironment *RR,const Capability &cap);
 
+	/**
+	 * Blacklist COM, tags, and capabilities before this time
+	 *
+	 * @param ts Blacklist cutoff
+	 */
+	inline void blacklistBefore(const uint64_t ts)
+	{
+		_blacklistBefore = ts;
+	}
+
 	/**
 	 * Clean up old or stale entries
 	 *
@@ -234,6 +271,9 @@ private:
 	// Last time we pushed our COM to this peer
 	uint64_t _lastPushedCom;
 
+	// Time before which to blacklist credentials from this peer
+	uint64_t _blacklistBefore;
+
 	// COM from this peer
 	CertificateOfMembership _com;
 

+ 4 - 8
node/Network.cpp

@@ -888,16 +888,12 @@ bool Network::_isAllowed(const SharedPtr<Peer> &peer) const
 	// Assumes _lock is locked
 	try {
 		if (_config) {
-			if (_config.isPublic()) {
-				return true;
-			} else {
-				const Membership *m = _memberships.get(peer->address());
-				if (m)
-					return _config.com.agreesWith(m->com());
-			}
+			const Membership *const m = _memberships.get(peer->address());
+			if (m)
+				return m->isAllowedOnNetwork(_config);
 		}
 	} catch ( ... ) {
-		TRACE("isAllowed() check failed for peer %s: unexpected exception: unexpected exception",peer->address().toString().c_str());
+		TRACE("isAllowed() check failed for peer %s: unexpected exception",peer->address().toString().c_str());
 	}
 	return false;
 }

+ 11 - 0
node/Network.hpp

@@ -387,6 +387,17 @@ public:
 		return _memberships[tag.issuedTo()].addCredential(RR,tag);
 	}
 
+	/**
+	 * Blacklist COM, tags, and capabilities before this time
+	 *
+	 * @param ts Blacklist cutoff
+	 */
+	inline void blacklistBefore(const Address &peerAddress,const uint64_t ts)
+	{
+		Mutex::Lock _l(_lock);
+		_memberships[peerAddress].blacklistBefore(ts);
+	}
+
 	/**
 	 * Destroy this network
 	 *

+ 0 - 12
node/NetworkConfig.hpp

@@ -360,18 +360,6 @@ public:
 		return (Tag *)0;
 	}
 
-	/**
-	 * Check whether a capability or tag is expired
-	 *
-	 * @param cred Credential to check -- must have timestamp() accessor method
-	 * @return True if credential is NOT expired
-	 */
-	template<typename C>
-	inline bool isCredentialTimestampValid(const C &cred) const
-	{
-		return ( (cred.timestamp() >= timestamp) || ((timestamp - cred.timestamp()) <= credentialTimeToLive) );
-	}
-
 	/*
 	inline void dump() const
 	{

+ 17 - 8
node/Packet.hpp

@@ -742,14 +742,23 @@ public:
 		VERB_NETWORK_CONFIG_REQUEST = 0x0b,
 
 		/**
-		 * Network configuration refresh request:
-		 *   <[...] array of 64-bit network IDs>
-		 *
-		 * This can be sent by the network controller to inform a node that it
-		 * should now make a NETWORK_CONFIG_REQUEST.
-		 *
-		 * It does not generate an OK or ERROR message, and is treated only as
-		 * a hint to refresh now.
+		 * Network configuration update push:
+		 *   <[8] network ID to refresh>
+		 *   <[2] 16-bit number of address/timestamp pairs to blacklist>
+		 *  [<[5] ZeroTier address of peer being revoked>]
+		 *  [<[8] blacklist credentials older than this timestamp>]
+		 *  [<[...] additional address/timestamp pairs>]
+		 *
+		 * This can be sent by a network controller to both request that a network
+		 * config be updated and push instantaneous revocations of specific peers
+		 * or peer credentials.
+		 *
+		 * Specific revocations can be pushed to blacklist a specific peer's
+		 * credentials (COM, tags, and capabilities) if older than a specified
+		 * timestamp. This can be used to accomplish expedited revocation of
+		 * a peer's access to things on a network or to the network itself among
+		 * those other peers that can currently reach the controller. This is not
+		 * the only mechanism for revocation of course, but it's the fastest.
 		 */
 		VERB_NETWORK_CONFIG_REFRESH = 0x0c,