#include "StdAfx.h" #include "Profiler.h" #include "NodeRelay.h" #include "GateRelay.h" #include "ConnectorBridge.h" #include "DeviceBridge.h" #include "Common/FSecure/CppTools/Utils.h" //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::mutex FSecure::C3::Core::Profiler::Profile::m_Mutex; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Profiler(std::filesystem::path snapshotPath) : m_SnapshotPath(std::move(snapshotPath)) { } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Initialize(std::string name, std::shared_ptr gateway) { m_Gateway = Gateway{ shared_from_this(), name, gateway }; for (auto&& e : m_Gateway->m_Gateway.lock()->m_InterfaceFactory.GetMap()) m_BindersMappings.emplace_back(e.first, e.second.m_ClousureConnectorHash); RestoreFromSnapshot(); std::thread(&Profiler::DumpSnapshots, this).detach(); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::HandleActionsPacket(ByteView actionsPacket) { std::scoped_lock lock(m_AccessMutex); try { auto actions = json::parse(std::string{ actionsPacket }); auto jActions = actions.find("Command"); if (jActions == actions.end()) throw std::runtime_error{ "Command object is missing" }; (*jActions)["ByteForm"] = base64::encode(TranslateCommand(*jActions)); // Helper lambda that read particular element from actions JSON. auto ReadJsonElement = [&actions](std::string_view elementName) { auto element = actions.find(elementName); if (element != actions.end()) return element; throw std::runtime_error{ std::string{ elementName } +" is not specified." }; }; // Parse AgentId. if (auto relayAgentId = ReadJsonElement("relayAgentId"); relayAgentId->is_null()) m_Gateway->ParseAndRunCommand(actions); else if (auto agent = m_Gateway->m_Agents.Find(relayAgentId->get())) agent->ParseAndRunCommand(actions); else throw std::runtime_error{ "Unknown AgentId." }; } catch (std::exception& exception) { if (auto gateway = m_Gateway->m_Gateway.lock()) gateway->Log({ "Caught an exception while parsing Action. "s + exception.what(), FSecure::C3::LogMessage::Severity::Error}); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Profile FSecure::C3::Core::Profiler::Get() { return Profile{ *m_Gateway }; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// uint32_t FSecure::C3::Core::Profiler::GetBinderTo(uint32_t id) { for (auto&& e : m_BindersMappings) if (e.first == id) return e.second; else if (e.second == id) return e.first; return 0u; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::ByteVector FSecure::C3::Core::Profiler::TranslateCommand(json const& command) { return ByteVector::Create(command.at("id").get()).Concat(TranslateArguments(command.at("arguments"))); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::ByteVector FSecure::C3::Core::Profiler::TranslateArguments(json const& arguments) { ByteVector ret; auto translate = [&ret](auto arg) { ret.Concat(Translate(arg.at("type"), arg.at("value"))); }; for (auto argument : arguments) { if (!argument.is_array()) translate(argument); else for (auto const subargument : argument) translate(subargument); } return ret; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::ByteVector FSecure::C3::Core::Profiler::TranslateStartupCommand(json const& jcommand) { auto commandId = jcommand.at("id").get(); auto const& createCommands = m_Gateway->m_CreateCommands; auto command = std::find_if(createCommands.cbegin(), createCommands.cend(), [commandId](Gateway::CreateCommand const& cc) { return cc.m_IsDevice && cc.m_Id == commandId; }); // this function will only support create commands if (command == createCommands.cend() || !command->m_IsDevice) throw std::logic_error{ "Failed to find a create command" }; return ByteVector::Create(command->m_IsNegotiableChannel, command->m_Hash).Concat(TranslateArguments(jcommand.at("arguments"))); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::ByteVector FSecure::C3::Core::Profiler::Translate(std::string const& type, json::value_type const& value) { if (type == "uint8") return ByteVector{}.Write(value.is_string() ? FSecure::Utils::SafeCast(std::stoull(value.get())) : value.get()); if (type == "uint16") return ByteVector{}.Write(value.is_string() ? FSecure::Utils::SafeCast(std::stoull(value.get())) : value.get()); if (type == "uint32") return ByteVector{}.Write(value.is_string() ? FSecure::Utils::SafeCast(std::stoull(value.get())) : value.get()); if (type == "uint64") return ByteVector{}.Write(value.is_string() ? FSecure::Utils::SafeCast(std::stoull(value.get())) : value.get()); if (type == "int8") return ByteVector{}.Write(value.is_string() ? FSecure::Utils::SafeCast(std::stoll(value.get())) : value.get()); if (type == "int16") return ByteVector{}.Write(value.is_string() ? FSecure::Utils::SafeCast(std::stoll(value.get())) : value.get()); if (type == "int32") return ByteVector{}.Write(value.is_string() ? FSecure::Utils::SafeCast(std::stoll(value.get())) : value.get()); if (type == "int64") return ByteVector{}.Write(value.is_string() ? FSecure::Utils::SafeCast(std::stoll(value.get())) : value.get()); if (type == "float") return ByteVector{}.Write(value.is_string() ? std::stof(value.get()) : value.get()); if (type == "boolean") return ByteVector{}.Write(value.get()); if (type == "string" || type == "ip") return ByteVector{}.Write(value.get()); if (type == "binary") return ByteVector{}.Write(base64::decode(value.get())); throw std::invalid_argument{ "Unrecognized argument type: " + type }; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::DumpSnapshots() { auto sp = GetSnapshotProxy(); while (true) { if (sp.CheckUpdates()) { const auto snapshotTmpPath = std::filesystem::path(m_SnapshotPath).replace_extension(".tmp"); { std::ofstream snapshotTmp{ snapshotTmpPath }; snapshotTmp << sp.GetSnapshot().dump() << std::endl; } std::filesystem::rename(snapshotTmpPath, m_SnapshotPath); } std::this_thread::sleep_for(1s); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::RestoreFromSnapshot() { json snapshot; // get snapshot { auto ifile = std::ifstream{ m_SnapshotPath }; auto str = std::string((std::istreambuf_iterator(ifile)), std::istreambuf_iterator()); if (str.empty()) return; snapshot = json::parse(str); } // restore gateway. { for (auto&& b : snapshot["_RegisteredBuilds"]) { m_Gateway->AddAgentBuild ( b.at("_bid").get(), BuildProperties { b.at("_x64").get(), b.at("_ban").get(), b.at("_startCmd"), } ); } for (auto&& channel : snapshot["channels"]) { auto gateRelay = Get().m_Gateway.m_Gateway.lock(); if (!gateRelay) throw std::logic_error("Parent GateRelay cannot be locked"); auto byteForm = base64::decode(channel["startupCommand"]["ByteForm"].get()); auto readView = ByteView{ byteForm }; readView.remove_prefix(sizeof(uint16_t)); // remove number of command auto did = DeviceId{ channel["iId"].get() }; auto device = gateRelay->CreateAndAttachDevice ( did, channel["type"].get(), channel["isNegotiationChannel"].get(), readView ); auto jitter = std::pair{ FSecure::Utils::ToMilliseconds(channel["jitter"][0].get()), FSecure::Utils::ToMilliseconds(channel["jitter"][1].get()) }; device->SetUpdateDelay(jitter.first, jitter.second); auto profile = Get(); // we need to take profile each time, as it is also taken in CreateAndAttachDevice and that would lead to deadlock. auto channelProfile = profile.m_Gateway.m_Channels.Find(did); channelProfile->m_StartupArguments = channel["startupCommand"]; channelProfile->m_Jitter = jitter; } for (auto&& connector : snapshot["connectors"]) { auto gateRelay = Get().m_Gateway.m_Gateway.lock(); if (!gateRelay) throw std::logic_error("Parent GateRelay cannot be locked"); auto byteForm = base64::decode(connector["startupCommand"]["ByteForm"].get()); auto readView = ByteView{ byteForm }; readView.remove_prefix(sizeof(uint16_t)); // remove number of command auto did = connector["type"].get(); gateRelay->TurnOnConnector ( did, readView ); auto profile = Get(); // we need to take profile each time, as it is also taken in CreateAndAttachDevice and that would lead to deadlock. auto connectorProfile = profile.m_Gateway.m_Connectors.Find(did); connectorProfile->m_StartupArguments = connector["startupCommand"]; }; } // restore agents { auto profile = Get(); for (auto&& route : snapshot["routes"]) profile.m_Gateway.ReAddRoute(RouteId(route["destinationAgent"].get(), route["receivingInterface"].get()), route["outgoingInterface"].get(), route["isNeighbour"].get()); for (auto&& relay : snapshot["relays"]) { auto agent = profile.m_Gateway.ReAddAgent ( relay["agentId"].get(), relay["buildId"].get(), base64::decode(relay["publicKey"].get()), false, relay["timestamp"].get(), HostInfo(relay["hostInfo"]) ); agent->m_LastDeviceId = relay["_LastDeviceId"].get(); for (auto&& channel : relay["channels"]) { auto device = agent->ReAddChannel(channel["iId"].get(), channel["type"].get(), channel["isReturnChannel"].get(), channel["isNegotiationChannel"].get()); device->m_StartupArguments = channel["startupCommand"]; device->m_Jitter = std::pair{ FSecure::Utils::ToMilliseconds(channel["jitter"][0].get()), FSecure::Utils::ToMilliseconds(channel["jitter"][1].get()) }; } for (auto&& peripheral : relay["peripherals"]) { auto device = agent->ReAddPeripheral(peripheral["iId"].get(), peripheral["type"].get()); device->m_StartupArguments = peripheral["startupCommand"]; device->m_Jitter = std::pair{ FSecure::Utils::ToMilliseconds(peripheral["jitter"][0].get()), FSecure::Utils::ToMilliseconds(peripheral["jitter"][1].get()) }; } for (auto&& route : relay["routes"]) agent->ReAddRoute(RouteId(route["destinationAgent"].get(), route["receivingInterface"].get()), route["outgoingInterface"].get(), route["isNeighbour"].get()); } } // Restore real routing table { auto profile = Get(); auto gateRelay = profile.m_Gateway.m_Gateway.lock(); if (!gateRelay) throw std::logic_error("Parent GateRelay cannot be locked"); auto const& routes = profile.m_Gateway.m_Routes.GetUnderlyingContainer(); for (auto const& route : routes) { auto outgoingDevice = gateRelay->FindDevice(route.m_OutgoingDevice); if (!outgoingDevice) throw std::logic_error("Outgoing device cannot be locked"); gateRelay->AddRoute(route.m_Id, outgoingDevice); } profile.m_Gateway.m_LastDeviceId = snapshot["_LastDeviceId"].get(); } // restore connections in connector. It must be last step in the chain. { auto gateway = m_Gateway->m_Gateway.lock(); if (!gateway) return; for (auto&& relay : snapshot["relays"]) { auto profile = Get(); for (auto&& peripheral : relay["peripherals"]) { auto connectorHash = GetBinderTo(peripheral["type"].get()); auto connector = gateway->m_Connectors.Find([&](auto const& e) { return e->GetNameHash() == connectorHash; }); if (!connector) continue; auto route = gateway->FindRoute(AgentId{ relay["agentId"].get() }); if (!route) continue; auto creationCommand = base64::decode(peripheral["startupCommand"]["ByteForm"].get()); auto readView = ByteView{ creationCommand }; auto connectionId = ByteVector::Create(RouteId{ route->m_RouteId.GetAgentId(), DeviceId{ peripheral["iId"].get() } }); readView.remove_prefix(sizeof(uint16_t)); // remove command id connector->PeripheralCreationCommand(connectionId, readView); // throw away the response. connector->OnCommandFromBinder(connectionId, base64::decode(peripheral["startupCommand"]["FirstResponse"].get())); } } } // Gateway peripherals will not be restored. Supporting this case is not important, and they would need new staging to be done in this function. Might be added in the future. //for (auto&& peripheral : snapshot["peripherals"]) //{ // auto gateRelay = Get().m_Gateway.m_Gateway.lock(); // if (!gateRelay) // throw std::logic_error("Parent GateRelay cannot be locked"); // auto byteForm = base64::decode(peripheral["startupCommand"]["ByteForm"].get()); // auto readView = ByteView{ byteForm }; // readView.remove_prefix(sizeof(uint16_t)); // remove number of command // auto did = DeviceId{ peripheral["iId"].get() }; // gateRelay->CreateAndAttachDevice // ( // did, // peripheral["type"].get(), // false, // readView // ); // auto profile = Get(); // we need to take profile each time, as it is also taken in CreateAndAttachDevice and that would lead to deadlock. // auto peripheralProfile = profile.m_Gateway.m_Peripherals.Find(did); // peripheralProfile->m_StartupArguments = peripheral["startupCommand"]; //} } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// json FSecure::C3::Core::Profiler::HandleNewBuildMessage(json const& message) { auto newBuildId = message.at("BuildId").get(); auto const& command = message.at("Command"); auto commandBinary = TranslateStartupCommand(command); auto response = json { {"BinaryCommand", base64::encode(commandBinary)}, }; { // Add build to profiler auto arch = message.at("Arch").get(); std::transform(arch.begin(), arch.end(), arch.begin(), [](char c) { return std::tolower(c); }); auto isX64 = arch == "x64"; Get().m_Gateway.AddAgentBuild(newBuildId, { isX64, false, command }); } return response; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// json FSecure::C3::Core::Profiler::Gateway::CreateProfileSnapshot() const { auto gateway = m_Gateway.lock(); if (!gateway) return {}; // Fill in basic data. json profile = { { "agentId", gateway->m_AgentId.ToString() }, { "buildId", gateway->m_BuildId.ToString() }, {"_LastDeviceId", m_LastDeviceId }, { "timestamp", m_LastSeen }, { "isActive", true} }; if (!m_ErrorState.empty()) profile["error"] = m_ErrorState; // Populate output json with all containers and their elements Profiles. profile["channels"] = m_Channels.CreateProfileSnapshot(); profile["peripherals"] = m_Peripherals.CreateProfileSnapshot(); profile["routes"] = m_Routes.CreateProfileSnapshot(); profile["connectors"] = m_Connectors.CreateProfileSnapshot(); profile["relays"] = m_Agents.CreateProfileSnapshot(); json registeredBuilds; for (auto b : m_AgentBuilds) { registeredBuilds.push_back ( { {"_bid", b.first.ToUnderlyingType()}, {"_ban", b.second.m_IsBanned}, {"_x64", b.second.m_IsX64}, {"_startCmd", b.second.m_StartupCmd}, } ); } profile["_RegisteredBuilds"] = registeredBuilds; return profile; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Agent::Agent(std::weak_ptr owner, AgentId agentId, BuildId buildId, FSecure::Crypto::PublicKey encryptionKey, bool isBanned, int32_t lastSeen, bool isX64, HostInfo hostInfo) : Relay(owner, agentId, buildId, lastSeen) , m_EncryptionKey(encryptionKey) , m_HostInfo(std::move(hostInfo)) , m_IsBanned(isBanned) , m_IsX64(isX64) { } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// json FSecure::C3::Core::Profiler::Agent::CreateProfileSnapshot() const { // Fill in basic data. json profile = { { "agentId", m_Id.ToString() }, { "buildId", m_BuildId.ToString() }, { "publicKey", m_EncryptionKey.ToBase64() }, { "_LastDeviceId", m_LastDeviceId }, { "timestamp", m_LastSeen }, { "hostInfo", m_HostInfo }, { "isActive", FSecure::Utils::TimeSinceEpoch() - m_LastSeen < 300 } }; if (!m_ErrorState.empty()) profile["error"] = m_ErrorState; // Populate output json with all containers and their elements Profiles. profile["channels"] = m_Channels.CreateProfileSnapshot(); profile["peripherals"] = m_Peripherals.CreateProfileSnapshot(); profile["routes"] = m_Routes.CreateProfileSnapshot(); return profile; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Agent::ParseAndRunCommand(json const& jCommandElement) noexcept(false) { auto profiler = m_Owner.lock(); if (!profiler) return; // probably shutting down // check if the command should be run on device std::optional deviceId; bool deviceIsChannel = false; // have meaning only when it is command for device. if (auto jChannelId = jCommandElement.find("channelId"); jChannelId != jCommandElement.end() && !jChannelId->is_null()) { if (auto channel = m_Channels.Find(jChannelId->get()); channel) { deviceId = channel->m_Id; deviceIsChannel = true; } else throw std::runtime_error{ "Unknown ChannelId." }; } else if (auto jPeripheralId = jCommandElement.find("peripheralId"); jPeripheralId != jCommandElement.end() && !jPeripheralId->is_null()) { if (auto peripheral = m_Peripherals.Find(jPeripheralId->get()); peripheral) deviceId = peripheral->m_Id; else throw std::runtime_error{ "Unknown PeripheralId." }; } auto commandWithArgs = base64::decode(jCommandElement["Command"]["ByteForm"].get()); auto gateRelay = profiler->m_Gateway->m_Gateway.lock(); if (!gateRelay) return; // probably shutting down if (deviceId) { std::function finalizer = []() {}; auto commandReadView = ByteView{ commandWithArgs }; auto commandId = commandReadView.Read(); if (commandId > static_cast(-256)) { // generic interface commands switch (static_cast(commandId)) { case FSecure::C3::Command::Close: finalizer = [&]() { if (deviceIsChannel) { m_Channels.TryRemove(*deviceId); m_Routes.RemoveIf([deviceId](Profiler::Route const& route) { return route.m_OutgoingDevice == deviceId; }); } else { // Find connector hash auto element = m_Peripherals.Find(*deviceId); if (!element) return; auto connectorHash = profiler->GetBinderTo(element->m_TypeHash); // remove peripheral m_Peripherals.TryRemove(*deviceId); // Get connector auto connector = gateRelay->m_Connectors.Find([&](auto const& e) { return e->GetNameHash() == connectorHash; }); if (!connector) return; // Remove connection. connector->CloseConnection(ByteVector::Create(RouteId{ m_Id, *deviceId })); } }; break; case FSecure::C3::Command::UpdateJitter: finalizer = [this, deviceId, deviceIsChannel, commandReadView]() mutable { Device* device = deviceIsChannel ? m_Channels.Find(*deviceId) : m_Peripherals.Find(*deviceId); if (!device) throw std::runtime_error{ "Device not found" }; device->m_Jitter.first = FSecure::Utils::ToMilliseconds(commandReadView.Read()); device->m_Jitter.second = FSecure::Utils::ToMilliseconds(commandReadView.Read()); }; break; default: break; } } auto route = gateRelay->FindRoute(m_Id); if (!route) throw std::runtime_error("Failed to find route to agent id = " + m_Id.ToString()); auto outgoingChannel = route->m_Channel.lock(); if (!outgoingChannel) throw std::runtime_error("Tried to send command through dead channel"); // TODO maybe try through different route auto query = ProceduresG2X::RunCommandOnDeviceQuery::Create(route->m_RouteId, gateRelay->m_Signature, m_EncryptionKey, gateRelay->m_DecryptionKey, *deviceId, ByteView{ commandWithArgs }); gateRelay->LockAndSendPacket(query->ComposeQueryPacket(), outgoingChannel); finalizer(); } else// If we're here then let NodeRelay run Command on itself. { PerformCreateCommand(jCommandElement); RunCommand(commandWithArgs); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Agent::RunCommand(ByteView commandWithArguments) { auto commandReadView = commandWithArguments; auto commandId = commandReadView.Read(); // this function handle only last part of commands range. if (commandId <= static_cast(-256)) return; auto profiler = m_Owner.lock(); if (!profiler) return; // probably shutting down auto gateRelay = profiler->m_Gateway->m_Gateway.lock(); if (!gateRelay) return; // probably shutting down auto route = gateRelay->FindRoute(m_Id); if (!route) throw std::runtime_error("Failed to find route to agent id = " + m_Id.ToString()); std::function finalizer = []() {}; switch (static_cast(commandId)) { case Command::Close: { finalizer = [&]() { auto owner = m_Owner.lock(); if (!owner) throw std::runtime_error{ "Cannot obtain owner" }; m_Channels.Clear(); for (auto&& element : m_Peripherals.GetUnderlyingContainer()) { auto connectorHash = profiler->GetBinderTo(element.m_TypeHash); auto connector = gateRelay->m_Connectors.Find([&](auto const& e) { return e->GetNameHash() == connectorHash; }); if (!connector) break; // Remove connection. connector->CloseConnection(ByteVector::Create(RouteId{ m_Id, element.m_Id })); } m_Peripherals.Clear(); owner->Get().m_Gateway.m_Agents.Remove(m_Id); }; break; } case Command::CreateRoute: { finalizer = [this, commandReadView]() mutable { auto [ridStr, didStr, isNbr] = commandReadView.Read(); ReAddRoute(ridStr, didStr, isNbr); }; break; } case Command::RemoveRoute: { finalizer = [this, commandReadView]() mutable { ReRemoveRoute(commandReadView.Read()); }; break; } case Command::SetGRC: { finalizer = [this, commandReadView]() mutable { auto did = DeviceId{ commandReadView.Read() }; if (!m_Channels.Find(did)) throw std::runtime_error{ "Channel not found" }; for (auto& e : m_Channels.GetUnderlyingContainer()) e.m_IsReturnChannel = e.m_Id == did; }; break; } case Command::Ping: break; default: throw std::runtime_error("Profiler received an unknown command for agent id: " + m_Id.ToString()); } auto outgoingChannel = route->m_Channel.lock(); if (!outgoingChannel) throw std::runtime_error("Tried to send command through dead channel"); // TODO maybe try through different route auto query = ProceduresG2X::RunCommandOnAgentQuery::Create(route->m_RouteId, gateRelay->m_Signature, m_EncryptionKey, gateRelay->m_DecryptionKey, commandWithArguments); gateRelay->LockAndSendPacket(query->ComposeQueryPacket(), outgoingChannel); finalizer(); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Agent::PerformCreateCommand(json const& jCommandElement) { auto profiler = m_Owner.lock(); if (!profiler) return; // probably shutting down auto gateRelay = profiler->m_Gateway->m_Gateway.lock(); if (!gateRelay) return; // probably shutting down auto route = gateRelay->FindRoute(m_Id); if (!route) throw std::runtime_error("Failed to find route to agent id = " + m_Id.ToString()); auto commandWithArguments = base64::decode(jCommandElement["Command"]["ByteForm"].get()); // check if command is a create device command -> then we need to assign deviceId and retreive its hash and repack commandWithArguments // [CommandId{CreateAndAttachDevice}(u16)][NewDeviceId(u16)][NewDeviceTypeHash(HashT)][arguments] auto commandReadView = ByteView{ commandWithArguments }; auto commandId = commandReadView.Read(); auto const& createCommands = profiler->m_Gateway->m_CreateCommands; auto command = std::find_if(createCommands.cbegin(), createCommands.cend(), [commandId](Gateway::CreateCommand const& cc) { return cc.m_IsDevice && cc.m_Id == commandId; }); // this function will only suport create commands if (command == createCommands.cend()) return; // it is a create command DeviceId newDeviceId = ++m_LastDeviceId; ByteVector repacked; repacked.Write(Command::AddDevice, newDeviceId, command->m_IsNegotiableChannel, command->m_Hash); if (auto binder = profiler->GetBinderTo(command->m_Hash); binder && command->m_IsDevice) // peripheral, check if payload is needed. { auto connector = profiler->m_Gateway->m_Gateway.lock()->GetConnector(binder); if (!connector) throw std::runtime_error{ "Connector for requested peripheral is closed" }; auto updatedArguments = connector->PeripheralCreationCommand(ByteVector::Create(RouteId{ route->m_RouteId.GetAgentId(), newDeviceId }), commandReadView, m_IsX64); repacked.Concat(updatedArguments); } else { repacked.Concat(commandReadView); } commandWithArguments = repacked; auto outgoingChannel = route->m_Channel.lock(); if (!outgoingChannel) throw std::runtime_error("Tried to send command through dead channel"); // TODO maybe try through different route // push json with startup arguments to some container, use it if channel is created. AddScheduledDevice(newDeviceId, jCommandElement["Command"]); auto query = ProceduresG2X::RunCommandOnAgentQuery::Create(route->m_RouteId, gateRelay->m_Signature, m_EncryptionKey, gateRelay->m_DecryptionKey, commandWithArguments); gateRelay->LockAndSendPacket(query->ComposeQueryPacket(), outgoingChannel); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Channel* FSecure::C3::Core::Profiler::Agent::ReAddChannel(Device::Id did, HashT typeNameHash, bool isReturnChannel /*= false*/, bool isNegotiationChannel /*= false*/) { auto channel = Relay::ReAddChannel(did, typeNameHash, isReturnChannel, isNegotiationChannel); if (auto it = m_ScheduledDevices.find(did.ToUnderlyingType()); it != m_ScheduledDevices.cend()) channel->m_StartupArguments = it->second; return channel; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Device* FSecure::C3::Core::Profiler::Agent::ReAddPeripheral(Device::Id did, HashT typeNameHash) { auto device = Relay::ReAddPeripheral(did, typeNameHash); if (auto it = m_ScheduledDevices.find(did.ToUnderlyingType()); it != m_ScheduledDevices.cend()) device->m_StartupArguments = it->second; return device; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Agent::AddScheduledDevice(DeviceId deviceId, json command) { m_ScheduledDevices.emplace(deviceId.ToUnderlyingType(), std::move(command)); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Channel* FSecure::C3::Core::Profiler::Agent::FindGrc() { for (auto& e : m_Channels.GetUnderlyingContainer()) if (e.m_IsReturnChannel) return &e; return nullptr; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Gateway::Gateway(std::weak_ptr owner, std::string name, std::shared_ptr gateway) : Relay(owner, gateway->m_AgentId, gateway->m_BuildId, FSecure::Utils::TimeSinceEpoch()) , m_Name(std::move(name)) , m_Gateway(gateway) { } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Gateway::ParseAndRunCommand(json const& jCommandElement) noexcept(false) { auto commandWithArgs = base64::decode(jCommandElement["Command"]["ByteForm"].get()); auto commandReadView = ByteView{ commandWithArgs }; auto conditionalRun = [&](auto jsonEntryToFind, bool isDevice) -> bool { if (auto jConnectorId = jCommandElement.find(jsonEntryToFind); jConnectorId != jCommandElement.end() && !jConnectorId->is_null()) { auto id = std::stoull(jConnectorId->template get(), nullptr, 16); // throws on error. if (isDevice) { auto gateway = m_Gateway.lock(); if (auto device = gateway->FindDevice(FSecure::Utils::SafeCast(id)); device) { auto localView = commandReadView; switch (FSecure::C3::Command(localView.Read())) { case FSecure::C3::Command::UpdateJitter: { Device* profilerElement = m_Channels.Find(device->GetDid()); if (!profilerElement) profilerElement = m_Peripherals.Find(device->GetDid()); if (!profilerElement) throw std::runtime_error{ "Device not found" }; profilerElement->m_Jitter.first = FSecure::Utils::ToMilliseconds(localView.Read()); profilerElement->m_Jitter.second = FSecure::Utils::ToMilliseconds(localView.Read()); break; } case FSecure::C3::Command::Close: { if (auto profilerElement = m_Peripherals.Find(device->GetDid())) { auto connectorHash = m_Owner.lock()->GetBinderTo(profilerElement->m_TypeHash); auto connector = m_Gateway.lock()->m_Connectors.Find([&](auto const& e) { return e->GetNameHash() == connectorHash; }); if (!connector) break; // Remove connection. connector->CloseConnection(ByteVector::Create(RouteId{ m_Id, device->GetDid() })); } else if (auto profilerElemnt = m_Channels.Find(device->GetDid())) { m_Routes.RemoveIf([did = device->GetDid()](Profiler::Route const& route) { return route.m_OutgoingDevice == did; }); } break; } default: break; } device->RunCommand(commandReadView); return true; } } else { if (auto connector = m_Connectors.Find(FSecure::Utils::SafeCast(id)); connector) return connector->RunCommand(commandReadView), true; } throw std::runtime_error{ "Unknown Id." }; } return false; }; if (conditionalRun("connectorId", false)) return; if (conditionalRun("channelId", true)) return; if (conditionalRun("peripheralId", true)) return; // If we're here then let NodeRelay run Command on itself. RunCommand(commandReadView); // try performing create command. PerformCreateCommand(jCommandElement); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// json FSecure::C3::Core::Profiler::Gateway::GetCapability() { // Request access to Gateway object. auto gateway = m_Gateway.lock(); if (!gateway) return {}; // TODO This function require refactoring. // Construct the InitialPacket. json initialPacket = json::parse(gateway->m_InterfaceFactory.GetCapability()); for (auto& interface : initialPacket["channels"]) EnsureCreateExists(interface); // Create method in interface is constructor. It must be a relay/gateway command. // initialPacket is copied to original to prevent iterator invalidation. // last 256 commands will be reserved for common commands. auto modifyEntry = [&, id = static_cast(-257), oryginal = initialPacket](std::vector const& relayTypes, auto interfaceType, std::vector prefix, bool isDevice) mutable { auto idToErase = 0; std::vector>> buffer; buffer.resize(prefix.size()); for (auto&& element : oryginal[interfaceType]) { try { for (auto i = 0u; i < prefix.size(); ++i) { auto arguments = element.at("create").at("arguments"); if (i) // NegotiationChannel command { if (arguments.empty() || arguments[0].size() != 2) continue; arguments[0] = { {"type", "string"}, {"name", "Negotiation Identifier"}, {"randomize", true}, {"description", "One identifier used to negotiate identifiers for each joining relay"} }; } for (auto&& relayType : relayTypes) buffer[i][relayType].push_back(json{ {"name", prefix[i] + element["name"].template get()}, {"arguments", arguments}, {"id", id} }); m_CreateCommands.push_back({ id, initialPacket[interfaceType][idToErase]["type"].template get(), isDevice, !!i }); // store command id and hash. --id; } // modify initial Packet with extra entries. initialPacket[interfaceType][idToErase].erase("create"); AddBuildInCommands(initialPacket[interfaceType][idToErase], isDevice); } catch (std::exception& e) { m_Gateway.lock()->Log({ std::string("Exception caught when parsing interface capability. ") + e.what(), LogMessage::Severity::Error }); } ++idToErase; } // move commands from buffer to modified json in correct order. for (auto prefixEntryIterator = buffer.rbegin(); prefixEntryIterator != buffer.rend(); ++prefixEntryIterator) for (auto&& relayEntry : *prefixEntryIterator) for (auto &&e : relayEntry.second) initialPacket[relayEntry.first]["commands"].push_back(std::move(e)); }; m_CreateCommands.clear(); // clear old create commands. modifyEntry({ "gateway", "relay"}, "channels", { "AddChannel", "AddNegotiationChannel" }, true); modifyEntry({ "gateway", "relay" }, "peripherals", { "AddPeripheral" }, true); modifyEntry({ "gateway" }, "connectors", { "TurnOnConnector" }, false); // Add common commands for relay. auto addRelayCommand = [&](std::vector relayTypes, json const& newCommand) { for (auto&& relayType : relayTypes) initialPacket[relayType]["commands"].push_back(newCommand); }; addRelayCommand({ "gateway", "relay" }, json{ {"name", "Close"}, {"id", static_cast>(Command::Close) }, {"arguments", json::array()} }); addRelayCommand({ "gateway", "relay" }, json{ {"name", "CreateRoute"}, {"id", static_cast>(Command::CreateRoute) }, {"arguments", { {{"type", "string"}, {"name", "RouteID"}, {"description", "Id of route in string form."}, {"min", 1}}, {{"type", "string"}, {"name", "DeviceId"}, {"description", "Id of device in string form."}, {"min", 1}}, {{"type", "boolean"}, {"name", "Neighbor"}, {"description", "Informs if relay is direct neighbor."}, {"defaultValue", true}} }} }); addRelayCommand({ "gateway", "relay" }, json{ {"name", "RemoveRoute"}, {"id", static_cast>(Command::RemoveRoute) }, {"arguments", { {{"type", "string"}, {"name", "RouteID"}, {"description", "Id of route in string form."}, {"min", 1}} }} }); addRelayCommand({ "relay" }, json{ {"name", "SetGatewayReturnChannel"}, {"id", static_cast>(Command::SetGRC) }, {"arguments", { {{"type", "string"}, {"name", "DeviceID"}, {"description", "Id of device in string form."}, {"min", 1}} }} }); addRelayCommand({ "relay" }, json{ {"name", "Ping"}, {"id", static_cast>(Command::Ping) }, {"arguments", json::array() } }); addRelayCommand({ "gateway" }, json{ {"name", "ClearNetwork"}, {"id", static_cast>(Command::ClearNetwork) }, {"arguments", { {{"type", "boolean"}, {"name", "Are you sure?"}, {"description", "Confirm clearing the network. All network state will be lost, this can not be undone."}, {"default", false}} }} }); // add extra fields. auto gatewayPushBack = [&](auto key, auto value) { initialPacket["gateway"][key] = value; }; gatewayPushBack("agentId", m_Id.ToString()); gatewayPushBack("buildId", m_BuildId.ToString()); gatewayPushBack("publicKey", Crypto::ExtractPublic(gateway->m_Signature).ToBase64()); gatewayPushBack("broadcastKey", gateway->m_BroadcastKey.ToBase64()); gatewayPushBack("name", m_Name); return initialPacket; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Gateway::EnsureCreateExists(json& interface) { if (!interface.contains("create")) interface["create"] = json::parse(R"( { "arguments" : [ { "type": "binary", "description": "Blob of data that will be provided to Channel constructor.", "name": "arguments" } ] } )"); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Gateway::AddBuildInCommands(json& interface, bool isDevice) { interface["commands"].push_back(json{ {"name", isDevice ? "Close" : "TurnOff"}, {"id", static_cast>(Command::Close) }, {"arguments", json::array()} }); if (isDevice) interface["commands"].push_back(json{ {"name", "Set UpdateDelayJitter"}, {"description", "Set delay between receiving function calls."}, {"id", static_cast>(Command::UpdateJitter) }, {"arguments", { {{"type", "float"}, {"name", "Min"}, {"description", "Minimal delay in seconds"}, {"min", 0.03}}, {{"type", "float"}, {"name", "Max"}, {"description", "Maximal delay in seconds. "}, {"min", 0.03}} }} }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Gateway::RunCommand(ByteView commandWithArguments) { auto pin = m_Gateway.lock(); if (!pin) return; switch (static_cast(commandWithArguments.Read>())) { case Command::Close: pin->Close(); break; case Command::CreateRoute: { pin->CreateRoute(commandWithArguments); auto [ridStr, didStr, isNbr] = commandWithArguments.Read(); ReAddRoute(ridStr, didStr, isNbr); break; } case Command::RemoveRoute: { pin->RemoveRoute(commandWithArguments); ReRemoveRoute(commandWithArguments.Read()); break; } case Command::ClearNetwork: { // Clear real gateway pin->Reset(); // clear profiler view { auto profiler = m_Owner.lock(); auto profile = profiler->Get(); profile.m_Gateway.Reset(); } break; } default: break; } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Gateway::PerformCreateCommand(json const& jCommandElement)//GateRelay& gate, std::uint16_t commandId, ByteView arguments) { auto pin = m_Gateway.lock(); if (!pin) throw std::runtime_error{ "Cannot lock gateway" }; auto& gate = *pin; auto command = base64::decode(jCommandElement["Command"]["ByteForm"].get()); auto arguments = ByteView{ command }; auto commandId = arguments.Read(); auto profiler = m_Owner.lock(); // bad code, refactor. auto createCommandIt = std::find_if(m_CreateCommands.cbegin(), m_CreateCommands.cend(), [commandId](auto const& cc) {return cc.m_Id == commandId; }); if (createCommandIt == m_CreateCommands.cend()) return; auto createCommand = *createCommandIt; if (createCommand.m_IsDevice) { ByteVector updatedArguments; //createCommand. auto binder = profiler->GetBinderTo(createCommand.m_Hash); if (binder) { auto connector = gate.GetConnector(binder); if (!connector) throw std::runtime_error{ "Connector for requested peripheral is closed" }; updatedArguments = connector->PeripheralCreationCommand(ByteVector::Create(RouteId{ gate.GetAgentId(), DeviceId(m_LastDeviceId + 1) }), arguments, FSecure::Utils::IsProcess64bit()); arguments = updatedArguments; } auto device = gate.CreateAndAttachDevice(++m_LastDeviceId, createCommand.m_Hash, createCommand.m_IsNegotiableChannel, arguments); // TODO refactor - seriously bad code CreateAndAttach calls ReAddChannel / ReAddDevice and here we search for it Device* deviceView; if (device->IsChannel()) deviceView = profiler->Get().m_Gateway.m_Channels.Find(device->GetDid()); else deviceView = profiler->Get().m_Gateway.m_Peripherals.Find(device->GetDid()); if (deviceView) deviceView->m_StartupArguments = jCommandElement["Command"]; } else { gate.TurnOnConnector(createCommand.m_Hash, arguments); auto connectorView = profiler->Get().m_Gateway.m_Connectors.Find(createCommand.m_Hash); if (connectorView) connectorView->m_StartupArguments = jCommandElement["Command"]; } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Gateway::ReTurnOnConnector(HashT typeNameHash, std::shared_ptr connector) { m_Connectors.Add(typeNameHash, Connector{ m_Owner, typeNameHash, connector }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Gateway::ReTurnOffConnector(HashT connectorNameHash) { m_Connectors.Remove(connectorNameHash); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Gateway::ReDeleteChannel(DeviceId iidOfDeviceToDetach) { m_Channels.Remove(iidOfDeviceToDetach); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Gateway::ReDeletePeripheral(DeviceId iidOfDeviceToDetach) { m_Peripherals.Remove(iidOfDeviceToDetach); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Agent* FSecure::C3::Core::Profiler::Gateway::ReAddAgent(AgentId agentId, BuildId buildId, FSecure::Crypto::PublicKey encryptionKey, bool isBanned, int32_t lastSeen, HostInfo hostInfo) { auto build = m_AgentBuilds.find(buildId); if (build == m_AgentBuilds.cend()) throw std::runtime_error{ "Tried to add agent with unknown buildId" }; if (build->second.m_IsBanned) return nullptr; auto agent = m_Agents.Add(agentId, Agent{ m_Owner, agentId, buildId, encryptionKey, isBanned, lastSeen, build->second.m_IsX64, std::move(hostInfo) }); agent->AddScheduledDevice(0u, build->second.m_StartupCmd); return agent; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Agent* FSecure::C3::Core::Profiler::Gateway::ReAddRemoteAgent(RouteId childRouteId, BuildId buildId, FSecure::Crypto::PublicKey encryptionKey, RouteId ridOfConectionPlace, HashT childGrcHash, int32_t lastSeen, HostInfo hostInfo) { // add agent auto newAgent = ReAddAgent(childRouteId.GetAgentId(), buildId, encryptionKey, false, lastSeen, std::move(hostInfo)); // TODO check if is banned // add routes Relay* current = this; while (current->m_Id != ridOfConectionPlace.GetAgentId()) { DeviceId outDevice = current->FindDirectionDevice(ridOfConectionPlace.GetAgentId()); current->ReAddRoute(childRouteId, outDevice, false); current = FindNeighborOnDevice(*current, outDevice); } // add route to neighboring device current->ReAddRoute(childRouteId, ridOfConectionPlace.GetInterfaceId(), true); // add return channel to new agent newAgent->ReAddChannel(childRouteId.GetInterfaceId(), childGrcHash, true); return newAgent; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Relay::UpdateFromNegotiationChannel(DeviceId negotiationDid, DeviceId newDeviceId, std::string newInputId, std::string newOutputId) { auto oldDeviceProfile = m_Channels.Find(negotiationDid); auto newDeviceProfile = m_Channels.Find(newDeviceId); if (oldDeviceProfile && newDeviceProfile) { newDeviceProfile->m_StartupArguments = oldDeviceProfile->m_StartupArguments; newDeviceProfile->m_StartupArguments["arguments"][0] = { { {"name", "Input ID"}, {"type", "string"}, {"value", newInputId} }, { {"name", "Output ID"}, {"type", "string"}, {"value", newOutputId} } }; auto oldDeviceCmd = base64::decode(oldDeviceProfile->m_StartupArguments["ByteForm"].get()); auto oldDeviceCmdReadView = ByteView{ oldDeviceCmd }; auto [cmdId, uniqueId] = oldDeviceCmdReadView.Read(); auto newDeviceCmd = ByteVector{}.Write(cmdId, newInputId, newOutputId).Concat(oldDeviceCmdReadView); newDeviceProfile->m_StartupArguments["ByteForm"] = base64::encode(newDeviceCmd); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Agent* FSecure::C3::Core::Profiler::Gateway::FindNeighborOnDevice(Relay& relay, DeviceId did) { auto& routes = relay.m_Routes.GetUnderlyingContainer(); auto it = std::find_if(routes.begin(), routes.end(), [&](auto& e) { return e.m_IsNeighbour && e.m_OutgoingDevice == did; }); if (it == routes.end()) throw std::logic_error{ "There is no route from provided agent" }; auto ret = m_Agents.Find(it->m_Id.GetAgentId()); if (!ret) throw std::logic_error{ "There is no route from provided agent" }; return ret; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Gateway::UpdateRouteTimestamps(AgentId agentId, int32_t timestamp) { Agent* agent = m_Agents.Find(agentId); if (!agent) throw std::runtime_error{ "Agent not found" }; for (auto e : GetPathFromAgent(agent)) e->m_LastSeen = timestamp; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Agent* FSecure::C3::Core::Profiler::Gateway::FindGatewaySideAgent(Agent* agent) { auto& channels = agent->m_Channels.GetUnderlyingContainer(); auto grcIt = std::find_if(channels.begin(), channels.end(), [](auto& e) {return e.m_IsReturnChannel; }); if (grcIt == channels.end()) throw std::runtime_error{ "GRC not found" }; for (auto& current : m_Agents.GetUnderlyingContainer()) { for (auto& route : current.m_Routes.GetUnderlyingContainer()) { if (route.m_IsNeighbour && route.m_Id.GetAgentId() == agent->m_Id && route.m_Id.GetInterfaceId() == grcIt->m_Id) return ¤t; } } return nullptr; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::vector FSecure::C3::Core::Profiler::Gateway::GetPathFromAgent(Agent* agent) { std::vector ret; auto depthLimit = 128; while (agent && agent->m_Id != m_Id) { if (!(--depthLimit)) return {}; ret.push_back(agent); agent = FindGatewaySideAgent(agent); } return ret; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool FSecure::C3::Core::Profiler::Gateway::ConnectionExist(AgentId agentId) { if (auto path = GetPathFromAgent(m_Agents.Find(agentId)); !path.empty()) for (auto& e : m_Routes.GetUnderlyingContainer()) if (e.m_IsNeighbour && e.m_Id.GetAgentId() == path.back()->m_Id) return true; return false; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Gateway::AddAgentBuild(BuildId bid, BuildProperties properties) { auto emplaced = m_AgentBuilds.emplace(bid, properties); if (!emplaced.second) throw std::logic_error{ "Tried to add new build with existing buildId" }; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Gateway::ConditionalUpdateChannelParameters(RouteId connectionPlace) { try { // parent is known Relay* parent = connectionPlace.GetAgentId() == m_Id ? static_cast(this) : m_Agents.Find(connectionPlace.GetAgentId()); if (!parent) return; auto parentChannel = parent->m_Channels.Find(connectionPlace.GetInterfaceId()); if (!parentChannel || parentChannel->m_StartupArguments.is_null()) return; Agent* agent = FindNeighborOnDevice(*parent, connectionPlace.GetInterfaceId()); auto agentChannel = agent->FindGrc(); if (!agentChannel) return; // Channel does not have startup arguments. if (!agentChannel->m_StartupArguments.is_null()) return; // build started with neg channel auto buildIterator = m_AgentBuilds.find(agent->m_BuildId); if (buildIterator == m_AgentBuilds.end() || buildIterator->second.m_StartupCmd["command"].get().find("AddNegotiationChannel") == std::string::npos) return; // update json startupArguments = { {"arguments", json::array()} }; startupArguments["arguments"][0][0] = parentChannel->m_StartupArguments["arguments"][0][1]; startupArguments["arguments"][0][1] = parentChannel->m_StartupArguments["arguments"][0][0]; for (auto i = 1u; i < buildIterator->second.m_StartupCmd["arguments"].size(); ++i) startupArguments["arguments"][i] = buildIterator->second.m_StartupCmd["arguments"][i]; agentChannel->m_StartupArguments = startupArguments; } catch (const std::exception&) { return; } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Gateway::Reset() { m_Agents.Clear(); m_Connectors.Clear(); m_Peripherals.Clear(); m_Channels.Clear(); m_Routes.Clear(); m_AgentBuilds.clear(); m_LastDeviceId = 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Device::RunCommand(ByteView commandWithArguments) { } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Device::Device(std::weak_ptr owner, Id id, HashT typeHash) : ProfileElement(owner) , m_Id(id) , m_TypeHash(typeHash) { if (auto ptrCh = InterfaceFactory::Instance().GetInterfaceData(m_TypeHash); ptrCh) m_Jitter = ptrCh->m_StartupJitter; else if (auto ptrP = InterfaceFactory::Instance().GetInterfaceData(m_TypeHash); ptrP) m_Jitter = ptrP->m_StartupJitter; else throw std::runtime_error("Type hash does is not device: " + std::to_string(m_TypeHash)); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Route::Route(std::weak_ptr owner, RouteId id, Device::Id outgoingDeviceId, bool isNeighbour) : ProfileElement(owner) , m_OutgoingDevice(outgoingDeviceId) , m_Id(id) , m_IsNeighbour(isNeighbour) { } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// json FSecure::C3::Core::Profiler::Route::CreateProfileSnapshot() const { // Construct profile. json profile = { { "outgoingInterface", m_OutgoingDevice.ToString() }, { "destinationAgent", m_Id.GetAgentId().ToString() }, { "receivingInterface", m_Id.GetInterfaceId().ToString() }, { "isNeighbour", m_IsNeighbour}, }; // Add state indicators if needed. if (!m_ErrorState.empty()) profile["error"] = m_ErrorState; // Return profile. return profile; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Route::RunCommand(ByteView commandWithArguments) { } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Relay::Relay(std::weak_ptr owner, AgentId agentId, BuildId buildId, int32_t lastSeen) : ProfileElement(owner) , m_Id(agentId) , m_BuildId(buildId) , m_LastSeen(lastSeen) { } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Relay::ParseAndRunCommand(json const& jCommandElement) noexcept(false) { throw std::runtime_error("Relay::ParseAndRunCommand should be overriden"); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Relay::RunCommand(ByteView commandWithArguments) { throw std::runtime_error("Relay::RunCommand should be overriden"); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Channel* FSecure::C3::Core::Profiler::Relay::ReAddChannel(Device::Id did, HashT typeNameHash, bool isReturnChannel /*= false*/, bool isNegotiationChannel /*= false*/) { auto ret = m_Channels.Add(did, Channel{ m_Owner, did, typeNameHash, isReturnChannel, isNegotiationChannel }); return ret; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Device* FSecure::C3::Core::Profiler::Relay::ReAddPeripheral(Device::Id did, HashT typeNameHash) { auto ret = m_Peripherals.Add(did, Device{ m_Owner, did, typeNameHash }); return ret; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Relay::ReAddRoute(RouteId receivingRid, DeviceId outgoingInterface, bool isNeighbour) { m_Routes.Add(receivingRid, Route{ m_Owner, receivingRid, outgoingInterface, isNeighbour }); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Relay::ReRemoveRoute(RouteId rid) { m_Routes.Remove(rid); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::DeviceId FSecure::C3::Core::Profiler::Relay::FindDirectionDevice(AgentId aid) { auto& routes = m_Routes.GetUnderlyingContainer(); auto it = std::find_if(routes.begin(), routes.end(), [&](auto& e) {return e.m_Id.GetAgentId() == aid; }); if (it == routes.end()) throw std::logic_error{ "There is no route to provided agent" }; return it->m_OutgoingDevice; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void FSecure::C3::Core::Profiler::Gateway::Connector::RunCommand(ByteView commandWithArguments) { if (auto pin = m_Connector.lock(); pin) pin->RunCommand(commandWithArguments); else throw std::runtime_error{ "Connector cannot be obtained" }; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Gateway::Connector::Connector(std::weak_ptr owner, Id id, std::shared_ptr connector) : ProfileElement(owner) , m_Id(id) , m_Connector(connector) { } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Profile::Profile(Gateway& gateway) : m_Gateway(gateway) , m_Lock(m_Mutex) { } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// json FSecure::C3::Core::Profiler::ProfileElement::CreateProfileSnapshot() const { // Start with empty JSON. json profile; if (!m_ErrorState.empty()) profile["error"] = m_ErrorState; // Done. return profile; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::ProfileElement::ProfileElement(std::weak_ptr owner) : m_Owner(owner) { } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// json FSecure::C3::Core::Profiler::Gateway::Connector::CreateProfileSnapshot() const { auto profile = __super::CreateProfileSnapshot(); profile["iId"] = Identifier(m_Id).ToString(); profile["type"] = m_Id; profile["startupCommand"] = m_StartupArguments; if (auto lock = m_Connector.lock(); lock && !lock->GetErrorStatus().empty()) profile["error"] = lock->GetErrorStatus(); return profile; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// json FSecure::C3::Core::Profiler::Device::CreateProfileSnapshot() const { auto profile = __super::CreateProfileSnapshot(); profile["iId"] = m_Id.ToString(); profile["type"] = m_TypeHash; profile["startupCommand"] = m_StartupArguments; profile["jitter"] = { FSecure::Utils::DoubleSeconds(m_Jitter.first).count(), FSecure::Utils::DoubleSeconds(m_Jitter.second).count() }; // get error here. return profile; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::Channel::Channel(std::weak_ptr owner, Id id, HashT typeHash, bool isReturnChannel /*= false*/, bool isNegotiationChannel /*= false*/) : Device(owner, id, typeHash) , m_IsReturnChannel(isReturnChannel) , m_IsNegotiationChannel(isNegotiationChannel) { } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// json FSecure::C3::Core::Profiler::Channel::CreateProfileSnapshot() const { auto profile = __super::CreateProfileSnapshot(); profile["isReturnChannel"] = m_IsReturnChannel; profile["isNegotiationChannel"] = m_IsNegotiationChannel; return profile; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FSecure::C3::Core::Profiler::SnapshotProxy::SnapshotProxy(Profiler& profiler) : m_Profiler(profiler) { } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool FSecure::C3::Core::Profiler::SnapshotProxy::CheckUpdates() { m_CurrentSnapshot = m_Profiler.Get().m_Gateway.CreateProfileSnapshot(); auto currentHash = std::hash{}(m_CurrentSnapshot); if (m_PreviousHash && currentHash == *m_PreviousHash) return false; m_PreviousHash = currentHash; return true; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// json const& FSecure::C3::Core::Profiler::SnapshotProxy::GetSnapshot() const { return m_CurrentSnapshot; }