From d5885dd6774afb598a69009efa576a908e6fce18 Mon Sep 17 00:00:00 2001 From: diafero Date: Wed, 3 Aug 2011 01:18:08 +0200 Subject: [PATCH 1/2] if no auth server is set, ask the gate keeper about it (does NOT really add a new message, just enables it. DS already supports it in master) this makes the setup even easier... just one server to be configured in the server.ini. --- .../plNetClientComm/plNetClientComm.cpp | 53 ++++++++++++++++--- .../plNetGameLib/Private/plNglGateKeeper.cpp | 12 ++++- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.cpp b/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.cpp index 29a003a8..6ebcc363 100644 --- a/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.cpp @@ -91,8 +91,10 @@ static NetCommAge s_age; static NetCommAge s_startupAge; static bool s_needAvatarLoad = true; static bool s_loginComplete = false; +static bool s_hasAuthSrvIpAddress = false; static bool s_hasFileSrvIpAddress = false; static ENetError s_authResult = kNetErrAuthenticationFailed; +static wchar s_authSrvAddr[256]; static wchar s_fileSrvAddr[256]; static wchar s_iniServerAddr[256]; @@ -829,6 +831,16 @@ static void IReadNetIni() { IniClose(ini); } +//============================================================================ +static void AuthSrvIpAddressCallback ( + ENetError result, + void * param, + const wchar addr[] +) { + StrCopy(s_authSrvAddr, addr, arrsize(s_authSrvAddr)); + s_hasAuthSrvIpAddress = true; +} + //============================================================================ static void FileSrvIpAddressCallback ( ENetError result, @@ -979,21 +991,47 @@ void NetCommConnect () { const wchar ** addrs; unsigned count; - - count = GetAuthSrvHostnames(&addrs); - NetCliAuthStartConnect(addrs, count); + hsBool connectedToKeeper = false; + + // if a console override was specified for a authserv, connect directly to the authserver rather than going through the gatekeeper + if((count = GetAuthSrvHostnames(&addrs)) && wcslen(addrs[0])) + { + NetCliAuthStartConnect(addrs, count); + } + else + { + count = GetGateKeeperSrvHostnames(&addrs); + NetCliGateKeeperStartConnect(addrs, count); + connectedToKeeper = true; + + // request an auth server ip address + NetCliGateKeeperAuthSrvIpAddressRequest(AuthSrvIpAddressCallback, nil); + + while(!s_hasAuthSrvIpAddress && !s_netError) { + NetClientUpdate(); + AsyncSleep(10); + } + + const wchar * authSrv[] = { + s_authSrvAddr + }; + NetCliAuthStartConnect(authSrv, 1); + } if (!gDataServerLocal) { // if a console override was specified for a filesrv, connect directly to the fileserver rather than going through the gatekeeper - if(GetFileSrvHostnames(&addrs) && wcslen(addrs[0])) + if((count = GetFileSrvHostnames(&addrs)) && wcslen(addrs[0])) { NetCliFileStartConnect(addrs, count); } else { - count = GetGateKeeperSrvHostnames(&addrs); - NetCliGateKeeperStartConnect(addrs, count); + if (!connectedToKeeper) { + count = GetGateKeeperSrvHostnames(&addrs); + NetCliGateKeeperStartConnect(addrs, count); + connectedToKeeper = true; + } // request a file server ip address NetCliGateKeeperFileSrvIpAddressRequest(FileSrvIpAddressCallback, nil, false); @@ -1009,6 +1047,9 @@ void NetCommConnect () { NetCliFileStartConnect(fileSrv, 1); } } + + if (connectedToKeeper) + NetCliGateKeeperDisconnect(); } //============================================================================ diff --git a/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGateKeeper.cpp b/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGateKeeper.cpp index 92c7af4b..11b4669b 100644 --- a/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGateKeeper.cpp +++ b/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGateKeeper.cpp @@ -735,6 +735,7 @@ static NetMsgInitSend s_send[] = { static NetMsgInitRecv s_recv[] = { { MSG(PingReply) }, { MSG(FileSrvIpAddressReply) }, + { MSG(AuthSrvIpAddressReply) }, }; #undef MSG @@ -1115,4 +1116,13 @@ void NetCliGateKeeperFileSrvIpAddressRequest ( ) { FileSrvIpAddressRequestTrans * trans = NEW(FileSrvIpAddressRequestTrans)(callback, param, isPatcher); NetTransSend(trans); -} \ No newline at end of file +} + +//============================================================================ +void NetCliGateKeeperAuthSrvIpAddressRequest ( + FNetCliGateKeeperAuthSrvIpAddressRequestCallback callback, + void * param +) { + AuthSrvIpAddressRequestTrans * trans = NEW(AuthSrvIpAddressRequestTrans)(callback, param); + NetTransSend(trans); +} From 837dae9b6981a8c0f065363b54aac1d789236940 Mon Sep 17 00:00:00 2001 From: diafero Date: Thu, 4 Aug 2011 13:21:39 +0200 Subject: [PATCH 2/2] if the N value is zero, send an empty seed to the server and expect an empty seed back. Don't use encryption. --- .../NucleusLib/pnNetCli/pnNcChannel.cpp | 6 +- .../Plasma/NucleusLib/pnNetCli/pnNcCli.cpp | 146 ++++++++++-------- .../NucleusLib/pnNetCli/pnNcEncrypt.cpp | 28 ++-- .../NucleusLib/pnUtils/Private/pnUtBigNum.h | 4 + 4 files changed, 105 insertions(+), 79 deletions(-) diff --git a/Sources/Plasma/NucleusLib/pnNetCli/pnNcChannel.cpp b/Sources/Plasma/NucleusLib/pnNetCli/pnNcChannel.cpp index 6dbcf49e..fd16710b 100644 --- a/Sources/Plasma/NucleusLib/pnNetCli/pnNcChannel.cpp +++ b/Sources/Plasma/NucleusLib/pnNetCli/pnNcChannel.cpp @@ -341,9 +341,9 @@ void NetMsgChannelGetDhConstants ( const BigNum ** dh_xa, const BigNum ** dh_n ) { - *dh_g = channel->m_dh_g; - *dh_xa = &channel->m_dh_xa; - *dh_n = &channel->m_dh_n; + if (dh_g) *dh_g = channel->m_dh_g; + if (dh_xa) *dh_xa = &channel->m_dh_xa; + if (dh_n) *dh_n = &channel->m_dh_n; } diff --git a/Sources/Plasma/NucleusLib/pnNetCli/pnNcCli.cpp b/Sources/Plasma/NucleusLib/pnNetCli/pnNcCli.cpp index 5bd8d392..153dfcb7 100644 --- a/Sources/Plasma/NucleusLib/pnNetCli/pnNcCli.cpp +++ b/Sources/Plasma/NucleusLib/pnNetCli/pnNcCli.cpp @@ -40,8 +40,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com # define NCCLI_LOG NULL_STMT #endif -//#define NO_ENCRYPTION - #ifndef PLASMA_EXTERNAL_RELEASE struct NetLogMessage_Header @@ -120,8 +118,8 @@ struct NetCli : THashKeyVal { ENetCliMode mode; FNetCliEncrypt encryptFcn; byte seed[kNetMaxSymmetricSeedBytes]; - CryptKey * cryptIn; - CryptKey * cryptOut; + CryptKey * cryptIn; // nil if encrytpion is disabled + CryptKey * cryptOut; // nil if encrytpion is disabled void * encryptParam; // Message buffers @@ -174,9 +172,8 @@ static void PutBufferOnWire (NetCli * cli, void * data, unsigned bytes) { } #endif // PLASMA_EXTERNAL_RELEASE - if (cli->mode == kNetCliModeEncrypted) { + if (cli->mode == kNetCliModeEncrypted && cli->cryptOut) { // Encrypt data... -#ifndef NO_ENCRYPTION if (bytes <= 2048) // byte count is small, use stack-based buffer temp = ALLOCA(byte, bytes); @@ -187,7 +184,6 @@ static void PutBufferOnWire (NetCli * cli, void * data, unsigned bytes) { MemCopy(temp, data, bytes); CryptEncrypt(cli->cryptOut, bytes, temp); data = temp; -#endif } if (cli->sock) AsyncSocketSend(cli->sock, data, bytes); @@ -644,7 +640,7 @@ static void ClientConnect (NetCli * cli) { if (cli->sock) { unsigned bytes; NetCli_Cli2Srv_Connect msg; - unsigned char * data = serverSeed.GetData_LE(&bytes); + unsigned char * data = serverSeed.GetData_LE(&bytes); // will be 0 if encryption is disabled, and thereby send an empty seed ASSERTMSG(bytes <= sizeof(msg.dh_y_data), "4"); msg.message = kNetCliCli2SrvConnect; msg.length = (byte) (sizeof(msg) - sizeof(msg.dh_y_data) + bytes); @@ -668,48 +664,55 @@ static bool ServerRecvConnect ( * (const NetCli_Cli2Srv_Connect *) &pkt; if (pkt.length < sizeof(msg)) return false; + int seedLength = msg.length - sizeof(pkt); // Send the server seed to the client (unencrypted) if (cli->sock) { NetCli_Srv2Cli_Encrypt reply; reply.message = kNetCliSrv2CliEncrypt; - reply.length = sizeof(reply); + reply.length = seedLength == 0 ? 0 : sizeof(reply); // reply with empty seed if we got empty seed (this means: no encryption) MemCopy(reply.serverSeed, cli->seed, sizeof(reply.serverSeed)); - AsyncSocketSend(cli->sock, &reply, sizeof(reply)); + AsyncSocketSend(cli->sock, &reply, reply.length); } - // Compute client seed - byte clientSeed[kNetMaxSymmetricSeedBytes]; - { + if (seedLength == 0) { // client wishes no encryption (that's okay, nobody else can "fake" us as nobody has the private key, so if the client actually wants encryption it will only work with the correct peer) + cli->cryptIn = nil; + cli->cryptOut = nil; + } + else { + // Compute client seed + byte clientSeed[kNetMaxSymmetricSeedBytes]; BigNum clientSeedValue; - NetMsgCryptServerConnect( - cli->channel, - msg.length - sizeof(pkt), - msg.dh_y_data, - &clientSeedValue + { + NetMsgCryptServerConnect( + cli->channel, + seedLength, + msg.dh_y_data, + &clientSeedValue + ); + + ZERO(clientSeed); + unsigned bytes; + unsigned char * data = clientSeedValue.GetData_LE(&bytes); + MemCopy(clientSeed, data, min(bytes, sizeof(clientSeed))); + delete [] data; + } + + // Create the symmetric key from a combination + // of the client seed and the server seed + byte sharedSeed[kNetMaxSymmetricSeedBytes]; + CreateSymmetricKey( + sizeof(cli->seed), cli->seed, // server seed + sizeof(clientSeed), clientSeed, // client seed + sizeof(sharedSeed), sharedSeed // combined seed ); - ZERO(clientSeed); - unsigned bytes; - unsigned char * data = clientSeedValue.GetData_LE(&bytes); - MemCopy(clientSeed, data, min(bytes, sizeof(clientSeed))); - delete [] data; + // Switch to encrypted mode + cli->cryptIn = CryptKeyCreate(kCryptRc4, sizeof(sharedSeed), sharedSeed); + cli->cryptOut = CryptKeyCreate(kCryptRc4, sizeof(sharedSeed), sharedSeed); } - - // Create the symmetric key from a combination - // of the client seed and the server seed - byte sharedSeed[kNetMaxSymmetricSeedBytes]; - CreateSymmetricKey( - sizeof(cli->seed), cli->seed, // server seed - sizeof(clientSeed), clientSeed, // client seed - sizeof(sharedSeed), sharedSeed // combined seed - ); - - // Switch to encrypted mode - cli->mode = kNetCliModeEncrypted; - cli->cryptIn = CryptKeyCreate(kCryptRc4, sizeof(sharedSeed), sharedSeed); - cli->cryptOut = CryptKeyCreate(kCryptRc4, sizeof(sharedSeed), sharedSeed); - + + cli->mode = kNetCliModeEncrypted; // should rather be called "established", but whatever return cli->encryptFcn(kNetSuccess, cli->encryptParam); } @@ -722,26 +725,39 @@ static bool ClientRecvEncrypt ( if (cli->mode != kNetCliModeClientStart) return false; - // Validate message size + // find out if we want encryption + const BigNum * DH_N; + NetMsgChannelGetDhConstants(cli->channel, nil, nil, &DH_N); + bool encrypt = !DH_N->isZero(); + + // Process message const NetCli_Srv2Cli_Encrypt & msg = * (const NetCli_Srv2Cli_Encrypt *) &pkt; - if (pkt.length != sizeof(msg)) - return false; + if (encrypt) { // we insist on encryption, don't let some MitM decide for us! + if (pkt.length != sizeof(msg)) + return false; - // Create the symmetric key from a combination - // of the client seed and the server seed - byte sharedSeed[kNetMaxSymmetricSeedBytes]; - CreateSymmetricKey( - sizeof(msg.serverSeed), msg.serverSeed, // server seed - sizeof(cli->seed), cli->seed, // client seed - sizeof(sharedSeed), sharedSeed // combined seed - ); + // Create the symmetric key from a combination + // of the client seed and the server seed + byte sharedSeed[kNetMaxSymmetricSeedBytes]; + CreateSymmetricKey( + sizeof(msg.serverSeed), msg.serverSeed, // server seed + sizeof(cli->seed), cli->seed, // client seed + sizeof(sharedSeed), sharedSeed // combined seed + ); - // Switch to encrypted mode - cli->mode = kNetCliModeEncrypted; - cli->cryptIn = CryptKeyCreate(kCryptRc4, sizeof(sharedSeed), sharedSeed); - cli->cryptOut = CryptKeyCreate(kCryptRc4, sizeof(sharedSeed), sharedSeed); + // Switch to encrypted mode + cli->cryptIn = CryptKeyCreate(kCryptRc4, sizeof(sharedSeed), sharedSeed); + cli->cryptOut = CryptKeyCreate(kCryptRc4, sizeof(sharedSeed), sharedSeed); + } + else { // honestly we do not care what the other side sends, we will send plaintext + if (pkt.length != sizeof(pkt)) + return false; + cli->cryptIn = nil; + cli->cryptOut = nil; + } + cli->mode = kNetCliModeEncrypted; // should rather be called "established", but whatever return cli->encryptFcn(kNetSuccess, cli->encryptParam); } @@ -1061,18 +1077,18 @@ bool NetCliDispatch ( // Decrypt data... byte * temp, * heap = NULL; -#ifndef NO_ENCRYPTION - if (bytes <= 2048) - // byte count is small, use stack-based buffer - temp = ALLOCA(byte, bytes); - else - // byte count is large, use heap-based buffer - temp = heap = (byte *)ALLOC(bytes); - - MemCopy(temp, data, bytes); - CryptDecrypt(cli->cryptIn, bytes, temp); - data = temp; -#endif + if (cli->cryptIn) { + if (bytes <= 2048) + // byte count is small, use stack-based buffer + temp = ALLOCA(byte, bytes); + else + // byte count is large, use heap-based buffer + temp = heap = (byte *)ALLOC(bytes); + + MemCopy(temp, data, bytes); + CryptDecrypt(cli->cryptIn, bytes, temp); + data = temp; + } // Add data to accumulator and dispatch cli->input.Add(bytes, data); diff --git a/Sources/Plasma/NucleusLib/pnNetCli/pnNcEncrypt.cpp b/Sources/Plasma/NucleusLib/pnNetCli/pnNcEncrypt.cpp index 6d4fb6f9..eb700330 100644 --- a/Sources/Plasma/NucleusLib/pnNetCli/pnNcEncrypt.cpp +++ b/Sources/Plasma/NucleusLib/pnNetCli/pnNcEncrypt.cpp @@ -83,6 +83,7 @@ static void GetCachedServerKey ( const BigNum * DH_A; const BigNum * DH_N; NetMsgChannelGetDhConstants(channel, &DH_G, &DH_A, &DH_N); + hsAssert(!DH_N->isZero(), "DH_N must not be zero in encrypted mode"); // Compute the result ka->PowMod(dh_y, *DH_A, *DH_N); @@ -107,17 +108,22 @@ void NetMsgCryptClientStart ( const BigNum * DH_X; const BigNum * DH_N; NetMsgChannelGetDhConstants(channel, &DH_G, &DH_X, &DH_N); - - // Client chooses b and y on connect - BigNum g(DH_G); - BigNum seed(seedBytes, seedData); - BigNum b; b.Rand(kNetDiffieHellmanKeyBits, &seed); - - // Client computes key: kb = x^b mod n - clientSeed->PowMod(*DH_X, b, *DH_N); - - // Client sends y to server - serverSeed->PowMod(g, b, *DH_N); + if (DH_N->isZero()) { // no actual encryption, but the caller expects a seed + clientSeed->SetZero(); + serverSeed->SetZero(); + } + else { + // Client chooses b and y on connect + BigNum g(DH_G); + BigNum seed(seedBytes, seedData); + BigNum b; b.Rand(kNetDiffieHellmanKeyBits, &seed); + + // Client computes key: kb = x^b mod n + clientSeed->PowMod(*DH_X, b, *DH_N); + + // Client sends y to server + serverSeed->PowMod(g, b, *DH_N); + } } //============================================================================ diff --git a/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBigNum.h b/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBigNum.h index b56d7df2..2fdaf986 100644 --- a/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBigNum.h +++ b/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBigNum.h @@ -89,6 +89,10 @@ public: { return BN_cmp(&m_number, &a.m_number); } + bool isZero() const + { + return BN_is_zero(&m_number); + } void Div (const BigNum & a, dword b, dword * remainder) {