summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Protocol/Protocol17x.cpp102
-rw-r--r--src/Protocol/Protocol17x.h2
-rw-r--r--src/Server.cpp3
-rw-r--r--src/Server.h28
4 files changed, 120 insertions, 15 deletions
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index 0d349de03..267c18a99 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -53,6 +53,12 @@ Implements the 1.7.x protocol classes:
+const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows...
+
+
+
+
+
// fwd: main.cpp:
extern bool g_ShouldLogCommIn, g_ShouldLogCommOut;
@@ -1351,7 +1357,64 @@ void cProtocol172::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
void cProtocol172::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBuffer)
{
- // TODO: Add protocol encryption
+ short EncKeyLength, EncNonceLength;
+ a_ByteBuffer.ReadBEShort(EncKeyLength);
+ AString EncKey;
+ if (!a_ByteBuffer.ReadString(EncKey, EncKeyLength))
+ {
+ return;
+ }
+ a_ByteBuffer.ReadBEShort(EncNonceLength);
+ AString EncNonce;
+ if (!a_ByteBuffer.ReadString(EncNonce, EncNonceLength))
+ {
+ return;
+ }
+ if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN))
+ {
+ LOGD("Too long encryption");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+
+ // Decrypt EncNonce using privkey
+ cRSAPrivateKey & rsaDecryptor = cRoot::Get()->GetServer()->GetPrivateKey();
+ Int32 DecryptedNonce[MAX_ENC_LEN / sizeof(Int32)];
+ int res = rsaDecryptor.Decrypt((const Byte *)EncNonce.data(), EncNonce.size(), (Byte *)DecryptedNonce, sizeof(DecryptedNonce));
+ if (res != 4)
+ {
+ LOGD("Bad nonce length: got %d, exp %d", res, 4);
+ m_Client->Kick("Hacked client");
+ return;
+ }
+ if (ntohl(DecryptedNonce[0]) != (unsigned)(uintptr_t)this)
+ {
+ LOGD("Bad nonce value");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+
+ // Decrypt the symmetric encryption key using privkey:
+ Byte DecryptedKey[MAX_ENC_LEN];
+ res = rsaDecryptor.Decrypt((const Byte *)EncKey.data(), EncKey.size(), DecryptedKey, sizeof(DecryptedKey));
+ if (res != 16)
+ {
+ LOGD("Bad key length");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+
+ StartEncryption(DecryptedKey);
+
+ // Send login success:
+ {
+ cPacketizer Pkt(*this, 0x02); // Login success packet
+ Pkt.WriteString(Printf("%d", m_Client->GetUniqueID())); // TODO: proper UUID
+ Pkt.WriteString(m_Client->GetUsername());
+ }
+
+ m_State = 3; // State = Game
+ m_Client->HandleLogin(4, m_Client->GetUsername());
}
@@ -1363,14 +1426,26 @@ void cProtocol172::HandlePacketLoginStart(cByteBuffer & a_ByteBuffer)
AString Username;
a_ByteBuffer.ReadVarUTF8String(Username);
- // TODO: Protocol encryption should be set up here if not localhost / auth
-
if (!m_Client->HandleHandshake(Username))
{
// The client is not welcome here, they have been sent a Kick packet already
return;
}
+ // If auth is required, then send the encryption request:
+ // if (cRoot::Get()->GetServer()->ShouldAuthenticate())
+ {
+ cPacketizer Pkt(*this, 0x01);
+ Pkt.WriteString(cRoot::Get()->GetServer()->GetServerID());
+ const AString & PubKeyDer = cRoot::Get()->GetServer()->GetPublicKeyDER();
+ Pkt.WriteShort(PubKeyDer.size());
+ Pkt.WriteBuf(PubKeyDer.data(), PubKeyDer.size());
+ Pkt.WriteShort(4);
+ Pkt.WriteInt((int)(intptr_t)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :)
+ m_Client->SetUsername(Username);
+ return;
+ }
+
// Send login success:
{
cPacketizer Pkt(*this, 0x02); // Login success packet
@@ -1882,6 +1957,27 @@ void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
+void cProtocol172::StartEncryption(const Byte * a_Key)
+{
+ m_Encryptor.Init(a_Key, a_Key);
+ m_Decryptor.Init(a_Key, a_Key);
+ m_IsEncrypted = true;
+
+ // Prepare the m_AuthServerID:
+ cSHA1Checksum Checksum;
+ const AString & ServerID = cRoot::Get()->GetServer()->GetServerID();
+ Checksum.Update((const Byte *)ServerID.c_str(), ServerID.length());
+ Checksum.Update(a_Key, 16);
+ Checksum.Update((const Byte *)cRoot::Get()->GetServer()->GetPublicKeyDER().data(), cRoot::Get()->GetServer()->GetPublicKeyDER().size());
+ Byte Digest[20];
+ Checksum.Finalize(Digest);
+ cSHA1Checksum::DigestToJava(Digest, m_AuthServerID);
+}
+
+
+
+
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cProtocol172::cPacketizer:
diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h
index 72544b575..6a75e41c8 100644
--- a/src/Protocol/Protocol17x.h
+++ b/src/Protocol/Protocol17x.h
@@ -281,6 +281,8 @@ protected:
/// Parses item metadata as read by ReadItem(), into the item enchantments.
void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata);
+
+ void StartEncryption(const Byte * a_Key);
} ;
diff --git a/src/Server.cpp b/src/Server.cpp
index 3f9f8a4ac..ba2b46d55 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -237,7 +237,8 @@ bool cServer::InitServer(cIniFile & a_SettingsIni)
m_bIsConnected = true;
m_ServerID = "-";
- if (a_SettingsIni.GetValueSetB("Authentication", "Authenticate", true))
+ m_ShouldAuthenticate = a_SettingsIni.GetValueSetB("Authentication", "Authenticate", true);
+ if (m_ShouldAuthenticate)
{
MTRand mtrand1;
unsigned int r1 = (mtrand1.randInt() % 1147483647) + 1000000000;
diff --git a/src/Server.h b/src/Server.h
index 15eafc00a..b5280c59d 100644
--- a/src/Server.h
+++ b/src/Server.h
@@ -71,13 +71,13 @@ public: // tolua_export
bool Command(cClientHandle & a_Client, AString & a_Cmd);
- /// Executes the console command, sends output through the specified callback
+ /** Executes the console command, sends output through the specified callback */
void ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output);
- /// Lists all available console commands and their helpstrings
+ /** Lists all available console commands and their helpstrings */
void PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & a_Output);
- /// Binds the built-in console commands with the plugin manager
+ /** Binds the built-in console commands with the plugin manager */
static void BindBuiltInConsoleCommands(void);
void Shutdown(void);
@@ -97,13 +97,13 @@ public: // tolua_export
void RemoveClient(const cClientHandle * a_Client); // Removes the clienthandle from m_SocketThreads
- /// Don't tick a_Client anymore, it will be ticked from its cPlayer instead
+ /** Don't tick a_Client anymore, it will be ticked from its cPlayer instead */
void ClientMovedToWorld(const cClientHandle * a_Client);
- /// Notifies the server that a player was created; the server uses this to adjust the number of players
+ /** Notifies the server that a player was created; the server uses this to adjust the number of players */
void PlayerCreated(const cPlayer * a_Player);
- /// Notifies the server that a player is being destroyed; the server uses this to adjust the number of players
+ /** Notifies the server that a player is being destroyed; the server uses this to adjust the number of players */
void PlayerDestroying(const cPlayer * a_Player);
/** Returns base64 encoded favicon data (obtained from favicon.png) */
@@ -112,11 +112,13 @@ public: // tolua_export
cRSAPrivateKey & GetPrivateKey(void) { return m_PrivateKey; }
const AString & GetPublicKeyDER(void) const { return m_PublicKeyDER; }
+ bool ShouldAuthenticate(void) const { return m_ShouldAuthenticate; }
+
private:
friend class cRoot; // so cRoot can create and destroy cServer
- /// When NotifyClientWrite() is called, it is queued for this thread to process (to avoid deadlocks between cSocketThreads, cClientHandle and cChunkMap)
+ /** When NotifyClientWrite() is called, it is queued for this thread to process (to avoid deadlocks between cSocketThreads, cClientHandle and cChunkMap) */
class cNotifyWriteThread :
public cIsThread
{
@@ -140,7 +142,7 @@ private:
void NotifyClientWrite(const cClientHandle * a_Client);
} ;
- /// The server tick thread takes care of the players who aren't yet spawned in a world
+ /** The server tick thread takes care of the players who aren't yet spawned in a world */
class cTickThread :
public cIsThread
{
@@ -195,18 +197,22 @@ private:
cTickThread m_TickThread;
cEvent m_RestartEvent;
- /// The server ID used for client authentication
+ /** The server ID used for client authentication */
AString m_ServerID;
+ /** If true, players will be online-authenticated agains Mojang servers.
+ This setting is the same as the "online-mode" setting in Vanilla. */
+ bool m_ShouldAuthenticate;
+
cServer(void);
- /// Loads, or generates, if missing, RSA keys for protocol encryption
+ /** Loads, or generates, if missing, RSA keys for protocol encryption */
void PrepareKeys(void);
bool Tick(float a_Dt);
- /// Ticks the clients in m_Clients, manages the list in respect to removing clients
+ /** Ticks the clients in m_Clients, manages the list in respect to removing clients */
void TickClients(float a_Dt);
// cListenThread::cCallback overrides: