2
0
Эх сурвалжийг харах

Simply network auth logic and always sent error on auth failure even for unknown networks to prevent forensics.

Adam Ierymenko 9 жил өмнө
parent
commit
9f550292fe

+ 37 - 24
node/IncomingPacket.cpp

@@ -169,7 +169,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 				// Peers can send this in response to frames if they do not have a recent enough COM from us
 				const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
 				const uint64_t now = RR->node->now();
-				if ( (network) && (network->config().com) && (peer->rateGateComRequest(now)) )
+				if ( (network) && (network->config().com) && (peer->rateGateIncomingComRequest(now)) )
 					network->pushCredentialsNow(peer->address(),now);
 			}	break;
 
@@ -184,7 +184,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 				// Members of networks can use this error to indicate that they no longer
 				// want to receive multicasts on a given channel.
 				const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
-				if ((network)&&(network->gate(peer,verb(),packetId()))) {
+				if ((network)&&(network->gate(peer))) {
 					const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at<uint32_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14));
 					TRACE("%.16llx: peer %s unsubscrubed from multicast group %s",network->id(),peer->address().toString().c_str(),mg.toString().c_str());
 					RR->mc->remove(network->id(),mg,peer->address());
@@ -375,7 +375,6 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
 	try {
 		const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB];
 		const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID);
-		bool trustEstablished = false;
 
 		if (!RR->node->expectingReplyTo(inRePacketId)) {
 			TRACE("%s(%s): OK(%s) DROPPED: not expecting reply to %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),Packet::verbString(inReVerb),packetId());
@@ -441,8 +440,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
 			case Packet::VERB_MULTICAST_GATHER: {
 				const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_NETWORK_ID);
 				const SharedPtr<Network> network(RR->node->network(nwid));
-				if ((network)&&(network->gateMulticastGatherReply(peer,verb(),packetId()))) {
-					trustEstablished = true;
+				if (network) {
 					const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_ADI));
 					//TRACE("%s(%s): OK(MULTICAST_GATHER) %.16llx/%s length %u",source().toString().c_str(),_path->address().toString().c_str(),nwid,mg.toString().c_str(),size());
 					const unsigned int count = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 4);
@@ -468,15 +466,12 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
 							network->addCredential(com);
 					}
 
-					if (network->gateMulticastGatherReply(peer,verb(),packetId())) {
-						trustEstablished = true;
-						if ((flags & 0x02) != 0) {
-							// OK(MULTICAST_FRAME) includes implicit gather results
-							offset += ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS;
-							unsigned int totalKnown = at<uint32_t>(offset); offset += 4;
-							unsigned int count = at<uint16_t>(offset); offset += 2;
-							RR->mc->addMultiple(RR->node->now(),nwid,mg,field(offset,count * 5),count,totalKnown);
-						}
+					if ((flags & 0x02) != 0) {
+						// OK(MULTICAST_FRAME) includes implicit gather results
+						offset += ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS;
+						unsigned int totalKnown = at<uint32_t>(offset); offset += 4;
+						unsigned int count = at<uint16_t>(offset); offset += 2;
+						RR->mc->addMultiple(RR->node->now(),nwid,mg,field(offset,count * 5),count,totalKnown);
 					}
 				}
 			}	break;
@@ -484,7 +479,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
 			default: break;
 		}
 
-		peer->received(_path,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb,trustEstablished);
+		peer->received(_path,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb,false);
 	} catch ( ... ) {
 		TRACE("dropped OK from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
 	}
@@ -581,9 +576,7 @@ bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 		const SharedPtr<Network> network(RR->node->network(nwid));
 		bool trustEstablished = false;
 		if (network) {
-			if (!network->gate(peer,verb(),packetId())) {
-				TRACE("dropped FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),(unsigned long long)network->id());
-			} else {
+			if (network->gate(peer)) {
 				trustEstablished = true;
 				if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) {
 					const unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
@@ -593,9 +586,13 @@ bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 					if (network->filterIncomingPacket(peer,RR->identity.address(),sourceMac,network->mac(),frameData,frameLen,etherType,0) > 0)
 						RR->node->putFrame(nwid,network->userPtr(),sourceMac,network->mac(),etherType,0,(const void *)frameData,frameLen);
 				}
+			} else {
+				TRACE("dropped FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),(unsigned long long)network->id());
+				_sendErrorNeedCredentials(RR,peer,nwid);
 			}
 		} else {
 			TRACE("dropped FRAME from %s(%s): we are not a member of network %.16llx",source().toString().c_str(),_path->address().toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID));
+			_sendErrorNeedCredentials(RR,peer,nwid);
 		}
 		peer->received(_path,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP,trustEstablished);
 	} catch ( ... ) {
@@ -620,8 +617,9 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
 					network->addCredential(com);
 			}
 
-			if (!network->gate(peer,verb(),packetId())) {
+			if (!network->gate(peer)) {
 				TRACE("dropped EXT_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),network->id());
+				_sendErrorNeedCredentials(RR,peer,nwid);
 				peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false);
 				return true;
 			}
@@ -681,6 +679,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
 			peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true);
 		} else {
 			TRACE("dropped EXT_FRAME from %s(%s): we are not connected to network %.16llx",source().toString().c_str(),_path->address().toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID));
+			_sendErrorNeedCredentials(RR,peer,nwid);
 			peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false);
 		}
 	} catch ( ... ) {
@@ -737,7 +736,7 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const Shared
 			if (!auth) {
 				if ((!network)||(network->id() != nwid))
 					network = RR->node->network(nwid);
-				const bool authOnNet = ((network)&&(network->gate(peer,verb(),packetId())));
+				const bool authOnNet = ((network)&&(network->gate(peer)));
 				trustEstablished |= authOnNet;
 				if (authOnNet||RR->mc->cacheAuthorized(peer->address(),nwid,now)) {
 					auth = true;
@@ -986,7 +985,6 @@ bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,const Shared
 				_path->send(RR,outp.data(),outp.size(),RR->node->now());
 			}
 		}
-
 		peer->received(_path,hops(),packetId(),Packet::VERB_NETWORK_CONFIG,0,Packet::VERB_NOP,false);
 	} catch ( ... ) {
 		TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
@@ -1020,7 +1018,7 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,const Shar
 			}
 		}
 
-		const bool trustEstablished = ((network)&&(network->gate(peer,verb(),packetId())));
+		const bool trustEstablished = ((network)&&(network->gate(peer)));
 		if ( ( trustEstablished || RR->mc->cacheAuthorized(peer->address(),nwid,RR->node->now()) ) && (gatherLimit > 0) ) {
 			Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
 			outp.append((unsigned char)Packet::VERB_MULTICAST_GATHER);
@@ -1067,8 +1065,9 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
 					network->addCredential(com);
 			}
 
-			if (!network->gate(peer,verb(),packetId())) {
+			if (!network->gate(peer)) {
 				TRACE("dropped MULTICAST_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),(unsigned long long)network->id());
+				_sendErrorNeedCredentials(RR,peer,nwid);
 				peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false);
 				return true;
 			}
@@ -1143,6 +1142,7 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
 
 			peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true);
 		} else {
+			_sendErrorNeedCredentials(RR,peer,nwid);
 			peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false);
 		}
 	} catch ( ... ) {
@@ -1288,7 +1288,7 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt
 				peer->received(_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP,false);
 				return true;
 			}
-			if (network->gate(peer,verb(),packetId()))
+			if (network->gate(peer))
 				reportFlags |= ZT_CIRCUIT_TEST_REPORT_FLAGS_UPSTREAM_AUTHORIZED_IN_PATH;
 		} else {
 			TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s did not specify a credential or credential type",source().toString().c_str(),_path->address().toString().c_str(),originatorAddress.toString().c_str());
@@ -1479,6 +1479,19 @@ bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const
 	return true;
 }
 
