add support for handling remote EKC management via new metadata traffic opcodes;

pull/121/merge
Bryan Biedenkapp 6 days ago
parent 74473bb6ad
commit 7d5db97449

@ -229,6 +229,8 @@ master:
file: key_container.ekc
# Container password.
password: "PASSWORD"
# Remote access password for the crypto container.
remoteAccessPassword: "PASSWORD"
# Amount of time between updates of crypto container file. (minutes)
time: 30

@ -68,6 +68,9 @@ namespace network
ACK = 0x7EU, //!< Packet Acknowledge
NAK = 0x7FU, //!< Packet Negative Acknowledge
KEYS_INVENTORY = 0x8EU, //!< Encryption Key Container Inventory
KEYS_UPDATE = 0x8FU, //!< Encryption Key Container Update
TRANSFER = 0x90U, //!< Network Transfer Function
ANNOUNCE = 0x91U, //!< Network Announce Function

@ -141,9 +141,11 @@ std::mutex CryptoContainer::s_mutex;
/* Initializes a new instance of the CryptoContainer class. */
CryptoContainer::CryptoContainer(const std::string& filename, const std::string& password, uint32_t reloadTime, bool enabled) : Thread(),
CryptoContainer::CryptoContainer(const std::string& filename, const std::string& password, const std::string& remotePassword,
uint32_t reloadTime, bool enabled) : Thread(),
m_file(filename),
m_password(password),
m_remotePassword(remotePassword),
m_reloadTime(reloadTime),
m_lastLoadTime(0U),
#if !defined(ENABLE_SSL)

@ -167,10 +167,11 @@ public:
* @brief Initializes a new instance of the CryptoContainer class.
* @param filename Full-path to the crypto container file.
* @param password Crypto container file access password.
* @param remotePassword Remote access password for the crypto container.
* @param reloadTime Interval of time to reload the crypto container.
* @param enabled Flag indicating if crypto container is enabled.
*/
CryptoContainer(const std::string& filename, const std::string& password, uint32_t reloadTime, bool enabled);
CryptoContainer(const std::string& filename, const std::string& password, const std::string& remotePassword, uint32_t reloadTime, bool enabled);
/**
* @brief Finalizes a instance of the CryptoContainer class.
*/
@ -251,9 +252,21 @@ public:
*/
const uint64_t lastLoadTime() const { return m_lastLoadTime; }
/**
* @brief Returns the filename of this lookup table.
* @return const std::string& Filename of this lookup table.
*/
const std::string& filename() const { return m_file; }
/**
* @brief Returns the remote access password for the crypto container.
* @return const std::string& Remote access password.
*/
const std::string& getRemotePassword() const { return m_remotePassword; }
private:
std::string m_file;
std::string m_password;
std::string m_remotePassword;
uint32_t m_reloadTime;
uint64_t m_lastLoadTime;

@ -415,6 +415,7 @@ bool HostFNE::readParams()
#endif // ENABLE_SSL
std::string cryptoContainerEKC = cryptoContainer["file"].as<std::string>();
std::string cryptoContainerPassword = cryptoContainer["password"].as<std::string>();
std::string cryptoContainerRemotePassword = cryptoContainer["remoteAccessPassword"].as<std::string>();
uint32_t cryptoContainerReload = cryptoContainer["time"].as<uint32_t>(30U);
std::string peerListLookupFile = systemConf["peer_acl"]["file"].as<std::string>();
@ -458,7 +459,7 @@ bool HostFNE::readParams()
if (cryptoContainerReload > 0U)
LogInfo(" Reload: %u mins", cryptoContainerReload);
m_cryptoLookup = new CryptoContainer(cryptoContainerEKC, cryptoContainerPassword, cryptoContainerReload, cryptoContainerEnabled);
m_cryptoLookup = new CryptoContainer(cryptoContainerEKC, cryptoContainerPassword, cryptoContainerRemotePassword, cryptoContainerReload, cryptoContainerEnabled);
m_cryptoLookup->read();
return true;

