diff options
author | Adam Ierymenko <adam.ierymenko@gmail.com> | 2015-05-20 19:38:49 -0700 |
---|---|---|
committer | Adam Ierymenko <adam.ierymenko@gmail.com> | 2015-05-20 19:38:49 -0700 |
commit | 12130739166895db88dca0c230f3972bd3ac3d6a (patch) | |
tree | ec5ce7cc4b2e804dcca69f874e58cb4f1aa6fbff | |
parent | b6698d8415728a249426ee784fcbebfdfb8e4632 (diff) | |
download | infinitytier-12130739166895db88dca0c230f3972bd3ac3d6a.tar.gz infinitytier-12130739166895db88dca0c230f3972bd3ac3d6a.zip |
Apple auto-update stuff, now for Windows.
-rw-r--r-- | attic/SoftwareUpdater.cpp | 328 | ||||
-rw-r--r-- | attic/SoftwareUpdater.hpp | 186 | ||||
-rwxr-xr-x | ext/installfiles/mac/postinst.sh | 6 | ||||
-rwxr-xr-x | ext/installfiles/mac/preinst.sh | 22 | ||||
-rw-r--r-- | make-mac.mk | 4 | ||||
-rw-r--r-- | node/Node.cpp | 2 | ||||
-rw-r--r-- | node/Peer.hpp | 25 | ||||
-rw-r--r-- | node/Utils.hpp | 27 | ||||
-rw-r--r-- | service/OneService.cpp | 180 | ||||
-rw-r--r-- | updater.cpp | 177 |
10 files changed, 227 insertions, 730 deletions
diff --git a/attic/SoftwareUpdater.cpp b/attic/SoftwareUpdater.cpp deleted file mode 100644 index e3789bcb..00000000 --- a/attic/SoftwareUpdater.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <stdexcept> - -#include "../version.h" - -#include "Constants.hpp" -#include "SoftwareUpdater.hpp" -#include "Dictionary.hpp" -#include "C25519.hpp" -#include "Identity.hpp" -#include "Logger.hpp" -#include "RuntimeEnvironment.hpp" -#include "Thread.hpp" -#include "Node.hpp" -#include "Utils.hpp" -#include "HttpClient.hpp" - -#ifdef __UNIX_LIKE__ -#include <unistd.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/stat.h> -#endif - -namespace ZeroTier { - -static inline std::map< Address,Identity > _mkUpdateAuth() -{ - std::map< Address,Identity > ua; - - { // 0001 - Identity id("e9bc3707b5:0:c4cef17bde99eadf9748c4fd11b9b06dc5cd8eb429227811d2c336e6b96a8d329e8abd0a4f45e47fe1bcebf878c004c822d952ff77fc2833af4c74e65985c435"); - ua[id.address()] = id; - } - { // 0002 - Identity id("56520eaf93:0:7d858b47988b34399a9a31136de07b46104d7edb4a98fa1d6da3e583d3a33e48be531532b886f0b12cd16794a66ab9220749ec5112cbe96296b18fe0cc79ca05"); - ua[id.address()] = id; - } - { // 0003 - Identity id("7c195de2e0:0:9f659071c960f9b0f0b96f9f9ecdaa27c7295feed9c79b7db6eedcc11feb705e6dd85c70fa21655204d24c897865b99eb946b753a2bbcf2be5f5e006ae618c54"); - ua[id.address()] = id; - } - { // 0004 - Identity id("415f4cfde7:0:54118e87777b0ea5d922c10b337c4f4bd1db7141845bd54004b3255551a6e356ba6b9e1e85357dbfafc45630b8faa2ebf992f31479e9005f0472685f2d8cbd6e"); - ua[id.address()] = id; - } - - return ua; -} - -static inline const char *_mkUpdateUrl() -{ -#if defined(__LINUX__) && ( defined(__i386__) || defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__i386) ) - if (sizeof(void *) == 8) - return "http://download.zerotier.com/ZeroTierOneInstaller-linux-x64-LATEST.nfo"; - else return "http://download.zerotier.com/ZeroTierOneInstaller-linux-x86-LATEST.nfo"; -#define GOT_UPDATE_URL -#endif - -#ifdef __APPLE__ - return "http://download.zerotier.com/ZeroTierOneInstaller-mac-combined-LATEST.nfo"; -#define GOT_UPDATE_URL -#endif - -#ifdef __WINDOWS__ - return "http://download.zerotier.com/ZeroTierOneInstaller-windows-intel-LATEST.nfo"; -#define GOT_UPDATE_URL -#endif - -#ifndef GOT_UPDATE_URL - return ""; -#endif -} - -SoftwareUpdater::SoftwareUpdater(const RuntimeEnvironment *renv) : - RR(renv), - _myVersion(packVersion(ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION)), - _lastUpdateAttempt(0), - _status(UPDATE_STATUS_IDLE), - _die(false), - _lock() -{ -} - -SoftwareUpdater::~SoftwareUpdater() -{ - _die = true; - for(;;) { - _lock.lock(); - bool ip = (_status != UPDATE_STATUS_IDLE); - _lock.unlock(); - if (ip) - Thread::sleep(500); - else break; - } -} - -void SoftwareUpdater::cleanOldUpdates() -{ - std::string updatesDir(RR->homePath + ZT_PATH_SEPARATOR_S + "updates.d"); - std::map<std::string,bool> dl(Utils::listDirectory(updatesDir.c_str())); - for(std::map<std::string,bool>::iterator i(dl.begin());i!=dl.end();++i) { - if (!i->second) - Utils::rm((updatesDir + ZT_PATH_SEPARATOR_S + i->first).c_str()); - } -} - -void SoftwareUpdater::sawRemoteVersion(unsigned int vmaj,unsigned int vmin,unsigned int rev) -{ - const uint64_t tmp = packVersion(vmaj,vmin,rev); - if (tmp > _myVersion) { - Mutex::Lock _l(_lock); - if ((_status == UPDATE_STATUS_IDLE)&&(!_die)&&(ZT_DEFAULTS.updateLatestNfoURL.length())) { - const uint64_t now = Utils::now(); - if ((now - _lastUpdateAttempt) >= ZT_UPDATE_MIN_INTERVAL) { - _lastUpdateAttempt = now; - _status = UPDATE_STATUS_GETTING_NFO; - RR->http->GET(ZT_DEFAULTS.updateLatestNfoURL,HttpClient::NO_HEADERS,ZT_UPDATE_HTTP_TIMEOUT,&_cbHandleGetLatestVersionInfo,this); - } - } - } -} - -void SoftwareUpdater::checkNow() -{ - Mutex::Lock _l(_lock); - if (_status == UPDATE_STATUS_IDLE) { - _lastUpdateAttempt = Utils::now(); - _status = UPDATE_STATUS_GETTING_NFO; - RR->http->GET(ZT_DEFAULTS.updateLatestNfoURL,HttpClient::NO_HEADERS,ZT_UPDATE_HTTP_TIMEOUT,&_cbHandleGetLatestVersionInfo,this); - } -} - -const char *SoftwareUpdater::parseNfo( - const char *nfoText, - unsigned int &vMajor, - unsigned int &vMinor, - unsigned int &vRevision, - Address &signedBy, - std::string &signature, - std::string &url) -{ - try { - Dictionary nfo(nfoText); - - vMajor = Utils::strToUInt(nfo.get("vMajor").c_str()); - vMinor = Utils::strToUInt(nfo.get("vMinor").c_str()); - vRevision = Utils::strToUInt(nfo.get("vRevision").c_str()); - signedBy = nfo.get("signedBy"); - signature = Utils::unhex(nfo.get("ed25519")); - url = nfo.get("url"); - - if (signature.length() != ZT_C25519_SIGNATURE_LEN) - return "bad ed25519 signature, invalid length"; - if ((url.length() <= 7)||(url.substr(0,7) != "http://")) - return "invalid URL, must begin with http://"; - - return (const char *)0; - } catch ( ... ) { - return "invalid NFO file format or one or more required fields missing"; - } -} - -bool SoftwareUpdater::validateUpdate( - const void *data, - unsigned int len, - const Address &signedBy, - const std::string &signature) -{ - std::map< Address,Identity >::const_iterator updateAuthority = ZT_DEFAULTS.updateAuthorities.find(signedBy); - if (updateAuthority == ZT_DEFAULTS.updateAuthorities.end()) - return false; - return updateAuthority->second.verify(data,len,signature.data(),(unsigned int)signature.length()); -} - -void SoftwareUpdater::_cbHandleGetLatestVersionInfo(void *arg,int code,const std::string &url,const std::string &body) -{ - SoftwareUpdater *upd = (SoftwareUpdater *)arg; - const RuntimeEnvironment *RR = (const RuntimeEnvironment *)upd->RR; - Mutex::Lock _l(upd->_lock); - - if ((upd->_die)||(upd->_status != UPDATE_STATUS_GETTING_NFO)) { - upd->_status = UPDATE_STATUS_IDLE; - return; - } - - if (code != 200) { - LOG("software update check failed: server responded with code %d",code); - upd->_status = UPDATE_STATUS_IDLE; - return; - } - - try { - unsigned int vMajor = 0,vMinor = 0,vRevision = 0; - Address signedBy; - std::string signature,url; - - const char *err = parseNfo(body.c_str(),vMajor,vMinor,vRevision,signedBy,signature,url); - - if (err) { - LOG("software update check aborted: .nfo file parse error: %s",err); - upd->_status = UPDATE_STATUS_IDLE; - return; - } - - if (!ZT_DEFAULTS.updateAuthorities.count(signedBy)) { - LOG("software update check aborted: .nfo file specifies unknown signing authority"); - upd->_status = UPDATE_STATUS_IDLE; - return; - } - -#ifndef ZT_ALWAYS_UPDATE /* for testing */ - if (packVersion(vMajor,vMinor,vRevision) <= upd->_myVersion) { - TRACE("software update check complete: version on update site is not newer than my version, no update necessary"); - upd->_status = UPDATE_STATUS_IDLE; - return; - } -#endif - - upd->_status = UPDATE_STATUS_GETTING_FILE; - upd->_signedBy = signedBy; - upd->_signature = signature; - - RR->http->GET(url,HttpClient::NO_HEADERS,ZT_UPDATE_HTTP_TIMEOUT,&_cbHandleGetLatestVersionBinary,arg); - } catch ( ... ) { - LOG("software update check failed: .nfo file invalid or missing field(s)"); - upd->_status = UPDATE_STATUS_IDLE; - } -} - -void SoftwareUpdater::_cbHandleGetLatestVersionBinary(void *arg,int code,const std::string &url,const std::string &body) -{ - SoftwareUpdater *upd = (SoftwareUpdater *)arg; - const RuntimeEnvironment *RR = (const RuntimeEnvironment *)upd->RR; - Mutex::Lock _l(upd->_lock); - - if (!validateUpdate(body.data(),(unsigned int)body.length(),upd->_signedBy,upd->_signature)) { - LOG("software update failed: update fetched from '%s' failed signature check (image size: %u)",url.c_str(),(unsigned int)body.length()); - upd->_status = UPDATE_STATUS_IDLE; - return; - } - - size_t lastSlash = url.rfind('/'); - if (lastSlash == std::string::npos) { // sanity check, shouldn't happen - LOG("software update failed: invalid URL"); - upd->_status = UPDATE_STATUS_IDLE; - return; - } - std::string updatesDir(RR->homePath + ZT_PATH_SEPARATOR_S + "updates.d"); - std::string updateFilename(url.substr(lastSlash + 1)); - if ((updateFilename.length() < 3)||(updateFilename.find("..") != std::string::npos)) { - LOG("software update failed: invalid URL: filename contains invalid characters"); - upd->_status = UPDATE_STATUS_IDLE; - return; - } - for(std::string::iterator c(updateFilename.begin());c!=updateFilename.end();++c) { - // Only allow a list of whitelisted characters to make up the filename to prevent any - // path shenanigans, esp on Windows where / is not the path separator. - if (!strchr("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.0123456789",*c)) { - LOG("software update failed: invalid URL: filename contains invalid characters"); - upd->_status = UPDATE_STATUS_IDLE; - return; - } - } - std::string updatePath(updatesDir + ZT_PATH_SEPARATOR_S + updateFilename); -#ifdef __WINDOWS__ - CreateDirectoryA(updatesDir.c_str(),NULL); -#else - mkdir(updatesDir.c_str(),0755); -#endif - - FILE *upf = fopen(updatePath.c_str(),"wb"); - if (!upf) { - LOG("software update failed: unable to open %s for writing",updatePath.c_str()); - upd->_status = UPDATE_STATUS_IDLE; - return; - } - if (fwrite(body.data(),body.length(),1,upf) != 1) { - LOG("software update failed: unable to write to %s",updatePath.c_str()); - upd->_status = UPDATE_STATUS_IDLE; - fclose(upf); - Utils::rm(updatePath); - return; - } - fclose(upf); - -#ifdef __UNIX_LIKE__ - ::chmod(updatePath.c_str(),0755); -#endif - - // We exit with this reason code and the path as the text. It is the - // caller's responsibility (main.c) to pick this up and do the right - // thing. - upd->_status = UPDATE_STATUS_IDLE; - RR->node->terminate(Node::NODE_RESTART_FOR_UPGRADE,updatePath.c_str()); -} - -} // namespace ZeroTier diff --git a/attic/SoftwareUpdater.hpp b/attic/SoftwareUpdater.hpp deleted file mode 100644 index 9beaa8ad..00000000 --- a/attic/SoftwareUpdater.hpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef ZT_SOFTWAREUPDATER_HPP -#define ZT_SOFTWAREUPDATER_HPP - -#include <stdint.h> - -#include <string> - -#include "../node/Constants.hpp" -#include "../node/Mutex.hpp" -#include "../node/Address.hpp" - -#include "HttpClient.hpp" - -/** - * Delay between fetches of the root topology update URL - * - * 86400000 = check once every 24 hours (this doesn't change often) - */ -#define ZT_UPDATE_ROOT_TOPOLOGY_CHECK_INTERVAL 86400000 - -/** - * Minimum interval between attempts to do a software update - */ -#define ZT_UPDATE_MIN_INTERVAL 120000 - -/** - * Maximum interval between checks for new versions - */ -#define ZT_UPDATE_MAX_INTERVAL 7200000 - -/** - * Software update HTTP timeout in seconds - */ -#define ZT_UPDATE_HTTP_TIMEOUT 120 - -namespace ZeroTier { - -/** - * Software updater - */ -class SoftwareUpdater -{ -public: - SoftwareUpdater(); - ~SoftwareUpdater(); - - /** - * Remove old updates in updates.d - */ - void cleanOldUpdates(); - - /** - * Called on each version message from a peer - * - * If a peer has a newer version, that causes an update to be started. - * - * @param vmaj Peer's major version - * @param vmin Peer's minor version - * @param rev Peer's revision - */ - void sawRemoteVersion(unsigned int vmaj,unsigned int vmin,unsigned int rev); - - /** - * Check for updates now regardless of last check time or version - * - * This only starts a check if one is not in progress. Otherwise it does - * nothing. - */ - void checkNow(); - - /** - * Check for updates now if it's been longer than ZT_UPDATE_MAX_INTERVAL - * - * This is called periodically from the main loop. - */ - inline void checkIfMaxIntervalExceeded(uint64_t now) - { - if ((now - _lastUpdateAttempt) >= ZT_UPDATE_MAX_INTERVAL) - checkNow(); - } - - /** - * Pack three-component version into a 64-bit integer - * - * @param vmaj Major version (0..65535) - * @param vmin Minor version (0..65535) - * @param rev Revision (0..65535) - * @return Version packed into an easily comparable 64-bit integer - */ - static inline uint64_t packVersion(unsigned int vmaj,unsigned int vmin,unsigned int rev) - throw() - { - return ( ((uint64_t)(vmaj & 0xffff) << 32) | ((uint64_t)(vmin & 0xffff) << 16) | (uint64_t)(rev & 0xffff) ); - } - - /** - * Parse NFO data from .nfo file on software update site - * - * The first argument is the NFO data, and all the remaining arguments are - * result parameters to be filled with results. If an error is returned the - * results in the parameters should be considered undefined. - * - * @param nfo NFO data - * @param vMajor Result: major version - * @param vMinor Result: minor version - * @param vRevision Result: revision number - * @param signedBy Result: signing identity - * @param signature Result: Ed25519 signature data - * @param url Result: URL of update binary - * @return NULL on success or error message on failure - */ - static const char *parseNfo( - const char *nfoText, - unsigned int &vMajor, - unsigned int &vMinor, - unsigned int &vRevision, - Address &signedBy, - std::string &signature, - std::string &url); - - /** - * Validate an update once downloaded - * - * This obtains the identity corresponding to the address from the compiled-in - * list of valid signing identities. - * - * @param data Update data - * @param len Length of update data - * @param signedBy Signing authority address - * @param signature Signing authority signature - * @return True on validation success, false if rejected - */ - static bool validateUpdate( - const void *data, - unsigned int len, - const Address &signedBy, - const std::string &signature); - -private: - static void _cbHandleGetLatestVersionInfo(void *arg,int code,const std::string &url,const std::string &body); - static void _cbHandleGetLatestVersionBinary(void *arg,int code,const std::string &url,const std::string &body); - - HttpClient httpClient; - const uint64_t _myVersion; - volatile uint64_t _lastUpdateAttempt; - volatile enum { - UPDATE_STATUS_IDLE, - UPDATE_STATUS_GETTING_NFO, - UPDATE_STATUS_GETTING_FILE - } _status; - volatile bool _die; - Address _signedBy; - std::string _signature; - Mutex _lock; -}; - -} // namespace ZeroTier - -#endif diff --git a/ext/installfiles/mac/postinst.sh b/ext/installfiles/mac/postinst.sh index 094eea48..7d3d516f 100755 --- a/ext/installfiles/mac/postinst.sh +++ b/ext/installfiles/mac/postinst.sh @@ -3,15 +3,11 @@ export PATH=/bin:/usr/bin:/sbin:/usr/sbin launchctl unload /Library/LaunchDaemons/com.zerotier.one.plist >>/dev/null 2>&1 -sleep 1 -killall zerotier-one -sleep 1 -killall -9 zerotier-one cd "/Library/Application Support/ZeroTier/One" rm -rf node.log node.log.old root-topology shutdownIfUnreadable autoupdate.log updates.d if [ ! -f authtoken.secret ]; then - head -c 1024 /dev/urandom | md5 | head -c 24 >authtoken.secret + head -c 4096 /dev/urandom | md5 | head -c 24 >authtoken.secret chown root authtoken.secret chgrp wheel authtoken.secret chmod 0600 authtoken.secret diff --git a/ext/installfiles/mac/preinst.sh b/ext/installfiles/mac/preinst.sh index 9fa50bef..c2cb494b 100755 --- a/ext/installfiles/mac/preinst.sh +++ b/ext/installfiles/mac/preinst.sh @@ -4,11 +4,23 @@ export PATH=/bin:/usr/bin:/sbin:/usr/sbin if [ -f /Library/LaunchDaemons/com.zerotier.one.plist ]; then launchctl unload /Library/LaunchDaemons/com.zerotier.one.plist >>/dev/null 2>&1 - sleep 1 - killall zerotier-one - sleep 1 - killall -9 zerotier-one fi -cd /Applications +sleep 1 + +if [ -d "/Library/Application Support/ZeroTier/One" ]; then + cd "/Library/Application Support/ZeroTier/One" + if [ -f "zerotier-one.pid" ]; then + ztpid=`cat zerotier-one.pid` + if [ "$ztpid" -gt "0" ]; then + kill `cat zerotier-one.pid` + fi + fi +fi + +sleep 1 + +cd "/Applications" rm -rf "ZeroTier One.app" + +exit 0 diff --git a/make-mac.mk b/make-mac.mk index 4ad4c5ad..7d730ae5 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -20,6 +20,10 @@ ifeq ($(ZT_OFFICIAL_RELEASE),1) CODESIGN_CERT="Developer ID Application: ZeroTier Networks LLC (8ZD9JUCZ4V)" endif +ifeq ($(ZT_AUTO_UPDATE),1) + DEFS+=-DZT_AUTO_UPDATE +endif + # Build with ZT_ENABLE_NETWORK_CONTROLLER=1 to build with the Sqlite network controller ifeq ($(ZT_ENABLE_NETWORK_CONTROLLER),1) DEFS+=-DZT_ENABLE_NETWORK_CONTROLLER diff --git a/node/Node.cpp b/node/Node.cpp index 0e3ddd14..8eb9ae90 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -443,7 +443,7 @@ std::string Node::dataStoreGet(const char *name) void Node::postNewerVersionIfNewer(unsigned int major,unsigned int minor,unsigned int rev) { - if (Peer::compareVersion(major,minor,rev,_newestVersionSeen[0],_newestVersionSeen[1],_newestVersionSeen[2]) > 0) { + if (Utils::compareVersion(major,minor,rev,_newestVersionSeen[0],_newestVersionSeen[1],_newestVersionSeen[2]) > 0) { _newestVersionSeen[0] = major; _newestVersionSeen[1] = minor; _newestVersionSeen[2] = rev; diff --git a/node/Peer.hpp b/node/Peer.hpp index 343cfcfa..3d52761a 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -407,31 +407,6 @@ public: else return std::pair<InetAddress,InetAddress>(); } - /** - * Compare Peer version tuples - */ - static inline int compareVersion(unsigned int maj1,unsigned int min1,unsigned int rev1,unsigned int maj2,unsigned int min2,unsigned int rev2) - throw() - { - if (maj1 > maj2) - return 1; - else if (maj1 < maj2) - return -1; - else { - if (min1 > min2) - return 1; - else if (min1 < min2) - return -1; - else { - if (rev1 > rev2) - return 1; - else if (rev1 < rev2) - return -1; - else return 0; - } - } - } - private: void _announceMulticastGroups(const RuntimeEnvironment *RR,uint64_t now); diff --git a/node/Utils.hpp b/node/Utils.hpp index bdd673a9..bd567cf5 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -382,6 +382,33 @@ public: static inline int64_t ntoh(int64_t n) throw() { return (int64_t)ntoh((uint64_t)n); } /** + * Compare Peer version tuples + * + * @return -1, 0, or 1 based on whether first tuple is less than, equal to, or greater than second + */ + static inline int compareVersion(unsigned int maj1,unsigned int min1,unsigned int rev1,unsigned int maj2,unsigned int min2,unsigned int rev2) + throw() + { + if (maj1 > maj2) + return 1; + else if (maj1 < maj2) + return -1; + else { + if (min1 > min2) + return 1; + else if (min1 < min2) + return -1; + else { + if (rev1 > rev2) + return 1; + else if (rev1 < rev2) + return -1; + else return 0; + } + } + } + + /** * Hexadecimal characters 0-f */ static const char HEXCHARS[16]; diff --git a/service/OneService.cpp b/service/OneService.cpp index c2ea034b..b8a66f6f 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -28,6 +28,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <stdint.h> #include <string> #include <map> @@ -46,9 +47,12 @@ #include "../node/Utils.hpp" #include "../node/InetAddress.hpp" #include "../node/MAC.hpp" +#include "../node/Identity.hpp" #include "../osdep/Phy.hpp" +#include "../osdep/Thread.hpp" #include "../osdep/OSUtils.hpp" +#include "../osdep/Http.hpp" #include "OneService.hpp" #include "ControlPlane.hpp" @@ -57,10 +61,14 @@ #include "../controller/SqliteNetworkController.hpp" #else class SqliteNetworkController; -#endif +#endif // ZT_ENABLE_NETWORK_CONTROLLER #ifdef __WINDOWS__ #include <ShlObj.h> +#else +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> #endif // Include the right tap device driver for this platform -- add new platforms here @@ -98,6 +106,168 @@ namespace ZeroTier { namespace { +#ifdef ZT_AUTO_UPDATE +#define ZT_AUTO_UPDATE_MAX_HTTP_RESPONSE_SIZE (1024 * 1024 * 64) +class BackgroundSoftwareUpdateChecker +{ +public: + bool isValidSigningIdentity(const Identity &id) + { + return ( + /* 0005 */ (id == Identity("ba57ea350e:0:9d4be6d7f86c5660d5ee1951a3d759aa6e12a84fc0c0b74639500f1dbc1a8c566622e7d1c531967ebceb1e9d1761342f88324a8ba520c93c35f92f35080fa23f")) + /* 0006 */ ||(id == Identity("5067b21b83:0:8af477730f5055c48135b84bed6720a35bca4c0e34be4060a4c636288b1ec22217eb22709d610c66ed464c643130c51411bbb0294eef12fbe8ecc1a1e2c63a7a")) + /* 0007 */ ||(id == Identity("4f5e97a8f1:0:57880d056d7baeb04bbc057d6f16e6cb41388570e87f01492fce882485f65a798648595610a3ad49885604e7fb1db2dd3c2c534b75e42c3c0b110ad07b4bb138")) + /* 0008 */ ||(id == Identity("580bbb8e15:0:ad5ef31155bebc6bc413991992387e083fed26d699997ef76e7c947781edd47d1997161fa56ba337b1a2b44b129fd7c7197ce5185382f06011bc88d1363b4ddd")) + ); + } + + void doUpdateCheck() + { + std::string url(OneService::autoUpdateUrl()); + if ((url.length() <= 7)||(url.substr(0,7) != "http://")) + return; + + std::string httpHost; + std::string httpPath; + { + std::size_t slashIdx = url.substr(7).find_first_of('/'); + if (slashIdx == std::string::npos) { + httpHost = url.substr(7); + httpPath = "/"; + } else { + httpHost = url.substr(7,slashIdx); + httpPath = url.substr(slashIdx + 7); + } + } + if (httpHost.length() == 0) + return; + + std::vector<InetAddress> ips(OSUtils::resolve(httpHost.c_str())); + for(std::vector<InetAddress>::iterator ip(ips.begin());ip!=ips.end();++ip) { + if (!ip->port()) + ip->setPort(80); + std::string nfoPath = httpPath + "LATEST.nfo"; + std::map<std::string,std::string> requestHeaders,responseHeaders; + std::string body; + requestHeaders["Host"] = httpHost; + unsigned int scode = Http::GET(ZT_AUTO_UPDATE_MAX_HTTP_RESPONSE_SIZE,60000,reinterpret_cast<const struct sockaddr *>(&(*ip)),nfoPath.c_str(),requestHeaders,responseHeaders,body); + //fprintf(stderr,"UPDATE %s %s %u %lu\n",ip->toString().c_str(),nfoPath.c_str(),scode,body.length()); + if ((scode == 200)&&(body.length() > 0)) { + /* NFO fields: + * + * file=<filename> + * signedBy=<signing identity> + * ed25519=<ed25519 ECC signature of archive> + * vMajor=<major version> + * vMinor=<minor version> + * vRevision=<revision> */ + Dictionary nfo(body); + + unsigned int vMajor = Utils::strToUInt(nfo.get("vMajor","0").c_str()); + unsigned int vMinor = Utils::strToUInt(nfo.get("vMinor","0").c_str()); + unsigned int vRevision = Utils::strToUInt(nfo.get("vRevision","0").c_str()); + if (Utils::compareVersion(vMajor,vMinor,vRevision,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION) <= 0) { + //fprintf(stderr,"UPDATE %u.%u.%u is not newer than our version\n",vMajor,vMinor,vRevision); + return; + } + + Identity signedBy; + if ((!signedBy.fromString(nfo.get("signedBy","")))||(!isValidSigningIdentity(signedBy))) { + //fprintf(stderr,"UPDATE invalid signedBy or not authorized signing identity.\n"); + return; + } + + std::string filePath(nfo.get("file","")); + if ((!filePath.length())||(filePath.find("..") != std::string::npos)) + return; + filePath = httpPath + filePath; + + std::string fileData; + if (Http::GET(ZT_AUTO_UPDATE_MAX_HTTP_RESPONSE_SIZE,60000,reinterpret_cast<const struct sockaddr *>(&(*ip)),filePath.c_str(),requestHeaders,responseHeaders,fileData) != 200) { + //fprintf(stderr,"UPDATE GET %s failed\n",filePath.c_str()); + return; + } + + std::string ed25519(nfo.get("ed25519","")); + if ((ed25519.length() == 0)||(!signedBy.verify(fileData.data(),(unsigned int)fileData.length(),ed25519.data(),(unsigned int)ed25519.length()))) { + //fprintf(stderr,"UPDATE %s failed signature check!\n",filePath.c_str()); + return; + } + + /* --------------------------------------------------------------- */ + /* We made it! Begin OS-specific installation code. */ + +#ifdef __APPLE__ + /* OSX version is in the form of a MacOSX .pkg file, so we will + * launch installer (normally in /usr/sbin) to install it. It will + * then turn around and shut down the service, update files, and + * relaunch. */ + { + char bashp[128],pkgp[128]; + Utils::snprintf(bashp,sizeof(bashp),"/tmp/ZeroTierOne-update-%u.%u.%u.sh",vMajor,vMinor,vRevision); + Utils::snprintf(pkgp,sizeof(pkgp),"/tmp/ZeroTierOne-update-%u.%u.%u.pkg",vMajor,vMinor,vRevision); + FILE *pkg = fopen(pkgp,"w"); + if ((!pkg)||(fwrite(fileData.data(),fileData.length(),1,pkg) != 1)) { + fclose(pkg); + unlink(bashp); + unlink(pkgp); + fprintf(stderr,"UPDATE error writing %s\n",pkgp); + return; + } + fclose(pkg); + FILE *bash = fopen(bashp,"w"); + if (!bash) { + fclose(pkg); + unlink(bashp); + unlink(pkgp); + fprintf(stderr,"UPDATE error writing %s\n",bashp); + return; + } + fprintf(bash, + "#!/bin/bash\n" + "export PATH=/bin:/usr/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/sbin\n" + "sleep 2\n" + "installer -pkg \"%s\" -target /\n" + "sleep 1\n" + "rm -f \"%s\" \"%s\"\n" + "exit 0\n", + pkgp, + pkgp, + bashp); + fclose(bash); + long pid = (long)vfork(); + if (pid == 0) { + execl("/bin/bash","/bin/bash",bashp,(char *)0); + exit(0); + } + } +#endif // __APPLE__ + +#ifdef __WINDOWS__ + /* Windows version comes in the form of .MSI package that + * takes care of everything. */ + { + } +#endif // __WINDOWS__ + + /* --------------------------------------------------------------- */ + + return; + } // else try to fetch from next IP address + } + } + + void threadMain() + throw() + { + try { + this->doUpdateCheck(); + } catch ( ... ) {} + } +}; +static BackgroundSoftwareUpdateChecker backgroundSoftwareUpdateChecker; +#endif // ZT_AUTO_UPDATE + class OneServiceImpl; static int SnodeVirtualNetworkConfigFunction(ZT1_Node *node,void *uptr,uint64_t nwid,enum ZT1_VirtualNetworkConfigOperation op,const ZT1_VirtualNetworkConfig *nwconf); @@ -197,6 +367,10 @@ public: char portstr[64]; Utils::snprintf(portstr,sizeof(portstr),"%u",port); OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "zerotier-one.port").c_str(),std::string(portstr)); + +#ifdef ZT_AUTO_UPDATE + Thread::start(&backgroundSoftwareUpdateChecker); +#endif } virtual ~OneServiceImpl() @@ -956,11 +1130,11 @@ std::string OneService::autoUpdateUrl() */ #if defined(__APPLE__) && ( defined(__i386__) || defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__i386) ) - return "http://download.zerotier.com/update/mac_intel/LATEST.nfo"; + return "http://download.zerotier.com/update/mac_intel/"; #endif #ifdef __WINDOWS__ - return "http://download.zerotier.com/update/win_intel/LATEST.nfo"; + return "http://download.zerotier.com/update/win_intel/"; #endif #endif // ZT_AUTO_UPDATE diff --git a/updater.cpp b/updater.cpp deleted file mode 100644 index bc36394b..00000000 --- a/updater.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdint.h> -#include <time.h> - -#include <string> -#include <vector> -#include <map> -#include <algorithm> -#include <stdexcept> - -#include "version.h" -#include "include/ZeroTierOne.h" -#include "node/Constants.hpp" - -#ifdef __WINDOWS__ -#include <WinSock2.h> -#include <Windows.h> -#include <tchar.h> -#include <wchar.h> -#include <lmcons.h> -#include <newdev.h> -#include <atlbase.h> -#else -#include <unistd.h> -#include <pwd.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <signal.h> -#endif - -#include "node/Utils.hpp" -#include "node/Address.hpp" -#include "node/Dictionary.hpp" -#include "node/Identity.hpp" -#include "osdep/OSUtils.hpp" -#include "osdep/Http.hpp" - -using namespace ZeroTier; - -namespace { - -static std::map< Address,Identity > updateAuthorities() -{ - std::map< Address,Identity > ua; - { // 0001 - Identity id("e9bc3707b5:0:c4cef17bde99eadf9748c4fd11b9b06dc5cd8eb429227811d2c336e6b96a8d329e8abd0a4f45e47fe1bcebf878c004c822d952ff77fc2833af4c74e65985c435"); - ua[id.address()] = id; - } - { // 0002 - Identity id("56520eaf93:0:7d858b47988b34399a9a31136de07b46104d7edb4a98fa1d6da3e583d3a33e48be531532b886f0b12cd16794a66ab9220749ec5112cbe96296b18fe0cc79ca05"); - ua[id.address()] = id; - } - { // 0003 - Identity id("7c195de2e0:0:9f659071c960f9b0f0b96f9f9ecdaa27c7295feed9c79b7db6eedcc11feb705e6dd85c70fa21655204d24c897865b99eb946b753a2bbcf2be5f5e006ae618c54"); - ua[id.address()] = id; - } - { // 0004 - Identity id("415f4cfde7:0:54118e87777b0ea5d922c10b337c4f4bd1db7141845bd54004b3255551a6e356ba6b9e1e85357dbfafc45630b8faa2ebf992f31479e9005f0472685f2d8cbd6e"); - ua[id.address()] = id; - } - return ua; -} - -static bool validateUpdate( - const void *data, - unsigned int len, - const Address &signedBy, - const std::string &signature) -{ - std::map< Address,Identity > ua(updateAuthorities()); - std::map< Address,Identity >::const_iterator updateAuthority = ua.find(signedBy); - if (updateAuthority == ua.end()) - return false; - return updateAuthority->second.verify(data,len,signature.data(),(unsigned int)signature.length()); -} - -/* -static inline const char *updateUrl() -{ -#if defined(__LINUX__) && ( defined(__i386__) || defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__i386) ) - if (sizeof(void *) == 8) - return "http://download.zerotier.com/ZeroTierOneInstaller-linux-x64-LATEST.nfo"; - else return "http://download.zerotier.com/ZeroTierOneInstaller-linux-x86-LATEST.nfo"; -#define GOT_UPDATE_URL -#endif - -#ifdef __APPLE__ - return "http://download.zerotier.com/ZeroTierOneInstaller-mac-combined-LATEST.nfo"; -#define GOT_UPDATE_URL -#endif - -#ifdef __WINDOWS__ - return "http://download.zerotier.com/ZeroTierOneInstaller-windows-intel-LATEST.nfo"; -#define GOT_UPDATE_URL -#endif - -#ifndef GOT_UPDATE_URL - return ""; -#endif -} -*/ - -static const char *parseUpdateNfo( - const char *nfoText, - unsigned int &vMajor, - unsigned int &vMinor, - unsigned int &vRevision, - Address &signedBy, - std::string &signature, - std::string &url) -{ - try { - Dictionary nfo(nfoText); - - vMajor = Utils::strToUInt(nfo.get("vMajor").c_str()); - vMinor = Utils::strToUInt(nfo.get("vMinor").c_str()); - vRevision = Utils::strToUInt(nfo.get("vRevision").c_str()); - signedBy = nfo.get("signedBy"); - signature = Utils::unhex(nfo.get("ed25519")); - url = nfo.get("url"); - - if (signature.length() != ZT_C25519_SIGNATURE_LEN) - return "bad ed25519 signature, invalid length"; - if ((url.length() <= 7)||(url.substr(0,7) != "http://")) - return "invalid URL, must begin with http://"; - - return (const char *)0; - } catch ( ... ) { - return "invalid NFO file format or one or more required fields missing"; - } -} - -} // anonymous namespace - -#ifdef __WINDOWS__ -int _tmain(int argc, _TCHAR* argv[]) -#else -int main(int argc,char **argv) -#endif -{ -#ifdef __WINDOWS__ - WSADATA wsaData; - WSAStartup(MAKEWORD(2,2),&wsaData); -#endif - - return 0; -} |