|
|
|
|
@ -258,9 +258,10 @@ SoftwareUpdater::SoftwareUpdater(Node &node,const std::string &homePath) :
|
|
|
|
|
_lastCheckTime(0), |
|
|
|
|
_homePath(homePath), |
|
|
|
|
_channel(ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL), |
|
|
|
|
_latestBinLength(0), |
|
|
|
|
_latestBinValid(false) |
|
|
|
|
_latestValid(false), |
|
|
|
|
_downloadLength(0) |
|
|
|
|
{ |
|
|
|
|
// Check for a cached newer update. If there's a cached update that is not newer, delete.
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
SoftwareUpdater::~SoftwareUpdater() |
|
|
|
|
@ -286,6 +287,7 @@ void SoftwareUpdater::setUpdateDistribution(bool distribute)
|
|
|
|
|
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
|
|
|
|
|
d.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIZE] = d.bin.length(); // override with correct value -- setting this in meta json is optional
|
|
|
|
|
_dist[Array<uint8_t,16>(sha512)] = d; |
|
|
|
|
printf("update-dist.d: %s\n",u->c_str()); |
|
|
|
|
} |
|
|
|
|
@ -351,19 +353,24 @@ void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void
|
|
|
|
|
if ((len <= ZT_SOFTWARE_UPDATE_MAX_SIZE)&&(hash.length() >= 16)) { |
|
|
|
|
if (_latestMeta != req) { |
|
|
|
|
_latestMeta = req; |
|
|
|
|
_latestBin = ""; |
|
|
|
|
memcpy(_latestBinHashPrefix.data,hash.data(),16); |
|
|
|
|
_latestBinLength = len; |
|
|
|
|
_latestBinValid = false; |
|
|
|
|
printf("<< LATEST\n%s\n",OSUtils::jsonDump(req).c_str()); |
|
|
|
|
_latestValid = false; |
|
|
|
|
|
|
|
|
|
OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S + ZT_SOFTWARE_UPDATE_META_FILENAME).c_str()); |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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 ((_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(ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size()); |
|
|
|
|
printf(">> GET_DATA @%u\n",(unsigned int)_download.length()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -379,7 +386,7 @@ void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void
|
|
|
|
|
idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 20); |
|
|
|
|
printf("<< GET_DATA @%u from %.10llx for %s\n",(unsigned int)idx,origin,Utils::hex(reinterpret_cast<const uint8_t *>(data) + 1,16).c_str()); |
|
|
|
|
std::map< Array<uint8_t,16>,_D >::iterator d(_dist.find(Array<uint8_t,16>(reinterpret_cast<const uint8_t *>(data) + 1))); |
|
|
|
|
if ((d != _dist.end())&&(idx < d->second.bin.length())) { |
|
|
|
|
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); |
|
|
|
|
@ -392,21 +399,21 @@ void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case VERB_DATA: |
|
|
|
|
if ((len >= 21)&&(!memcmp(_latestBinHashPrefix.data,reinterpret_cast<const uint8_t *>(data) + 1,16))) { |
|
|
|
|
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); |
|
|
|
|
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<const char *>(data) + 21,len - 21); |
|
|
|
|
if (_latestBin.length() < _latestBinLength) { |
|
|
|
|
printf("<< DATA @%u / %u bytes (we now have %u bytes)\n",(unsigned int)idx,(unsigned int)(len - 21),(unsigned int)_download.length()); |
|
|
|
|
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(_latestBinHashPrefix.data,16); |
|
|
|
|
gd.append((uint32_t)_latestBin.length()); |
|
|
|
|
gd.append(_downloadHashPrefix.data,16); |
|
|
|
|
gd.append((uint32_t)_download.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()); |
|
|
|
|
printf(">> GET_DATA @%u\n",(unsigned int)_download.length()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -447,44 +454,50 @@ bool SoftwareUpdater::check(const uint64_t now)
|
|
|
|
|
printf(">> GET_LATEST\n"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (_latestBinLength > 0) { |
|
|
|
|
if (_latestBin.length() >= _latestBinLength) { |
|
|
|
|
if (_latestBinValid) { |
|
|
|
|
return true; |
|
|
|
|
} else { |
|
|
|
|
// 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],"~")) { |
|
|
|
|
// (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())) { |
|
|
|
|
// If we passed both of these, the update is good!
|
|
|
|
|
_latestBinValid = true; |
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
// (1) Check the hash itself to make sure the image is basically okay
|
|
|
|
|
uint8_t sha512[ZT_SHA512_DIGEST_LEN]; |
|
|
|
|
SHA512::hash(sha512,_download.data(),(unsigned int)_download.length()); |
|
|
|
|
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(_download.data(),(unsigned int)_download.length(),sig.data(),(unsigned int)sig.length())) { |
|
|
|
|
// If we passed both of these, the update is good!
|
|
|
|
|
if (OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + ZT_SOFTWARE_UPDATE_META_FILENAME).c_str(),OSUtils::jsonDump(_latestMeta)) && OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + ZT_SOFTWARE_UPDATE_BIN_FILENAME).c_str(),_download)) { |
|
|
|
|
_latestValid = true; |
|
|
|
|
printf("VALID UPDATE\n%s\n",OSUtils::jsonDump(_latestMeta).c_str()); |
|
|
|
|
return true; |
|
|
|
|
} else { |
|
|
|
|
_latestMeta = nlohmann::json(); |
|
|
|
|
_latestValid = false; |
|
|
|
|
} |
|
|
|
|
_download = std::string(); |
|
|
|
|
_downloadLength = 0; |
|
|
|
|
return _latestValid; |
|
|
|
|
} |
|
|
|
|
} catch ( ... ) {} // any exception equals verification failure
|
|
|
|
|
printf("INVALID UPDATE (!!!)\n%s\n",OSUtils::jsonDump(_latestMeta).c_str()); |
|
|
|
|
|
|
|
|
|
// If we get here, checks failed.
|
|
|
|
|
_latestMeta = nlohmann::json(); |
|
|
|
|
_latestBin = ""; |
|
|
|
|
_latestBinLength = 0; |
|
|
|
|
_latestBinValid = false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} catch ( ... ) {} // any exception equals verification failure
|
|
|
|
|
|
|
|
|
|
// If we get here, checks failed.
|
|
|
|
|
printf("INVALID UPDATE (!!!)\n%s\n",OSUtils::jsonDump(_latestMeta).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(_latestBinHashPrefix.data,16); |
|
|
|
|
gd.append((uint32_t)_latestBin.length()); |
|
|
|
|
gd.append(_downloadHashPrefix.data,16); |
|
|
|
|
gd.append((uint32_t)_download.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()); |
|
|
|
|
printf(">> GET_DATA @%u\n",(unsigned int)_download.length()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -493,8 +506,6 @@ bool SoftwareUpdater::check(const uint64_t now)
|
|
|
|
|
|
|
|
|
|
void SoftwareUpdater::apply() |
|
|
|
|
{ |
|
|
|
|
if ((_latestBin.length() > 0)&&(_latestBinValid)) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} // namespace ZeroTier
|
|
|
|
|
|