mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
260 lines
7.7 KiB
C++
260 lines
7.7 KiB
C++
/*************************************************************************
|
|
* UrBackup - Client/Server backup system
|
|
* Copyright (C) 2011-2016 Martin Raiber
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
**************************************************************************/
|
|
|
|
#include "action_header.h"
|
|
#include <algorithm>
|
|
#include "../../cryptoplugin/ICryptoFactory.h"
|
|
#include "../server_settings.h"
|
|
|
|
extern ICryptoFactory *crypto_fak;
|
|
extern std::string server_token;
|
|
|
|
namespace
|
|
{
|
|
bool verify_signature(const std::string& exe_extension, const std::string& basename)
|
|
{
|
|
if(crypto_fak==NULL)
|
|
return false;
|
|
|
|
#ifdef _WIN32
|
|
std::string pubkey_fn="urbackup_ecdsa409k1.pub";
|
|
#else
|
|
std::string pubkey_fn="urbackup/urbackup_ecdsa409k1.pub";
|
|
#endif
|
|
std::string p_pubkey_fn = Server->getServerParameter("urbackup_public_key");
|
|
|
|
if(!p_pubkey_fn.empty())
|
|
{
|
|
pubkey_fn = p_pubkey_fn;
|
|
}
|
|
|
|
return crypto_fak->verifyFile(pubkey_fn, "urbackup/"+basename+"."+ exe_extension, "urbackup/"+ basename+".sig2");
|
|
}
|
|
|
|
std::string constructClientSettings(Helper& helper, int clientid, const std::string& clientname, const std::string& authkey, const std::string& access_keys)
|
|
{
|
|
ServerSettings settings(helper.getDatabase(), clientid);
|
|
SSettings *settingsptr=settings.getSettings();
|
|
|
|
std::string ret="\r\n";
|
|
ret+="internet_mode_enabled="+convert(settingsptr->internet_mode_enabled)+"\r\n";
|
|
ret+="internet_server="+settingsptr->internet_server+"\r\n";
|
|
ret+="internet_server_port="+convert(settingsptr->internet_server_port)+"\r\n";
|
|
ret+="internet_authkey="+(authkey.empty() ? settingsptr->internet_authkey : authkey ) +"\r\n";
|
|
if(!clientname.empty())
|
|
{
|
|
ret+="computername="+clientname+"\r\n";
|
|
}
|
|
if (!access_keys.empty())
|
|
{
|
|
ret += "access_keys=" + access_keys + "\r\n";
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool replaceToken(const std::string end_token, const std::string& repl, std::string& data, size_t& offset)
|
|
{
|
|
for(size_t i=offset;i<data.size();++i)
|
|
{
|
|
if(next(data, i, end_token) )
|
|
{
|
|
if(i-offset>repl.size())
|
|
{
|
|
data.replace(data.begin()+offset, data.begin()+offset+repl.size(), repl.begin(), repl.end());
|
|
offset=i+end_token.size();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Cannot replace, because data is too large", LL_ERROR);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool replaceStrings(Helper& helper, const std::string& settings, std::string& data)
|
|
{
|
|
const std::string settings_start_token="#48692563-17e4-4ccb-a078-f14372fdbe20";
|
|
const std::string settings_end_token="#6e7f6ba0-8478-4946-b70a-f1c7e83d28cc";
|
|
|
|
const std::string ident_start_token="#17460620-769b-4add-85aa-a764efe84ab7";
|
|
const std::string ident_end_token="#569d42d2-1b40-4745-a426-e86a577c7f1a";
|
|
|
|
bool replaced_settings=false;
|
|
bool replaced_ident=false;
|
|
|
|
for(size_t i=0;i<data.size();++i)
|
|
{
|
|
if(next(data, i, settings_start_token))
|
|
{
|
|
i+=settings_start_token.size();
|
|
|
|
if(!replaceToken(settings_end_token, settings, data, i))
|
|
{
|
|
return false;
|
|
}
|
|
replaced_settings=true;
|
|
}
|
|
|
|
if(next(data, i, ident_start_token))
|
|
{
|
|
i+=ident_start_token.size();
|
|
|
|
if(!replaceToken(ident_end_token, "\r\n"+helper.getStrippedServerIdentity()+"\r\n", data, i))
|
|
{
|
|
return false;
|
|
}
|
|
replaced_ident=true;
|
|
}
|
|
|
|
if( replaced_ident && replaced_settings)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ACTION_IMPL(download_client)
|
|
{
|
|
Helper helper(tid, &GET, &PARAMS);
|
|
|
|
SUser *session=helper.getSession();
|
|
if(session!=NULL && session->id==SESSION_ID_INVALID) return;
|
|
|
|
bool all_client_rights;
|
|
std::vector<int> clientids = helper.clientRights(RIGHT_SETTINGS, all_client_rights);
|
|
|
|
bool all_browse_backups;
|
|
std::vector<int> browse_backups_rights = helper.clientRights(RIGHT_BROWSE_BACKUPS, all_browse_backups);
|
|
|
|
std::string authkey = GET["authkey"];
|
|
|
|
std::string errstr;
|
|
if( !authkey.empty()
|
|
|| (session!=NULL && (all_client_rights || !clientids.empty()) ) )
|
|
{
|
|
helper.releaseAll();
|
|
session = NULL;
|
|
|
|
int clientid=watoi(GET["clientid"]);
|
|
|
|
if(!authkey.empty() || all_client_rights ||
|
|
std::find(clientids.begin(), clientids.end(), clientid)!=clientids.end() )
|
|
{
|
|
std::string os = GET["os"];
|
|
|
|
std::string exe_extension = "exe";
|
|
std::string basename = "UrBackupUpdate";
|
|
|
|
if (os == "osx" || os=="mac")
|
|
{
|
|
exe_extension = "sh";
|
|
basename = "UrBackupUpdateMac";
|
|
}
|
|
else if (os == "linux")
|
|
{
|
|
exe_extension = "sh";
|
|
basename = "UrBackupUpdateLinux";
|
|
}
|
|
|
|
Server->Log("Verifying "+ basename +"."+exe_extension+" signature...", LL_INFO);
|
|
if(verify_signature(exe_extension, basename))
|
|
{
|
|
IQuery *q=helper.getDatabase()->Prepare("SELECT name FROM clients WHERE id=?");
|
|
q->Bind(clientid);
|
|
db_results res=q->Read();
|
|
q->Reset();
|
|
|
|
std::string clientname;
|
|
if(!res.empty())
|
|
{
|
|
clientname=(res[0]["name"]);
|
|
}
|
|
|
|
std::string access_keys;
|
|
|
|
if ( all_client_rights || std::find(clientids.begin(), clientids.end(), clientid) != clientids.end() )
|
|
{
|
|
if (all_browse_backups
|
|
|| std::find(browse_backups_rights.begin(), browse_backups_rights.end(), clientid) != browse_backups_rights.end() )
|
|
{
|
|
if (os == "linux" || os == "osx" || os == "mac")
|
|
{
|
|
//There is only ~4KB available space. Add only root for now
|
|
IQuery* q_root_token = helper.getDatabase()->Prepare("SELECT token FROM user_tokens WHERE username='root' AND tgroup IS NULL AND clientid = ? ORDER BY created DESC");
|
|
q_root_token->Bind(clientid);
|
|
db_results res_root_token = q_root_token->Read();
|
|
q_root_token->Reset();
|
|
|
|
if (!res_root_token.empty())
|
|
{
|
|
access_keys = "uroot:" + res_root_token[0]["token"];
|
|
}
|
|
}
|
|
|
|
ServerSettings settings(helper.getDatabase(), clientid);
|
|
std::string client_access_key = settings.getSettings()->client_access_key;
|
|
if (!client_access_key.empty())
|
|
{
|
|
if (!access_keys.empty())
|
|
{
|
|
access_keys += ";";
|
|
}
|
|
|
|
access_keys += "t" + server_token + ":" + client_access_key;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string data=getFile("urbackup/"+basename+"."+ exe_extension);
|
|
if( replaceStrings(helper, constructClientSettings(helper, clientid, clientname, authkey, access_keys), data) )
|
|
{
|
|
Server->setContentType(tid, "application/octet-stream");
|
|
Server->addHeader(tid, "Content-Disposition: attachment; filename=\"UrBackup Client ("+clientname+")."+exe_extension+"\"");
|
|
Server->addHeader(tid, "Content-Length: "+convert(data.size()) );
|
|
Server->WriteRaw(tid, data.c_str(), data.size(), false);
|
|
}
|
|
else
|
|
{
|
|
errstr="Replacing data in install file failed";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errstr="Signature verification failed";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errstr="No right to download client";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errstr="No right to download any client";
|
|
}
|
|
|
|
if(!errstr.empty())
|
|
{
|
|
Server->Log(errstr, LL_ERROR);
|
|
helper.Write("ERROR: "+errstr, false);
|
|
}
|
|
} |