+void IncomingPacket::_sendErrorNeedCredentials(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,const uint64_t nwid)
+{
+	if (peer->rateGateOutgoingComRequest(RR->node->now())) {
+		Packet outp(source(),RR->identity.address(),Packet::VERB_ERROR);
+		outp.append((uint8_t)verb());
+		outp.append(packetId());
+		outp.append((uint8_t)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE);
+		outp.append(nwid);
+		outp.armor(peer->key(),true);
+		_path->send(RR,outp.data(),outp.size(),RR->node->now());
+	}
+}
+
 void IncomingPacket::computeSalsa2012Sha512ProofOfWork(unsigned int difficulty,const void *challenge,unsigned int challengeLength,unsigned char result[16])
 {
 	unsigned char salsabuf[131072]; // 131072 == protocol constant, size of memory buffer for this proof of work function

+ 2 - 0
node/IncomingPacket.hpp

@@ -154,6 +154,8 @@ private:
 	bool _doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
 	bool _doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
 
+	void _sendErrorNeedCredentials(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,const uint64_t nwid);
+
 	uint64_t _receiveTime;
 	SharedPtr<Path> _path;
 };

+ 3 - 18
node/Network.cpp

@@ -50,6 +50,7 @@ static const char *_rtn(const ZT_VirtualNetworkRuleType rt)
 		case ZT_NETWORK_RULE_ACTION_DROP: return "ACTION_DROP";
 		case ZT_NETWORK_RULE_ACTION_ACCEPT: return "ACTION_ACCEPT";
 		case ZT_NETWORK_RULE_ACTION_TEE: return "ACTION_TEE";
+		case ZT_NETWORK_RULE_ACTION_WATCH: return "ACTION_WATCH";
 		case ZT_NETWORK_RULE_ACTION_REDIRECT: return "ACTION_REDIRECT";
 		case ZT_NETWORK_RULE_ACTION_DEBUG_LOG: return "ACTION_DEBUG_LOG";
 		case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS: return "MATCH_SOURCE_ZEROTIER_ADDRESS";
@@ -882,7 +883,7 @@ uint64_t Network::handleConfigChunk(const Packet &chunk,unsigned int ptr)
 	const unsigned int start = ptr;
 
 	ptr += 8; // skip network ID, which is already obviously known
-	const uint16_t chunkLen = chunk.at<uint16_t>(ptr); ptr += 2;
+	const unsigned int chunkLen = chunk.at<uint16_t>(ptr); ptr += 2;
 	const void *chunkData = chunk.field(ptr,chunkLen); ptr += chunkLen;
 
 	Mutex::Lock _l(_lock);
@@ -975,8 +976,6 @@ uint64_t Network::handleConfigChunk(const Packet &chunk,unsigned int ptr)
 
 	if (c->updateId != configUpdateId) {
 		c->updateId = configUpdateId;
-		for(int i=0;i<ZT_NETWORK_MAX_UPDATE_CHUNKS;++i)
-			c->haveChunkIds[i] = 0;
 		c->haveChunks = 0;
 		c->haveBytes = 0;
 	}
@@ -1065,7 +1064,7 @@ void Network::requestConfiguration()
 	RR->sw->send(outp,true);
 }
 
