LoginController.java 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837
  1. /*
  2. * This program is free software: you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation, either version 3 of the License, or (at your option) any later
  5. * version.
  6. *
  7. * This program is distributed in the hope that it will be useful, but WITHOUT
  8. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  9. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  10. * details.
  11. *
  12. * You should have received a copy of the GNU General Public License along with
  13. * this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. package com.l2jserver.loginserver;
  16. import java.net.InetAddress;
  17. import java.net.UnknownHostException;
  18. import java.security.GeneralSecurityException;
  19. import java.security.KeyPairGenerator;
  20. import java.security.MessageDigest;
  21. import java.security.interfaces.RSAPrivateKey;
  22. import java.security.spec.RSAKeyGenParameterSpec;
  23. import java.sql.Connection;
  24. import java.sql.PreparedStatement;
  25. import java.sql.ResultSet;
  26. import java.util.Collection;
  27. import java.util.Map;
  28. import java.util.logging.Level;
  29. import java.util.logging.Logger;
  30. import javax.crypto.Cipher;
  31. import javolution.util.FastMap;
  32. import com.l2jserver.Config;
  33. import com.l2jserver.L2DatabaseFactory;
  34. import com.l2jserver.loginserver.GameServerTable.GameServerInfo;
  35. import com.l2jserver.loginserver.network.L2LoginClient;
  36. import com.l2jserver.loginserver.network.gameserverpackets.ServerStatus;
  37. import com.l2jserver.loginserver.network.serverpackets.LoginFail.LoginFailReason;
  38. import com.l2jserver.util.Base64;
  39. import com.l2jserver.util.Rnd;
  40. import com.l2jserver.util.crypt.ScrambledKeyPair;
  41. import com.l2jserver.util.lib.Log;
  42. /**
  43. * This class ...
  44. * @version $Revision: 1.7.4.3 $ $Date: 2005/03/27 15:30:09 $
  45. */
  46. public class LoginController
  47. {
  48. protected static final Logger _log = Logger.getLogger(LoginController.class.getName());
  49. private static LoginController _instance;
  50. /** Time before kicking the client if he didn't logged yet */
  51. public static final int LOGIN_TIMEOUT = 60 * 1000;
  52. /** Authed Clients on LoginServer */
  53. protected FastMap<String, L2LoginClient> _loginServerClients = new FastMap<String, L2LoginClient>().shared();
  54. private final Map<String, BanInfo> _bannedIps = new FastMap<String, BanInfo>().shared();
  55. private final Map<InetAddress, FailedLoginAttempt> _hackProtection;
  56. protected ScrambledKeyPair[] _keyPairs;
  57. private final Thread _purge;
  58. protected byte[][] _blowfishKeys;
  59. private static final int BLOWFISH_KEYS = 20;
  60. private static final String USER_INFO_SELECT = "SELECT password, IF(? > value OR value IS NULL, accessLevel, -1) AS accessLevel, lastServer, userIp " + "FROM accounts LEFT JOIN (account_data) ON (account_data.account_name=accounts.login AND account_data.var=\"ban_temp\") WHERE login=?";
  61. public static void load() throws GeneralSecurityException
  62. {
  63. synchronized (LoginController.class)
  64. {
  65. if (_instance == null)
  66. {
  67. _instance = new LoginController();
  68. }
  69. else
  70. {
  71. throw new IllegalStateException("LoginController can only be loaded a single time.");
  72. }
  73. }
  74. }
  75. public static LoginController getInstance()
  76. {
  77. return _instance;
  78. }
  79. private LoginController() throws GeneralSecurityException
  80. {
  81. _log.info("Loading LoginController...");
  82. _hackProtection = new FastMap<InetAddress, FailedLoginAttempt>();
  83. _keyPairs = new ScrambledKeyPair[10];
  84. KeyPairGenerator keygen = null;
  85. keygen = KeyPairGenerator.getInstance("RSA");
  86. RSAKeyGenParameterSpec spec = new RSAKeyGenParameterSpec(1024, RSAKeyGenParameterSpec.F4);
  87. keygen.initialize(spec);
  88. // generate the initial set of keys
  89. for (int i = 0; i < 10; i++)
  90. {
  91. _keyPairs[i] = new ScrambledKeyPair(keygen.generateKeyPair());
  92. }
  93. _log.info("Cached 10 KeyPairs for RSA communication");
  94. testCipher((RSAPrivateKey) _keyPairs[0]._pair.getPrivate());
  95. // Store keys for blowfish communication
  96. generateBlowFishKeys();
  97. _purge = new PurgeThread();
  98. _purge.setDaemon(true);
  99. _purge.start();
  100. }
  101. /**
  102. * This is mostly to force the initialization of the Crypto Implementation, avoiding it being done on runtime when its first needed.<BR>
  103. * In short it avoids the worst-case execution time on runtime by doing it on loading.
  104. * @param key Any private RSA Key just for testing purposes.
  105. * @throws GeneralSecurityException if a underlying exception was thrown by the Cipher
  106. */
  107. private void testCipher(RSAPrivateKey key) throws GeneralSecurityException
  108. {
  109. // avoid worst-case execution, KenM
  110. Cipher rsaCipher = Cipher.getInstance("RSA/ECB/nopadding");
  111. rsaCipher.init(Cipher.DECRYPT_MODE, key);
  112. }
  113. private void generateBlowFishKeys()
  114. {
  115. _blowfishKeys = new byte[BLOWFISH_KEYS][16];
  116. for (int i = 0; i < BLOWFISH_KEYS; i++)
  117. {
  118. for (int j = 0; j < _blowfishKeys[i].length; j++)
  119. {
  120. _blowfishKeys[i][j] = (byte) (Rnd.nextInt(255) + 1);
  121. }
  122. }
  123. _log.info("Stored " + _blowfishKeys.length + " keys for Blowfish communication");
  124. }
  125. /**
  126. * @return Returns a random key
  127. */
  128. public byte[] getBlowfishKey()
  129. {
  130. return _blowfishKeys[(int) (Math.random() * BLOWFISH_KEYS)];
  131. }
  132. public SessionKey assignSessionKeyToClient(String account, L2LoginClient client)
  133. {
  134. SessionKey key;
  135. key = new SessionKey(Rnd.nextInt(), Rnd.nextInt(), Rnd.nextInt(), Rnd.nextInt());
  136. _loginServerClients.put(account, client);
  137. return key;
  138. }
  139. public void removeAuthedLoginClient(String account)
  140. {
  141. if (account == null)
  142. {
  143. return;
  144. }
  145. _loginServerClients.remove(account);
  146. }
  147. public L2LoginClient getAuthedClient(String account)
  148. {
  149. return _loginServerClients.get(account);
  150. }
  151. public static enum AuthLoginResult
  152. {
  153. INVALID_PASSWORD,
  154. ACCOUNT_BANNED,
  155. ALREADY_ON_LS,
  156. ALREADY_ON_GS,
  157. AUTH_SUCCESS
  158. }
  159. public AuthLoginResult tryAuthLogin(String account, String password, L2LoginClient client)
  160. {
  161. AuthLoginResult ret = AuthLoginResult.INVALID_PASSWORD;
  162. // check auth
  163. if (loginValid(account, password, client))
  164. {
  165. // login was successful, verify presence on Gameservers
  166. ret = AuthLoginResult.ALREADY_ON_GS;
  167. if (!isAccountInAnyGameServer(account))
  168. {
  169. // account isnt on any GS verify LS itself
  170. ret = AuthLoginResult.ALREADY_ON_LS;
  171. if (_loginServerClients.putIfAbsent(account, client) == null)
  172. {
  173. ret = AuthLoginResult.AUTH_SUCCESS;
  174. }
  175. }
  176. }
  177. else
  178. {
  179. if (client.getAccessLevel() < 0)
  180. {
  181. ret = AuthLoginResult.ACCOUNT_BANNED;
  182. }
  183. }
  184. return ret;
  185. }
  186. /**
  187. * Adds the address to the ban list of the login server, with the given duration.
  188. * @param address The Address to be banned.
  189. * @param expiration Timestamp in miliseconds when this ban expires
  190. * @throws UnknownHostException if the address is invalid.
  191. */
  192. public void addBanForAddress(String address, long expiration) throws UnknownHostException
  193. {
  194. InetAddress netAddress = InetAddress.getByName(address);
  195. if (!_bannedIps.containsKey(netAddress.getHostAddress()))
  196. {
  197. _bannedIps.put(netAddress.getHostAddress(), new BanInfo(netAddress, expiration));
  198. }
  199. }
  200. /**
  201. * Adds the address to the ban list of the login server, with the given duration.
  202. * @param address The Address to be banned.
  203. * @param duration is miliseconds
  204. */
  205. public void addBanForAddress(InetAddress address, long duration)
  206. {
  207. if (!_bannedIps.containsKey(address.getHostAddress()))
  208. {
  209. _bannedIps.put(address.getHostAddress(), new BanInfo(address, System.currentTimeMillis() + duration));
  210. }
  211. }
  212. public boolean isBannedAddress(InetAddress address)
  213. {
  214. String[] parts = address.getHostAddress().split("\\.");
  215. BanInfo bi = _bannedIps.get(address.getHostAddress());
  216. if (bi == null)
  217. {
  218. bi = _bannedIps.get(parts[0] + "." + parts[1] + "." + parts[2] + ".0");
  219. }
  220. if (bi == null)
  221. {
  222. bi = _bannedIps.get(parts[0] + "." + parts[1] + ".0.0");
  223. }
  224. if (bi == null)
  225. {
  226. bi = _bannedIps.get(parts[0] + ".0.0.0");
  227. }
  228. if (bi != null)
  229. {
  230. if (bi.hasExpired())
  231. {
  232. _bannedIps.remove(address.getHostAddress());
  233. return false;
  234. }
  235. return true;
  236. }
  237. return false;
  238. }
  239. public Map<String, BanInfo> getBannedIps()
  240. {
  241. return _bannedIps;
  242. }
  243. /**
  244. * Remove the specified address from the ban list
  245. * @param address The address to be removed from the ban list
  246. * @return true if the ban was removed, false if there was no ban for this ip
  247. */
  248. public boolean removeBanForAddress(InetAddress address)
  249. {
  250. return _bannedIps.remove(address.getHostAddress()) != null;
  251. }
  252. /**
  253. * Remove the specified address from the ban list
  254. * @param address The address to be removed from the ban list
  255. * @return true if the ban was removed, false if there was no ban for this ip or the address was invalid.
  256. */
  257. public boolean removeBanForAddress(String address)
  258. {
  259. try
  260. {
  261. return this.removeBanForAddress(InetAddress.getByName(address));
  262. }
  263. catch (UnknownHostException e)
  264. {
  265. return false;
  266. }
  267. }
  268. public SessionKey getKeyForAccount(String account)
  269. {
  270. L2LoginClient client = _loginServerClients.get(account);
  271. if (client != null)
  272. {
  273. return client.getSessionKey();
  274. }
  275. return null;
  276. }
  277. public boolean isAccountInAnyGameServer(String account)
  278. {
  279. Collection<GameServerInfo> serverList = GameServerTable.getInstance().getRegisteredGameServers().values();
  280. for (GameServerInfo gsi : serverList)
  281. {
  282. GameServerThread gst = gsi.getGameServerThread();
  283. if ((gst != null) && gst.hasAccountOnGameServer(account))
  284. {
  285. return true;
  286. }
  287. }
  288. return false;
  289. }
  290. public GameServerInfo getAccountOnGameServer(String account)
  291. {
  292. Collection<GameServerInfo> serverList = GameServerTable.getInstance().getRegisteredGameServers().values();
  293. for (GameServerInfo gsi : serverList)
  294. {
  295. GameServerThread gst = gsi.getGameServerThread();
  296. if ((gst != null) && gst.hasAccountOnGameServer(account))
  297. {
  298. return gsi;
  299. }
  300. }
  301. return null;
  302. }
  303. public void getCharactersOnAccount(String account)
  304. {
  305. Collection<GameServerInfo> serverList = GameServerTable.getInstance().getRegisteredGameServers().values();
  306. for (GameServerInfo gsi : serverList)
  307. {
  308. if (gsi.isAuthed())
  309. {
  310. gsi.getGameServerThread().requestCharacters(account);
  311. }
  312. }
  313. }
  314. /**
  315. * @param client
  316. * @param serverId
  317. * @return
  318. */
  319. public boolean isLoginPossible(L2LoginClient client, int serverId)
  320. {
  321. GameServerInfo gsi = GameServerTable.getInstance().getRegisteredGameServerById(serverId);
  322. int access = client.getAccessLevel();
  323. if ((gsi != null) && gsi.isAuthed())
  324. {
  325. boolean loginOk = ((gsi.getCurrentPlayerCount() < gsi.getMaxPlayers()) && (gsi.getStatus() != ServerStatus.STATUS_GM_ONLY)) || (access > 0);
  326. if (loginOk && (client.getLastServer() != serverId))
  327. {
  328. Connection con = null;
  329. try
  330. {
  331. con = L2DatabaseFactory.getInstance().getConnection();
  332. String stmt = "UPDATE accounts SET lastServer = ? WHERE login = ?";
  333. PreparedStatement statement = con.prepareStatement(stmt);
  334. statement.setInt(1, serverId);
  335. statement.setString(2, client.getAccount());
  336. statement.executeUpdate();
  337. statement.close();
  338. }
  339. catch (Exception e)
  340. {
  341. _log.log(Level.WARNING, "Could not set lastServer: " + e.getMessage(), e);
  342. }
  343. finally
  344. {
  345. L2DatabaseFactory.close(con);
  346. }
  347. }
  348. return loginOk;
  349. }
  350. return false;
  351. }
  352. public void setAccountAccessLevel(String account, int banLevel)
  353. {
  354. Connection con = null;
  355. try
  356. {
  357. con = L2DatabaseFactory.getInstance().getConnection();
  358. String stmt = "UPDATE accounts SET accessLevel=? WHERE login=?";
  359. PreparedStatement statement = con.prepareStatement(stmt);
  360. statement.setInt(1, banLevel);
  361. statement.setString(2, account);
  362. statement.executeUpdate();
  363. statement.close();
  364. }
  365. catch (Exception e)
  366. {
  367. _log.log(Level.WARNING, "Could not set accessLevel: " + e.getMessage(), e);
  368. }
  369. finally
  370. {
  371. L2DatabaseFactory.close(con);
  372. }
  373. }
  374. public void setAccountLastTracert(String account, String pcIp, String hop1, String hop2, String hop3, String hop4)
  375. {
  376. Connection con = null;
  377. try
  378. {
  379. con = L2DatabaseFactory.getInstance().getConnection();
  380. String stmt = "UPDATE accounts SET pcIp=?, hop1=?, hop2=?, hop3=?, hop4=? WHERE login=?";
  381. PreparedStatement statement = con.prepareStatement(stmt);
  382. statement.setString(1, pcIp);
  383. statement.setString(2, hop1);
  384. statement.setString(3, hop2);
  385. statement.setString(4, hop3);
  386. statement.setString(5, hop4);
  387. statement.setString(6, account);
  388. statement.executeUpdate();
  389. statement.close();
  390. }
  391. catch (Exception e)
  392. {
  393. _log.log(Level.WARNING, "Could not set last tracert: " + e.getMessage(), e);
  394. }
  395. finally
  396. {
  397. L2DatabaseFactory.close(con);
  398. }
  399. }
  400. public void setCharactersOnServer(String account, int charsNum, long[] timeToDel, int serverId)
  401. {
  402. L2LoginClient client = _loginServerClients.get(account);
  403. if (client == null)
  404. {
  405. return;
  406. }
  407. if (charsNum > 0)
  408. {
  409. client.setCharsOnServ(serverId, charsNum);
  410. }
  411. if (timeToDel.length > 0)
  412. {
  413. client.serCharsWaitingDelOnServ(serverId, timeToDel);
  414. }
  415. }
  416. /**
  417. * <p>
  418. * This method returns one of the cached {@link ScrambledKeyPair ScrambledKeyPairs} for communication with Login Clients.
  419. * </p>
  420. * @return a scrambled keypair
  421. */
  422. public ScrambledKeyPair getScrambledRSAKeyPair()
  423. {
  424. return _keyPairs[Rnd.nextInt(10)];
  425. }
  426. /**
  427. * User name is not case sensitive any more.
  428. * @param user
  429. * @param password
  430. * @param client
  431. * @return
  432. */
  433. public boolean loginValid(String user, String password, L2LoginClient client)// throws HackingException
  434. {
  435. boolean ok = false;
  436. InetAddress address = client.getConnection().getInetAddress();
  437. // player disconnected meanwhile
  438. if ((address == null) || (user == null))
  439. {
  440. return false;
  441. }
  442. Connection con = null;
  443. try
  444. {
  445. MessageDigest md = MessageDigest.getInstance("SHA");
  446. byte[] raw = password.getBytes("UTF-8");
  447. byte[] hash = md.digest(raw);
  448. byte[] expected = null;
  449. int access = 0;
  450. int lastServer = 1;
  451. String userIP = null;
  452. con = L2DatabaseFactory.getInstance().getConnection();
  453. PreparedStatement statement = con.prepareStatement(USER_INFO_SELECT);
  454. statement.setString(1, Long.toString(System.currentTimeMillis()));
  455. statement.setString(2, user);
  456. ResultSet rset = statement.executeQuery();
  457. if (rset.next())
  458. {
  459. expected = Base64.decode(rset.getString("password"));
  460. access = rset.getInt("accessLevel");
  461. lastServer = rset.getInt("lastServer");
  462. userIP = rset.getString("userIP");
  463. if (lastServer <= 0)
  464. {
  465. lastServer = 1; // minServerId is 1 in Interlude
  466. }
  467. if (Config.DEBUG)
  468. {
  469. _log.fine("account exists");
  470. }
  471. }
  472. rset.close();
  473. statement.close();
  474. // if account doesnt exists
  475. if (expected == null)
  476. {
  477. if (Config.AUTO_CREATE_ACCOUNTS)
  478. {
  479. if ((user.length() >= 2) && (user.length() <= 14))
  480. {
  481. statement = con.prepareStatement("INSERT INTO accounts (login,password,lastactive,accessLevel,lastIP) values(?,?,?,?,?)");
  482. statement.setString(1, user);
  483. statement.setString(2, Base64.encodeBytes(hash));
  484. statement.setLong(3, System.currentTimeMillis());
  485. statement.setInt(4, 0);
  486. statement.setString(5, address.getHostAddress());
  487. statement.execute();
  488. statement.close();
  489. if (Config.LOG_LOGIN_CONTROLLER)
  490. {
  491. Log.add("'" + user + "' " + address.getHostAddress() + " - OK : AccountCreate", "loginlog");
  492. }
  493. _log.info("Created new account for " + user);
  494. return true;
  495. }
  496. if (Config.LOG_LOGIN_CONTROLLER)
  497. {
  498. Log.add("'" + user + "' " + address.getHostAddress() + " - ERR : ErrCreatingACC", "loginlog");
  499. }
  500. _log.warning("Invalid username creation/use attempt: " + user);
  501. }
  502. else
  503. {
  504. if (Config.LOG_LOGIN_CONTROLLER)
  505. {
  506. Log.add("'" + user + "' " + address.getHostAddress() + " - ERR : AccountMissing", "loginlog");
  507. }
  508. _log.warning("Account missing for user " + user);
  509. FailedLoginAttempt failedAttempt = _hackProtection.get(address);
  510. int failedCount;
  511. if (failedAttempt == null)
  512. {
  513. _hackProtection.put(address, new FailedLoginAttempt(address, password));
  514. failedCount = 1;
  515. }
  516. else
  517. {
  518. failedAttempt.increaseCounter();
  519. failedCount = failedAttempt.getCount();
  520. }
  521. if (failedCount >= Config.LOGIN_TRY_BEFORE_BAN)
  522. {
  523. _log.info("Banning '" + address.getHostAddress() + "' for " + Config.LOGIN_BLOCK_AFTER_BAN + " seconds due to " + failedCount + " invalid user name attempts");
  524. this.addBanForAddress(address, Config.LOGIN_BLOCK_AFTER_BAN * 1000);
  525. }
  526. }
  527. return false;
  528. }
  529. // is this account banned?
  530. if (access < 0)
  531. {
  532. if (Config.LOG_LOGIN_CONTROLLER)
  533. {
  534. Log.add("'" + user + "' " + address.getHostAddress() + " - ERR : AccountBanned", "loginlog");
  535. }
  536. client.setAccessLevel(access);
  537. return false;
  538. }
  539. // Check IP
  540. if (userIP != null)
  541. {
  542. if (!isValidIPAddress(userIP))
  543. {
  544. // Address is not valid so it's a domain name, get IP
  545. try
  546. {
  547. InetAddress addr = InetAddress.getByName(userIP);
  548. userIP = addr.getHostAddress();
  549. }
  550. catch (Exception e)
  551. {
  552. return false;
  553. }
  554. }
  555. if (!address.getHostAddress().equalsIgnoreCase(userIP))
  556. {
  557. if (Config.LOG_LOGIN_CONTROLLER)
  558. {
  559. Log.add("'" + user + "' " + address.getHostAddress() + "/" + userIP + " - ERR : INCORRECT IP", "loginlog");
  560. }
  561. return false;
  562. }
  563. }
  564. // check password hash
  565. ok = true;
  566. for (int i = 0; i < expected.length; i++)
  567. {
  568. if (hash[i] != expected[i])
  569. {
  570. ok = false;
  571. break;
  572. }
  573. }
  574. if (ok)
  575. {
  576. client.setAccessLevel(access);
  577. client.setLastServer(lastServer);
  578. statement = con.prepareStatement("UPDATE accounts SET lastactive=?, lastIP=? WHERE login=?");
  579. statement.setLong(1, System.currentTimeMillis());
  580. statement.setString(2, address.getHostAddress());
  581. statement.setString(3, user);
  582. statement.execute();
  583. statement.close();
  584. }
  585. }
  586. catch (Exception e)
  587. {
  588. _log.log(Level.WARNING, "Could not check password:" + e.getMessage(), e);
  589. ok = false;
  590. }
  591. finally
  592. {
  593. L2DatabaseFactory.close(con);
  594. }
  595. if (!ok)
  596. {
  597. if (Config.LOG_LOGIN_CONTROLLER)
  598. {
  599. Log.add("'" + user + "' " + address.getHostAddress() + " - ERR : LoginFailed", "loginlog");
  600. }
  601. FailedLoginAttempt failedAttempt = _hackProtection.get(address);
  602. int failedCount;
  603. if (failedAttempt == null)
  604. {
  605. _hackProtection.put(address, new FailedLoginAttempt(address, password));
  606. failedCount = 1;
  607. }
  608. else
  609. {
  610. failedAttempt.increaseCounter(password);
  611. failedCount = failedAttempt.getCount();
  612. }
  613. if (failedCount >= Config.LOGIN_TRY_BEFORE_BAN)
  614. {
  615. _log.info("Banning '" + address.getHostAddress() + "' for " + Config.LOGIN_BLOCK_AFTER_BAN + " seconds due to " + failedCount + " invalid user/pass attempts");
  616. this.addBanForAddress(address, Config.LOGIN_BLOCK_AFTER_BAN * 1000);
  617. }
  618. }
  619. else
  620. {
  621. _hackProtection.remove(address);
  622. if (Config.LOG_LOGIN_CONTROLLER)
  623. {
  624. Log.add("'" + user + "' " + address.getHostAddress() + " - OK : LoginOk", "loginlog");
  625. }
  626. }
  627. return ok;
  628. }
  629. public boolean isValidIPAddress(String ipAddress)
  630. {
  631. String[] parts = ipAddress.split("\\.");
  632. if (parts.length != 4)
  633. {
  634. return false;
  635. }
  636. for (String s : parts)
  637. {
  638. int i = Integer.parseInt(s);
  639. if ((i < 0) || (i > 255))
  640. {
  641. return false;
  642. }
  643. }
  644. return true;
  645. }
  646. class FailedLoginAttempt
  647. {
  648. // private InetAddress _ipAddress;
  649. private int _count;
  650. private long _lastAttempTime;
  651. private String _lastPassword;
  652. public FailedLoginAttempt(InetAddress address, String lastPassword)
  653. {
  654. // _ipAddress = address;
  655. _count = 1;
  656. _lastAttempTime = System.currentTimeMillis();
  657. _lastPassword = lastPassword;
  658. }
  659. public void increaseCounter(String password)
  660. {
  661. if (!_lastPassword.equals(password))
  662. {
  663. // check if theres a long time since last wrong try
  664. if ((System.currentTimeMillis() - _lastAttempTime) < (300 * 1000))
  665. {
  666. _count++;
  667. }
  668. else
  669. {
  670. // restart the status
  671. _count = 1;
  672. }
  673. _lastPassword = password;
  674. _lastAttempTime = System.currentTimeMillis();
  675. }
  676. else
  677. // trying the same password is not brute force
  678. {
  679. _lastAttempTime = System.currentTimeMillis();
  680. }
  681. }
  682. public int getCount()
  683. {
  684. return _count;
  685. }
  686. public void increaseCounter()
  687. {
  688. _count++;
  689. }
  690. }
  691. class BanInfo
  692. {
  693. private final InetAddress _ipAddress;
  694. // Expiration
  695. private final long _expiration;
  696. public BanInfo(InetAddress ipAddress, long expiration)
  697. {
  698. _ipAddress = ipAddress;
  699. _expiration = expiration;
  700. }
  701. public InetAddress getAddress()
  702. {
  703. return _ipAddress;
  704. }
  705. public boolean hasExpired()
  706. {
  707. return (System.currentTimeMillis() > _expiration) && (_expiration > 0);
  708. }
  709. }
  710. class PurgeThread extends Thread
  711. {
  712. public PurgeThread()
  713. {
  714. setName("PurgeThread");
  715. }
  716. @Override
  717. public void run()
  718. {
  719. while (!isInterrupted())
  720. {
  721. for (L2LoginClient client : _loginServerClients.values())
  722. {
  723. if (client == null)
  724. {
  725. continue;
  726. }
  727. if ((client.getConnectionStartTime() + LOGIN_TIMEOUT) < System.currentTimeMillis())
  728. {
  729. client.close(LoginFailReason.REASON_ACCESS_FAILED);
  730. }
  731. }
  732. try
  733. {
  734. Thread.sleep(LOGIN_TIMEOUT / 2);
  735. }
  736. catch (InterruptedException e)
  737. {
  738. return;
  739. }
  740. }
  741. }
  742. }
  743. }