@ -8,6 +8,7 @@
*
*/
#include "fne/Defines.h"
#include "common/edac/SHA256.h"
#include "common/zlib/Compression.h"
#include "common/Log.h"
#include "common/Utils.h"
@ -710,6 +711,412 @@ void MetadataNetwork::taskNetworkRx(NetPacketRequest* req)
}
break;
case NET_FUNC::KEYS_INVENTORY: // Encryption Key Container Inventory
{
lookups::PeerId peerEntry = network->m_peerListLookup->find(peerId);
if (peerEntry.peerDefault()) {
LogError(LOG_MASTER, "PEER %u requested enc. key inventory but is not allowed, no response", peerId);
break;
} else {
if (!peerEntry.canRequestKeys()) {
LogError(LOG_MASTER, "PEER %u requested enc. key inventory but is not allowed, no response", peerId);
break;
}
}
// keys inventory operates differently from the rest of the network opcodes...and does not require
// an established connection to the master, so we will not validate the peer connection state here
if (peerId > 0 && !peerEntry.peerDefault()) {
if (req->length < 80) {
LogError(LOG_MASTER, "PEER %u requested enc. key inventory, but payload length was invalid (%u bytes), no response", peerId, req->length);
break;
}
// scope intentional
{
// get the peer password hash from the frame message
DECLARE_UINT8_ARRAY(peerHash, 32U);
::memcpy(peerHash, req->buffer + 8U, 32U);
uint8_t peerSalt[4U];
::memset(peerSalt, 0x00U, 4U);
::memcpy(peerSalt, req->buffer + 40U, 4U);
std::string passwordForPeer = network->m_password;
// check if the peer is in the peer ACL list
bool validAcl = true;
if (network->m_peerListLookup->getACL()) {
if (!network->m_peerListLookup->isPeerAllowed(peerId) && !network->m_peerListLookup->isPeerListEmpty()) {
LogWarning(LOG_MASTER, "PEER %u RPTK, failed peer ACL check", peerId);
validAcl = false;
} else {
lookups::PeerId peerEntry = network->m_peerListLookup->find(peerId);
if (peerEntry.peerDefault()) {
validAcl = false; // default peer IDs are a no-no as they have no data thus fail ACL check
} else {
passwordForPeer = peerEntry.peerPassword();
if (passwordForPeer.length() == 0) {
passwordForPeer = network->m_password;
}
}
}
if (network->m_peerListLookup->isPeerListEmpty()) {
LogWarning(LOG_MASTER, "Peer List ACL enabled, but we have an empty peer list? Passing all peers.");
validAcl = true;
}
}
if (validAcl) {
size_t size = passwordForPeer.size();
uint8_t* in = new uint8_t[size + sizeof(uint32_t)];
::memcpy(in, peerSalt, sizeof(uint32_t));
for (size_t i = 0U; i < size; i++)
in[i + sizeof(uint32_t)] = passwordForPeer.at(i);
uint8_t out[32U];
edac::SHA256 sha256;
sha256.buffer(in, (uint32_t)(size + sizeof(uint32_t)), out);
delete[] in;
// validate hash
bool validHash = false;
if (req->length >= 80) {
validHash = true;
for (uint8_t i = 0; i < 32U; i++) {
if (peerHash[i] != out[i]) {
validHash = false;
break;
}
}
}
if (!validHash) {
LogError(LOG_MASTER, "PEER %u requested enc. key inventory, but had invalid authentication, no response", peerId);
break;
}
} else {
LogError(LOG_MASTER, "PEER %u requested enc. key inventory, but had invalid ACL, no response", peerId);
break;
}
}
// scope intentional
{
// get remote access password hash from the frame message
DECLARE_UINT8_ARRAY(remoteAccessHash, 32U);
::memcpy(remoteAccessHash, req->buffer + 44U, 32U);
uint8_t remoteSalt[4U];
::memset(remoteSalt, 0x00U, 4U);
::memcpy(remoteSalt, req->buffer + 76U, 4U);
std::string remoteAccessPassword = network->m_host->m_cryptoLookup->getRemotePassword();
size_t size = remoteAccessPassword.size();
uint8_t* in = new uint8_t[size + sizeof(uint32_t)];
::memcpy(in, remoteSalt, sizeof(uint32_t));
for (size_t i = 0U; i < size; i++)
in[i + sizeof(uint32_t)] = remoteAccessPassword.at(i);
uint8_t out[32U];
edac::SHA256 sha256;
sha256.buffer(in, (uint32_t)(size + sizeof(uint32_t)), out);
delete[] in;
// validate hash
bool validHash = false;
if (req->length >= 80) {
validHash = true;
for (uint8_t i = 0; i < 32U; i++) {
if (remoteAccessHash[i] != out[i]) {
validHash = false;
break;
}
}
}
if (!validHash) {
LogError(LOG_MASTER, "PEER %u requested enc. key inventory, but had invalid access authentication, no response", peerId);
break;
}
}
// scope intentional
{
// read entire file into buffer
std::stringstream b;
std::ifstream stream(network->m_host->m_cryptoLookup->filename(), std::ios::in | std::ios::binary);
uint32_t len = 0U;
UInt8Array bufferUInt8Array = nullptr;
uint8_t* buffer = nullptr;
if (stream.is_open()) {
stream.seekg(0, std::ios::end);
len = (uint32_t)stream.tellg();
stream.seekg(0, std::ios::beg);
bufferUInt8Array = std::make_unique<uint8_t[]>(len);
buffer = bufferUInt8Array.get();
::memset(buffer, 0x00U, len);
uint32_t i = 0U;
while (stream.peek() != EOF) {
buffer[i] = (uint8_t)stream.get();
i++;
}
stream.close();
}
PacketBuffer pkt(true, "Remote EKC, Key Inventory");
pkt.encode((uint8_t*)buffer, len);
LogInfoEx(LOG_REPL, "PEER %u Remote EKC, Key Inventory, blocks %u, streamId = %u", peerId, pkt.fragments.size(), streamId);
if (pkt.fragments.size() > 0U) {
for (auto frag : pkt.fragments) {
network->writePeer(peerId, network->m_peerId, { NET_FUNC::KEYS_INVENTORY, NET_SUBFUNC::NOP },
frag.second->data, FRAG_SIZE, 0U, streamId);
Thread::sleep(60U); // pace block transmission
}
}
pkt.clear();
}
}
}
break;
case NET_FUNC::KEYS_UPDATE: // Encryption Key Container Update
{
lookups::PeerId peerEntry = network->m_peerListLookup->find(peerId);
if (peerEntry.peerDefault()) {
LogError(LOG_MASTER, "PEER %u requested enc. key update but is not allowed, no response", peerId);
break;
} else {
if (!peerEntry.canRequestKeys()) {
LogError(LOG_MASTER, "PEER %u requested enc. key update but is not allowed, no response", peerId);
break;
}
}
// keys inventory operates differently from the rest of the network opcodes...and does not require
// an established connection to the master, so we will not validate the peer connection state here
if (peerId > 0 && !peerEntry.peerDefault()) {
// scope intentional
{
// get the peer password hash from the frame message
DECLARE_UINT8_ARRAY(peerHash, 32U);
::memcpy(peerHash, req->buffer + 8U, 32U);
uint8_t peerSalt[4U];
::memset(peerSalt, 0x00U, 4U);
::memcpy(peerSalt, req->buffer + 40U, 4U);
std::string passwordForPeer = network->m_password;
// check if the peer is in the peer ACL list
bool validAcl = true;
if (network->m_peerListLookup->getACL()) {
if (!network->m_peerListLookup->isPeerAllowed(peerId) && !network->m_peerListLookup->isPeerListEmpty()) {
LogWarning(LOG_MASTER, "PEER %u RPTK, failed peer ACL check", peerId);
validAcl = false;
} else {
lookups::PeerId peerEntry = network->m_peerListLookup->find(peerId);
if (peerEntry.peerDefault()) {
validAcl = false; // default peer IDs are a no-no as they have no data thus fail ACL check
} else {
passwordForPeer = peerEntry.peerPassword();
if (passwordForPeer.length() == 0) {
passwordForPeer = network->m_password;
}
}
}
if (network->m_peerListLookup->isPeerListEmpty()) {
LogWarning(LOG_MASTER, "Peer List ACL enabled, but we have an empty peer list? Passing all peers.");
validAcl = true;
}
}
if (validAcl) {
size_t size = passwordForPeer.size();
uint8_t* in = new uint8_t[size + sizeof(uint32_t)];
::memcpy(in, peerSalt, sizeof(uint32_t));
for (size_t i = 0U; i < size; i++)
in[i + sizeof(uint32_t)] = passwordForPeer.at(i);
uint8_t out[32U];
edac::SHA256 sha256;
sha256.buffer(in, (uint32_t)(size + sizeof(uint32_t)), out);
delete[] in;
// validate hash
bool validHash = false;
if (req->length - 8U == 32U) {
validHash = true;
for (uint8_t i = 0; i < 32U; i++) {
if (peerHash[i] != out[i]) {
validHash = false;
break;
}
}
}
if (!validHash) {
LogError(LOG_MASTER, "PEER %u requested enc. key update, but had invalid authentication, no response", peerId);
break;
}
} else {
LogError(LOG_MASTER, "PEER %u requested enc. key update, but had invalid ACL, no response", peerId);
break;
}
}
// scope intentional
{
// get remote access password hash from the frame message
DECLARE_UINT8_ARRAY(remoteAccessHash, 32U);
::memcpy(remoteAccessHash, req->buffer + 44U, 32U);
uint8_t remoteSalt[4U];
::memset(remoteSalt, 0x00U, 4U);
::memcpy(remoteSalt, req->buffer + 76U, 4U);
std::string remoteAccessPassword = network->m_host->m_cryptoLookup->getRemotePassword();
size_t size = remoteAccessPassword.size();
uint8_t* in = new uint8_t[size + sizeof(uint32_t)];
::memcpy(in, remoteSalt, sizeof(uint32_t));
for (size_t i = 0U; i < size; i++)
in[i + sizeof(uint32_t)] = remoteAccessPassword.at(i);
uint8_t out[32U];
edac::SHA256 sha256;
sha256.buffer(in, (uint32_t)(size + sizeof(uint32_t)), out);
delete[] in;
// validate hash
bool validHash = false;
if (req->length - 8U == 32U) {
validHash = true;
for (uint8_t i = 0; i < 32U; i++) {
if (remoteAccessHash[i] != out[i]) {
validHash = false;
break;
}
}
}
if (!validHash) {
LogError(LOG_MASTER, "PEER %u requested enc. key update, but had invalid access authentication, no response", peerId);
break;
}
}
// scope intentional
{
DECLARE_UINT8_ARRAY(rawPayload, req->length);
::memcpy(rawPayload, req->buffer, req->length);
// Utils::dump(1U, "MetadataNetwork::taskNetworkRx(), KEYS_UPDATE, Raw Payload", rawPayload, req->length);
if (mdNetwork->m_peerKeyUpdatePkt.find(peerId) == mdNetwork->m_peerKeyUpdatePkt.end()) {
mdNetwork->m_peerKeyUpdatePkt.insert(peerId, MetadataNetwork::PacketBufferEntry());
MetadataNetwork::PacketBufferEntry& pkt = mdNetwork->m_peerKeyUpdatePkt[peerId];
pkt.buffer = new PacketBuffer(true, "Remote EKC, Key Update");
pkt.streamId = streamId;
pkt.locked = false;
} else {
MetadataNetwork::PacketBufferEntry& pkt = mdNetwork->m_peerKeyUpdatePkt[peerId];
if (!pkt.locked && pkt.streamId != streamId) {
LogError(LOG_REPL, "PEER %u Remote EKC, Key Update, stream ID mismatch, expected %u, got %u", peerId, pkt.streamId, streamId);
pkt.buffer->clear();
pkt.streamId = streamId;
}
if (pkt.streamId != streamId) {
// otherwise drop the packet
break;
}
}
MetadataNetwork::PacketBufferEntry& pkt = mdNetwork->m_peerKeyUpdatePkt[peerId];
if (pkt.locked) {
while (pkt.locked && pkt.timeout < TIMEOUT_MAX_REPL) {
pkt.timeout++;
Thread::sleep(1U);
}
if (pkt.timeout >= TIMEOUT_MAX_REPL) {
LogError(LOG_STP, "PEER %u Remote EKC, Key Update, timeout waiting for packet buffer to unlock", peerId);
pkt.buffer->clear();
pkt.streamId = 0U;
mdNetwork->m_peerKeyUpdatePkt.erase(peerId);
break;
}
}
pkt.locked = true;
pkt.timeout = 0U;
uint32_t decompressedLen = 0U;
uint8_t* decompressed = nullptr;
if (pkt.buffer->decode(rawPayload, &decompressed, &decompressedLen)) {
mdNetwork->m_peerKeyUpdatePkt.lock();
// randomize filename
std::ostringstream s;
s << network->m_cryptoLookup->filename();
std::string filename = s.str();
std::ofstream file(filename, std::ofstream::out);
if (file.fail()) {
LogError(LOG_PEER, "Cannot open the crypto container file - %s", filename.c_str());
pkt.buffer->clear();
delete pkt.buffer;
pkt.streamId = 0U;
if (decompressed != nullptr) {
delete[] decompressed;
}
mdNetwork->m_peerKeyUpdatePkt.unlock();
mdNetwork->m_peerKeyUpdatePkt.erase(peerId);
break;
}
for (uint32_t i = 0U; i < decompressedLen; i++) {
file << (char)decompressed[i];
}
file.close();
network->m_cryptoLookup->stop(true);
network->m_cryptoLookup->reload();
pkt.buffer->clear();
delete pkt.buffer;
pkt.streamId = 0U;
if (decompressed != nullptr) {
delete[] decompressed;
}
mdNetwork->m_peerKeyUpdatePkt.unlock();
mdNetwork->m_peerKeyUpdatePkt.erase(peerId);
} else {
pkt.locked = false;
}
}
}
}
break;
case NET_FUNC::REPL:
if (req->fneHeader.getSubFunction() == NET_SUBFUNC::REPL_ACT_PEER_LIST) { // Peer Replication Active Peer List
if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end())) {

@ -117,6 +117,7 @@ namespace network
bool locked;
uint32_t timeout;
};
concurrent::unordered_map<uint32_t, PacketBufferEntry> m_peerKeyUpdatePkt;
concurrent::unordered_map<uint32_t, PacketBufferEntry> m_peerReplicaActPkt;
concurrent::unordered_map<uint32_t, PacketBufferEntry> m_peerTreeListPkt;

Loading…
Cancel
Save

Powered by TurnKey Linux.