-bool Network::gate(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uint64_t packetId)
+bool Network::gate(const SharedPtr<Peer> &peer)
 {
 	const uint64_t now = RR->node->now();
 	Mutex::Lock _l(_lock);
@@ -1081,15 +1080,6 @@ bool Network::gate(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uin
 					m->likingMulticasts(now);
 				}
 				return true;
-			} else {
-				if (peer->rateGateRequestCredentials(now)) {
-					Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
-					outp.append((uint8_t)verb);
-					outp.append(packetId);
-					outp.append((uint8_t)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE);
-					outp.append(_id);
-					RR->sw->send(outp,true);
-				}
 			}
 		}
 	} catch ( ... ) {
@@ -1098,11 +1088,6 @@ bool Network::gate(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uin
 	return false;
 }
 
-bool Network::gateMulticastGatherReply(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uint64_t packetId)
-{
-	return ( (peer->address() == controller()) || RR->topology->isUpstream(peer->identity()) || gate(peer,verb,packetId) || _config.isAnchor(peer->address()) );
-}
-
 void Network::clean()
 {
 	const uint64_t now = RR->node->now();

+ 1 - 14
node/Network.hpp

@@ -212,21 +212,8 @@ public:
 
 	/**
 	 * Determine whether this peer is permitted to communicate on this network
-	 *
-	 * This also performs certain periodic actions such as pushing renewed
-	 * credentials to peers, so like the filters it is not side-effect-free.
-	 *
-	 * @param peer Peer to check
-	 * @param verb Packet verb
-	 * @param packetId Packet ID
-	 * @return True if peer is allowed to communicate on this network
-	 */
-	bool gate(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uint64_t packetId);
-
-	/**
-	 * Check whether this peer is allowed to provide multicast info for this network
 	 */
-	bool gateMulticastGatherReply(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uint64_t packetId);
+	bool gate(const SharedPtr<Peer> &peer);
 
 	/**
 	 * Do periodic cleanup and housekeeping tasks

+ 5 - 3
node/Node.hpp

@@ -267,17 +267,19 @@ public:
 	}
 
 	/**
-	 * Check whether a given packet ID is something we are expecting a reply to
+	 * Check whether a given packet ID is something we are expecting a reply to (and erase from list)
 	 *
 	 * @param packetId Packet ID to check
 	 * @return True if we're expecting a reply
 	 */
-	inline bool expectingReplyTo(const uint64_t packetId) const
+	inline bool expectingReplyTo(const uint64_t packetId)
 	{
 		const unsigned long bucket = (unsigned long)(packetId & ZT_EXPECTING_REPLIES_BUCKET_MASK1);
 		for(unsigned long i=0;i<=ZT_EXPECTING_REPLIES_BUCKET_MASK2;++i) {
-			if (_expectingRepliesTo[bucket][i] == packetId)
+			if (_expectingRepliesTo[bucket][i] == packetId) {
+				_expectingRepliesTo[bucket][i] = 0;
 				return true;
+			}
 		}
 		return false;
 	}

+ 1 - 0
node/Peer.cpp

@@ -50,6 +50,7 @@ Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Ident
 	_lastWhoisRequestReceived(0),
 	_lastEchoRequestReceived(0),
 	_lastComRequestReceived(0),
+	_lastComRequestSent(0),
 	_lastCredentialsReceived(0),
 	_lastTrustEstablishedPacketReceived(0),
 	RR(renv),

+ 15 - 2
node/Peer.hpp

@@ -395,9 +395,9 @@ public:
 	}
 
 	/**
-	 * Rate gate requests for network COM
+	 * Rate gate incoming requests for network COM
 	 */
-	inline bool rateGateComRequest(const uint64_t now)
+	inline bool rateGateIncomingComRequest(const uint64_t now)
 	{
 		if ((now - _lastComRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
 			_lastComRequestReceived = now;
@@ -406,6 +406,18 @@ public:
 		return false;
 	}
 
+	/**
+	 * Rate gate outgoing requests for network COM
+	 */
+	inline bool rateGateOutgoingComRequest(const uint64_t now)
+	{
+		if ((now - _lastComRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
+			_lastComRequestSent = now;
+			return true;
+		}
+		return false;
+	}
+
 	/**
 	 * Find a common set of addresses by which two peers can link, if any
 	 *
@@ -465,6 +477,7 @@ private:
 	uint64_t _lastWhoisRequestReceived;
 	uint64_t _lastEchoRequestReceived;
 	uint64_t _lastComRequestReceived;
+	uint64_t _lastComRequestSent;
 	uint64_t _lastCredentialsReceived;
 	uint64_t _lastTrustEstablishedPacketReceived;
 	const RuntimeEnvironment *RR;