int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) { // NOTE: ATM the socket is singlethread, have this in mind ... uint8 digest[20]; uint32 clientSeed; uint32 unk2; uint32 BuiltNumberClient; uint32 id, security; //uint8 expansion = 0; LocaleConstant locale; std::string account; Sha1Hash sha1; BigNumber v, s, g, N, x, I; WorldPacket packet, SendAddonPacked; BigNumber K; if (recvPacket.size () < (4 + 4 + 1 + 4 + 20)) { sLog.outError ("WorldSocket::HandleAuthSession: wrong packet size"); return -1; } // Read the content of the packet recvPacket >> BuiltNumberClient; // for now no use recvPacket >> unk2; recvPacket >> account; if (recvPacket.size () < (4 + 4 + (account.size () + 1) + 4 + 20)) { sLog.outError ("WorldSocket::HandleAuthSession: wrong packet size second check"); return -1; } recvPacket >> clientSeed; recvPacket.read (digest, 20); DEBUG_LOG ("WorldSocket::HandleAuthSession: client %u, unk2 %u, account %s, clientseed %u", BuiltNumberClient, unk2, account.c_str (), clientSeed); // Get the account information from the realmd database std::string safe_account = account; // Duplicate, else will screw the SHA hash verification below LoginDatabase.escape_string (safe_account); // No SQL injection, username escaped. QueryResult *result = LoginDatabase.PQuery ("SELECT " "id, " //0 "gmlevel, " //1 "sessionkey, " //2 "last_ip, " //3 "locked, " //4 "sha_pass_hash, " //5 "v, " //6 "s, " //7 "expansion, " //8 "mutetime, " //9 "locale " //10 "FROM account " "WHERE username = '******'", safe_account.c_str ()); // Stop if the account is not found if (!result) { packet.Initialize (SMSG_AUTH_RESPONSE, 1); packet << uint8 (AUTH_UNKNOWN_ACCOUNT); SendPacket (packet); sLog.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (unknown account)."); return -1; } Field* fields = result->Fetch (); uint8 expansion = fields[8].GetUInt8(); uint32 world_expansion = sWorld.getConfig(CONFIG_EXPANSION); if(expansion > world_expansion) expansion = world_expansion; //expansion = ((sWorld.getConfig(CONFIG_EXPANSION) > fields[8].GetUInt8()) ? fields[8].GetUInt8() : sWorld.getConfig(CONFIG_EXPANSION)); N.SetHexStr ("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7"); g.SetDword (7); I.SetHexStr (fields[5].GetString ()); //In case of leading zeros in the I hash, restore them uint8 mDigest[SHA_DIGEST_LENGTH]; memset (mDigest, 0, SHA_DIGEST_LENGTH); if (I.GetNumBytes () <= SHA_DIGEST_LENGTH) memcpy (mDigest, I.AsByteArray (), I.GetNumBytes ()); std::reverse (mDigest, mDigest + SHA_DIGEST_LENGTH); s.SetHexStr (fields[7].GetString ()); sha1.UpdateData (s.AsByteArray (), s.GetNumBytes ()); sha1.UpdateData (mDigest, SHA_DIGEST_LENGTH); sha1.Finalize (); x.SetBinary (sha1.GetDigest (), sha1.GetLength ()); v = g.ModExp (x, N); const char* sStr = s.AsHexStr (); //Must be freed by OPENSSL_free() const char* vStr = v.AsHexStr (); //Must be freed by OPENSSL_free() const char* vold = fields[6].GetString (); DEBUG_LOG ("WorldSocket::HandleAuthSession: (s,v) check s: %s v_old: %s v_new: %s", sStr, vold, vStr); LoginDatabase.PExecute ("UPDATE account " "SET " "v = '0', " "s = '0' " "WHERE username = '******'", safe_account.c_str ()); if (!vold || strcmp (vStr, vold)) { packet.Initialize (SMSG_AUTH_RESPONSE, 1); packet << uint8 (AUTH_UNKNOWN_ACCOUNT); SendPacket (packet); delete result; OPENSSL_free ((void*) sStr); OPENSSL_free ((void*) vStr); sLog.outBasic ("WorldSocket::HandleAuthSession: User not logged."); return -1; } OPENSSL_free ((void*) sStr); OPENSSL_free ((void*) vStr); ///- Re-check ip locking (same check as in realmd). if (fields[4].GetUInt8 () == 1) // if ip is locked { if (strcmp (fields[3].GetString (), GetRemoteAddress ().c_str ())) { packet.Initialize (SMSG_AUTH_RESPONSE, 1); packet << uint8 (AUTH_FAILED); SendPacket (packet); delete result; sLog.outBasic ("WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs)."); return -1; } } id = fields[0].GetUInt32 (); security = fields[1].GetUInt16 (); K.SetHexStr (fields[2].GetString ()); time_t mutetime = time_t (fields[9].GetUInt64 ()); locale = LocaleConstant (fields[10].GetUInt8 ()); if (locale >= MAX_LOCALE) locale = LOCALE_enUS; delete result; // Re-check account ban (same check as in realmd) QueryResult *banresult = LoginDatabase.PQuery ("SELECT " "bandate, " "unbandate " "FROM account_banned " "WHERE id = '%u' " "AND active = 1", id); if (banresult) // if account banned { packet.Initialize (SMSG_AUTH_RESPONSE, 1); packet << uint8 (AUTH_BANNED); SendPacket (packet); delete banresult; sLog.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (Account banned)."); return -1; } // Check locked state for server sWorld.UpdateAllowedSecurity(); AccountTypes allowedAccountType = sWorld.GetPlayerSecurityLimit (); sLog.outDebug("Allowed Level: %u Player Level %u", allowedAccountType, AccountTypes(security)); if (allowedAccountType > SEC_PLAYER && security < allowedAccountType) { WorldPacket Packet (SMSG_AUTH_RESPONSE, 1); Packet << uint8 (AUTH_UNAVAILABLE); SendPacket (packet); sLog.outDetail ("WorldSocket::HandleAuthSession: User tries to login but his security level is not enough"); return -1; } // Check that Key and account name are the same on client and server Sha1Hash sha; uint32 t = 0; uint32 seed = m_Seed; sha.UpdateData (account); sha.UpdateData ((uint8 *) & t, 4); sha.UpdateData ((uint8 *) & clientSeed, 4); sha.UpdateData ((uint8 *) & seed, 4); sha.UpdateBigNumbers (&K, NULL); sha.Finalize (); if (memcmp (sha.GetDigest (), digest, 20)) { packet.Initialize (SMSG_AUTH_RESPONSE, 1); packet << uint8 (AUTH_FAILED); SendPacket (packet); sLog.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (authentification failed)."); return -1; } std::string address = GetRemoteAddress (); DEBUG_LOG ("WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.", account.c_str (), address.c_str ()); // Update the last_ip in the database // No SQL injection, username escaped. LoginDatabase.escape_string (address); LoginDatabase.PExecute ("UPDATE account " "SET last_ip = '%s' " "WHERE username = '******'", address.c_str (), safe_account.c_str ()); // NOTE ATM the socket is singlethreaded, have this in mind ... ACE_NEW_RETURN (m_Session, WorldSession (id, this, security, expansion, mutetime, locale), -1); m_Crypt.SetKey (&K); m_Crypt.Init (); // In case needed sometime the second arg is in microseconds 1 000 000 = 1 sec ACE_OS::sleep (ACE_Time_Value (0, 10000)); sWorld.AddSession (m_Session); // Create and send the Addon packet if (sAddOnHandler.BuildAddonPacket (&recvPacket, &SendAddonPacked)) SendPacket (SendAddonPacked); return 0; }
/// Logon Proof command handler bool AuthSocket::_HandleLogonProof() { DEBUG_LOG("Entering _HandleLogonProof"); ///- Read the packet if (ibuf.GetLength() < sizeof(sAuthLogonProof_C)) return false; sAuthLogonProof_C lp; ibuf.Read((char *)&lp, sizeof(sAuthLogonProof_C)); ///- Check if the client has one of the expected version numbers bool valid_version=false; int accepted_versions[]=EXPECTED_MANGOS_CLIENT_BUILD; for(int i=0;accepted_versions[i];i++) { if(_build==accepted_versions[i]) { valid_version=true; break; } } /// <ul><li> If the client has no valid version if(!valid_version) { ///- Check if we have the apropriate patch on the disk // 24 = len("./patches/65535enGB.mpq")+1 char tmp[24]; // No buffer overflow (fixed length of arguments) sprintf(tmp,"./patches/%d%s.mpq",_build, _localizationName.c_str()); // This will be closed at the destruction of the AuthSocket (client deconnection) FILE *pFile=fopen(tmp,"rb"); if(!pFile) { ByteBuffer pkt; pkt << (uint8) AUTH_LOGON_CHALLENGE; pkt << (uint8) 0x00; pkt << (uint8) REALM_AUTH_WRONG_BUILD_NUMBER; DEBUG_LOG("[AuthChallenge] %u is not a valid client version!", _build); DEBUG_LOG("[AuthChallenge] Patch %s not found",tmp); SendBuf((char const*)pkt.contents(), pkt.size()); return true; } else // have patch { pPatch=pFile; XFER_INIT xferh; ///- Get the MD5 hash of the patch file (get it from preloaded Patcher cache or calculate it) if(PatchesCache.GetHash(tmp,(uint8*)&xferh.md5)) { DEBUG_LOG("\n[AuthChallenge] Found precached patch info for patch %s",tmp); } else { // calculate patch md5 printf("\n[AuthChallenge] Patch info for %s was not cached.",tmp); PatchesCache.LoadPatchMD5(tmp); PatchesCache.GetHash(tmp,(uint8*)&xferh.md5); } ///- Send a packet to the client with the file length and MD5 hash uint8 data[2]={AUTH_LOGON_PROOF,REALM_AUTH_UPDATE_CLIENT}; SendBuf((const char*)data,sizeof(data)); memcpy(&xferh,"0\x05Patch",7); xferh.cmd=XFER_INITIATE; fseek(pPatch,0,SEEK_END); xferh.file_size=ftell(pPatch); SendBuf((const char*)&xferh,sizeof(xferh)); return true; } } /// </ul> ///- Continue the SRP6 calculation based on data received from the client BigNumber A; A.SetBinary(lp.A, 32); Sha1Hash sha; sha.UpdateBigNumbers(&A, &B, NULL); 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]; } K.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(_login); sha.Finalize(); uint8 t4[SHA_DIGEST_LENGTH]; memcpy(t4, sha.GetDigest(), SHA_DIGEST_LENGTH); sha.Initialize(); sha.UpdateBigNumbers(&t3, NULL); sha.UpdateData(t4, SHA_DIGEST_LENGTH); sha.UpdateBigNumbers(&s, &A, &B, &K, NULL); sha.Finalize(); BigNumber M; M.SetBinary(sha.GetDigest(), 20); ///- Check if SRP6 results match (password is correct), else send an error if (!memcmp(M.AsByteArray(), lp.M1, 20)) { sLog.outBasic("User '%s' successfully authenticated", _login.c_str()); ///- Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account // No SQL injection (escaped user name) and IP address as received by socket const char* K_hex = K.AsHexStr(); loginDatabase.PExecute("UPDATE account SET sessionkey = '%s', last_ip = '%s', last_login = NOW(), locale = '%u', failed_logins = 0 WHERE username = '******'", K_hex, GetRemoteAddress().c_str(), GetLocaleByName(_localizationName), _safelogin.c_str() ); OPENSSL_free((void*)K_hex); ///- Finish SRP6 and send the final result to the client sha.Initialize(); sha.UpdateBigNumbers(&A, &M, &K, NULL); sha.Finalize(); sAuthLogonProof_S proof; memcpy(proof.M2, sha.GetDigest(), 20); proof.cmd = AUTH_LOGON_PROOF; proof.error = 0; proof.unk1 = 0x00800000; proof.unk2 = 0x00; proof.unk3 = 0x00; SendBuf((char *)&proof, sizeof(proof)); ///- Set _authed to true! _authed = true; } else { char data[4]={AUTH_LOGON_PROOF,REALM_AUTH_NO_MATCH,3,0}; SendBuf(data,sizeof(data)); sLog.outBasic("[AuthChallenge] account %s tried to login with wrong password!",_login.c_str ()); uint32 MaxWrongPassCount = sConfig.GetIntDefault("WrongPass.MaxCount", 0); if(MaxWrongPassCount > 0) { //Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP loginDatabase.PExecute("UPDATE account SET failed_logins = failed_logins + 1 WHERE username = '******'",_safelogin.c_str()); if(QueryResult *loginfail = loginDatabase.PQuery("SELECT id, failed_logins FROM account WHERE username = '******'", _safelogin.c_str())) { Field* fields = loginfail->Fetch(); uint32 failed_logins = fields[1].GetUInt32(); if( failed_logins >= MaxWrongPassCount ) { uint32 WrongPassBanTime = sConfig.GetIntDefault("WrongPass.BanTime", 600); bool WrongPassBanType = sConfig.GetBoolDefault("WrongPass.BanType", false); if(WrongPassBanType) { uint32 acc_id = fields[0].GetUInt32(); loginDatabase.PExecute("INSERT INTO account_banned VALUES ('%u',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+'%u','MaNGOS realmd','Failed login autoban',1)", acc_id, WrongPassBanTime); sLog.outBasic("[AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times", _login.c_str(), WrongPassBanTime, failed_logins); } else { std::string current_ip = GetRemoteAddress(); loginDatabase.escape_string(current_ip); loginDatabase.PExecute("INSERT INTO ip_banned VALUES ('%s',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+'%u','MaNGOS realmd','Failed login autoban')", current_ip.c_str(), WrongPassBanTime); sLog.outBasic("[AuthChallenge] IP %s got banned for '%u' seconds because account %s failed to authenticate '%u' times", current_ip.c_str(), WrongPassBanTime, _login.c_str(), failed_logins); } } delete loginfail; } } } return true; }
/// Logon Proof command handler bool AuthSocket::_HandleLogonProof() { DEBUG_LOG("Entering _HandleLogonProof"); ///- Read the packet sAuthLogonProof_C lp; if (!recv((char*)&lp, sizeof(sAuthLogonProof_C))) return false; ///- Check if the client has one of the expected version numbers bool valid_version = FindBuildInfo(_build) != NULL; /// <ul><li> If the client has no valid version if (!valid_version) { if (this->patch_ != ACE_INVALID_HANDLE) return false; ///- Check if we have the apropriate patch on the disk // file looks like: 65535enGB.mpq char tmp[64]; snprintf(tmp, 24, "./patches/%d%s.mpq", _build, _localizationName.c_str()); char filename[PATH_MAX]; if (ACE_OS::realpath(tmp, filename) != NULL) { patch_ = ACE_OS::open(filename, GENERIC_READ | FILE_FLAG_SEQUENTIAL_SCAN); } if (patch_ == ACE_INVALID_HANDLE) { // no patch found ByteBuffer pkt; pkt << (uint8) CMD_AUTH_LOGON_CHALLENGE; pkt << (uint8) 0x00; pkt << (uint8) WOW_FAIL_VERSION_INVALID; DEBUG_LOG("[AuthChallenge] %u is not a valid client version!", _build); DEBUG_LOG("[AuthChallenge] Patch %s not found", tmp); send((char const*)pkt.contents(), pkt.size()); return true; } XFER_INIT xferh; ACE_OFF_T file_size = ACE_OS::filesize(this->patch_); if (file_size == -1) { close_connection(); return false; } if (!PatchCache::instance()->GetHash(tmp, (uint8*)&xferh.md5)) { // calculate patch md5, happens if patch was added while realmd was running PatchCache::instance()->LoadPatchMD5(tmp); PatchCache::instance()->GetHash(tmp, (uint8*)&xferh.md5); } uint8 data[2] = { CMD_AUTH_LOGON_PROOF, WOW_FAIL_VERSION_UPDATE}; send((const char*)data, sizeof(data)); memcpy(&xferh, "0\x05Patch", 7); xferh.cmd = CMD_XFER_INITIATE; xferh.file_size = file_size; send((const char*)&xferh, sizeof(xferh)); return true; } /// </ul> ///- Continue the SRP6 calculation based on data received from the client BigNumber A; A.SetBinary(lp.A, 32); // SRP safeguard: abort if A==0 if (A.isZero()) return false; Sha1Hash sha; sha.UpdateBigNumbers(&A, &B, NULL); 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), 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]; } K.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(_login); sha.Finalize(); uint8 t4[SHA_DIGEST_LENGTH]; memcpy(t4, sha.GetDigest(), SHA_DIGEST_LENGTH); sha.Initialize(); sha.UpdateBigNumbers(&t3, NULL); sha.UpdateData(t4, SHA_DIGEST_LENGTH); sha.UpdateBigNumbers(&s, &A, &B, &K, NULL); sha.Finalize(); BigNumber M; M.SetBinary(sha.GetDigest(), 20); ///- Check if SRP6 results match (password is correct), else send an error if (!memcmp(M.AsByteArray(), lp.M1, 20)) { BASIC_LOG("User '%s' successfully authenticated", _login.c_str()); ///- Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account // No SQL injection (escaped user name) and IP address as received by socket const char* K_hex = K.AsHexStr(); LoginDatabase.PExecute("UPDATE account SET sessionkey = '%s', last_ip = '%s', last_login = NOW(), locale = '%u', os = '%s', failed_logins = 0 WHERE username = '******'", K_hex, get_remote_address().c_str(), GetLocaleByName(_localizationName), _os.c_str(), _safelogin.c_str()); OPENSSL_free((void*)K_hex); ///- Finish SRP6 and send the final result to the client sha.Initialize(); sha.UpdateBigNumbers(&A, &M, &K, NULL); sha.Finalize(); SendProof(sha); ///- Set _authed to true! _authed = true; } else { if (_build > 6005) // > 1.12.2 { char data[4] = { CMD_AUTH_LOGON_PROOF, WOW_FAIL_UNKNOWN_ACCOUNT, 3, 0}; send(data, sizeof(data)); } else { // 1.x not react incorrectly at 4-byte message use 3 as real error char data[2] = { CMD_AUTH_LOGON_PROOF, WOW_FAIL_UNKNOWN_ACCOUNT}; send(data, sizeof(data)); } BASIC_LOG("[AuthChallenge] account %s tried to login with wrong password!", _login.c_str()); uint32 MaxWrongPassCount = sConfig.GetIntDefault("WrongPass.MaxCount", 0); if (MaxWrongPassCount > 0) { // Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP LoginDatabase.PExecute("UPDATE account SET failed_logins = failed_logins + 1 WHERE username = '******'", _safelogin.c_str()); if (QueryResult* loginfail = LoginDatabase.PQuery("SELECT id, failed_logins FROM account WHERE username = '******'", _safelogin.c_str())) { Field* fields = loginfail->Fetch(); uint32 failed_logins = fields[1].GetUInt32(); if (failed_logins >= MaxWrongPassCount) { uint32 WrongPassBanTime = sConfig.GetIntDefault("WrongPass.BanTime", 600); bool WrongPassBanType = sConfig.GetBoolDefault("WrongPass.BanType", false); if (WrongPassBanType) { uint32 acc_id = fields[0].GetUInt32(); LoginDatabase.PExecute("INSERT INTO account_banned VALUES ('%u',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+'%u','MaNGOS realmd','Failed login autoban',1)", acc_id, WrongPassBanTime); BASIC_LOG("[AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times", _login.c_str(), WrongPassBanTime, failed_logins); } else { std::string current_ip = get_remote_address(); LoginDatabase.escape_string(current_ip); LoginDatabase.PExecute("INSERT INTO ip_banned VALUES ('%s',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+'%u','MaNGOS realmd','Failed login autoban')", current_ip.c_str(), WrongPassBanTime); BASIC_LOG("[AuthChallenge] IP %s got banned for '%u' seconds because account %s failed to authenticate '%u' times", current_ip.c_str(), WrongPassBanTime, _login.c_str(), failed_logins); } } delete loginfail; } } } return true; }
void LogonServer::Run(int argc, char ** argv) { UNIXTIME = time(NULL); g_localTime = *localtime(&UNIXTIME); #ifdef WIN32 char * config_file = "ascent-logonserver.conf"; #else char * config_file = (char*)CONFDIR "/ascent-logonserver.conf"; #endif int file_log_level = DEF_VALUE_NOT_SET; int screen_log_level = DEF_VALUE_NOT_SET; int do_check_conf = 0; int do_version = 0; struct ascent_option longopts[] = { { "checkconf", ascent_no_argument, &do_check_conf, 1 }, { "screenloglevel", ascent_required_argument, &screen_log_level, 1 }, { "fileloglevel", ascent_required_argument, &file_log_level, 1 }, { "version", ascent_no_argument, &do_version, 1 }, { "conf", ascent_required_argument, NULL, 'c' }, { 0, 0, 0, 0 } }; char c; while ((c = ascent_getopt_long_only(argc, argv, ":f:", longopts, NULL)) != -1) { switch (c) { case 'c': /* Log filename was set */ config_file = new char[strlen(ascent_optarg)]; strcpy(config_file,ascent_optarg); break; case 0: break; default: sLog.m_fileLogLevel = -1; sLog.m_screenLogLevel = 3; printf("Usage: %s [--checkconf] [--screenloglevel <level>] [--fileloglevel <level>] [--conf <filename>] [--version]\n", argv[0]); return; } } // Startup banner if(!do_version && !do_check_conf) { sLog.Init(-1, 3); } else { sLog.m_fileLogLevel = -1; sLog.m_screenLogLevel = 3; } sLog.outString(BANNER, BUILD_REVISION, CONFIG, PLATFORM_TEXT, ARCH); #ifdef REPACK sLog.outString("Repack: %s | Author: %s | %s\n", REPACK, REPACK_AUTHOR, REPACK_WEBSITE); #endif sLog.outString("=============================================================================="); sLog.outString(""); if(do_version) return; if(do_check_conf) { printf("Checking config file: %s\n", config_file); if(Config.MainConfig.SetSource(config_file, true)) printf(" Passed without errors.\n"); else printf(" Encountered one or more errors.\n"); /* test for die variables */ string die; if(Config.MainConfig.GetString("die", "msg", &die) || Config.MainConfig.GetString("die2", "msg", &die)) printf("Die directive received: %s", die.c_str()); return; } sLog.outString("The key combination <Ctrl-C> will safely shut down the server at any time."); sLog.outString(""); Log.Notice("System","Initializing Random Number Generators..."); Log.Notice("Config", "Loading Config Files..."); if(!Rehash()) return; Log.Notice("ThreadMgr", "Starting..."); ThreadPool.Startup(); if(!startdb()) return; Log.Notice("AccountMgr", "Starting..."); new AccountMgr; new IPBanner; Log.Notice("InfoCore", "Starting..."); new InformationCore; new PatchMgr; Log.Notice("AccountMgr", "Precaching accounts..."); sAccountMgr.ReloadAccounts(true); Log.Notice("AccountMgr", "%u accounts are loaded and ready.", sAccountMgr.GetCount()); Log.Line(); // Spawn periodic function caller thread for account reload every 10mins int atime = Config.MainConfig.GetIntDefault("Rates", "AccountRefresh",600); atime *= 1000; PeriodicFunctionCaller<AccountMgr> * pfc = new PeriodicFunctionCaller<AccountMgr>(AccountMgr::getSingletonPtr(),&AccountMgr::ReloadAccountsCallback, atime); ThreadPool.ExecuteTask(pfc); // Load conf settings.. uint32 cport = Config.MainConfig.GetIntDefault("Listen", "RealmListPort", 3724); uint32 sport = Config.MainConfig.GetIntDefault("Listen", "ServerPort", 8093); string host = Config.MainConfig.GetStringDefault("Listen", "Host", "0.0.0.0"); string shost = Config.MainConfig.GetStringDefault("Listen", "ISHost", host.c_str()); min_build = Config.MainConfig.GetIntDefault("Client", "MinBuild", 6180); max_build = Config.MainConfig.GetIntDefault("Client", "MaxBuild", 6999); string logon_pass = Config.MainConfig.GetStringDefault("LogonServer", "RemotePassword", "r3m0t3b4d"); Sha1Hash hash; hash.UpdateData(logon_pass); hash.Finalize(); memcpy(sql_hash, hash.GetDigest(), 20); ThreadPool.ExecuteTask(new LogonConsoleThread); new SocketMgr; new SocketGarbageCollector; sSocketMgr.SpawnWorkerThreads(); ListenSocket<AuthSocket> * cl = new ListenSocket<AuthSocket>(host.c_str(), cport); ListenSocket<LogonCommServerSocket> * sl = new ListenSocket<LogonCommServerSocket>(shost.c_str(), sport); // Spawn auth listener // Spawn interserver listener bool authsockcreated = cl->IsOpen(); bool intersockcreated = sl->IsOpen(); #ifdef WIN32 if(authsockcreated) ThreadPool.ExecuteTask(cl); if(intersockcreated) ThreadPool.ExecuteTask(sl); #endif // hook signals Log.Notice("LogonServer","Hooking signals..."); signal(SIGINT, _OnSignal); signal(SIGTERM, _OnSignal); signal(SIGABRT, _OnSignal); #ifdef _WIN32 signal(SIGBREAK, _OnSignal); #else signal(SIGHUP, _OnSignal); #endif /* write pid file */ FILE * fPid = fopen("logonserver.pid", "w"); if(fPid) { uint32 pid; #ifdef WIN32 pid = GetCurrentProcessId(); #else pid = getpid(); #endif fprintf(fPid, "%u", (unsigned int)pid); fclose(fPid); } uint32 loop_counter = 0; //ThreadPool.Gobble(); Log.Notice("LogonServer","Success! Ready for connections"); while(mrunning && authsockcreated && intersockcreated) { if(!(++loop_counter%10000)) // 2mins { ThreadPool.IntegrityCheck(2); //Logonserver don't need as many threads as world-server, 2 will do } if(!(loop_counter%100)) //100 loop ~ 1seconds { sInfoCore.TimeoutSockets(); sSocketGarbageCollector.Update(); CheckForDeadSockets(); // Flood Protection UNIXTIME = time(NULL); g_localTime = *localtime(&UNIXTIME); } PatchMgr::getSingleton().UpdateJobs(); Sleep(10); } Log.Notice("LogonServer","Shutting down..."); signal(SIGINT, 0); signal(SIGTERM, 0); signal(SIGABRT, 0); #ifdef _WIN32 signal(SIGBREAK, 0); #else signal(SIGHUP, 0); #endif pfc->kill(); cl->Close(); sl->Close(); sSocketMgr.CloseAll(); #ifdef WIN32 sSocketMgr.ShutdownThreads(); #endif sLogonConsole.Kill(); delete LogonConsole::getSingletonPtr(); // kill db sLog.outString("Waiting for database to close.."); sLogonSQL->EndThreads(); sLogonSQL->Shutdown(); delete sLogonSQL; ThreadPool.Shutdown(); // delete pid file remove("logonserver.pid"); delete AccountMgr::getSingletonPtr(); delete InformationCore::getSingletonPtr(); delete IPBanner::getSingletonPtr(); delete SocketMgr::getSingletonPtr(); delete SocketGarbageCollector::getSingletonPtr(); delete pfc; printf("Shutdown complete.\n"); }
void AccountMgr::AddAccount(Field* field) { Account * acct = new Account; Sha1Hash hash; string Username = field[1].GetString(); string Password = field[2].GetString(); string EncryptedPassword = field[3].GetString(); string GMFlags = field[4].GetString(); acct->AccountId = field[0].GetUInt32(); acct->AccountFlags = field[5].GetUInt8(); acct->Banned = field[6].GetUInt32(); if ( (uint32)UNIXTIME > acct->Banned && acct->Banned != 0 && acct->Banned != 1) //1 = perm ban? { //Accounts should be unbanned once the date is past their set expiry date. acct->Banned = 0; //me go boom :( //printf("Account %s's ban has expired.\n",acct->UsernamePtr->c_str()); sLogonSQL->Execute("UPDATE accounts SET banned = 0 WHERE acct=%u",acct->AccountId); } acct->SetGMFlags(GMFlags.c_str()); acct->Locale[0] = 'e'; acct->Locale[1] = 'n'; acct->Locale[2] = 'U'; acct->Locale[3] = 'S'; if(strcmp(field[7].GetString(), "enUS")) { // non-standard language forced memcpy(acct->Locale, field[7].GetString(), 4); acct->forcedLocale = true; } else acct->forcedLocale = false; acct->Muted = field[8].GetUInt32(); if ( (uint32)UNIXTIME > acct->Muted && acct->Muted != 0 && acct->Muted != 1) //1 = perm ban? { //Accounts should be unbanned once the date is past their set expiry date. acct->Muted= 0; //sLog.outDebug("Account %s's mute has expired.",acct->UsernamePtr->c_str()); sLogonSQL->Execute("UPDATE accounts SET muted = 0 WHERE acct=%u",acct->AccountId); } // Convert username/password to uppercase. this is needed ;) ASCENT_TOUPPER(Username); ASCENT_TOUPPER(Password); if( EncryptedPassword.size() > 0 ) { // prefer encrypted passwords over nonencrypted BigNumber bn; bn.SetHexStr( EncryptedPassword.c_str() ); if( bn.GetNumBytes() != 20 ) { printf("Account `%s` has incorrect number of bytes (%u) in encrypted password! Disabling.\n", Username.c_str(), bn.GetNumBytes()); memset(acct->SrpHash, 0, 20); } else { memcpy(acct->SrpHash, bn.AsByteArray(), 20); reverse_array(acct->SrpHash, 20); } } else { // Prehash the I value. hash.UpdateData((Username + ":" + Password)); hash.Finalize(); memcpy(acct->SrpHash, hash.GetDigest(), 20); } AccountDatabase[Username] = acct; }
int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) { // NOTE: ATM the socket is singlethread, have this in mind ... uint8 digest[20]; uint32 clientSeed, id, security; uint32 unk2; uint32 BuiltNumberClient; LocaleConstant locale; std::string account; Sha1Hash sha1; BigNumber v, s, g, N, K; WorldPacket packet, SendAddonPacked; // Read the content of the packet recvPacket >> BuiltNumberClient; recvPacket >> unk2; recvPacket >> account; recvPacket >> clientSeed; recvPacket.read(digest, 20); DEBUG_LOG("WorldSocket::HandleAuthSession: client %u, unk2 %u, account %s, clientseed %u", BuiltNumberClient, unk2, account.c_str(), clientSeed); // Check the version of client trying to connect if (!IsAcceptableClientBuild(BuiltNumberClient)) { packet.Initialize(SMSG_AUTH_RESPONSE, 1); packet << uint8(AUTH_VERSION_MISMATCH); SendPacket(packet); sLog.outError("WorldSocket::HandleAuthSession: Sent Auth Response (version mismatch)."); return -1; } // Get the account information from the realmd database std::string safe_account = account; // Duplicate, else will screw the SHA hash verification below LoginDatabase.escape_string(safe_account); // No SQL injection, username escaped. QueryResult* result = LoginDatabase.PQuery("SELECT " "id, " // 0 "gmlevel, " // 1 "sessionkey, " // 2 "last_ip, " // 3 "locked, " // 4 "v, " // 5 "s, " // 6 "mutetime, " // 7 "locale " // 8 "FROM account " "WHERE username = '******'", safe_account.c_str()); // Stop if the account is not found if (!result) { packet.Initialize(SMSG_AUTH_RESPONSE, 1); packet << uint8(AUTH_UNKNOWN_ACCOUNT); SendPacket(packet); sLog.outError("WorldSocket::HandleAuthSession: Sent Auth Response (unknown account)."); return -1; } Field* fields = result->Fetch(); N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7"); g.SetDword(7); v.SetHexStr(fields[5].GetString()); s.SetHexStr(fields[6].GetString()); const char* sStr = s.AsHexStr(); // Must be freed by OPENSSL_free() const char* vStr = v.AsHexStr(); // Must be freed by OPENSSL_free() DEBUG_LOG("WorldSocket::HandleAuthSession: (s,v) check s: %s v: %s", sStr, vStr); OPENSSL_free((void*) sStr); OPENSSL_free((void*) vStr); ///- Re-check ip locking (same check as in realmd). if (fields[4].GetUInt8() == 1) // if ip is locked { if (strcmp(fields[3].GetString(), GetRemoteAddress().c_str())) { packet.Initialize(SMSG_AUTH_RESPONSE, 1); packet << uint8(AUTH_FAILED); SendPacket(packet); delete result; BASIC_LOG("WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs)."); return -1; } } id = fields[0].GetUInt32(); security = fields[1].GetUInt16(); if (security > SEC_ADMINISTRATOR) // prevent invalid security settings in DB { security = SEC_ADMINISTRATOR; } K.SetHexStr(fields[2].GetString()); time_t mutetime = time_t (fields[7].GetUInt64()); locale = LocaleConstant(fields[8].GetUInt8()); if (locale >= MAX_LOCALE) { locale = LOCALE_enUS; } delete result; // Re-check account ban (same check as in realmd) QueryResult* banresult = LoginDatabase.PQuery("SELECT 1 FROM account_banned WHERE id = %u AND active = 1 AND (unbandate > UNIX_TIMESTAMP() OR unbandate = bandate)" "UNION " "SELECT 1 FROM ip_banned WHERE (unbandate = bandate OR unbandate > UNIX_TIMESTAMP()) AND ip = '%s'", id, GetRemoteAddress().c_str()); if (banresult) // if account banned { packet.Initialize(SMSG_AUTH_RESPONSE, 1); packet << uint8(AUTH_BANNED); SendPacket(packet); delete banresult; sLog.outError("WorldSocket::HandleAuthSession: Sent Auth Response (Account banned)."); return -1; } // Check locked state for server AccountTypes allowedAccountType = sWorld.GetPlayerSecurityLimit(); if (allowedAccountType > SEC_PLAYER && AccountTypes(security) < allowedAccountType) { WorldPacket Packet(SMSG_AUTH_RESPONSE, 1); Packet << uint8(AUTH_UNAVAILABLE); SendPacket(packet); BASIC_LOG("WorldSocket::HandleAuthSession: User tries to login but his security level is not enough"); return -1; } // Check that Key and account name are the same on client and server Sha1Hash sha; uint32 t = 0; uint32 seed = m_Seed; sha.UpdateData(account); sha.UpdateData((uint8*) & t, 4); sha.UpdateData((uint8*) & clientSeed, 4); sha.UpdateData((uint8*) & seed, 4); sha.UpdateBigNumbers(&K, NULL); sha.Finalize(); if (memcmp(sha.GetDigest(), digest, 20)) { packet.Initialize(SMSG_AUTH_RESPONSE, 1); packet << uint8(AUTH_FAILED); SendPacket(packet); sLog.outError("WorldSocket::HandleAuthSession: Sent Auth Response (authentification failed)."); return -1; } std::string address = GetRemoteAddress(); DEBUG_LOG("WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.", account.c_str(), address.c_str()); // Update the last_ip in the database // No SQL injection, username escaped. static SqlStatementID updAccount; SqlStatement stmt = LoginDatabase.CreateStatement(updAccount, "UPDATE account SET last_ip = ? WHERE username = ?"); stmt.PExecute(address.c_str(), account.c_str()); // NOTE ATM the socket is single-threaded, have this in mind ... ACE_NEW_RETURN(m_Session, WorldSession(id, this, AccountTypes(security), mutetime, locale), -1); m_Crypt.SetKey(K.AsByteArray(), 40); m_Crypt.Init(); m_Session->LoadTutorialsData(); // In case needed sometime the second arg is in microseconds 1 000 000 = 1 sec ACE_OS::sleep(ACE_Time_Value(0, 10000)); sWorld.AddSession(m_Session); // Create and send the Addon packet if (sAddOnHandler.BuildAddonPacket(&recvPacket, &SendAddonPacked)) { SendPacket(SendAddonPacked); } return 0; }
/// Logon Challenge command handler bool AuthSocket::_HandleLogonChallenge() { DEBUG_LOG("Entering _HandleLogonChallenge"); if (recv_len() < sizeof(sAuthLogonChallenge_C)) return false; ///- Read the first 4 bytes (header) to get the length of the remaining of the packet std::vector<uint8> buf; buf.resize(4); recv((char *)&buf[0], 4); EndianConvert(*((uint16*)(buf[0]))); uint16 remaining = ((sAuthLogonChallenge_C *)&buf[0])->size; DEBUG_LOG("[AuthChallenge] got header, body is %#04x bytes", remaining); if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size()) || (recv_len() < remaining)) return false; //No big fear of memory outage (size is int16, i.e. < 65536) buf.resize(remaining + buf.size() + 1); buf[buf.size() - 1] = 0; sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)&buf[0]; ///- Read the remaining of the packet recv((char *)&buf[4], remaining); DEBUG_LOG("[AuthChallenge] got full packet, %#04x bytes", ch->size); DEBUG_LOG("[AuthChallenge] name(%d): '%s'", ch->I_len, ch->I); // BigEndian code, nop in little endian case // size already converted EndianConvert(*((uint32*)(&ch->gamename[0]))); EndianConvert(ch->build); EndianConvert(*((uint32*)(&ch->platform[0]))); EndianConvert(*((uint32*)(&ch->os[0]))); EndianConvert(*((uint32*)(&ch->country[0]))); EndianConvert(ch->timezone_bias); EndianConvert(ch->ip); ByteBuffer pkt; _login = (const char*)ch->I; _build = ch->build; ///- Normalize account name //utf8ToUpperOnlyLatin(_login); -- client already send account in expected form //Escape the user login to avoid further SQL injection //Memory will be freed on AuthSocket object destruction _safelogin = _login; LoginDatabase.escape_string(_safelogin); pkt << (uint8) CMD_AUTH_LOGON_CHALLENGE; pkt << (uint8) 0x00; ///- Verify that this IP is not in the ip_banned table // No SQL injection possible (paste the IP address as passed by the socket) std::string address = get_remote_address(); LoginDatabase.escape_string(address); QueryResult *result = LoginDatabase.PQuery("SELECT unbandate FROM ip_banned WHERE " // permanent still banned "(unbandate = bandate OR unbandate > UNIX_TIMESTAMP()) AND ip = '%s'", address.c_str()); if (result) { pkt << (uint8)WOW_FAIL_BANNED; BASIC_LOG("[AuthChallenge] Banned ip %s tries to login!", get_remote_address().c_str()); delete result; } else { ///- Get the account details from the account table // No SQL injection (escaped user name) result = LoginDatabase.PQuery("SELECT sha_pass_hash,id,locked,last_ip,gmlevel,v,s FROM account WHERE username = '******'",_safelogin.c_str ()); if( result ) { ///- If the IP is 'locked', check that the player comes indeed from the correct IP address bool locked = false; if((*result)[2].GetUInt8() == 1) // if ip is locked { DEBUG_LOG("[AuthChallenge] Account '%s' is locked to IP - '%s'", _login.c_str(), (*result)[3].GetString()); DEBUG_LOG("[AuthChallenge] Player address is '%s'", get_remote_address().c_str()); if ( strcmp((*result)[3].GetString(),get_remote_address().c_str()) ) { DEBUG_LOG("[AuthChallenge] Account IP differs"); pkt << (uint8) WOW_FAIL_SUSPENDED; locked=true; } else { DEBUG_LOG("[AuthChallenge] Account IP matches"); } } else { DEBUG_LOG("[AuthChallenge] Account '%s' is not locked to ip", _login.c_str()); } if (!locked) { ///- If the account is banned, reject the logon attempt QueryResult *banresult = LoginDatabase.PQuery("SELECT bandate,unbandate FROM account_banned WHERE " "id = %u AND active = 1 AND (unbandate > UNIX_TIMESTAMP() OR unbandate = bandate)", (*result)[1].GetUInt32()); if(banresult) { if((*banresult)[0].GetUInt64() == (*banresult)[1].GetUInt64()) { pkt << (uint8) WOW_FAIL_BANNED; BASIC_LOG("[AuthChallenge] Banned account %s tries to login!",_login.c_str ()); } else { pkt << (uint8) WOW_FAIL_SUSPENDED; BASIC_LOG("[AuthChallenge] Temporarily banned account %s tries to login!",_login.c_str ()); } delete banresult; } else { ///- Get the password from the account table, upper it, and make the SRP6 calculation std::string rI = (*result)[0].GetCppString(); ///- Don't calculate (v, s) if there are already some in the database std::string databaseV = (*result)[5].GetCppString(); std::string databaseS = (*result)[6].GetCppString(); DEBUG_LOG("database authentication values: v='%s' s='%s'", databaseV.c_str(), databaseS.c_str()); // multiply with 2, bytes are stored as hexstring if(databaseV.size() != s_BYTE_SIZE*2 || databaseS.size() != s_BYTE_SIZE*2) _SetVSFields(rI); else { s.SetHexStr(databaseS.c_str()); v.SetHexStr(databaseV.c_str()); } b.SetRand(19 * 8); BigNumber gmod = g.ModExp(b, N); B = ((v * 3) + gmod) % N; MANGOS_ASSERT(gmod.GetNumBytes() <= 32); BigNumber unk3; unk3.SetRand(16 * 8); ///- Fill the response packet with the result pkt << uint8(WOW_SUCCESS); // B may be calculated < 32B so we force minimal length to 32B pkt.append(B.AsByteArray(32), 32); // 32 bytes pkt << uint8(1); pkt.append(g.AsByteArray(), 1); pkt << uint8(32); pkt.append(N.AsByteArray(32), 32); pkt.append(s.AsByteArray(), s.GetNumBytes());// 32 bytes pkt.append(unk3.AsByteArray(16), 16); uint8 securityFlags = 0; pkt << uint8(securityFlags); // security flags (0x0...0x04) if(securityFlags & 0x01) // PIN input { pkt << uint32(0); pkt << uint64(0) << uint64(0); // 16 bytes hash? } if(securityFlags & 0x02) // Matrix input { pkt << uint8(0); pkt << uint8(0); pkt << uint8(0); pkt << uint8(0); pkt << uint64(0); } if(securityFlags & 0x04) // Security token input { pkt << uint8(1); } uint8 secLevel = (*result)[4].GetUInt8(); _accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR; _localizationName.resize(4); for(int i = 0; i < 4; ++i) _localizationName[i] = ch->country[4-i-1]; BASIC_LOG("[AuthChallenge] account %s is using '%c%c%c%c' locale (%u)", _login.c_str (), ch->country[3], ch->country[2], ch->country[1], ch->country[0], GetLocaleByName(_localizationName)); } } delete result; } else // no account { if(_autoreg) { ///- Get the password from the account table, upper it, and make the SRP6 calculation std::transform(_safelogin.begin(), _safelogin.end(), _safelogin.begin(), std::towupper); Sha1Hash sha; std::string sI = _safelogin + ":" + _safelogin; sha.UpdateData(sI); sha.Finalize(); BigNumber bn; bn.SetBinary(sha.GetDigest(), sha.GetLength()); uint8 *val = bn.AsByteArray(); std::reverse(val, val+bn.GetNumBytes()); bn.SetBinary(val, bn.GetNumBytes()); const char* rI = bn.AsHexStr(); _SetVSFields(rI); OPENSSL_free((void*)rI); b.SetRand(19 * 8); BigNumber gmod=g.ModExp(b, N); B = ((v * 3) + gmod) % N; if (B.GetNumBytes() < 32) sLog.outDetail("Interesting, calculation of B in realmd is < 32."); MANGOS_ASSERT(gmod.GetNumBytes() <= 32); BigNumber unk3; unk3.SetRand(16*8); ///- Fill the response packet with the result pkt << (uint8)WOW_SUCCESS; pkt.append(B.AsByteArray(), 32); pkt << (uint8)1; pkt.append(g.AsByteArray(), 1); pkt << (uint8)32; pkt.append(N.AsByteArray(), 32); pkt.append(s.AsByteArray(), s.GetNumBytes()); pkt.append(unk3.AsByteArray(), 16); pkt << (uint8)0; // Added in 1.12.x client branch } else { pkt<< (uint8) WOW_FAIL_UNKNOWN_ACCOUNT; } } } send((char const*)pkt.contents(), pkt.size()); return true; }