mirror of
https://github.com/ZeroTier/ZeroTierOne
synced 2025-08-19 13:01:39 -07:00
Remove antique software updater code. It has not been used in many years, is not something we intend to use, and might in theory be an attack vector if someone found a problem with it. Better for it not to be there.
This commit is contained in:
parent
f6d52dd96c
commit
d3d738f93b
5 changed files with 5 additions and 694 deletions
3
.clangd
3
.clangd
|
@ -4,7 +4,8 @@ CompileFlags:
|
|||
- "-I../ext"
|
||||
- "-I../ext/prometheus-cpp-lite-1.0/core/include"
|
||||
- "-I../ext/prometheus-cpp-lite-1.0/simpleapi/include"
|
||||
- "-I../ext/opentelemetry-cpp-1.21.0/api/include"
|
||||
- "-I./ext"
|
||||
- "-I./ext/prometheus-cpp-lite-1.0/core/include"
|
||||
- "-I./ext/prometheus-cpp-lite-1.0/simpleapi/include"
|
||||
- "-I./ext/opentelemetry-cpp-1.21.0/api/include/"
|
||||
- "-I./ext/opentelemetry-cpp-1.21.0/api/include"
|
||||
|
|
|
@ -45,5 +45,4 @@ ONE_OBJS=\
|
|||
osdep/EthernetTap.o \
|
||||
osdep/ManagedRoute.o \
|
||||
osdep/Http.o \
|
||||
service/SoftwareUpdater.o \
|
||||
service/OneService.o
|
||||
|
|
|
@ -7,11 +7,8 @@
|
|||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <condition_variable>
|
||||
#include <exception>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -33,24 +30,17 @@
|
|||
#include "../node/MAC.hpp"
|
||||
#include "../node/Mutex.hpp"
|
||||
#include "../node/Node.hpp"
|
||||
#include "../node/PacketMultiplexer.hpp"
|
||||
#include "../node/Peer.hpp"
|
||||
#include "../node/Poly1305.hpp"
|
||||
#include "../node/SHA512.hpp"
|
||||
#include "../node/Salsa20.hpp"
|
||||
#include "../node/Utils.hpp"
|
||||
#include "../node/World.hpp"
|
||||
#include "../osdep/Binder.hpp"
|
||||
#include "../osdep/BlockingQueue.hpp"
|
||||
#include "../osdep/ExtOsdep.hpp"
|
||||
#include "../osdep/Http.hpp"
|
||||
#include "../osdep/ManagedRoute.hpp"
|
||||
#include "../osdep/OSUtils.hpp"
|
||||
#include "../osdep/Phy.hpp"
|
||||
#include "../osdep/PortMapper.hpp"
|
||||
#include "../version.h"
|
||||
#include "OneService.hpp"
|
||||
#include "SoftwareUpdater.hpp"
|
||||
|
||||
#include <cpp-httplib/httplib.h>
|
||||
|
||||
|
@ -81,8 +71,8 @@ namespace sdkmetrics = opentelemetry::v1::sdk::metrics;
|
|||
namespace sdklogs = opentelemetry::v1::sdk::logs;
|
||||
namespace sdkresource = opentelemetry::v1::sdk::resource;
|
||||
#else
|
||||
#include "opentelemetry/logs/logger.h"
|
||||
#include "opentelemetry/metrics/provider.h"
|
||||
// #include "opentelemetry/logs/logger.h"
|
||||
// #include "opentelemetry/metrics/provider.h"
|
||||
#include "opentelemetry/trace/provider.h"
|
||||
#endif
|
||||
|
||||
|
@ -836,7 +826,6 @@ class OneServiceImpl : public OneService {
|
|||
EmbeddedNetworkController* _controller;
|
||||
Phy<OneServiceImpl*> _phy;
|
||||
Node* _node;
|
||||
SoftwareUpdater* _updater;
|
||||
bool _updateAutoApply;
|
||||
|
||||
httplib::Server _controlPlane;
|
||||
|
@ -956,7 +945,6 @@ class OneServiceImpl : public OneService {
|
|||
, _controller((EmbeddedNetworkController*)0)
|
||||
, _phy(this, false, true)
|
||||
, _node((Node*)0)
|
||||
, _updater((SoftwareUpdater*)0)
|
||||
, _updateAutoApply(false)
|
||||
, _controlPlane()
|
||||
, _controlPlaneV6()
|
||||
|
@ -1327,13 +1315,6 @@ class OneServiceImpl : public OneService {
|
|||
restarted = true;
|
||||
}
|
||||
|
||||
// Check for updates (if enabled)
|
||||
if ((_updater) && ((now - lastUpdateCheck) > 10000)) {
|
||||
lastUpdateCheck = now;
|
||||
if (_updater->check(now) && _updateAutoApply)
|
||||
_updater->apply();
|
||||
}
|
||||
|
||||
// Reload local.conf if anything changed recently
|
||||
if ((now - lastLocalConfFileCheck) >= ZT_LOCAL_CONF_FILE_CHECK_INTERVAL) {
|
||||
lastLocalConfFileCheck = now;
|
||||
|
@ -1517,8 +1498,6 @@ class OneServiceImpl : public OneService {
|
|||
_nets.clear();
|
||||
}
|
||||
|
||||
delete _updater;
|
||||
_updater = (SoftwareUpdater*)0;
|
||||
delete _node;
|
||||
_node = (Node*)0;
|
||||
|
||||
|
@ -1786,7 +1765,7 @@ class OneServiceImpl : public OneService {
|
|||
if (_enableWebServer) {
|
||||
static std::string appUiPath = "/app";
|
||||
static char appUiDir[16384];
|
||||
sprintf(appUiDir, "%s%s", _homePath.c_str(), appUiPath.c_str());
|
||||
snprintf(appUiDir, sizeof(appUiDir), "%s%s", _homePath.c_str(), appUiPath.c_str());
|
||||
|
||||
auto ret = _controlPlane.set_mount_point(appUiPath, appUiDir);
|
||||
_controlPlaneV6.set_mount_point(appUiPath, appUiDir);
|
||||
|
@ -2488,10 +2467,6 @@ class OneServiceImpl : public OneService {
|
|||
settings["portMappingEnabled"] = OSUtils::jsonBool(settings["portMappingEnabled"], true);
|
||||
#else
|
||||
settings["portMappingEnabled"] = false; // not supported in build
|
||||
#endif
|
||||
#ifndef ZT_SDK
|
||||
settings["softwareUpdate"] = OSUtils::jsonString(settings["softwareUpdate"], ZT_SOFTWARE_UPDATE_DEFAULT);
|
||||
settings["softwareUpdateChannel"] = OSUtils::jsonString(settings["softwareUpdateChannel"], ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL);
|
||||
#endif
|
||||
const World planet(_node->planet());
|
||||
out["planetWorldId"] = planet.id();
|
||||
|
@ -2930,23 +2905,6 @@ class OneServiceImpl : public OneService {
|
|||
_cpuPinningEnabled = false;
|
||||
#endif
|
||||
|
||||
#ifndef ZT_SDK
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
json& ignoreIfs = settings["interfacePrefixBlacklist"];
|
||||
if (ignoreIfs.is_array()) {
|
||||
for (unsigned long i = 0; i < ignoreIfs.size(); ++i) {
|
||||
|
@ -3685,13 +3643,6 @@ class OneServiceImpl : public OneService {
|
|||
}
|
||||
} break;
|
||||
|
||||
case ZT_EVENT_USER_MESSAGE: {
|
||||
const ZT_UserMessage* um = reinterpret_cast<const ZT_UserMessage*>(metaData);
|
||||
if ((um->typeId == ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE) && (_updater)) {
|
||||
_updater->handleSoftwareUpdateUserMessage(um->origin, um->data, um->length);
|
||||
}
|
||||
} break;
|
||||
|
||||
case ZT_EVENT_REMOTE_TRACE: {
|
||||
const ZT_RemoteTrace* rt = reinterpret_cast<const ZT_RemoteTrace*>(metaData);
|
||||
if ((rt) && (rt->len > 0) && (rt->len <= ZT_MAX_REMOTE_TRACE_SIZE) && (rt->data))
|
||||
|
|
|
@ -1,441 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
#include "../node/Constants.hpp"
|
||||
#include "../version.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#include <iphlpapi.h>
|
||||
#include <netioapi.h>
|
||||
#include <shlobj.h>
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <ifaddrs.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "../node/Buffer.hpp"
|
||||
#include "../node/Node.hpp"
|
||||
#include "../node/SHA512.hpp"
|
||||
#include "../node/Utils.hpp"
|
||||
#include "../osdep/OSUtils.hpp"
|
||||
#include "SoftwareUpdater.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
static int _compareVersion(unsigned int maj1, unsigned int min1, unsigned int rev1, unsigned int b1, unsigned int maj2, unsigned int min2, unsigned int rev2, unsigned int b2)
|
||||
{
|
||||
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 {
|
||||
if (b1 > b2) {
|
||||
return 1;
|
||||
}
|
||||
else if (b1 < b2) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SoftwareUpdater::SoftwareUpdater(Node& node, const std::string& homePath) : _node(node), _lastCheckTime(0), _homePath(homePath), _channel(ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL), _distLog((FILE*)0), _latestValid(false), _downloadLength(0)
|
||||
{
|
||||
OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_BIN_FILENAME).c_str());
|
||||
}
|
||||
|
||||
SoftwareUpdater::~SoftwareUpdater()
|
||||
{
|
||||
if (_distLog)
|
||||
fclose(_distLog);
|
||||
}
|
||||
|
||||
void SoftwareUpdater::setUpdateDistribution(bool distribute)
|
||||
{
|
||||
_dist.clear();
|
||||
if (distribute) {
|
||||
_distLog = fopen((_homePath + ZT_PATH_SEPARATOR_S "update-dist.log").c_str(), "a");
|
||||
|
||||
const std::string udd(_homePath + ZT_PATH_SEPARATOR_S "update-dist.d");
|
||||
const std::vector<std::string> ud(OSUtils::listDirectory(udd.c_str()));
|
||||
for (std::vector<std::string>::const_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); // throws on invalid JSON
|
||||
|
||||
// If update meta is called e.g. foo.exe.json, then foo.exe is the update itself
|
||||
const std::string binPath(udd + ZT_PATH_SEPARATOR_S + u->substr(0, u->length() - 5));
|
||||
const std::string metaHash(OSUtils::jsonBinFromHex(d.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH]));
|
||||
if ((metaHash.length() == 64) && (OSUtils::readFile(binPath.c_str(), d.bin))) {
|
||||
std::array<uint8_t, 64> sha512;
|
||||
SHA512(sha512.data(), d.bin.data(), (unsigned int)d.bin.length());
|
||||
if (! memcmp(sha512.data(), metaHash.data(), 64)) { // double check that hash in JSON is correct
|
||||
d.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIZE] = d.bin.length(); // override with correct value -- setting this in meta json is optional
|
||||
std::array<uint8_t, 16> shakey;
|
||||
memcpy(shakey.data(), sha512.data(), 16);
|
||||
_dist[shakey] = d;
|
||||
if (_distLog) {
|
||||
fprintf(_distLog, ".......... INIT: DISTRIBUTING %s (%u bytes)" ZT_EOL_S, binPath.c_str(), (unsigned int)d.bin.length());
|
||||
fflush(_distLog);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
} // ignore bad meta JSON, etc.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (_distLog) {
|
||||
fclose(_distLog);
|
||||
_distLog = (FILE*)0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin, const void* data, unsigned int len)
|
||||
{
|
||||
if (! len)
|
||||
return;
|
||||
const MessageVerb v = (MessageVerb) reinterpret_cast<const uint8_t*>(data)[0];
|
||||
try {
|
||||
switch (v) {
|
||||
case VERB_GET_LATEST:
|
||||
case VERB_LATEST: {
|
||||
nlohmann::json req = OSUtils::jsonParse(std::string(reinterpret_cast<const char*>(data) + 1, len - 1)); // throws on invalid JSON
|
||||
if (req.is_object()) {
|
||||
const unsigned int rvMaj = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_VERSION_MAJOR], 0);
|
||||
const unsigned int rvMin = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_VERSION_MINOR], 0);
|
||||
const unsigned int rvRev = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_VERSION_REVISION], 0);
|
||||
const unsigned int rvBld = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_VERSION_BUILD], 0);
|
||||
const unsigned int rvPlatform = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_PLATFORM], 0);
|
||||
const unsigned int rvArch = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_ARCHITECTURE], 0);
|
||||
const unsigned int rvVendor = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_VENDOR], 0);
|
||||
const std::string rvChannel(OSUtils::jsonString(req[ZT_SOFTWARE_UPDATE_JSON_CHANNEL], ""));
|
||||
|
||||
if (v == VERB_GET_LATEST) {
|
||||
if (! _dist.empty()) {
|
||||
const nlohmann::json* latest = (const nlohmann::json*)0;
|
||||
const std::string expectedSigner = OSUtils::jsonString(req[ZT_SOFTWARE_UPDATE_JSON_EXPECT_SIGNED_BY], "");
|
||||
unsigned int bestVMaj = rvMaj;
|
||||
unsigned int bestVMin = rvMin;
|
||||
unsigned int bestVRev = rvRev;
|
||||
unsigned int bestVBld = rvBld;
|
||||
for (std::map<std::array<uint8_t, 16>, _D>::const_iterator d(_dist.begin()); d != _dist.end(); ++d) {
|
||||
// The arch field in update description .json files can be an array for e.g. multi-arch update files
|
||||
const nlohmann::json& dvArch2 = d->second.meta[ZT_SOFTWARE_UPDATE_JSON_ARCHITECTURE];
|
||||
std::vector<unsigned int> dvArch;
|
||||
if (dvArch2.is_array()) {
|
||||
for (unsigned long i = 0; i < dvArch2.size(); ++i)
|
||||
dvArch.push_back((unsigned int)OSUtils::jsonInt(dvArch2[i], 0));
|
||||
}
|
||||
else {
|
||||
dvArch.push_back((unsigned int)OSUtils::jsonInt(dvArch2, 0));
|
||||
}
|
||||
|
||||
if ((OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_PLATFORM], 0) == rvPlatform) && (std::find(dvArch.begin(), dvArch.end(), rvArch) != dvArch.end())
|
||||
&& (OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_VENDOR], 0) == rvVendor) && (OSUtils::jsonString(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_CHANNEL], "") == rvChannel)
|
||||
&& (OSUtils::jsonString(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNED_BY], "") == expectedSigner)) {
|
||||
const unsigned int dvMaj = (unsigned int)OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_MAJOR], 0);
|
||||
const unsigned int dvMin = (unsigned int)OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_MINOR], 0);
|
||||
const unsigned int dvRev = (unsigned int)OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_REVISION], 0);
|
||||
const unsigned int dvBld = (unsigned int)OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_BUILD], 0);
|
||||
if (_compareVersion(dvMaj, dvMin, dvRev, dvBld, bestVMaj, bestVMin, bestVRev, bestVBld) > 0) {
|
||||
latest = &(d->second.meta);
|
||||
bestVMaj = dvMaj;
|
||||
bestVMin = dvMin;
|
||||
bestVRev = dvRev;
|
||||
bestVBld = dvBld;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (latest) {
|
||||
std::string lj;
|
||||
lj.push_back((char)VERB_LATEST);
|
||||
lj.append(OSUtils::jsonDump(*latest));
|
||||
_node.sendUserMessage((void*)0, origin, ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE, lj.data(), (unsigned int)lj.length());
|
||||
if (_distLog) {
|
||||
fprintf(
|
||||
_distLog,
|
||||
"%.10llx GET_LATEST %u.%u.%u_%u platform %u arch %u vendor %u channel %s -> LATEST %u.%u.%u_%u" ZT_EOL_S,
|
||||
(unsigned long long)origin,
|
||||
rvMaj,
|
||||
rvMin,
|
||||
rvRev,
|
||||
rvBld,
|
||||
rvPlatform,
|
||||
rvArch,
|
||||
rvVendor,
|
||||
rvChannel.c_str(),
|
||||
bestVMaj,
|
||||
bestVMin,
|
||||
bestVRev,
|
||||
bestVBld);
|
||||
fflush(_distLog);
|
||||
}
|
||||
}
|
||||
} // else no reply, since we have nothing to distribute
|
||||
}
|
||||
else { // VERB_LATEST
|
||||
|
||||
if ((origin == ZT_SOFTWARE_UPDATE_SERVICE) && (_compareVersion(rvMaj, rvMin, rvRev, rvBld, ZEROTIER_ONE_VERSION_MAJOR, ZEROTIER_ONE_VERSION_MINOR, ZEROTIER_ONE_VERSION_REVISION, ZEROTIER_ONE_VERSION_BUILD) > 0)
|
||||
&& (OSUtils::jsonString(req[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNED_BY], "") == ZT_SOFTWARE_UPDATE_SIGNING_AUTHORITY)) {
|
||||
const unsigned long len = (unsigned long)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIZE], 0);
|
||||
const std::string hash = OSUtils::jsonBinFromHex(req[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH]);
|
||||
if ((len <= ZT_SOFTWARE_UPDATE_MAX_SIZE) && (hash.length() >= 16)) {
|
||||
if (_latestMeta != req) {
|
||||
_latestMeta = req;
|
||||
_latestValid = false;
|
||||
OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_BIN_FILENAME).c_str());
|
||||
_download = std::string();
|
||||
memcpy(_downloadHashPrefix.data(), hash.data(), 16);
|
||||
_downloadLength = len;
|
||||
}
|
||||
|
||||
if ((_downloadLength > 0) && (_download.length() < _downloadLength)) {
|
||||
Buffer<128> gd;
|
||||
gd.append((uint8_t)VERB_GET_DATA);
|
||||
gd.append(_downloadHashPrefix.data(), 16);
|
||||
gd.append((uint32_t)_download.length());
|
||||
_node.sendUserMessage((void*)0, ZT_SOFTWARE_UPDATE_SERVICE, ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE, gd.data(), gd.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case VERB_GET_DATA:
|
||||
if ((len >= 21) && (! _dist.empty())) {
|
||||
unsigned long idx = (unsigned long)*(reinterpret_cast<const uint8_t*>(data) + 17) << 24;
|
||||
idx |= (unsigned long)*(reinterpret_cast<const uint8_t*>(data) + 18) << 16;
|
||||
idx |= (unsigned long)*(reinterpret_cast<const uint8_t*>(data) + 19) << 8;
|
||||
idx |= (unsigned long)*(reinterpret_cast<const uint8_t*>(data) + 20);
|
||||
std::array<uint8_t, 16> shakey;
|
||||
memcpy(shakey.data(), reinterpret_cast<const uint8_t*>(data) + 1, 16);
|
||||
std::map<std::array<uint8_t, 16>, _D>::iterator d(_dist.find(shakey));
|
||||
if ((d != _dist.end()) && (idx < (unsigned long)d->second.bin.length())) {
|
||||
Buffer<ZT_SOFTWARE_UPDATE_CHUNK_SIZE + 128> buf;
|
||||
buf.append((uint8_t)VERB_DATA);
|
||||
buf.append(reinterpret_cast<const uint8_t*>(data) + 1, 16);
|
||||
buf.append((uint32_t)idx);
|
||||
buf.append(d->second.bin.data() + idx, std::min((unsigned long)ZT_SOFTWARE_UPDATE_CHUNK_SIZE, (unsigned long)(d->second.bin.length() - idx)));
|
||||
_node.sendUserMessage((void*)0, origin, ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE, buf.data(), buf.size());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VERB_DATA:
|
||||
if ((len >= 21) && (_downloadLength > 0) && (! memcmp(_downloadHashPrefix.data(), reinterpret_cast<const uint8_t*>(data) + 1, 16))) {
|
||||
unsigned long idx = (unsigned long)*(reinterpret_cast<const uint8_t*>(data) + 17) << 24;
|
||||
idx |= (unsigned long)*(reinterpret_cast<const uint8_t*>(data) + 18) << 16;
|
||||
idx |= (unsigned long)*(reinterpret_cast<const uint8_t*>(data) + 19) << 8;
|
||||
idx |= (unsigned long)*(reinterpret_cast<const uint8_t*>(data) + 20);
|
||||
if (idx == (unsigned long)_download.length()) {
|
||||
_download.append(reinterpret_cast<const char*>(data) + 21, len - 21);
|
||||
if (_download.length() < _downloadLength) {
|
||||
Buffer<128> gd;
|
||||
gd.append((uint8_t)VERB_GET_DATA);
|
||||
gd.append(_downloadHashPrefix.data(), 16);
|
||||
gd.append((uint32_t)_download.length());
|
||||
_node.sendUserMessage((void*)0, ZT_SOFTWARE_UPDATE_SERVICE, ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE, gd.data(), gd.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (_distLog) {
|
||||
fprintf(_distLog, "%.10llx WARNING: bad update message verb==%u length==%u (unrecognized verb)" ZT_EOL_S, (unsigned long long)origin, (unsigned int)v, len);
|
||||
fflush(_distLog);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
if (_distLog) {
|
||||
fprintf(_distLog, "%.10llx WARNING: bad update message verb==%u length==%u (unexpected exception, likely invalid JSON)" ZT_EOL_S, (unsigned long long)origin, (unsigned int)v, len);
|
||||
fflush(_distLog);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SoftwareUpdater::check(const int64_t now)
|
||||
{
|
||||
if ((now - _lastCheckTime) >= ZT_SOFTWARE_UPDATE_CHECK_PERIOD) {
|
||||
_lastCheckTime = now;
|
||||
char tmp[512];
|
||||
const unsigned int len = OSUtils::ztsnprintf(
|
||||
tmp,
|
||||
sizeof(tmp),
|
||||
"%c{\"" ZT_SOFTWARE_UPDATE_JSON_VERSION_MAJOR "\":%d,"
|
||||
"\"" ZT_SOFTWARE_UPDATE_JSON_VERSION_MINOR "\":%d,"
|
||||
"\"" ZT_SOFTWARE_UPDATE_JSON_VERSION_REVISION "\":%d,"
|
||||
"\"" ZT_SOFTWARE_UPDATE_JSON_VERSION_BUILD "\":%d,"
|
||||
"\"" ZT_SOFTWARE_UPDATE_JSON_EXPECT_SIGNED_BY "\":\"%s\","
|
||||
"\"" ZT_SOFTWARE_UPDATE_JSON_PLATFORM "\":%d,"
|
||||
"\"" ZT_SOFTWARE_UPDATE_JSON_ARCHITECTURE "\":%d,"
|
||||
"\"" ZT_SOFTWARE_UPDATE_JSON_VENDOR "\":%d,"
|
||||
"\"" ZT_SOFTWARE_UPDATE_JSON_CHANNEL "\":\"%s\"}",
|
||||
(char)VERB_GET_LATEST,
|
||||
ZEROTIER_ONE_VERSION_MAJOR,
|
||||
ZEROTIER_ONE_VERSION_MINOR,
|
||||
ZEROTIER_ONE_VERSION_REVISION,
|
||||
ZEROTIER_ONE_VERSION_BUILD,
|
||||
ZT_SOFTWARE_UPDATE_SIGNING_AUTHORITY,
|
||||
ZT_BUILD_PLATFORM,
|
||||
ZT_BUILD_ARCHITECTURE,
|
||||
(int)ZT_VENDOR_ZEROTIER,
|
||||
_channel.c_str());
|
||||
_node.sendUserMessage((void*)0, ZT_SOFTWARE_UPDATE_SERVICE, ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE, tmp, len);
|
||||
}
|
||||
|
||||
if (_latestValid)
|
||||
return true;
|
||||
|
||||
if (_downloadLength > 0) {
|
||||
if (_download.length() >= _downloadLength) {
|
||||
// This is the very important security validation part that makes sure
|
||||
// this software update doesn't have cooties.
|
||||
|
||||
const std::string binPath(_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_BIN_FILENAME);
|
||||
try {
|
||||
// (1) Check the hash itself to make sure the image is basically okay
|
||||
uint8_t sha512[64];
|
||||
SHA512(sha512, _download.data(), (unsigned int)_download.length());
|
||||
char hexbuf[(64 * 2) + 2];
|
||||
if (OSUtils::jsonString(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH], "") == Utils::hex(sha512, 64, hexbuf)) {
|
||||
// (2) Check signature by signing authority
|
||||
const std::string sig(OSUtils::jsonBinFromHex(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNATURE]));
|
||||
if (Identity(ZT_SOFTWARE_UPDATE_SIGNING_AUTHORITY).verify(_download.data(), (unsigned int)_download.length(), sig.data(), (unsigned int)sig.length())) {
|
||||
// (3) Try to save file, and if so we are good.
|
||||
OSUtils::rm(binPath.c_str());
|
||||
if (OSUtils::writeFile(binPath.c_str(), _download)) {
|
||||
OSUtils::lockDownFile(binPath.c_str(), false);
|
||||
_latestValid = true;
|
||||
_download = std::string();
|
||||
_downloadLength = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
} // any exception equals verification failure
|
||||
|
||||
// If we get here, checks failed.
|
||||
OSUtils::rm(binPath.c_str());
|
||||
_latestMeta = nlohmann::json();
|
||||
_latestValid = false;
|
||||
_download = std::string();
|
||||
_downloadLength = 0;
|
||||
}
|
||||
else {
|
||||
Buffer<128> gd;
|
||||
gd.append((uint8_t)VERB_GET_DATA);
|
||||
gd.append(_downloadHashPrefix.data(), 16);
|
||||
gd.append((uint32_t)_download.length());
|
||||
_node.sendUserMessage((void*)0, ZT_SOFTWARE_UPDATE_SERVICE, ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE, gd.data(), gd.size());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SoftwareUpdater::apply()
|
||||
{
|
||||
std::string updatePath(_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_BIN_FILENAME);
|
||||
if ((_latestMeta.is_object()) && (_latestValid) && (OSUtils::fileExists(updatePath.c_str(), false))) {
|
||||
#ifdef __WINDOWS__
|
||||
std::string cmdArgs(OSUtils::jsonString(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_EXEC_ARGS], ""));
|
||||
if (cmdArgs.length() > 0) {
|
||||
updatePath.push_back(' ');
|
||||
updatePath.append(cmdArgs);
|
||||
}
|
||||
STARTUPINFOA si;
|
||||
PROCESS_INFORMATION pi;
|
||||
memset(&si, 0, sizeof(si));
|
||||
memset(&pi, 0, sizeof(pi));
|
||||
CreateProcessA(NULL, const_cast<LPSTR>(updatePath.c_str()), NULL, NULL, FALSE, CREATE_NO_WINDOW | CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi);
|
||||
// Windows doesn't exit here -- updater will stop the service during update, etc. -- but we do want to stop multiple runs from happening
|
||||
_latestMeta = nlohmann::json();
|
||||
_latestValid = false;
|
||||
#else
|
||||
char* argv[256];
|
||||
unsigned long ac = 0;
|
||||
argv[ac++] = const_cast<char*>(updatePath.c_str());
|
||||
const std::vector<std::string> argsSplit(OSUtils::split(OSUtils::jsonString(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_EXEC_ARGS], "").c_str(), " ", "\\", "\""));
|
||||
for (std::vector<std::string>::const_iterator a(argsSplit.begin()); a != argsSplit.end(); ++a) {
|
||||
argv[ac] = const_cast<char*>(a->c_str());
|
||||
if (++ac == 255)
|
||||
break;
|
||||
}
|
||||
argv[ac] = (char*)0;
|
||||
chmod(updatePath.c_str(), 0700);
|
||||
|
||||
// Close all open file descriptors except stdout/stderr/etc.
|
||||
int minMyFd = STDIN_FILENO;
|
||||
if (STDOUT_FILENO > minMyFd)
|
||||
minMyFd = STDOUT_FILENO;
|
||||
if (STDERR_FILENO > minMyFd)
|
||||
minMyFd = STDERR_FILENO;
|
||||
++minMyFd;
|
||||
#ifdef _SC_OPEN_MAX
|
||||
int maxMyFd = (int)sysconf(_SC_OPEN_MAX);
|
||||
if (maxMyFd <= minMyFd)
|
||||
maxMyFd = 65536;
|
||||
#else
|
||||
int maxMyFd = 65536;
|
||||
#endif
|
||||
while (minMyFd < maxMyFd)
|
||||
close(minMyFd++);
|
||||
|
||||
execv(updatePath.c_str(), argv);
|
||||
fprintf(stderr, "FATAL: unable to execute software update binary at %s\n", updatePath.c_str());
|
||||
exit(1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
|
@ -1,199 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* (c) ZeroTier, Inc.
|
||||
* https://www.zerotier.com/
|
||||
*/
|
||||
|
||||
#ifndef ZT_SOFTWAREUPDATER_HPP
|
||||
#define ZT_SOFTWAREUPDATER_HPP
|
||||
|
||||
#include "../include/ZeroTierOne.h"
|
||||
#include "../node/Identity.hpp"
|
||||
#include "../node/Packet.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* VERB_USER_MESSAGE type ID for software update messages
|
||||
*/
|
||||
#define ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE 100
|
||||
|
||||
/**
|
||||
* ZeroTier address of node that provides software updates
|
||||
*/
|
||||
#define ZT_SOFTWARE_UPDATE_SERVICE 0xb1d366e81fULL
|
||||
|
||||
/**
|
||||
* ZeroTier identity that must be used to sign software updates
|
||||
*
|
||||
* df24360f3e - update-signing-key-0010 generated Fri Jan 13th, 2017 at 4:05pm PST
|
||||
*/
|
||||
#define ZT_SOFTWARE_UPDATE_SIGNING_AUTHORITY "df24360f3e:0:06072642959c8dfb68312904d74d90197c8a7692697caa1b3fd769eca714f4370fab462fcee6ebcb5fffb63bc5af81f28a2514b2cd68daabb42f7352c06f21db"
|
||||
|
||||
/**
|
||||
* Chunk size for in-band downloads (can be changed, designed to always fit in one UDP packet easily)
|
||||
*/
|
||||
#define ZT_SOFTWARE_UPDATE_CHUNK_SIZE (ZT_PROTO_MAX_PACKET_LENGTH - 128)
|
||||
|
||||
/**
|
||||
* Sanity limit for the size of an update binary image
|
||||
*/
|
||||
#define ZT_SOFTWARE_UPDATE_MAX_SIZE (1024 * 1024 * 256)
|
||||
|
||||
/**
|
||||
* How often (ms) do we check?
|
||||
*/
|
||||
#define ZT_SOFTWARE_UPDATE_CHECK_PERIOD (60 * 10 * 1000)
|
||||
|
||||
/**
|
||||
* Default update channel
|
||||
*/
|
||||
#define ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL "release"
|
||||
|
||||
/**
|
||||
* Filename for latest update's binary image
|
||||
*/
|
||||
#define ZT_SOFTWARE_UPDATE_BIN_FILENAME "latest-update.exe"
|
||||
|
||||
#define ZT_SOFTWARE_UPDATE_JSON_VERSION_MAJOR "vMajor"
|
||||
#define ZT_SOFTWARE_UPDATE_JSON_VERSION_MINOR "vMinor"
|
||||
#define ZT_SOFTWARE_UPDATE_JSON_VERSION_REVISION "vRev"
|
||||
#define ZT_SOFTWARE_UPDATE_JSON_VERSION_BUILD "vBuild"
|
||||
#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 "signer"
|
||||
#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNATURE "signature"
|
||||
#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH "hash"
|
||||
#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIZE "size"
|
||||
#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_EXEC_ARGS "execArgs"
|
||||
#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_URL "url"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class Node;
|
||||
|
||||
/**
|
||||
* This class handles retrieving and executing updates, or serving them
|
||||
*/
|
||||
class SoftwareUpdater {
|
||||
public:
|
||||
/**
|
||||
* Each message begins with an 8-bit message verb
|
||||
*/
|
||||
enum MessageVerb {
|
||||
/**
|
||||
* Payload: JSON containing current system platform, version, etc.
|
||||
*/
|
||||
VERB_GET_LATEST = 1,
|
||||
|
||||
/**
|
||||
* Payload: JSON describing latest update for this target. (No response is sent if there is none.)
|
||||
*/
|
||||
VERB_LATEST = 2,
|
||||
|
||||
/**
|
||||
* Payload:
|
||||
* <[16] first 128 bits of hash of data object>
|
||||
* <[4] 32-bit index of chunk to get>
|
||||
*/
|
||||
VERB_GET_DATA = 3,
|
||||
|
||||
/**
|
||||
* Payload:
|
||||
* <[16] first 128 bits of hash of data object>
|
||||
* <[4] 32-bit index of chunk>
|
||||
* <[...] chunk data>
|
||||
*/
|
||||
VERB_DATA = 4
|
||||
};
|
||||
|
||||
SoftwareUpdater(Node& node, const std::string& homePath);
|
||||
~SoftwareUpdater();
|
||||
|
||||
/**
|
||||
* 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 setUpdateDistribution(bool distribute);
|
||||
|
||||
/**
|
||||
* Handle a software update user message
|
||||
*
|
||||
* @param origin ZeroTier address of message origin
|
||||
* @param data Message payload
|
||||
* @param len Length of message
|
||||
*/
|
||||
void handleSoftwareUpdateUserMessage(uint64_t origin, const void* data, unsigned int len);
|
||||
|
||||
/**
|
||||
* Check for updates and do other update-related housekeeping
|
||||
*
|
||||
* It should be called about every 10 seconds.
|
||||
*
|
||||
* @return True if we've downloaded and verified an update
|
||||
*/
|
||||
bool check(const int64_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
|
||||
*
|
||||
* Depending on the platform this function may never return and may forcibly
|
||||
* exit the process. It does nothing if no update is ready.
|
||||
*/
|
||||
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;
|
||||
FILE* _distLog;
|
||||
|
||||
// Offered software updates if we are an update host (we have update-dist.d and update hosting is enabled)
|
||||
struct _D {
|
||||
nlohmann::json meta;
|
||||
std::string bin;
|
||||
};
|
||||
std::map<std::array<uint8_t, 16>, _D> _dist; // key is first 16 bytes of hash
|
||||
|
||||
nlohmann::json _latestMeta;
|
||||
bool _latestValid;
|
||||
|
||||
std::string _download;
|
||||
std::array<uint8_t, 16> _downloadHashPrefix;
|
||||
unsigned long _downloadLength;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue