Permalink
Browse files

Split rcon cvars to client and server

  • Loading branch information...
mbasaglia committed Mar 9, 2016
1 parent 97beb6a commit 4443f4a40346c2e7ad0e7eebd1d60c7a0011b27e
View
@@ -73,6 +73,10 @@ namespace Cvar {
// the user.
virtual OnValueChangedResult OnValueChanged(Str::StringRef newValue) = 0;
const std::string& Name() const {
return name;
}
protected:
std::string name;
@@ -47,6 +47,7 @@ cvar_t *cl_wavefilerecord;
#include "mumblelink/libmumblelink.h"
#include "qcommon/crypto.h"
#include "framework/Rcon.h"
#include "framework/Crypto.h"
#include "framework/Network.h"
#ifndef _WIN32
@@ -1428,12 +1429,70 @@ void CL_Connect_f()
Cvar_Set( "cl_currentServerIP", serverString );
}
static Cvar::Cvar<std::string> cvar_rcon_client_destination(
"rcon.client.destination",
"Destination address for rcon commands, if empty the current server.",
Cvar::NONE,
""
);
static Cvar::Cvar<std::string> cvar_rcon_client_password(
"rcon.client.password",
"Password used to protect the remote console",
Cvar::NONE,
""
);
static Cvar::Range<Cvar::Cvar<int>> cvar_rcon_client_secure(
"rcon.client.secure",
"How secure the Rcon protocol should be: "
"0: Allow unencrypted rcon, "
"1: Require encryption, "
"2: Require encryption and challenge check",
Cvar::NONE,
0,
0,
2
);
/*
* Sends the message to the remote server
*/
static void CL_RconDeliver(const Rcon::Message &message)
{
if ( message.secure == Rcon::Secure::Unencrypted )
{
Net::OutOfBandPrint(netsrc_t::NS_CLIENT, message.remote, "rcon %s %s", message.password, message.command);
return;
}
std::string method = "PLAIN";
Crypto::Data key = Crypto::Hash::Sha256(Crypto::String(message.password));
std::string plaintext = message.command;
if ( message.secure == Rcon::Secure::EncryptedChallenge )
{
method = "CHALLENGE";
plaintext = message.challenge + ' ' + plaintext;
}
Crypto::Data cypher;
if ( Crypto::Aes256Encrypt(Crypto::String(plaintext), key, cypher) )
{
Net::OutOfBandPrint(netsrc_t::NS_CLIENT, message.remote, "srcon %s %s",
method,
Crypto::String(Crypto::Encoding::Base64Encode(cypher))
);
}
}
static void CL_RconSend(const Rcon::Message &message)
{
std::string invalid_reason;
if ( message.Valid(&invalid_reason) )
{
message.Send();
CL_RconDeliver(message);
}
else
{
@@ -1476,7 +1535,7 @@ class RconChallengeQueue
server,
it->command,
Rcon::Secure::EncryptedChallenge,
Rcon::cvar_server_password.Get(),
cvar_rcon_client_password.Get(),
challenge
));
}
@@ -1500,10 +1559,10 @@ CL_Rcon_f
*/
void CL_Rcon_f()
{
if ( Rcon::cvar_server_password.Get().empty() )
if ( cvar_rcon_client_password.Get().empty() )
{
Log::Notice("You must set '%s' before issuing an rcon command.\n",
"rcon.server.password");
Log::Notice("You must set '%s' before issuing an rcon command.",
cvar_rcon_client_password.Name());
return;
}
@@ -1514,24 +1573,24 @@ void CL_Rcon_f()
}
else
{
if ( Rcon::cvar_client_destination.Get().empty() )
if ( cvar_rcon_client_destination.Get().empty() )
{
Log::Notice( "Connect to a server or set the '%s' cvar to issue rcon commands\n",
"rcon.client.destination"
Log::Notice( "Connect to a server or set the '%s' cvar to issue rcon commands",
cvar_rcon_client_destination.Name()
);
return;
}
NET_StringToAdr( Rcon::cvar_client_destination.Get().c_str(), &to, netadrtype_t::NA_UNSPEC );
NET_StringToAdr( cvar_rcon_client_destination.Get().c_str(), &to, netadrtype_t::NA_UNSPEC );
if ( to.port == 0 )
{
to.port = BigShort( PORT_SERVER );
}
}
if ( Rcon::Secure(Rcon::cvar_server_secure.Get()) == Rcon::Secure::EncryptedChallenge )
if ( Rcon::Secure(cvar_rcon_client_secure.Get()) == Rcon::Secure::EncryptedChallenge )
{
CL_RconChallengeQueue.Push({to, Cmd::GetCurrentArgs().EscapedArgs(1)});
Net::OutOfBandPrint(netsrc_t::NS_CLIENT, to, "getchallenge");
@@ -1541,8 +1600,8 @@ void CL_Rcon_f()
CL_RconSend(Rcon::Message(
to,
Cmd::GetCurrentArgs().EscapedArgs(1),
Rcon::Secure(Rcon::cvar_server_secure.Get()),
Rcon::cvar_server_password.Get()
Rcon::Secure(cvar_rcon_client_secure.Get()),
cvar_rcon_client_password.Get()
));
}
}
@@ -36,31 +36,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Rcon {
Cvar::Cvar<std::string> cvar_server_password(
"rcon.server.password",
"Password used to protect the remote console",
Cvar::NONE,
""
);
Cvar::Range<Cvar::Cvar<int>> cvar_server_secure(
"rcon.server.secure",
"How secure the Rcon protocol should be: "
"0: Allow unencrypted rcon, "
"1: Require encryption, "
"2: Require encryption and challenge check",
Cvar::NONE,
0,
0,
2
);
Cvar::Cvar<std::string> cvar_client_destination(
"rcon.client.destination",
"Destination address for rcon commands, if empty the current server.",
Cvar::NONE,
""
);
Message::Message( const netadr_t& remote, std::string command,
Secure secure, std::string password, std::string challenge )
@@ -75,38 +50,6 @@ Message::Message( std::string error_message )
: error(std::move(error_message))
{}
const std::string& Message::Command() const
{
return command;
}
void Message::Send() const
{
if ( secure == Secure::Unencrypted )
{
Net::OutOfBandPrint(netsrc_t::NS_CLIENT, remote, "rcon %s %s", password, command);
return;
}
std::string method = "PLAIN";
Crypto::Data key = Crypto::Hash::Sha256(Crypto::String(password));
std::string plaintext = command;
if ( secure == Secure::EncryptedChallenge )
{
method = "CHALLENGE";
plaintext = challenge + ' ' + plaintext;
}
Crypto::Data cypher;
if ( Crypto::Aes256Encrypt(Crypto::String(plaintext), key, cypher) )
{
Net::OutOfBandPrint(netsrc_t::NS_CLIENT, remote, "srcon %s %s",
method,
Crypto::String(Crypto::Encoding::Base64Encode(cypher))
);
}
}
bool Message::Valid(std::string *invalid_reason) const
{
@@ -145,108 +88,4 @@ bool Message::Valid(std::string *invalid_reason) const
return true;
}
bool Message::Acceptable(std::string *invalid_reason) const
{
auto invalid = [invalid_reason](const char* reason)
{
if ( invalid_reason )
*invalid_reason = reason;
return false;
};
if ( !Valid(invalid_reason) )
{
return false;
}
if ( secure < Secure(cvar_server_secure.Get()) )
{
return invalid("Weak security");
}
if ( cvar_server_password.Get().empty() )
{
return invalid("No rcon.server.password set on the server.");
}
if ( password != cvar_server_password.Get() )
{
return invalid("Bad password");
}
if ( secure == Secure::EncryptedChallenge )
{
if ( !ChallengeManager::MatchString(remote, challenge) )
{
return invalid("Mismatched challenge");
}
}
return true;
}
Message Message::Decode(const netadr_t& remote, const Cmd::Args& args)
{
if ( args.size() < 3 || (args[0] != "rcon" && args[0] != "srcon") )
{
return Message("Invalid command");
}
if ( cvar_server_password.Get().empty() )
{
return Message("rcon.server.password not set");
}
if ( args[0] == "rcon" )
{
return Message(remote, args.EscapedArgs(2), Secure::Unencrypted, args[1]);
}
auto authentication = args[1];
Crypto::Data cyphertext = Crypto::String(args[2]);
Crypto::Data data;
if ( !Crypto::Encoding::Base64Decode( cyphertext, data ) )
{
return Message("Invalid Base64 string");
}
Crypto::Data key = Crypto::Hash::Sha256( Crypto::String( Rcon::cvar_server_password.Get() ) );
if ( !Crypto::Aes256Decrypt( data, key, data ) )
{
return Message("Error during decryption");
}
std::string command = Crypto::String( data );
if ( authentication == "CHALLENGE" )
{
std::istringstream stream( command );
std::string challenge_hex;
stream >> challenge_hex;
while ( Str::cisspace( stream.peek() ) )
{
stream.ignore();
}
std::getline( stream, command );
return Message(remote, command, Secure::EncryptedChallenge,
Rcon::cvar_server_password.Get(), challenge_hex);
}
else if ( authentication == "PLAIN" )
{
return Message(remote, command, Secure::EncryptedPlain,
Rcon::cvar_server_password.Get());
}
else
{
return Message(remote, command, Secure::Invalid,
Rcon::cvar_server_password.Get());
}
}
} // namespace Rcon
@@ -61,28 +61,6 @@ class Message
*/
bool Valid(std::string *invalid_reason = nullptr) const;
/*
* Checks whether the message is acceptable by the server,
* it must be valid and match the rcon settings and challenges.
*/
bool Acceptable(std::string *invalid_reason = nullptr) const;
/*
* Sends the message to the remote server
*/
void Send() const;
/*
* Command to be executed
*/
const std::string& Command() const;
/*
* Decodes the arguments of an out of band message received by the server
*/
static Message Decode(const netadr_t& remote, const Cmd::Args& args);
private:
Secure secure;
std::string challenge;
std::string command;
@@ -91,9 +69,5 @@ class Message
std::string error;
};
extern Cvar::Cvar<std::string> cvar_server_password;
extern Cvar::Range<Cvar::Cvar<int>> cvar_server_secure;
extern Cvar::Cvar<std::string> cvar_client_destination;
} // namespace Rcon
#endif // COMMON_RCON_H_
@@ -80,7 +80,6 @@ void SV_DirectConnect( netadr_t from, const Cmd::Args& args )
return;
}
auto challenge = userinfo["challenge"];
int qport = atoi( userinfo["qport"].c_str() );
auto clients_begin = svs.clients;
@@ -110,7 +109,7 @@ void SV_DirectConnect( netadr_t from, const Cmd::Args& args )
{
// see if the challenge is valid (local clients don't need to challenge)
Challenge::Duration ping_duration;
if ( !ChallengeManager::MatchString( from, challenge, &ping_duration ) )
if ( !ChallengeManager::MatchString( from, userinfo["challenge"], &ping_duration ) )
{
Net::OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\n[err_dialog]No or bad challenge for address." );
return;
Oops, something went wrong.

0 comments on commit 4443f4a

Please sign in to comment.