From 8f2a42d1ad84e5dba590e7f593d8a46cc81389b3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C5=82=20Zieli=C5=84ski?= <michal@zielinscy.org.pl>
Date: Sun, 22 Jan 2017 23:02:34 +0100
Subject: [PATCH] allow user to specify arbitrary allowed IP networks in
 allowManaged

---
 node/Buffer.hpp          | 10 +++++-----
 service/ControlPlane.cpp | 11 ++++++++++-
 service/OneService.cpp   | 41 +++++++++++++++++++++++++++++++++++++---
 service/OneService.hpp   |  7 +++++++
 4 files changed, 60 insertions(+), 9 deletions(-)

diff --git a/node/Buffer.hpp b/node/Buffer.hpp
index 0b1715925..1a478894f 100644
--- a/node/Buffer.hpp
+++ b/node/Buffer.hpp
@@ -61,11 +61,11 @@ public:
 	// STL container idioms
 	typedef unsigned char value_type;
 	typedef unsigned char * pointer;
-	typedef const unsigned char * const_pointer;
-	typedef unsigned char & reference;
-	typedef const unsigned char & const_reference;
-	typedef unsigned char * iterator;
-	typedef const unsigned char * const_iterator;
+	typedef const char * const_pointer;
+	typedef char & reference;
+	typedef const char & const_reference;
+	typedef char * iterator;
+	typedef const char * const_iterator;
 	typedef unsigned int size_type;
 	typedef int difference_type;
 	typedef std::reverse_iterator<iterator> reverse_iterator;
diff --git a/service/ControlPlane.cpp b/service/ControlPlane.cpp
index 86158a91d..27027d3b8 100644
--- a/service/ControlPlane.cpp
+++ b/service/ControlPlane.cpp
@@ -121,6 +121,15 @@ static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_VirtualNetw
 		case ZT_NETWORK_TYPE_PUBLIC:                     ntype = "PUBLIC"; break;
 	}
 
+	std::string allowManaged = (localSettings.allowManaged) ? "true" : "false";
+	if (localSettings.allowManagedWhitelist.size() != 0) {
+		allowManaged = "";
+		for (InetAddress address : localSettings.allowManagedWhitelist) {
+			if (allowManaged.size() != 0) allowManaged += ',';
+			allowManaged += address.toIpString() + "/" + std::to_string(address.netmaskBits());
+		}
+	}
+
 	Utils::snprintf(json,sizeof(json),
 		"%s{\n"
 		"%s\t\"id\": \"%.16llx\",\n"
@@ -158,7 +167,7 @@ static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_VirtualNetw
 		prefix,_jsonEnumerate(nc->assignedAddresses,nc->assignedAddressCount).c_str(),
 		prefix,_jsonEnumerate(nc->routes,nc->routeCount).c_str(),
 		prefix,_jsonEscape(portDeviceName).c_str(),
-		prefix,(localSettings.allowManaged) ? "true" : "false",
+		prefix,allowManaged.c_str(),
 		prefix,(localSettings.allowGlobal) ? "true" : "false",
 		prefix,(localSettings.allowDefault) ? "true" : "false",
 		prefix);
diff --git a/service/OneService.cpp b/service/OneService.cpp
index 93f5b5f07..603234a22 100644
--- a/service/OneService.cpp
+++ b/service/OneService.cpp
@@ -1039,6 +1039,18 @@ public:
 	{
 		if (!n.settings.allowManaged)
 			return false;
+
+		if (n.settings.allowManagedWhitelist.size() > 0) {
+			bool allowed = false;
+			for (InetAddress addr : n.settings.allowManagedWhitelist) {
+				if (addr.containsAddress(target) && addr.netmaskBits() <= target.netmaskBits()) {
+					allowed = true;
+					break;
+				}
+			}
+			if (!allowed) return false;
+		}
+
 		if (target.isDefaultRoute())
 			return n.settings.allowDefault;
 		switch(target.ipScope()) {
@@ -1423,9 +1435,32 @@ public:
 						if (OSUtils::readFile(nlcpath,nlcbuf)) {
 							Dictionary<4096> nc;
 							nc.load(nlcbuf.c_str());
-							n.settings.allowManaged = nc.getB("allowManaged",true);
-							n.settings.allowGlobal = nc.getB("allowGlobal",false);
-							n.settings.allowDefault = nc.getB("allowDefault",false);
+							Buffer<1024> allowManaged;
+							if (nc.get("allowManaged", allowManaged) && allowManaged.size() != 0) {
+								std::string addresses (allowManaged.begin(), allowManaged.size());
+								if (allowManaged.size() <= 5) { // untidy parsing for backward compatibility
+									if (allowManaged[0] == '1' || allowManaged[0] == 't' || allowManaged[0] == 'T') {
+										n.settings.allowManaged = true;
+									} else {
+										n.settings.allowManaged = false;
+									}
+								} else {
+									// this should be a list of IP addresses
+									n.settings.allowManaged = true;
+									size_t pos = 0;
+									while (true) {
+										size_t nextPos = addresses.find(',', pos);
+										std::string address = addresses.substr(pos, (nextPos == std::string::npos ? addresses.size() : nextPos) - pos);
+										n.settings.allowManagedWhitelist.push_back(InetAddress(address));
+										if (nextPos == std::string::npos) break;
+										pos = nextPos + 1;
+									}
+								}
+							} else {
+								n.settings.allowManaged = true;
+							}
+							n.settings.allowGlobal = nc.getB("allowGlobal", false);
+							n.settings.allowDefault = nc.getB("allowDefault", false);
 						}
 					} catch (std::exception &exc) {
 #ifdef __WINDOWS__
diff --git a/service/OneService.hpp b/service/OneService.hpp
index 7aa3b3619..88225da4d 100644
--- a/service/OneService.hpp
+++ b/service/OneService.hpp
@@ -20,6 +20,7 @@
 #define ZT_ONESERVICE_HPP
 
 #include <string>
+#include <vector>
 
 namespace ZeroTier {
 
@@ -65,6 +66,12 @@ public:
 		 */
 		bool allowManaged;
 
+		/**
+		 * Whitelist of addresses that can be configured by this network.
+		 * If empty and allowManaged is true, allow all private/pseudoprivate addresses.
+		 */
+		std::vector<InetAddress> allowManagedWhitelist;
+
 		/**
 		 * Allow configuration of IPs and routes within global (Internet) IP space?
 		 */