From c6b0b076055f0b9b579c21bbbe83171a554a2600 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 12 Jan 2017 13:18:11 -0800 Subject: More software update work, and settings in local.conf. --- service/OneService.cpp | 19 ++++++++++-- service/SoftwareUpdater.cpp | 74 ++++++++++++++++++++++++--------------------- service/SoftwareUpdater.hpp | 33 ++++++++++++++++---- 3 files changed, 82 insertions(+), 44 deletions(-) (limited to 'service') diff --git a/service/OneService.cpp b/service/OneService.cpp index 9ee05f96..697be24a 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -264,6 +264,7 @@ public: Phy _phy; Node *_node; SoftwareUpdater *_updater; + bool _updateAutoApply; unsigned int _primaryPort; // Local configuration and memo-ized static path definitions @@ -367,6 +368,7 @@ public: ,_phy(this,false,true) ,_node((Node *)0) ,_updater((SoftwareUpdater *)0) + ,_updateAutoApply(false) ,_primaryPort(port) ,_controlPlane((ControlPlane *)0) ,_lastDirectReceiveFromGlobal(0) @@ -454,9 +456,6 @@ public: _node = new Node(this,&cb,OSUtils::now()); } - _updater = new SoftwareUpdater(*_node,_homePath); - _updater->loadUpdatesToDistribute(); - // Read local configuration { uint64_t trustedPathIds[ZT_MAX_TRUSTED_PATHS]; @@ -982,6 +981,20 @@ public: _node->setRelayPolicy(ZT_RELAY_POLICY_NEVER); else _node->setRelayPolicy(ZT_RELAY_POLICY_TRUSTED); + const std::string up(OSUtils::jsonString(settings["softwareUpdate"],ZT_SOFTWARE_UPDATE_DEFAULT)); + const bool udist = OSUtils::jsonBool(settings["softwareUpdateDist"],false); + if (((up == "apply")||(up == "download"))||(udist)) { + if (!_updater) + _updater = new SoftwareUpdater(*_node,_homePath); + _updateAutoApply = (up == "apply"); + _updater->setUpdateDistribution(udist); + _updater->setChannel(OSUtils::jsonString(settings["softwareUpdateChannel"],ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL)); + } else { + delete _updater; + _updater = (SoftwareUpdater *)0; + _updateAutoApply = false; + } + json &ignoreIfs = settings["interfacePrefixBlacklist"]; if (ignoreIfs.is_array()) { for(unsigned long i=0;i ud(OSUtils::listDirectory(udd.c_str())); - for(std::vector::iterator u(ud.begin());u!=ud.end();++u) { - // Each update has a companion .json file describing it. Other files are ignored. - if ((u->length() > 5)&&(u->substr(u->length() - 5,5) == ".json")) { - std::string buf; - if (OSUtils::readFile((udd + ZT_PATH_SEPARATOR_S + *u).c_str(),buf)) { - try { - _D d; - d.meta = OSUtils::jsonParse(buf); - std::string metaHash = OSUtils::jsonBinFromHex(d.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH]); - if ((metaHash.length() == ZT_SHA512_DIGEST_LEN)&&(OSUtils::readFile((udd + ZT_PATH_SEPARATOR_S + u->substr(0,u->length() - 5)).c_str(),d.bin))) { - uint8_t sha512[ZT_SHA512_DIGEST_LEN]; - SHA512::hash(sha512,d.bin.data(),(unsigned int)d.bin.length()); - if (!memcmp(sha512,metaHash.data(),ZT_SHA512_DIGEST_LEN)) { // double check that hash in JSON is correct - _dist[Array(sha512)] = d; - printf("update-dist.d: %s\n",u->c_str()); + _dist.clear(); + if (distribute) { + std::string udd(_homePath + ZT_PATH_SEPARATOR_S + "update-dist.d"); + std::vector ud(OSUtils::listDirectory(udd.c_str())); + for(std::vector::iterator u(ud.begin());u!=ud.end();++u) { + // Each update has a companion .json file describing it. Other files are ignored. + if ((u->length() > 5)&&(u->substr(u->length() - 5,5) == ".json")) { + std::string buf; + if (OSUtils::readFile((udd + ZT_PATH_SEPARATOR_S + *u).c_str(),buf)) { + try { + _D d; + d.meta = OSUtils::jsonParse(buf); + std::string metaHash = OSUtils::jsonBinFromHex(d.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH]); + if ((metaHash.length() == ZT_SHA512_DIGEST_LEN)&&(OSUtils::readFile((udd + ZT_PATH_SEPARATOR_S + u->substr(0,u->length() - 5)).c_str(),d.bin))) { + uint8_t sha512[ZT_SHA512_DIGEST_LEN]; + SHA512::hash(sha512,d.bin.data(),(unsigned int)d.bin.length()); + if (!memcmp(sha512,metaHash.data(),ZT_SHA512_DIGEST_LEN)) { // double check that hash in JSON is correct + _dist[Array(sha512)] = d; + printf("update-dist.d: %s\n",u->c_str()); + } } - } - } catch ( ... ) {} // ignore bad meta JSON, etc. + } catch ( ... ) {} // ignore bad meta JSON, etc. + } } } } @@ -393,18 +397,17 @@ void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void idx |= (unsigned long)*(reinterpret_cast(data) + 18) << 16; idx |= (unsigned long)*(reinterpret_cast(data) + 19) << 8; idx |= (unsigned long)*(reinterpret_cast(data) + 20); + printf("<< DATA @%u / %u bytes (we now have %u bytes)\n",(unsigned int)idx,(unsigned int)(len - 21),(unsigned int)_latestBin.length()); if (idx == _latestBin.length()) { _latestBin.append(reinterpret_cast(data) + 21,len - 21); - } - printf("<< DATA @%u / %u bytes (we now have %u bytes)\n",(unsigned int)idx,(unsigned int)(len - 21),(unsigned int)_latestBin.length()); - - if (_latestBin.length() < _latestBinLength) { - Buffer<128> gd; - gd.append((uint8_t)VERB_GET_DATA); - gd.append(_latestBinHashPrefix.data,16); - gd.append((uint32_t)_latestBin.length()); - _node.sendUserMessage(ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size()); - printf(">> GET_DATA @%u\n",(unsigned int)_latestBin.length()); + if (_latestBin.length() < _latestBinLength) { + Buffer<128> gd; + gd.append((uint8_t)VERB_GET_DATA); + gd.append(_latestBinHashPrefix.data,16); + gd.append((uint32_t)_latestBin.length()); + _node.sendUserMessage(ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size()); + printf(">> GET_DATA @%u\n",(unsigned int)_latestBin.length()); + } } } break; @@ -417,7 +420,7 @@ void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void } } -nlohmann::json SoftwareUpdater::check(const uint64_t now) +bool SoftwareUpdater::check(const uint64_t now) { if ((now - _lastCheckTime) >= ZT_SOFTWARE_UPDATE_CHECK_PERIOD) { _lastCheckTime = now; @@ -439,7 +442,7 @@ nlohmann::json SoftwareUpdater::check(const uint64_t now) ZT_BUILD_PLATFORM, ZT_BUILD_ARCHITECTURE, (int)ZT_VENDOR_ZEROTIER, - "release"); + _channel.c_str()); _node.sendUserMessage(ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,tmp,len); printf(">> GET_LATEST\n"); } @@ -449,13 +452,14 @@ nlohmann::json SoftwareUpdater::check(const uint64_t now) if (_latestBinValid) { return _latestMeta; } else { - // This is the important security verification part! + // This is the very important security validation part that makes sure + // this software update doesn't have cooties. try { // (1) Check the hash itself to make sure the image is basically okay uint8_t sha512[ZT_SHA512_DIGEST_LEN]; SHA512::hash(sha512,_latestBin.data(),(unsigned int)_latestBin.length()); - if (Utils::hex(sha512,ZT_SHA512_DIGEST_LEN) == OSUtils::jsonString(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH],"")) { + if (Utils::hex(sha512,ZT_SHA512_DIGEST_LEN) == OSUtils::jsonString(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH],"~")) { // (2) Check signature by signing authority std::string sig(OSUtils::jsonBinFromHex(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNATURE])); if (Identity(ZT_SOFTWARE_UPDATE_SIGNING_AUTHORITY).verify(_latestBin.data(),(unsigned int)_latestBin.length(),sig.data(),(unsigned int)sig.length())) { @@ -489,7 +493,7 @@ nlohmann::json SoftwareUpdater::check(const uint64_t now) void SoftwareUpdater::apply() { - if ((_latestBin.length() == _latestBinLength)&&(_latestBinLength > 0)&&(_latestBinValid)) { + if ((_latestBin.length() > 0)&&(_latestBinValid)) { } } diff --git a/service/SoftwareUpdater.hpp b/service/SoftwareUpdater.hpp index 0501ff23..b9cabf23 100644 --- a/service/SoftwareUpdater.hpp +++ b/service/SoftwareUpdater.hpp @@ -64,19 +64,25 @@ //#define ZT_SOFTWARE_UPDATE_CHECK_PERIOD (60 * 60 * 1000) #define ZT_SOFTWARE_UPDATE_CHECK_PERIOD 5000 +/** + * Default update channel + */ +#define ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL "release" + #define ZT_SOFTWARE_UPDATE_JSON_VERSION_MAJOR "versionMajor" #define ZT_SOFTWARE_UPDATE_JSON_VERSION_MINOR "versionMinor" #define ZT_SOFTWARE_UPDATE_JSON_VERSION_REVISION "versionRev" -#define ZT_SOFTWARE_UPDATE_JSON_EXPECT_SIGNED_BY "expectedSigner" #define ZT_SOFTWARE_UPDATE_JSON_PLATFORM "platform" #define ZT_SOFTWARE_UPDATE_JSON_ARCHITECTURE "arch" #define ZT_SOFTWARE_UPDATE_JSON_VENDOR "vendor" #define ZT_SOFTWARE_UPDATE_JSON_CHANNEL "channel" +#define ZT_SOFTWARE_UPDATE_JSON_EXPECT_SIGNED_BY "expectedSigner" #define ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNED_BY "updateSigner" #define ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNATURE "updateSig" #define ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH "updateHash" #define ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIZE "updateSize" -#define ZT_SOFTWARE_UPDATE_JSON_EXEC_ARGS "updateExecArgs" +#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_EXEC_ARGS "updateExecArgs" +#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_URL "updateUrl" namespace ZeroTier { @@ -123,9 +129,11 @@ public: ~SoftwareUpdater(); /** - * Load update-dist.d if it exists + * Set whether or not we will distribute updates + * + * @param distribute If true, scan update-dist.d now and distribute updates found there -- if false, clear and stop distributing */ - void loadUpdatesToDistribute(); + void setUpdateDistribution(bool distribute); /** * Handle a software update user message @@ -141,9 +149,14 @@ public: * * It should be called about every 10 seconds. * - * @return Null JSON object or update information if there is an update downloaded and ready + * @return True if we've downloaded and verified an update */ - nlohmann::json check(const uint64_t now); + bool check(const uint64_t now); + + /** + * @return Meta-data for downloaded update or NULL if none + */ + inline const nlohmann::json &pending() const { return _latestMeta; } /** * Apply any ready update now @@ -153,10 +166,18 @@ public: */ void apply(); + /** + * Set software update channel + * + * @param channel 'release', 'beta', etc. + */ + inline void setChannel(const std::string &channel) { _channel = channel; } + private: Node &_node; uint64_t _lastCheckTime; std::string _homePath; + std::string _channel; // Offered software updates if we are an update host (we have update-dist.d and update hosting is enabled) struct _D -- cgit v1.2.3