diff --git a/BNetServer/Managers/SessionManager.cs b/BNetServer/Managers/SessionManager.cs index d906e3dc6..ebae5b51b 100644 --- a/BNetServer/Managers/SessionManager.cs +++ b/BNetServer/Managers/SessionManager.cs @@ -84,9 +84,6 @@ namespace BNetServer input.Label = "Log In"; _formInputs.Inputs.Add(input); - _loginTicketCleanupTimer = new Timer(CleanupLoginTickets); - _loginTicketCleanupTimer.Change(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10)); - _certificate = new X509Certificate2("BNetServer.pfx"); return true; @@ -110,52 +107,12 @@ namespace BNetServer return _certificate; } - public void AddLoginTicket(string id, AccountInfo accountInfo) - { - _validLoginTickets[id] = new LoginTicket() { Id = id, Account = accountInfo, ExpiryTime = Time.UnixTime + 10 }; - } - - public AccountInfo VerifyLoginTicket(string id) - { - var accountInfo = _validLoginTickets.LookupByKey(id); - if (accountInfo != null) - { - if (accountInfo.ExpiryTime > Time.UnixTime) - { - _validLoginTickets.Remove(id); - return accountInfo.Account; - } - } - - return null; - } - - public void CleanupLoginTickets(object state) - { - long now = Time.UnixTime; - - foreach (var pair in _validLoginTickets.ToList()) - { - if (pair.Value.ExpiryTime < now) - _validLoginTickets.Remove(pair.Key); - } - } - FormInputs _formInputs; IPEndPoint _externalAddress; IPEndPoint _localAddress; - Dictionary _validLoginTickets = new Dictionary(); - Timer _loginTicketCleanupTimer; X509Certificate2 _certificate; } - class LoginTicket - { - public string Id; - public AccountInfo Account; - public long ExpiryTime; - } - public enum BanMode { Ip = 0, diff --git a/BNetServer/Networking/RestSession.cs b/BNetServer/Networking/RestSession.cs index 69e8940eb..d7ffa2249 100644 --- a/BNetServer/Networking/RestSession.cs +++ b/BNetServer/Networking/RestSession.cs @@ -92,112 +92,91 @@ namespace BNetServer.Networking } } - PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_BNET_ACCOUNT_INFO); + PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_BNET_AUTHENTICATION); stmt.AddValue(0, login); SQLResult result = DB.Login.Query(stmt); - if (result.IsEmpty()) + if (!result.IsEmpty()) { + uint accountId = result.Read(0); + string pass_hash = result.Read(1); + uint failedLogins = result.Read(2); + string loginTicket = result.Read(3); + uint loginTicketExpiry = result.Read(4); + bool isBanned = result.Read(5) != 0; + + if (CalculateShaPassHash(login, password) == pass_hash) + { + if (loginTicket.IsEmpty() || loginTicketExpiry < Time.UnixTime) + { + byte[] ticket = new byte[0].GenerateRandomKey(20); + loginTicket = "TC-" + ticket.ToHexString(); + } + + stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_BNET_AUTHENTICATION); + stmt.AddValue(0, loginTicket); + stmt.AddValue(1, Time.UnixTime + 3600); + stmt.AddValue(2, accountId); + + DB.Login.Execute(stmt); + loginResult.LoginTicket = loginTicket; + } + else if (!isBanned) + { + uint maxWrongPassword = ConfigMgr.GetDefaultValue("WrongPass.MaxCount", 0u); + + if (ConfigMgr.GetDefaultValue("WrongPass.Logging", false)) + Log.outDebug(LogFilter.Network, "[{0}, Account {1}, Id {2}] Attempted to connect with wrong password!", request.Host, login, accountId); + + if (maxWrongPassword != 0) + { + SQLTransaction trans = new SQLTransaction(); + stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_BNET_FAILED_LOGINS); + stmt.AddValue(0, accountId); + trans.Append(stmt); + + ++failedLogins; + + Log.outDebug(LogFilter.Network, "MaxWrongPass : {0}, failed_login : {1}", maxWrongPassword, accountId); + + if (failedLogins >= maxWrongPassword) + { + BanMode banType = ConfigMgr.GetDefaultValue("WrongPass.BanType", BanMode.Ip); + int banTime = ConfigMgr.GetDefaultValue("WrongPass.BanTime", 600); + + if (banType == BanMode.Account) + { + stmt = DB.Login.GetPreparedStatement(LoginStatements.INS_BNET_ACCOUNT_AUTO_BANNED); + stmt.AddValue(0, accountId); + } + else + { + stmt = DB.Login.GetPreparedStatement(LoginStatements.INS_IP_AUTO_BANNED); + stmt.AddValue(0, request.Host); + } + + stmt.AddValue(1, banTime); + trans.Append(stmt); + + stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_BNET_RESET_FAILED_LOGINS); + stmt.AddValue(0, accountId); + trans.Append(stmt); + } + + DB.Login.CommitTransaction(trans); + } + } + + loginResult.AuthenticationState = "DONE"; + SendResponse(HttpCode.Ok, loginResult); + } + else + { loginResult.AuthenticationState = "LOGIN"; loginResult.ErrorCode = "UNABLE_TO_DECODE"; loginResult.ErrorMessage = "There was an internal error while connecting to Battle.net. Please try again later."; SendResponse(HttpCode.BadRequest, loginResult); - - return; } - - string pass_hash = result.Read(13); - - var accountInfo = new AccountInfo(); - accountInfo.LoadResult(result); - - if (CalculateShaPassHash(login, password) == pass_hash) - { - stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_BNET_CHARACTER_COUNTS_BY_BNET_ID); - stmt.AddValue(0, accountInfo.Id); - - SQLResult characterCountsResult = DB.Login.Query(stmt); - if (!characterCountsResult.IsEmpty()) - { - do - { - accountInfo.GameAccounts[characterCountsResult.Read(0)] - .CharacterCounts[new RealmHandle(characterCountsResult.Read(3), characterCountsResult.Read(4), characterCountsResult.Read(2)).GetAddress()] = characterCountsResult.Read(1); - - } while (characterCountsResult.NextRow()); - } - - - stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_BNET_LAST_PLAYER_CHARACTERS); - stmt.AddValue(0, accountInfo.Id); - - SQLResult lastPlayerCharactersResult = DB.Login.Query(stmt); - if (!lastPlayerCharactersResult.IsEmpty()) - { - RealmHandle realmId = new RealmHandle(lastPlayerCharactersResult.Read(1), lastPlayerCharactersResult.Read(2), lastPlayerCharactersResult.Read(3)); - - LastPlayedCharacterInfo lastPlayedCharacter = new LastPlayedCharacterInfo(); - lastPlayedCharacter.RealmId = realmId; - lastPlayedCharacter.CharacterName = lastPlayerCharactersResult.Read(4); - lastPlayedCharacter.CharacterGUID = lastPlayerCharactersResult.Read(5); - lastPlayedCharacter.LastPlayedTime = lastPlayerCharactersResult.Read(6); - - accountInfo.GameAccounts[lastPlayerCharactersResult.Read(0)].LastPlayedCharacters[realmId.GetSubRegionAddress()] = lastPlayedCharacter; - } - - byte[] ticket = new byte[0].GenerateRandomKey(20); - loginResult.LoginTicket = "TC-" + ticket.ToHexString(); - - Global.SessionMgr.AddLoginTicket(loginResult.LoginTicket, accountInfo); - } - else if (!accountInfo.IsBanned) - { - uint maxWrongPassword = ConfigMgr.GetDefaultValue("WrongPass.MaxCount", 0u); - - if (ConfigMgr.GetDefaultValue("WrongPass.Logging", false)) - Log.outDebug(LogFilter.Network, "[{0}, Account {1}, Id {2}] Attempted to connect with wrong password!", request.Host, login, accountInfo.Id); - - if (maxWrongPassword != 0) - { - SQLTransaction trans = new SQLTransaction(); - stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_BNET_FAILED_LOGINS); - stmt.AddValue(0, accountInfo.Id); - trans.Append(stmt); - - ++accountInfo.FailedLogins; - - Log.outDebug(LogFilter.Network, "MaxWrongPass : {0}, failed_login : {1}", maxWrongPassword, accountInfo.Id); - - if (accountInfo.FailedLogins >= maxWrongPassword) - { - BanMode banType = ConfigMgr.GetDefaultValue("WrongPass.BanType", BanMode.Ip); - int banTime = ConfigMgr.GetDefaultValue("WrongPass.BanTime", 600); - - if (banType == BanMode.Account) - { - stmt = DB.Login.GetPreparedStatement(LoginStatements.INS_BNET_ACCOUNT_AUTO_BANNED); - stmt.AddValue(0, accountInfo.Id); - } - else - { - stmt = DB.Login.GetPreparedStatement(LoginStatements.INS_IP_AUTO_BANNED); - stmt.AddValue(0, request.Host); - } - - stmt.AddValue(1, banTime); - trans.Append(stmt); - - stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_BNET_RESET_FAILED_LOGINS); - stmt.AddValue(0, accountInfo.Id); - trans.Append(stmt); - } - - DB.Login.CommitTransaction(trans); - } - } - - loginResult.AuthenticationState = "DONE"; - SendResponse(HttpCode.Ok, loginResult); } void SendResponse(HttpCode code, T response) diff --git a/BNetServer/Networking/Session.cs b/BNetServer/Networking/Session.cs index 4737b034a..ff8788b1c 100644 --- a/BNetServer/Networking/Session.cs +++ b/BNetServer/Networking/Session.cs @@ -214,6 +214,7 @@ namespace BNetServer.Networking _locale = logonRequest.Locale; _os = logonRequest.Platform; + _build = (uint)logonRequest.ApplicationVersion; var endpoint = Global.SessionMgr.GetAddressForClient(GetRemoteIpAddress()); @@ -227,12 +228,54 @@ namespace BNetServer.Networking public BattlenetRpcErrorCode HandleVerifyWebCredentials(Bgs.Protocol.Authentication.V1.VerifyWebCredentialsRequest verifyWebCredentialsRequest) { - Bgs.Protocol.Authentication.V1.LogonResult logonResult = new Bgs.Protocol.Authentication.V1.LogonResult(); - logonResult.ErrorCode = 0; - _accountInfo = Global.SessionMgr.VerifyLoginTicket(verifyWebCredentialsRequest.WebCredentials.ToStringUtf8()); - if (_accountInfo == null) + PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_BNET_ACCOUNT_INFO); + stmt.AddValue(0, verifyWebCredentialsRequest.WebCredentials.ToStringUtf8()); + + SQLResult result = DB.Login.Query(stmt); + if (result.IsEmpty()) return BattlenetRpcErrorCode.Denied; + _accountInfo = new AccountInfo(); + _accountInfo.LoadResult(result); + + if (_accountInfo.LoginTicketExpiry < Time.UnixTime) + return BattlenetRpcErrorCode.TimedOut; + + stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_BNET_CHARACTER_COUNTS_BY_BNET_ID); + stmt.AddValue(0, _accountInfo.Id); + + SQLResult characterCountsResult = DB.Login.Query(stmt); + if (!characterCountsResult.IsEmpty()) + { + do + { + RealmHandle realmId = new RealmHandle(characterCountsResult.Read(3), characterCountsResult.Read(4), characterCountsResult.Read(2)); + _accountInfo.GameAccounts[characterCountsResult.Read(0)].CharacterCounts[realmId.GetAddress()] = characterCountsResult.Read(1); + + } while (characterCountsResult.NextRow()); + } + + stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_BNET_LAST_PLAYER_CHARACTERS); + stmt.AddValue(0, _accountInfo.Id); + + SQLResult lastPlayerCharactersResult = DB.Login.Query(stmt); + if (!lastPlayerCharactersResult.IsEmpty()) + { + do + { + RealmHandle realmId = new RealmHandle(lastPlayerCharactersResult.Read(1), lastPlayerCharactersResult.Read(2), lastPlayerCharactersResult.Read(3)); + + LastPlayedCharacterInfo lastPlayedCharacter = new LastPlayedCharacterInfo(); + lastPlayedCharacter.RealmId = realmId; + lastPlayedCharacter.CharacterName = lastPlayerCharactersResult.Read(4); + lastPlayedCharacter.CharacterGUID = lastPlayerCharactersResult.Read(5); + lastPlayedCharacter.LastPlayedTime = lastPlayerCharactersResult.Read(6); + + _accountInfo.GameAccounts[lastPlayerCharactersResult.Read(0)].LastPlayedCharacters[realmId.GetSubRegionAddress()] = lastPlayedCharacter; + + } while (lastPlayerCharactersResult.NextRow()); + } + string ip_address = GetRemoteIpAddress().ToString(); // If the IP is 'locked', check that the player comes indeed from the correct IP address @@ -274,6 +317,8 @@ namespace BNetServer.Networking } } + Bgs.Protocol.Authentication.V1.LogonResult logonResult = new Bgs.Protocol.Authentication.V1.LogonResult(); + logonResult.ErrorCode = 0; logonResult.AccountId = new EntityId(); logonResult.AccountId.Low = _accountInfo.Id; logonResult.AccountId.High = 0x100000000000000; @@ -412,15 +457,16 @@ namespace BNetServer.Networking if (_gameAccountInfo == null) return BattlenetRpcErrorCode.UtilServerInvalidIdentityArgs; + bool clientInfoOk = false; Variant clientInfo = GetParam(Params, "Param_ClientInfo"); if (clientInfo != null) { var realmListTicketClientInformation = Json.CreateObject(clientInfo.BlobValue.ToStringUtf8(), true); - _build = (uint)realmListTicketClientInformation.Info.ClientVersion.Build; + clientInfoOk = true; _clientSecret.AddRange(realmListTicketClientInformation.Info.Secret.Select(x => Convert.ToByte(x)).ToArray()); } - if (_build == 0) + if (!clientInfoOk) return BattlenetRpcErrorCode.WowServicesDeniedRealmListTicket; PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.UPD_BNET_LAST_LOGIN_INFO); @@ -592,7 +638,7 @@ namespace BNetServer.Networking IsLockedToIP = result.Read(2); LockCountry = result.Read(3); LastIP = result.Read(4); - FailedLogins = result.Read(5); + LoginTicketExpiry = result.Read(5); IsBanned = result.Read(6) != 0; IsPermanenetlyBanned = result.Read(7) != 0; PasswordVerifier = result.Read(9); @@ -614,7 +660,7 @@ namespace BNetServer.Networking public bool IsLockedToIP; public string LockCountry; public string LastIP; - public uint FailedLogins; + public uint LoginTicketExpiry; public bool IsBanned; public bool IsPermanenetlyBanned; public string PasswordVerifier; diff --git a/Framework/Database/Databases/LoginDatabase.cs b/Framework/Database/Databases/LoginDatabase.cs index 60ce9f805..f42b43fba 100644 --- a/Framework/Database/Databases/LoginDatabase.cs +++ b/Framework/Database/Databases/LoginDatabase.cs @@ -21,7 +21,7 @@ namespace Framework.Database { public override void PreparedStatements() { - const string BnetAccountInfo = "ba.id, ba.email, ba.locked, ba.lock_country, ba.last_ip, ba.failed_logins, bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, bab.unbandate = bab.bandate"; + const string BnetAccountInfo = "ba.id, ba.email, ba.locked, ba.lock_country, ba.last_ip, ba.LoginTicketExpiry, bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, bab.unbandate = bab.bandate"; const string BnetGameAccountInfo = "a.id, a.username, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, ab.unbandate = ab.bandate, aa.gmlevel"; PrepareStatement(LoginStatements.SEL_REALMLIST, "SELECT id, name, address, localAddress, localSubnetMask, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild, Region, Battlegroup FROM realmlist WHERE flag <> 3 ORDER BY name"); @@ -117,9 +117,11 @@ namespace Framework.Database PrepareStatement(LoginStatements.SEL_ACCOUNT_MUTE_INFO, "SELECT mutedate, mutetime, mutereason, mutedby FROM account_muted WHERE guid = ? ORDER BY mutedate ASC"); PrepareStatement(LoginStatements.DEL_ACCOUNT_MUTED, "DELETE FROM account_muted WHERE guid = ?"); - PrepareStatement(LoginStatements.SEL_BNET_ACCOUNT_INFO, "SELECT " + BnetAccountInfo + ", " + BnetGameAccountInfo + ", ba.sha_pass_hash" + + PrepareStatement(LoginStatements.SEL_BNET_AUTHENTICATION, "SELECT ba.id, ba.sha_pass_hash, ba.failed_logins, ba.LoginTicket, ba.LoginTicketExpiry, bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id WHERE email = ?"); + PrepareStatement(LoginStatements.UPD_BNET_AUTHENTICATION, "UPDATE battlenet_accounts SET LoginTicket = ?, LoginTicketExpiry = ? WHERE id = ?"); + PrepareStatement(LoginStatements.SEL_BNET_ACCOUNT_INFO, "SELECT " + BnetAccountInfo + ", " + BnetGameAccountInfo + " FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account a ON ba.id = a.battlenet_account" + - " LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID = -1 WHERE ba.email = ? ORDER BY a.id"); + " LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID = -1 WHERE ba.LoginTicket = ? ORDER BY a.id"); PrepareStatement(LoginStatements.UPD_BNET_LAST_LOGIN_INFO, "UPDATE battlenet_accounts SET last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE id = ?"); PrepareStatement(LoginStatements.UPD_BNET_GAME_ACCOUNT_LOGIN_INFO, "UPDATE account SET sessionkey = ?, last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE username = ?"); PrepareStatement(LoginStatements.SEL_BNET_CHARACTER_COUNTS_BY_ACCOUNT_ID, "SELECT rc.acctid, rc.numchars, r.id, r.Region, r.Battlegroup FROM realmcharacters rc INNER JOIN realmlist r ON rc.realmid = r.id WHERE rc.acctid = ?"); @@ -263,6 +265,8 @@ namespace Framework.Database SEL_ACCOUNT_MUTE_INFO, DEL_ACCOUNT_MUTED, + SEL_BNET_AUTHENTICATION, + UPD_BNET_AUTHENTICATION, SEL_BNET_ACCOUNT_INFO, UPD_BNET_LAST_LOGIN_INFO, UPD_BNET_GAME_ACCOUNT_LOGIN_INFO,