From ea1859541c29bc3cafcde5ad9be131c942522c5f Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 15 Apr 2015 18:32:25 -0700 Subject: [PATCH] More cleanup, and fix for the extremely unlikely case of identity collision. --- controller/README.md | 8 +-- controller/SqliteNetworkController.cpp | 17 ++--- controller/SqliteNetworkController.hpp | 4 +- node/IncomingPacket.cpp | 2 +- node/Network.cpp | 2 +- node/NetworkController.hpp | 2 + one.cpp | 96 ++++++++++++++++++++------ service/OneService.cpp | 16 ----- service/OneService.hpp | 4 +- 9 files changed, 94 insertions(+), 57 deletions(-) diff --git a/controller/README.md b/controller/README.md index 6037424e7..ee176d387 100644 --- a/controller/README.md +++ b/controller/README.md @@ -9,13 +9,11 @@ The standard implementation uses SQLite3 with the attached schema. A separate se By default this code is not built or included in the client. To build on Linux, BSD, or Mac add ZT_ENABLE_NETCONF_MASTER=1 to the make command line. It could be built on Windows as well, but you're on your own there. You'd have to build SQLite3 first, or get a pre-built copy somewhere. -### Running +### Createing databases -To enable netconf functionality, place a properly initialized SQLite3 database called **netconf.db** into the ZeroTier working directory of the node you wish to serve network configurations and restart it. If that file is present it will be opened and the network configuration master function will be enabled. You will see this in the log file. +If you execute a network controller enabled build of the ZeroTier One service, a *controller.db* will automatically be created and initialize. You can also create one manually with: -To initialize a database run: - - sqlite3 -init netconf-schema.sql netconf.db + sqlite3 -init schema.sql controller.db Then type '.quit' to exit the SQLite3 command shell. diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index c2e1a168f..4dfdf78e4 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -53,14 +53,10 @@ namespace ZeroTier { -SqliteNetworkController::SqliteNetworkController(const Identity &signingId,const char *dbPath) : - _signingId(signingId), +SqliteNetworkController::SqliteNetworkController(const char *dbPath) : _dbPath(dbPath), _db((sqlite3 *)0) { - if (!_signingId.hasPrivate()) - throw std::runtime_error("SqliteNetworkController signing identity must have a private key"); - if (sqlite3_open_v2(dbPath,&_db,SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE,(const char *)0) != SQLITE_OK) throw std::runtime_error("SqliteNetworkController cannot open database file"); sqlite3_busy_timeout(_db,10000); @@ -137,13 +133,18 @@ SqliteNetworkController::~SqliteNetworkController() } } -NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(const InetAddress &fromAddr,const Identity &identity,uint64_t nwid,const Dictionary &metaData,uint64_t haveRevision,Dictionary &netconf) +NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(const InetAddress &fromAddr,const Identity &signingId,const Identity &identity,uint64_t nwid,const Dictionary &metaData,uint64_t haveRevision,Dictionary &netconf) { Mutex::Lock _l(_lock); // Note: we can't reuse prepared statements that return const char * pointers without // making our own copy in e.g. a std::string first. + if ((!signingId)||(!signingId.hasPrivate())) { + netconf["error"] = "signing identity invalid or lacks private key"; + return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; + } + struct { char id[24]; const char *name; @@ -449,7 +450,7 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co if (network.isPrivate) { CertificateOfMembership com(network.revision,16,nwid,identity.address()); - if (com.sign(_signingId)) // basically can't fail unless our identity is invalid + if (com.sign(signingId)) // basically can't fail unless our identity is invalid netconf[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP] = com.toString(); else { netconf["error"] = "unable to sign COM"; @@ -457,7 +458,7 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co } } - if (!netconf.sign(_signingId)) { + if (!netconf.sign(signingId)) { netconf["error"] = "unable to sign netconf dictionary"; return NETCONF_QUERY_INTERNAL_SERVER_ERROR; } diff --git a/controller/SqliteNetworkController.hpp b/controller/SqliteNetworkController.hpp index 566e97d1a..5487b59a2 100644 --- a/controller/SqliteNetworkController.hpp +++ b/controller/SqliteNetworkController.hpp @@ -49,11 +49,12 @@ public: class DBC; friend class SqliteNetworkController::DBC; - SqliteNetworkController(const Identity &signingId,const char *dbPath); + SqliteNetworkController(const char *dbPath); virtual ~SqliteNetworkController(); virtual NetworkController::ResultCode doNetworkConfigRequest( const InetAddress &fromAddr, + const Identity &signingId, const Identity &identity, uint64_t nwid, const Dictionary &metaData, @@ -61,7 +62,6 @@ public: Dictionary &netconf); private: - Identity _signingId; std::string _dbPath; sqlite3 *_db; diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 967f50f2c..efb506fe9 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -674,7 +674,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons if (RR->localNetworkController) { Dictionary netconf; - switch(RR->localNetworkController->doNetworkConfigRequest((h > 0) ? InetAddress() : _remoteAddress,peer->identity(),nwid,metaData,haveRevision,netconf)) { + switch(RR->localNetworkController->doNetworkConfigRequest((h > 0) ? InetAddress() : _remoteAddress,RR->identity,peer->identity(),nwid,metaData,haveRevision,netconf)) { case NetworkController::NETCONF_QUERY_OK: { const std::string netconfStr(netconf.toString()); if (netconfStr.length() > 0xffff) { // sanity check since field ix 16-bit diff --git a/node/Network.cpp b/node/Network.cpp index 1c786d245..08856f02f 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -250,7 +250,7 @@ void Network::requestConfiguration() if (RR->localNetworkController) { SharedPtr nconf(config2()); Dictionary newconf; - switch(RR->localNetworkController->doNetworkConfigRequest(InetAddress(),RR->identity,_id,Dictionary(),(nconf) ? nconf->revision() : (uint64_t)0,newconf)) { + switch(RR->localNetworkController->doNetworkConfigRequest(InetAddress(),RR->identity,RR->identity,_id,Dictionary(),(nconf) ? nconf->revision() : (uint64_t)0,newconf)) { case NetworkController::NETCONF_QUERY_OK: this->setConfiguration(newconf,true); return; diff --git a/node/NetworkController.hpp b/node/NetworkController.hpp index 353c091ad..265ee3d44 100644 --- a/node/NetworkController.hpp +++ b/node/NetworkController.hpp @@ -71,6 +71,7 @@ public: * to indicate the error. * * @param fromAddr Originating wire address or null address if packet is not direct (or from self) + * @param signingId Identity that should be used to sign results -- must include private key * @param identity Originating peer ZeroTier identity * @param nwid 64-bit network ID * @param metaData Meta-data bundled with request (empty if none) @@ -80,6 +81,7 @@ public: */ virtual NetworkController::ResultCode doNetworkConfigRequest( const InetAddress &fromAddr, + const Identity &signingId, const Identity &identity, uint64_t nwid, const Dictionary &metaData, diff --git a/one.cpp b/one.cpp index b6967216d..73c4f3294 100644 --- a/one.cpp +++ b/one.cpp @@ -69,10 +69,11 @@ #define ZT1_AUTHTOKEN_SECRET_PATH "authtoken.secret" #define ZT1_PID_PATH "zerotier-one.pid" +#define ZT1_CONTROLLER_DB_PATH "controller.db" using namespace ZeroTier; -static OneService *zt1Service = (OneService *)0; +static OneService *volatile zt1Service = (OneService *)0; /****************************************************************************/ /* zerotier-cli personality */ @@ -437,29 +438,11 @@ static void printHelp(const char *cn,FILE *out) { fprintf(out,"ZeroTier One version %d.%d.%d"ZT_EOL_S"(c)2011-2015 ZeroTier, Inc."ZT_EOL_S,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION); fprintf(out,"Licensed under the GNU General Public License v3"ZT_EOL_S""ZT_EOL_S); - -#ifdef ZT_AUTO_UPDATE - fprintf(out,"Auto-update enabled build, will update from URL:"ZT_EOL_S); - fprintf(out," %s"ZT_EOL_S,ZT_DEFAULTS.updateLatestNfoURL.c_str()); - fprintf(out,"Update authentication signing authorities: "ZT_EOL_S); - int no = 0; - for(std::map< Address,Identity >::const_iterator sa(ZT_DEFAULTS.updateAuthorities.begin());sa!=ZT_DEFAULTS.updateAuthorities.end();++sa) { - if (no == 0) - fprintf(out," %s",sa->first.toString().c_str()); - else fprintf(out,", %s",sa->first.toString().c_str()); - if (++no == 6) { - fprintf(out,ZT_EOL_S); - no = 0; - } - } - fprintf(out,ZT_EOL_S""ZT_EOL_S); -#endif // ZT_AUTO_UPDATE - fprintf(out,"Usage: %s [-switches] [home directory] [-q ]"ZT_EOL_S""ZT_EOL_S,cn); fprintf(out,"Available switches:"ZT_EOL_S); fprintf(out," -h - Display this help"ZT_EOL_S); fprintf(out," -v - Show version"ZT_EOL_S); - fprintf(out," -p - Port for UDP (default: 9993)"ZT_EOL_S); + fprintf(out," -p - Port for UDP and TCP/HTTP (default: 9993)"ZT_EOL_S); //fprintf(out," -T - Override root topology, do not authenticate or update"ZT_EOL_S); #ifdef __UNIX_LIKE__ fprintf(out," -d - Fork and run as daemon (Unix-ish OSes)"ZT_EOL_S); @@ -643,8 +626,24 @@ int main(int argc,char **argv) if (!homeDir.length()) homeDir = OneService::platformDefaultHomePath(); - - OSUtils::mkdir(homeDir.c_str()); + if (!homeDir.length()) { + fprintf(stderr,"%s: no home path specified and no platform default available"ZT_EOL_S,argv[0]); + return 1; + } else { + std::vector hpsp(Utils::split(homeDir.c_str(),ZT_PATH_SEPARATOR_S,"","")); + std::string ptmp; + if (homeDir[0] == ZT_PATH_SEPARATOR) + ptmp.push_back(ZT_PATH_SEPARATOR); + for(std::vector::iterator pi(hpsp.begin());pi!=hpsp.end();++pi) { + if (ptmp.length() > 0) + ptmp.push_back(ZT_PATH_SEPARATOR); + ptmp.append(*pi); + if ((*pi != ".")&&(*pi != "..")) { + if (!OSUtils::mkdir(ptmp)) + throw std::runtime_error("home path does not exist, and could not create"); + } + } + } std::string authToken; { @@ -713,4 +712,57 @@ int main(int argc,char **argv) } #endif // __WINDOWS__ + NetworkController *controller = (NetworkController *)0; +#ifdef ZT_ENABLE_NETWORK_CONTROLLER + try { + controller = new SqliteNetworkController((homeDir + ZT_PATH_SEPARATOR_S + ZT1_CONTROLLER_DB_PATH).c_str()); + } catch (std::exception &exc) { + fprintf(stderr,"%s: failure initializing SqliteNetworkController: %s"ZT_EOL_S,exc.what()); + return 1; + } catch ( ... ) { + fprintf(stderr,"%s: failure initializing SqliteNetworkController: unknown exception"ZT_EOL_S); + return 1; + } +#endif // ZT_ENABLE_NETWORK_CONTROLLER + + unsigned int returnValue = 0; + + try { + for(;;) { + zt1Service = OneService::newInstance(homeDir.c_str(),port,controller,(overrideRootTopology.length() > 0) ? overrideRootTopology.c_str() : (const char *)0); + switch(zt1Service->run()) { + case OneService::ONE_STILL_RUNNING: // shouldn't happen, run() won't return until done + case OneService::ONE_NORMAL_TERMINATION: + break; + case OneService::ONE_UNRECOVERABLE_ERROR: + fprintf(stderr,"%s: fatal error: %s"ZT_EOL_S,argv[0],zt1Service->fatalErrorMessage().c_str()); + returnValue = 1; + break; + case OneService::ONE_IDENTITY_COLLISION: { + delete zt1Service; + zt1Service = (OneService *)0; + std::string oldid; + OSUtils::readFile((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret").c_str(),oldid); + if (oldid.length()) { + OSUtils::writeFile((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret.saved_after_collision").c_str(),oldid); + OSUtils::rm((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret").c_str()); + OSUtils::rm((homeDir + ZT_PATH_SEPARATOR_S + "identity.public").c_str()); + } + } continue; // restart! + } + break; // terminate loop -- normally we don't keep restarting + } + } catch (std::exception &exc) { + fprintf(stderr,"%s: fatal error: %s"ZT_EOL_S,argv[0],exc.what()); + returnValue = 1; + } catch ( ... ) { + fprintf(stderr,"%s: fatal error: unknown exception"ZT_EOL_S,argv[0]); + returnValue = 1; + } + + delete zt1Service; + zt1Service = (OneService *)0; + delete controller; + + return returnValue; } diff --git a/service/OneService.cpp b/service/OneService.cpp index 22938c662..38f6e1166 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -143,22 +143,6 @@ public: struct sockaddr_in in4; struct sockaddr_in6 in6; - if (*hp) { - std::vector hpsp(Utils::split(hp,ZT_PATH_SEPARATOR_S,"","")); - std::string ptmp; - if (*hp == '/') - ptmp.push_back('/'); - for(std::vector::iterator pi(hpsp.begin());pi!=hpsp.end();++pi) { - if (ptmp.length() > 0) - ptmp.push_back(ZT_PATH_SEPARATOR); - ptmp.append(*pi); - if ((*pi != ".")&&(*pi != "..")) { - if (!OSUtils::mkdir(ptmp)) - throw std::runtime_error("home path does not exist, and could not create"); - } - } - } - ::memset((void *)&in4,0,sizeof(in4)); in4.sin_family = AF_INET; in4.sin_port = Utils::hton((uint16_t)port); diff --git a/service/OneService.hpp b/service/OneService.hpp index bba3b9b80..9df651791 100644 --- a/service/OneService.hpp +++ b/service/OneService.hpp @@ -32,7 +32,7 @@ namespace ZeroTier { -class NetworkConfigMaster; +class NetworkController; /** * Local service for ZeroTier One as system VPN/NFV provider @@ -85,7 +85,7 @@ public: static OneService *newInstance( const char *hp, unsigned int port, - NetworkConfigMaster *master = (NetworkConfigMaster *)0, + NetworkController *master = (NetworkController *)0, const char *overrideRootTopology = (const char *)0); virtual ~OneService();