mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
Implement ransomware canary
This commit is contained in:
parent
2740774c22
commit
ad387460e8
BIN
urbackup/canary.docx
Normal file
BIN
urbackup/canary.docx
Normal file
Binary file not shown.
@ -42,6 +42,7 @@
|
||||
#include "../urbackupcommon/CompressedPipe2.h"
|
||||
#include "../urbackupcommon/CompressedPipeZstd.h"
|
||||
#include "../urbackupcommon/InternetServicePipe2.h"
|
||||
#include "RansomwareCanary.h"
|
||||
|
||||
#include <memory.h>
|
||||
#include <stdlib.h>
|
||||
@ -1861,6 +1862,17 @@ void ClientConnector::updateSettings(const std::string &pData, const std::string
|
||||
db->destroyQuery(q);
|
||||
}
|
||||
|
||||
std::string ransomware_canary_paths;
|
||||
std::string server_token;
|
||||
if (new_settings->getValue("ransomware_canary_paths", &ransomware_canary_paths)
|
||||
&& !ransomware_canary_paths.empty()
|
||||
&& new_settings->getValue("server_token", &server_token)
|
||||
&& !server_token.empty())
|
||||
{
|
||||
setupRansomwareCanaries(ransomware_canary_paths, server_token,
|
||||
facet_id, group_offset);
|
||||
}
|
||||
|
||||
std::auto_ptr<ISettingsReader> curr_settings(Server->createFileSettingsReader(settings_fn));
|
||||
|
||||
std::vector<std::string> critical_settings;
|
||||
@ -3391,8 +3403,8 @@ bool ClientConnector::calculateFilehashesOnClient(const std::string& clientsubna
|
||||
|
||||
bool ClientConnector::getBackupDest(const std::string& clientsubname, std::string& dest, int facet_id)
|
||||
{
|
||||
dest = "raw-file://D:\\tmp\\btrfs.img";
|
||||
return true;
|
||||
/*dest = "raw-file://D:\\tmp\\btrfs.img";
|
||||
return true;*/
|
||||
|
||||
if (facet_id == 0)
|
||||
return false;
|
||||
|
||||
345
urbackupclient/RansomwareCanary.cpp
Normal file
345
urbackupclient/RansomwareCanary.cpp
Normal file
@ -0,0 +1,345 @@
|
||||
#include "RansomwareCanary.h"
|
||||
#include <thread>
|
||||
#include "../stringtools.h"
|
||||
#include "../urbackupcommon/os_functions.h"
|
||||
#include "../Interface/Server.h"
|
||||
#include "../Interface/File.h"
|
||||
#include "../urbackupcommon/glob.h"
|
||||
#include "../common/miniz.h"
|
||||
#include "clientdao.h"
|
||||
#include "database.h"
|
||||
#ifdef _WIN32
|
||||
#include <aclapi.h>
|
||||
#endif
|
||||
|
||||
struct SOwner
|
||||
{
|
||||
bool has_owner = false;
|
||||
#ifdef _WIN32
|
||||
PSID Owner = nullptr;
|
||||
PSECURITY_DESCRIPTOR sec_d = nullptr;
|
||||
|
||||
~SOwner() {
|
||||
if (sec_d != nullptr)
|
||||
LocalFree(sec_d);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
static bool getOwner(const std::string& fn, SOwner& owner)
|
||||
{
|
||||
PSID newOwner = nullptr;
|
||||
PSECURITY_DESCRIPTOR new_sec_d = nullptr;
|
||||
DWORD rc = GetNamedSecurityInfoW(Server->ConvertToWchar(os_file_prefix(fn)).c_str(),
|
||||
SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &newOwner, NULL, NULL,
|
||||
NULL, &new_sec_d);
|
||||
|
||||
if (rc == ERROR_SUCCESS)
|
||||
{
|
||||
if (owner.sec_d != nullptr)
|
||||
LocalFree(owner.sec_d);
|
||||
|
||||
owner.Owner = newOwner;
|
||||
owner.sec_d = new_sec_d;
|
||||
owner.has_owner = true;
|
||||
}
|
||||
else if (new_sec_d != nullptr)
|
||||
{
|
||||
LocalFree(new_sec_d);
|
||||
}
|
||||
return rc == ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
HRESULT ModifyPrivilege(
|
||||
IN LPCTSTR szPrivilege,
|
||||
IN BOOL fEnable)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
TOKEN_PRIVILEGES NewState;
|
||||
LUID luid;
|
||||
HANDLE hToken = NULL;
|
||||
|
||||
if (!OpenProcessToken(GetCurrentProcess(),
|
||||
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
||||
&hToken))
|
||||
{
|
||||
return ERROR_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
if (!LookupPrivilegeValue(NULL,
|
||||
szPrivilege,
|
||||
&luid))
|
||||
{
|
||||
CloseHandle(hToken);
|
||||
return ERROR_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
|
||||
NewState.PrivilegeCount = 1;
|
||||
NewState.Privileges[0].Luid = luid;
|
||||
NewState.Privileges[0].Attributes =
|
||||
(fEnable ? SE_PRIVILEGE_ENABLED : 0);
|
||||
|
||||
if (!AdjustTokenPrivileges(hToken,
|
||||
FALSE,
|
||||
&NewState,
|
||||
0,
|
||||
NULL,
|
||||
NULL))
|
||||
{
|
||||
hr = ERROR_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
CloseHandle(hToken);
|
||||
|
||||
return hr;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool setOwner(const std::string& fn, const SOwner& owner)
|
||||
{
|
||||
if (!owner.has_owner || owner.Owner == nullptr)
|
||||
return false;
|
||||
|
||||
HRESULT hr = ModifyPrivilege(SE_TAKE_OWNERSHIP_NAME, TRUE);
|
||||
if (!SUCCEEDED(hr))
|
||||
return false;
|
||||
|
||||
std::wstring wfn = Server->ConvertToWchar(os_file_prefix(fn)).c_str();
|
||||
DWORD rc = SetNamedSecurityInfoW(&wfn[0],
|
||||
SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, owner.Owner, NULL, NULL,
|
||||
NULL);
|
||||
|
||||
return rc == ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static size_t zipWrite(void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)
|
||||
{
|
||||
IFsFile* out_file = reinterpret_cast<IFsFile*>(pOpaque);
|
||||
return out_file->Write(file_ofs, reinterpret_cast<const char*>(pBuf), n);
|
||||
}
|
||||
|
||||
static void setupRansomwareCanaryFile(const std::string& curr_path, const std::string& fn_prefix,
|
||||
const std::string& server_token, SOwner& owner)
|
||||
{
|
||||
std::string out_fn = os_file_prefix(curr_path + fn_prefix + "-" + server_token + ".docx");
|
||||
if (os_get_file_type(out_fn) != 0)
|
||||
{
|
||||
Server->Log("Ransomware canary at \"" + out_fn + "\" already exists");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!curr_path.empty())
|
||||
{
|
||||
std::vector<SFile> sib_files = getFiles(curr_path.substr(0, curr_path.size()-1));
|
||||
|
||||
for (SFile& sib_file : sib_files)
|
||||
{
|
||||
if (getOwner(curr_path + sib_file.name, owner))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string canary_path = Server->getServerWorkingDir() + os_file_sep() +
|
||||
"urbackup" + os_file_sep() + "canary.docx";
|
||||
|
||||
mz_zip_archive canary_doc = {};
|
||||
if (!mz_zip_reader_init_file(&canary_doc, canary_path.c_str(), 0))
|
||||
{
|
||||
Server->Log("Error opening canary.docx file for extraction", LL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ScopedDeleteFn delete_fn(out_fn+".new");
|
||||
std::unique_ptr<IFsFile> out_file(Server->openFile(out_fn+".new", MODE_WRITE));
|
||||
|
||||
if (out_file.get() == nullptr)
|
||||
{
|
||||
Server->Log("Error opening canary out at \"" + out_fn + ".new\". " + os_last_error_str(), LL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
mz_zip_archive canary_doc_out = {};
|
||||
canary_doc_out.m_pWrite = zipWrite;
|
||||
canary_doc_out.m_pIO_opaque = out_file.get();
|
||||
if (!mz_zip_writer_init(&canary_doc_out, 0))
|
||||
{
|
||||
Server->Log("Error init zip at \"" + out_fn + ".new\"", LL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0, num_files = mz_zip_reader_get_num_files(&canary_doc); i < num_files; ++i)
|
||||
{
|
||||
std::vector<char> buf(100);
|
||||
mz_zip_reader_get_filename(&canary_doc, i, buf.data(), buf.size());
|
||||
std::string curr_fn = buf.data();
|
||||
|
||||
if (curr_fn == "word/document.xml")
|
||||
{
|
||||
size_t fsize;
|
||||
void* buf = mz_zip_reader_extract_to_heap(&canary_doc, i, &fsize, 0);
|
||||
|
||||
if (buf == nullptr)
|
||||
{
|
||||
Server->Log("Error extracting word/document.xml", LL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string bdata(reinterpret_cast<char*>(buf), fsize);
|
||||
|
||||
std::string uuid;
|
||||
uuid.resize(16);
|
||||
Server->secureRandomFill(&uuid[0], 16);
|
||||
|
||||
bdata = greplace("$RAND$", bytesToHex(uuid), bdata);
|
||||
|
||||
if (!mz_zip_writer_add_mem(&canary_doc_out, curr_fn.c_str(),
|
||||
bdata.data(), bdata.size(), MZ_DEFAULT_COMPRESSION))
|
||||
{
|
||||
Server->Log("Error adding modified word/document.xml to doc", LL_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!mz_zip_writer_add_from_zip_reader(&canary_doc_out, &canary_doc, i))
|
||||
{
|
||||
Server->Log("Error adding file \"" + curr_fn + "\" to canary out doc", LL_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete_fn.release();
|
||||
out_file->Sync();
|
||||
out_file.reset();
|
||||
|
||||
if (!os_rename_file(out_fn + ".new", out_fn))
|
||||
{
|
||||
Server->Log("Error renaming canary " + out_fn + ".new", LL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
setOwner(out_fn, owner);
|
||||
}
|
||||
|
||||
static std::string getBackupPath(const std::string& name, int facet, int group)
|
||||
{
|
||||
ClientDAO clientdao(Server->getDatabase(Server->getThreadID(), URBACKUPDB_CLIENT));
|
||||
|
||||
std::vector<SBackupDir> backupdirs = clientdao.getBackupDirs();
|
||||
|
||||
for (SBackupDir& bd: backupdirs)
|
||||
{
|
||||
if (bd.facet != facet || bd.group != group)
|
||||
continue;
|
||||
|
||||
if (bd.tname == name)
|
||||
return bd.path;
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
static void setupRansomwareCanariesPath(const std::string& curr_path, const std::vector<std::string> path_components, size_t idx,
|
||||
const std::string& server_token, int facet, int group, SOwner& owner)
|
||||
{
|
||||
if (idx >= path_components.size())
|
||||
return;
|
||||
|
||||
bool last_comp = idx + 1 >= path_components.size();
|
||||
std::string comp = path_components[idx];
|
||||
|
||||
bool create = false;
|
||||
|
||||
if (comp[0] == '^'
|
||||
&& comp.find("*") == std::string::npos)
|
||||
{
|
||||
create = true;
|
||||
comp = comp.substr(1);
|
||||
}
|
||||
|
||||
if (idx == 0)
|
||||
{
|
||||
comp = getBackupPath(comp, facet, group);
|
||||
|
||||
if (comp.empty())
|
||||
return;
|
||||
}
|
||||
|
||||
if (!last_comp
|
||||
&& comp.find("*") != std::string::npos)
|
||||
{
|
||||
std::vector<SFile> files = getFiles(curr_path);
|
||||
for (size_t i = 0; i < files.size(); ++i)
|
||||
{
|
||||
if (files[i].isdir
|
||||
&& amatch(files[i].name.c_str(), comp.c_str()))
|
||||
{
|
||||
getOwner(curr_path + files[i].name, owner);
|
||||
|
||||
setupRansomwareCanariesPath(curr_path + files[i].name + os_file_sep(),
|
||||
path_components, idx + 1, server_token, facet, group, owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!create
|
||||
&& !last_comp)
|
||||
{
|
||||
getOwner(curr_path + comp, owner);
|
||||
}
|
||||
|
||||
if (!last_comp &&
|
||||
create &&
|
||||
!os_directory_exists(os_file_prefix(curr_path + comp)))
|
||||
{
|
||||
if (!os_create_dir(os_file_prefix(curr_path + comp)))
|
||||
{
|
||||
Server->Log("Error creating directory \"" + curr_path + comp +
|
||||
"\" for ransomware canary. " + os_last_error_str(), LL_ERROR);
|
||||
}
|
||||
}
|
||||
else if (last_comp)
|
||||
{
|
||||
setupRansomwareCanaryFile(curr_path,
|
||||
comp, server_token, owner);
|
||||
}
|
||||
|
||||
if(!last_comp)
|
||||
setupRansomwareCanariesPath(curr_path + comp + os_file_sep(), path_components,
|
||||
idx + 1, server_token, facet, group, owner);
|
||||
}
|
||||
}
|
||||
|
||||
static void setupRansomwareCanariesInt(const std::string& ransomware_canary_paths, const std::string& server_token,
|
||||
int facet, int group)
|
||||
{
|
||||
std::vector<std::string> paths;
|
||||
Tokenize(ransomware_canary_paths, paths, ";");
|
||||
|
||||
for (std::string path : paths)
|
||||
{
|
||||
std::vector<std::string> path_components;
|
||||
Tokenize(path, path_components, "/");
|
||||
|
||||
if (!path_components.empty())
|
||||
{
|
||||
SOwner owner;
|
||||
setupRansomwareCanariesPath(std::string(), path_components, 0, server_token, facet, group, owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setupRansomwareCanaries(const std::string& ransomware_canary_paths, const std::string& server_token,
|
||||
int facet, int group)
|
||||
{
|
||||
std::thread t([ransomware_canary_paths, server_token, facet, group]()
|
||||
{
|
||||
setupRansomwareCanariesInt(ransomware_canary_paths, server_token, facet, group);
|
||||
});
|
||||
|
||||
t.detach();
|
||||
}
|
||||
5
urbackupclient/RansomwareCanary.h
Normal file
5
urbackupclient/RansomwareCanary.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
void setupRansomwareCanaries(const std::string& ransomware_canary_paths,
|
||||
const std::string& server_token, int facet, int group);
|
||||
@ -68,6 +68,7 @@
|
||||
<ClCompile Include="LocalFullFileBackup.cpp" />
|
||||
<ClCompile Include="ParallelHash.cpp" />
|
||||
<ClCompile Include="PersistentOpenFiles.cpp" />
|
||||
<ClCompile Include="RansomwareCanary.cpp" />
|
||||
<ClCompile Include="RestoreDownloadThread.cpp" />
|
||||
<ClCompile Include="RestoreFiles.cpp" />
|
||||
<ClCompile Include="ServerIdentityMgr.cpp" />
|
||||
@ -128,6 +129,7 @@
|
||||
<ClInclude Include="LocalFullFileBackup.h" />
|
||||
<ClInclude Include="ParallelHash.h" />
|
||||
<ClInclude Include="PersistentOpenFiles.h" />
|
||||
<ClInclude Include="RansomwareCanary.h" />
|
||||
<ClInclude Include="RestoreDownloadThread.h" />
|
||||
<ClInclude Include="RestoreFiles.h" />
|
||||
<ClInclude Include="ServerIdentityMgr.h" />
|
||||
|
||||
@ -111,6 +111,7 @@ std::vector<std::string> getSettingsList(void)
|
||||
ret.push_back("hash_threads");
|
||||
ret.push_back("client_hash_threads");
|
||||
ret.push_back("image_compress_threads");
|
||||
ret.push_back("ransomware_canary_paths");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -160,6 +161,7 @@ std::vector<std::string> getClientMergableSettingsList()
|
||||
ret.push_back("image_letters");
|
||||
ret.push_back("vss_select_components");
|
||||
ret.push_back("archive");
|
||||
ret.push_back("ransomware_canary_paths");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -199,6 +201,7 @@ std::vector<std::string> getOnlyServerClientSettingsList(void)
|
||||
ret.push_back("alert_params");
|
||||
ret.push_back("archive");
|
||||
ret.push_back("client_settings_tray_access_pw");
|
||||
ret.push_back("ransomware_canary_paths");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@ -1933,6 +1933,7 @@ void ClientMain::sendSettings(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
s_settings += "server_token=" + server_token + "\n";
|
||||
escapeClientMessage(s_settings);
|
||||
if(!sendClientMessage("SETTINGS "+s_settings, "OK", "Sending settings to client failed", 10000))
|
||||
{
|
||||
|
||||
@ -40,6 +40,7 @@
|
||||
#include "../urbackupcommon/TreeHash.h"
|
||||
#include "../common/data.h"
|
||||
#include "PhashLoad.h"
|
||||
#include "../urbackupcommon/glob.h"
|
||||
|
||||
#ifndef NAME_MAX
|
||||
#define NAME_MAX _POSIX_NAME_MAX
|
||||
@ -865,6 +866,12 @@ bool FileBackup::doBackup()
|
||||
client_main->sendMailToAdmins("Fatal error occurred during backup", ServerLogger::getWarningLevelTextLogdata(logid));
|
||||
}
|
||||
|
||||
if (!checkRansomwareCanaries())
|
||||
{
|
||||
ServerLogger::Log(logid, "Ransomware canary check failed", LL_ERROR);
|
||||
backup_result = false;
|
||||
}
|
||||
|
||||
if((!has_early_error && !backup_result) || disk_error)
|
||||
{
|
||||
backup_result = false;
|
||||
@ -2504,6 +2511,151 @@ bool FileBackup::stopPhashDownloadThread(const std::string& async_id)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileBackup::checkRansomwareCanaryFile(const std::string& last_backuppath, const std::string& curr_path, const std::string& fn_prefix)
|
||||
{
|
||||
std::string canary_path = curr_path + fn_prefix + "-" + server_token + ".docx";
|
||||
|
||||
std::string curr_canary_path = backuppath + os_file_sep() + canary_path;
|
||||
std::string last_canary_path = last_backuppath + os_file_sep() + canary_path;
|
||||
|
||||
std::unique_ptr<IFile> lastf(Server->openFile(last_canary_path, MODE_READ));
|
||||
|
||||
if (lastf.get() == nullptr)
|
||||
return true;
|
||||
|
||||
std::unique_ptr<IFile> currf(Server->openFile(curr_canary_path, MODE_READ));
|
||||
|
||||
if (currf.get() == nullptr)
|
||||
{
|
||||
ServerLogger::Log(logid, "Ransomware canary at \"" + canary_path + "\" is now missing", LL_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<char> buf1(32768*2);
|
||||
std::vector<char> buf2(32768 * 2);
|
||||
|
||||
while (true)
|
||||
{
|
||||
_u32 read1 = lastf->Read(buf1.data(), buf1.size());
|
||||
_u32 read2 = currf->Read(buf2.data(), buf2.size());
|
||||
|
||||
if (read1 != read2)
|
||||
{
|
||||
ServerLogger::Log(logid, "Ransomware canary at \"" + canary_path + "\" has changed size", LL_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (read1 == 0)
|
||||
return true;
|
||||
|
||||
if (memcmp(buf1.data(), buf2.data(), read1) != 0)
|
||||
{
|
||||
ServerLogger::Log(logid, "Ransomware canary at \"" + canary_path + "\" has changed", LL_ERROR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FileBackup::checkRansomwareCanariesPath(const std::string& last_backuppath, const std::string& curr_path, const std::vector<std::string> path_components, size_t idx)
|
||||
{
|
||||
if (idx >= path_components.size())
|
||||
return true;
|
||||
|
||||
bool last_comp = idx + 1 >= path_components.size();
|
||||
std::string comp = path_components[idx];
|
||||
|
||||
bool create = false;
|
||||
|
||||
if (comp[0] == '^'
|
||||
&& comp.find("*") == std::string::npos)
|
||||
{
|
||||
create = true;
|
||||
comp = comp.substr(1);
|
||||
}
|
||||
|
||||
if (comp=="." || comp == "..")
|
||||
return false;
|
||||
|
||||
std::string curr_backuppath = backuppath + os_file_sep() + curr_path;
|
||||
std::string last_path = last_backuppath + os_file_sep() + curr_path;
|
||||
|
||||
if (!last_comp
|
||||
&& comp.find("*") != std::string::npos)
|
||||
{
|
||||
std::vector<SFile> files = getFiles(last_path);
|
||||
for (size_t i = 0; i < files.size(); ++i)
|
||||
{
|
||||
if (idx == 0
|
||||
&& files[i].isdir
|
||||
&& files[i].name == ".hashes")
|
||||
continue;
|
||||
|
||||
if (files[i].isdir
|
||||
&& amatch(files[i].name.c_str(), comp.c_str()))
|
||||
{
|
||||
bool b = checkRansomwareCanariesPath(last_backuppath, curr_path + files[i].name + os_file_sep(),
|
||||
path_components, idx + 1);
|
||||
if (!b)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (last_comp)
|
||||
{
|
||||
if (!checkRansomwareCanaryFile(last_backuppath, curr_path,
|
||||
comp))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!last_comp)
|
||||
{
|
||||
bool b = checkRansomwareCanariesPath(last_backuppath, curr_path + comp + os_file_sep(), path_components, idx + 1);
|
||||
if (!b)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileBackup::checkRansomwareCanariesInt(const std::string& last_backuppath, const std::string& ransomware_canary_paths)
|
||||
{
|
||||
std::vector<std::string> paths;
|
||||
Tokenize(ransomware_canary_paths, paths, ";");
|
||||
|
||||
for (std::string path : paths)
|
||||
{
|
||||
std::vector<std::string> path_components;
|
||||
Tokenize(path, path_components, "/");
|
||||
|
||||
if (!path_components.empty())
|
||||
{
|
||||
bool b = checkRansomwareCanariesPath(last_backuppath, std::string(), path_components, 0);
|
||||
if (!b)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileBackup::checkRansomwareCanaries()
|
||||
{
|
||||
if (server_settings->getSettings()->ransomware_canary_paths.empty())
|
||||
return true;
|
||||
|
||||
ServerBackupDao::SLastIncremental last = backup_dao->getLastIncrementalCompleteFileBackup(clientid, group);
|
||||
|
||||
if (!last.exists)
|
||||
return true;
|
||||
|
||||
std::string last_backuppath = server_settings->getSettings()->backupfolder + os_file_sep() + clientname + os_file_sep() + last.path;
|
||||
|
||||
return checkRansomwareCanariesInt(last_backuppath, server_settings->getSettings()->ransomware_canary_paths);
|
||||
}
|
||||
|
||||
void FileBackup::save_debug_data(const std::string& rfn, const std::string& local_hash, const std::string& remote_hash)
|
||||
{
|
||||
ServerLogger::Log(logid, "Local hash: "+local_hash+" remote hash: "+remote_hash, LL_INFO);
|
||||
|
||||
@ -250,6 +250,10 @@ protected:
|
||||
bool loadWindowsBackupComponentConfigXml(FileClient &fc);
|
||||
bool startPhashDownloadThread(const std::string& async_id);
|
||||
bool stopPhashDownloadThread(const std::string& async_id);
|
||||
bool checkRansomwareCanaries();
|
||||
bool checkRansomwareCanariesInt(const std::string& last_backuppath, const std::string& ransomware_canary_paths);
|
||||
bool checkRansomwareCanariesPath(const std::string& last_backuppath, const std::string& curr_path, const std::vector<std::string> path_components, size_t idx);
|
||||
bool checkRansomwareCanaryFile(const std::string& last_backuppath, const std::string& curr_path, const std::string& fn_prefix);
|
||||
|
||||
int group;
|
||||
bool use_tmpfiles;
|
||||
|
||||
@ -578,6 +578,8 @@ void ServerSettings::readSettingsDefault(ISettingsReader* settings_default,
|
||||
readIntClientSetting(q_get_client_setting, "client_hash_threads", &settings->client_hash_threads, false);
|
||||
settings->image_compress_threads = 0;
|
||||
readIntClientSetting(q_get_client_setting, "image_compress_threads", &settings->image_compress_threads, false);
|
||||
|
||||
readStringClientSetting(q_get_client_setting, "ransomware_canary_paths", std::string(";"), &settings->ransomware_canary_paths, false);
|
||||
}
|
||||
|
||||
void ServerSettings::readSettingsClient(ISettingsReader* settings_client, IQuery* q_get_client_setting)
|
||||
@ -701,6 +703,8 @@ void ServerSettings::readSettingsClient(ISettingsReader* settings_client, IQuery
|
||||
readIntClientSetting(q_get_client_setting, "hash_threads", &settings->hash_threads, false);
|
||||
readIntClientSetting(q_get_client_setting, "client_hash_threads", &settings->client_hash_threads, false);
|
||||
readIntClientSetting(q_get_client_setting, "image_compress_threads", &settings->image_compress_threads, false);
|
||||
|
||||
readStringClientSetting(q_get_client_setting, "ransomware_canary_paths", ";", &settings->ransomware_canary_paths, false);
|
||||
}
|
||||
|
||||
void ServerSettings::readStringClientSetting(IQuery * q_get_client_setting, int clientid, const std::string & name, const std::string & merge_sep, std::string * output, bool allow_client_value)
|
||||
|
||||
@ -150,6 +150,7 @@ struct SSettings
|
||||
int hash_threads;
|
||||
int client_hash_threads;
|
||||
int image_compress_threads;
|
||||
std::string ransomware_canary_paths;
|
||||
};
|
||||
|
||||
struct SLDAPSettings
|
||||
|
||||
@ -276,6 +276,7 @@ JSON::Object getJSONClientSettings(IDatabase *db, int t_clientid)
|
||||
SET_SETTING_INT(hash_threads);
|
||||
SET_SETTING_INT(client_hash_threads);
|
||||
SET_SETTING_INT(image_compress_threads);
|
||||
SET_SETTING_STR(ransomware_canary_paths);
|
||||
#undef SET_SETTING
|
||||
return ret;
|
||||
}
|
||||
@ -351,6 +352,7 @@ void getGeneralSettings(JSON::Object& obj, IDatabase *db, ServerSettings &settin
|
||||
SET_SETTING_DB(restore_authkey, std::string());
|
||||
SET_SETTING_DB(internet_expect_endpoint, std::string());
|
||||
SET_SETTING_DB(internet_server_bind_port, std::string());
|
||||
SET_SETTING_DB(ransomware_canary_paths, std::string());
|
||||
#undef SET_SETTING
|
||||
#undef SET_SETTING_DB
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -4178,7 +4178,8 @@ g.settings_list=[
|
||||
"download_threads",
|
||||
"hash_threads",
|
||||
"client_hash_threads",
|
||||
"image_compress_threads"
|
||||
"image_compress_threads",
|
||||
"ransomware_canary_paths"
|
||||
];
|
||||
g.general_settings_list=[
|
||||
"backupfolder",
|
||||
@ -4240,7 +4241,8 @@ g.mergable_settings_list=[
|
||||
"default_dirs",
|
||||
"image_letters",
|
||||
"vss_select_components",
|
||||
"archive"
|
||||
"archive",
|
||||
"ransomware_canary_paths"
|
||||
];
|
||||
g.client_settings_list=[
|
||||
"update_freq_incr",
|
||||
|
||||
@ -93,6 +93,16 @@
|
||||
</div>
|
||||
<div id="backup_dirs_optional_sw" style="display: inline"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label" for="ransomware_canary_paths">{tRansomware canary paths:}</label>
|
||||
<div class="col-sm-6">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="ransomware_canary_paths" value="{ransomware_canary_paths|s}"/>
|
||||
<div class="input-group-addon"><a href="help.htm#ransomware_canary_paths" target="_blank">?</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ransomware_canary_paths_sw" style="display: inline"></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user