/* * Ascent MMORPG Server * Copyright (C) 2005-2007 Ascent Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "LogonStdAfx.h" enum _errors { CE_SUCCESS = 0x00, CE_IPBAN=0x01, //2bd -- unable to connect (some internal problem) CE_ACCOUNT_CLOSED=0x03, // "This account has been closed and is no longer in service -- Please check the registered email address of this account for further information."; CE_NO_ACCOUNT=0x04, //(5)The information you have entered is not valid. Please check the spelling of the account name and password. If you need help in retrieving a lost or stolen password and account CE_ACCOUNT_IN_USE=0x06, //This account is already logged in. Please check the spelling and try again. CE_PREORDER_TIME_LIMIT=0x07, CE_SERVER_FULL=0x08, //Could not log in at this time. Please try again later. CE_WRONG_BUILD_NUMBER=0x09, //Unable to validate game version. This may be caused by file corruption or the interference of another program. CE_UPDATE_CLIENT=0x0a, CE_ACCOUNT_FREEZED=0x0c } ; AuthSocket::AuthSocket(SOCKET fd) : Socket(fd, 32768, 4096) { N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7"); g.SetDword(7); s.SetRand(256); m_authenticated = false; m_account = 0; last_recv = time(NULL); removedFromSet = false; _authSocketLock.Acquire(); _authSockets.insert(this); _authSocketLock.Release(); } AuthSocket::~AuthSocket() { } void AuthSocket::OnDisconnect() { if(m_authenticated && m_account) sInfoCore.DeleteSessionKey(m_account->AccountId); if(!removedFromSet) { _authSocketLock.Acquire(); _authSockets.erase(this); _authSocketLock.Release(); } } void AuthSocket::HandleChallenge() { // No header if(GetReadBufferSize() < 4) return; // Check the rest of the packet is complete. uint8 * ReceiveBuffer = this->GetReadBuffer(0); #ifdef USING_BIG_ENDIAN uint16 full_size = swap16(*(uint16*)&ReceiveBuffer[2]); #else uint16 full_size = *(uint16*)&ReceiveBuffer[2]; #endif sLog.outDetail("[AuthChallenge] got header, body is 0x%02X bytes", full_size); if(GetReadBufferSize() < uint32(full_size+4)) return; // Copy the data into our cached challenge structure if(full_size > sizeof(sAuthLogonChallenge_C)) { Disconnect(); return; } sLog.outDebug("[AuthChallenge] got full packet."); memcpy(&m_challenge, ReceiveBuffer, full_size + 4); RemoveReadBufferBytes(full_size + 4, true); //#ifdef USING_BIG_ENDIAN // uint16 build = swap16(m_challenge.build); // printf("Build: %u\n", build); //#endif // Check client build. #ifdef USING_BIG_ENDIAN if(swap16(m_challenge.build) > LogonServer::getSingleton().max_build || swap16(m_challenge.build) < LogonServer::getSingleton().min_build) #else if(m_challenge.build > LogonServer::getSingleton().max_build || m_challenge.build < LogonServer::getSingleton().min_build) #endif { SendChallengeError(CE_WRONG_BUILD_NUMBER); return; } // Check for a possible IP ban on this client. BAN_STATUS ipb = IPBanner::getSingleton().CalculateBanStatus(GetRemoteAddress()); switch(ipb) { case BAN_STATUS_PERMANENT_BAN: SendChallengeError(CE_ACCOUNT_CLOSED); return; case BAN_STATUS_TIME_LEFT_ON_BAN: SendChallengeError(CE_ACCOUNT_FREEZED); return; } // Null-terminate the account string m_challenge.I[m_challenge.I_len] = 0; // Look up the account information string AccountName = (char*)&m_challenge.I; sLog.outDebug("[AuthChallenge] Account Name: \"%s\"", AccountName.c_str()); m_account = AccountMgr::getSingleton().GetAccount(AccountName); if(m_account == 0) { sLog.outDebug("[AuthChallenge] Invalid account."); // Non-existant account SendChallengeError(CE_NO_ACCOUNT); return; } sLog.outDebug("[AuthChallenge] Account banned state = %u", m_account->Banned); // Check that the account isn't banned. if(m_account->Banned == 1) { SendChallengeError(CE_ACCOUNT_CLOSED); return; } else if(m_account->Banned > 0) { SendChallengeError(CE_ACCOUNT_FREEZED); return; } Sha1Hash sha; //uint32 tc = s.GetNumBytes(); sha.UpdateData( s.AsByteArray(), 32 ); sha.UpdateData( m_account->SrpHash, 20 ); sha.Finalize(); BigNumber x; x.SetBinary( sha.GetDigest(), sha.GetLength() ); v = g.ModExp(x, N); b.SetRand(152); BigNumber gmod = g.ModExp(b, N); B = ((v * 3) + gmod) % N; ASSERT(gmod.GetNumBytes() <= 32); BigNumber unk; unk.SetRand(128); uint8 response[200]; uint32 c = 0; response[c] = 0; c += 1; response[c] = 0; c += 1; response[c] = CE_SUCCESS; c += 1; memcpy(&response[c], B.AsByteArray(), 32); c += 32; response[c] = 1; c += 1; response[c] = g.AsByteArray()[0]; c += 1; response[c] = 32; c += 1; memcpy(&response[c], N.AsByteArray(), 32); c += 32; memcpy(&response[c], s.AsByteArray(), s.GetNumBytes()); c += s.GetNumBytes(); memcpy(&response[c], unk.AsByteArray(), 16); c += 16; response[c] = 0; c += 1; Send(response, c); } void AuthSocket::HandleProof() { if(!m_account || GetReadBufferSize() < sizeof(sAuthLogonProof_C)) return ; sLog.outDebug("[AuthLogonProof] Interleaving and checking proof..."); sAuthLogonProof_C lp; Read(sizeof(sAuthLogonProof_C), (uint8*)&lp); BigNumber A; A.SetBinary(lp.A, 32); Sha1Hash sha; sha.UpdateBigNumbers(&A, &B, 0); sha.Finalize(); BigNumber u; u.SetBinary(sha.GetDigest(), 20); BigNumber S = (A * (v.ModExp(u, N))).ModExp(b, N); uint8 t[32]; uint8 t1[16]; uint8 vK[40]; memcpy(t, S.AsByteArray(), 32); for (int i = 0; i < 16; i++) { t1[i] = t[i*2]; } sha.Initialize(); sha.UpdateData(t1, 16); sha.Finalize(); for (int i = 0; i < 20; i++) { vK[i*2] = sha.GetDigest()[i]; } for (int i = 0; i < 16; i++) { t1[i] = t[i*2+1]; } sha.Initialize(); sha.UpdateData(t1, 16); sha.Finalize(); for (int i = 0; i < 20; i++) { vK[i*2+1] = sha.GetDigest()[i]; } m_sessionkey.SetBinary(vK, 40); uint8 hash[20]; sha.Initialize(); sha.UpdateBigNumbers(&N, NULL); sha.Finalize(); memcpy(hash, sha.GetDigest(), 20); sha.Initialize(); sha.UpdateBigNumbers(&g, NULL); sha.Finalize(); for (int i = 0; i < 20; i++) { hash[i] ^= sha.GetDigest()[i]; } BigNumber t3; t3.SetBinary(hash, 20); sha.Initialize(); sha.UpdateData(m_account->Username); sha.Finalize(); BigNumber t4; t4.SetBinary(sha.GetDigest(), 20); sha.Initialize(); sha.UpdateBigNumbers(&t3, &t4, &s, &A, &B, &m_sessionkey, NULL); sha.Finalize(); BigNumber M; M.SetBinary(sha.GetDigest(), 20); // Compare M1 values. if(memcmp(lp.M1, M.AsByteArray(), 20) != 0) { // Authentication failed. //SendProofError(4, 0); SendChallengeError(CE_NO_ACCOUNT); sLog.outDebug("[AuthLogonProof] M1 values don't match."); return; } // Store sessionkey BigNumber * bs = new BigNumber(m_sessionkey); sInfoCore.SetSessionKey(m_account->AccountId, bs); // let the client know sha.Initialize(); sha.UpdateBigNumbers(&A, &M, &m_sessionkey, 0); sha.Finalize(); SendProofError(0, sha.GetDigest()); sLog.outDebug("[AuthLogonProof] Authentication Success."); // we're authenticated now :) m_authenticated = true; // Don't update when IP banned, but update anyway if it's an account ban sLogonSQL->Execute("UPDATE accounts SET lastlogin=NOW(), lastip='%s' WHERE acct=%u;", GetRemoteIP().c_str(), m_account->AccountId); } void AuthSocket::SendChallengeError(uint8 Error) { uint8 buffer[3]; buffer[0] = buffer[1] = 0; buffer[2] = Error; Send(buffer, 3); } void AuthSocket::SendProofError(uint8 Error, uint8 * M2) { uint8 buffer[28]; memset(buffer, 0, 28); buffer[0] = 1; buffer[1] = Error; if(M2 == 0) { #ifdef USING_BIG_ENDIAN *(uint32*)&buffer[2] = swap32(3); #else *(uint32*)&buffer[2] = 3; #endif Send(buffer, 6); return; } memcpy(&buffer[2], M2, 20); Send(buffer, 28); } void AuthSocket::OnRead() { if(GetReadBufferSize() < 1) return; uint8 Command = GetReadBuffer(0)[0]; // Handle depending on command switch(Command) { case 0: // AUTH_CHALLENGE last_recv = time(NULL); HandleChallenge(); break; case 1: // AUTH_PROOF last_recv = time(NULL); HandleProof(); break; case 0x10: // REALM_LIST last_recv = time(NULL); HandleRealmlist(); break; } } void AuthSocket::HandleRealmlist() { sInfoCore.SendRealms(this); }