diff --git a/CHANGELOG.md b/CHANGELOG.md index 63205161..c4268a25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,13 @@ ######0.5 +* Added autologin - See config +* Added config * Added isRegistered API method * Added forceRegister API method * Fixed CrazyLogin player data restore -> Fixes memory leaks with this plugin +* Fixed premium name check to protocolsupport + ######0.4 * Added forward premium skin diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/EncryptionUtil.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/EncryptionUtil.java index 9df73752..10c8ff5a 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/EncryptionUtil.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/EncryptionUtil.java @@ -37,7 +37,7 @@ public class EncryptionUtil { try { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); - keyPairGenerator.initialize(1024); + keyPairGenerator.initialize(1_024); return keyPairGenerator.generateKeyPair(); } catch (NoSuchAlgorithmException nosuchalgorithmexception) { //Should be existing in every vm diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java index 51305c1b..e55c962a 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java @@ -17,13 +17,12 @@ import com.google.common.collect.Sets; import com.google.common.reflect.ClassPath; import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.URL; import java.security.KeyPair; import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import java.util.logging.Level; +import org.apache.commons.lang.RandomStringUtils; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; @@ -33,10 +32,6 @@ import org.bukkit.plugin.java.JavaPlugin; */ public class FastLoginBukkit extends JavaPlugin { - //http connection, read timeout and user agent for a connection to mojang api servers - private static final int TIMEOUT = 1 * 1000; - private static final String USER_AGENT = "Premium-Checker"; - //provide a immutable key pair to be thread safe | used for encrypting and decrypting traffic private final KeyPair keyPair = EncryptionUtil.generateKeyPair(); @@ -62,9 +57,11 @@ public class FastLoginBukkit extends JavaPlugin { }); private AuthPlugin authPlugin; + private final MojangApiConnector mojangApiConnector = new MojangApiConnector(this); @Override public void onEnable() { + saveDefaultConfig(); if (getServer().getOnlineMode() || !registerHooks()) { //we need to require offline to prevent a session request for a offline player getLogger().severe("Server have to be in offline mode and have an auth plugin installed"); @@ -108,6 +105,10 @@ public class FastLoginBukkit extends JavaPlugin { } } + public String generateStringPassword() { + return RandomStringUtils.random(8, true, true); + } + /** * Gets a thread-safe map about players which are connecting to the server are being checked to be premium (paid * account) @@ -158,22 +159,13 @@ public class FastLoginBukkit extends JavaPlugin { } /** - * Prepares a Mojang API connection. The connection is not started in this method + * Gets the a connection in order to access important + * features from the Mojang API. * - * @param url the url connecting to - * @return the prepared connection - * - * @throws IOException on invalid url format or on {@link java.net.URL#openConnection() } + * @return the connector instance */ - public HttpURLConnection getConnection(String url) throws IOException { - HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); - connection.setConnectTimeout(TIMEOUT); - connection.setReadTimeout(TIMEOUT); - //the new Mojang API just uses json as response - connection.setRequestProperty("Content-Type", "application/json"); - connection.setRequestProperty("User-Agent", USER_AGENT); - - return connection; + public MojangApiConnector getApiConnector() { + return mojangApiConnector; } private boolean registerHooks() { diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/MojangApiConnector.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/MojangApiConnector.java new file mode 100644 index 00000000..2e0fe209 --- /dev/null +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/MojangApiConnector.java @@ -0,0 +1,103 @@ +package com.github.games647.fastlogin.bukkit; + +import com.comphenix.protocol.wrappers.WrappedSignedProperty; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.logging.Level; +import java.util.regex.Pattern; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; + +public class MojangApiConnector { + + //http connection, read timeout and user agent for a connection to mojang api servers + private static final int TIMEOUT = 1 * 1_000; + private static final String USER_AGENT = "Premium-Checker"; + + //mojang api check to prove a player is logged in minecraft and made a join server request + private static final String HAS_JOINED_URL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?"; + + //only premium (paid account) users have a uuid from here + private static final String UUID_LINK = "https://api.mojang.com/users/profiles/minecraft/"; + //this includes a-zA-Z1-9_ + private static final String VALID_PLAYERNAME = "^\\w{2,16}$"; + + //compile the pattern only on plugin enable -> and this have to be threadsafe + private final Pattern playernameMatcher = Pattern.compile(VALID_PLAYERNAME); + + private final FastLoginBukkit plugin; + + public MojangApiConnector(FastLoginBukkit plugin) { + this.plugin = plugin; + } + + public boolean isPremiumName(String playerName) { + //check if it's a valid playername + if (playernameMatcher.matcher(playerName).matches()) { + //only make a API call if the name is valid existing mojang account + try { + HttpURLConnection connection = getConnection(UUID_LINK + playerName); + int responseCode = connection.getResponseCode(); + + return responseCode == HttpURLConnection.HTTP_OK; + //204 - no content for not found + } catch (IOException ex) { + plugin.getLogger().log(Level.SEVERE, "Failed to check if player has a paid account", ex); + } + //this connection doesn't need to be closed. So can make use of keep alive in java + } + + return false; + } + + public boolean hasJoinedServer(PlayerSession session, String serverId) { + try { + String url = HAS_JOINED_URL + "username=" + session.getUsername() + "&serverId=" + serverId; + HttpURLConnection conn = getConnection(url); + + BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String line = reader.readLine(); + if (line != null && !line.equals("null")) { + //validate parsing + //http://wiki.vg/Protocol_Encryption#Server + JSONObject userData = (JSONObject) JSONValue.parseWithException(line); + String uuid = (String) userData.get("id"); + + JSONArray properties = (JSONArray) userData.get("properties"); + JSONObject skinProperty = (JSONObject) properties.get(0); + + String propertyName = (String) skinProperty.get("name"); + if (propertyName.equals("textures")) { + String skinValue = (String) skinProperty.get("value"); + String signature = (String) skinProperty.get("signature"); + session.setSkin(WrappedSignedProperty.fromValues(propertyName, skinValue, signature)); + } + + return true; + } + } catch (Exception ex) { + //catch not only ioexceptions also parse and NPE on unexpected json format + plugin.getLogger().log(Level.WARNING, "Failed to verify session", ex); + } + + //this connection doesn't need to be closed. So can make use of keep alive in java + return false; + } + + private HttpURLConnection getConnection(String url) throws IOException { + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setConnectTimeout(TIMEOUT); + connection.setReadTimeout(TIMEOUT); + //the new Mojang API just uses json as response + connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestProperty("User-Agent", USER_AGENT); + + return connection; + } +} diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/PlayerSession.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/PlayerSession.java index 34838467..43000f49 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/PlayerSession.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/PlayerSession.java @@ -17,6 +17,7 @@ public class PlayerSession { private WrappedSignedProperty skinProperty; private boolean verified; + private boolean registered; public PlayerSession(String username, String serverId, byte[] verifyToken) { this.username = username; @@ -73,6 +74,24 @@ public class PlayerSession { this.skinProperty = skinProperty; } + /** + * Sets whether the account of this player already exists + * + * @param registered whether the account exists + */ + public synchronized void setRegistered(boolean registered) { + this.registered = registered; + } + + /** + * Gets whether the account of this player already exists. + * + * @return whether the account exists + */ + public synchronized boolean needsRegistration() { + return !registered; + } + /** * Sets whether the player has a premium (paid account) account * and valid session diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/hooks/AuthMeHook.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/hooks/AuthMeHook.java index 3fc43b82..d0f58908 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/hooks/AuthMeHook.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/hooks/AuthMeHook.java @@ -16,6 +16,7 @@ public class AuthMeHook implements AuthPlugin { NewAPI.getInstance().forceLogin(player); } + @Override public boolean isRegistered(String playerName) { return NewAPI.getInstance().isRegistered(playerName); } diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/BukkitJoinListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/BukkitJoinListener.java index 2073bd75..0d3a8cae 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/BukkitJoinListener.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/BukkitJoinListener.java @@ -8,6 +8,7 @@ import com.github.games647.fastlogin.bukkit.PlayerSession; import java.util.logging.Level; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -55,8 +56,19 @@ public class BukkitJoinListener implements Listener { player.setMetadata(plugin.getName(), new FixedMetadataValue(plugin, true)); //check if it's the same player as we checked before if (session != null && player.getName().equals(session.getUsername()) && session.isVerified()) { - plugin.getLogger().log(Level.FINE, "Logging player {0} in", player.getName()); - plugin.getAuthPlugin().forceLogin(player); + if (session.needsRegistration()) { + plugin.getLogger().log(Level.FINE, "Register player {0}", player.getName()); + + String generatedPassword = plugin.generateStringPassword(); + plugin.getAuthPlugin().forceRegister(player, generatedPassword); + player.sendMessage(ChatColor.DARK_GREEN + "Auto registered with password: " + + generatedPassword); + player.sendMessage(ChatColor.DARK_GREEN + "You may want change it?"); + } else { + plugin.getLogger().log(Level.FINE, "Logging player {0} in", player.getName()); + plugin.getAuthPlugin().forceLogin(player); + player.sendMessage(ChatColor.DARK_GREEN + "Auto logged in"); + } } } } diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/EncryptionPacketListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/EncryptionPacketListener.java index 0dfcc9a9..a7442701 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/EncryptionPacketListener.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/EncryptionPacketListener.java @@ -9,18 +9,14 @@ import com.comphenix.protocol.injector.server.TemporaryPlayerFactory; import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.wrappers.WrappedChatComponent; import com.comphenix.protocol.wrappers.WrappedGameProfile; -import com.comphenix.protocol.wrappers.WrappedSignedProperty; import com.github.games647.fastlogin.bukkit.EncryptionUtil; import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.PlayerSession; -import java.io.BufferedReader; -import java.io.InputStreamReader; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.math.BigInteger; -import java.net.HttpURLConnection; import java.security.PrivateKey; import java.util.Arrays; import java.util.UUID; @@ -29,9 +25,6 @@ import java.util.logging.Level; import javax.crypto.SecretKey; import org.bukkit.entity.Player; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; /** * Handles incoming encryption responses from connecting clients. @@ -50,9 +43,6 @@ import org.json.simple.JSONValue; */ public class EncryptionPacketListener extends PacketAdapter { - //mojang api check to prove a player is logged in minecraft and made a join server request - private static final String HAS_JOINED_URL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?"; - private final ProtocolManager protocolManager; //hides the inherit Plugin plugin field, but we need this type private final FastLoginBukkit plugin; @@ -111,7 +101,7 @@ public class EncryptionPacketListener extends PacketAdapter { String serverId = (new BigInteger(serverIdHash)).toString(16); String username = session.getUsername(); - if (hasJoinedServer(session, serverId)) { + if (plugin.getApiConnector().hasJoinedServer(session, serverId)) { plugin.getLogger().log(Level.FINE, "Player {0} has a verified premium account", username); session.setVerified(true); @@ -203,40 +193,6 @@ public class EncryptionPacketListener extends PacketAdapter { } } - private boolean hasJoinedServer(PlayerSession session, String serverId) { - try { - String url = HAS_JOINED_URL + "username=" + session.getUsername() + "&serverId=" + serverId; - HttpURLConnection conn = plugin.getConnection(url); - - BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); - String line = reader.readLine(); - if (line != null && !line.equals("null")) { - //validate parsing - //http://wiki.vg/Protocol_Encryption#Server - JSONObject userData = (JSONObject) JSONValue.parseWithException(line); - String uuid = (String) userData.get("id"); - - JSONArray properties = (JSONArray) userData.get("properties"); - JSONObject skinProperty = (JSONObject) properties.get(0); - - String propertyName = (String) skinProperty.get("name"); - if (propertyName.equals("textures")) { - String skinValue = (String) skinProperty.get("value"); - String signature = (String) skinProperty.get("signature"); - session.setSkin(WrappedSignedProperty.fromValues(propertyName, skinValue, signature)); - } - - return true; - } - } catch (Exception ex) { - //catch not only ioexceptions also parse and NPE on unexpected json format - plugin.getLogger().log(Level.WARNING, "Failed to verify session", ex); - } - - //this connection doesn't need to be closed. So can make use of keep alive in java - return false; - } - //fake a new login packet in order to let the server handle all the other stuff private void receiveFakeStartPacket(String username, Player from) { //see StartPacketListener for packet information diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/ProtcolSupportListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/ProtcolSupportListener.java index 8af2a1c9..334d3986 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/ProtcolSupportListener.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/ProtcolSupportListener.java @@ -8,6 +8,7 @@ import java.net.InetSocketAddress; import java.util.logging.Level; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -15,6 +16,7 @@ import org.bukkit.event.player.PlayerJoinEvent; import protocolsupport.api.events.PlayerLoginStartEvent; import protocolsupport.api.events.PlayerPropertiesResolveEvent; +import protocolsupport.api.events.PlayerPropertiesResolveEvent.ProfileProperty; public class ProtcolSupportListener implements Listener { @@ -34,12 +36,11 @@ public class ProtcolSupportListener implements Listener { String playerName = loginStartEvent.getName(); if (plugin.getEnabledPremium().contains(playerName)) { - loginStartEvent.setOnlineMode(true); - InetSocketAddress address = loginStartEvent.getAddress(); - - PlayerSession playerSession = new PlayerSession(playerName, null, null); - plugin.getSessions().put(address.toString(), playerSession); -// loginStartEvent.setUseOnlineModeUUID(true); + //the player have to be registered in order to invoke the command + startPremiumSession(playerName, loginStartEvent, true); + } else if (plugin.getConfig().getBoolean("autologin") && !plugin.getAuthPlugin().isRegistered(playerName)) { + startPremiumSession(playerName, loginStartEvent, false); + plugin.getEnabledPremium().add(playerName); } } @@ -50,8 +51,7 @@ public class ProtcolSupportListener implements Listener { if (session != null) { session.setVerified(true); - PlayerPropertiesResolveEvent.ProfileProperty skinProperty = propertiesResolveEvent.getProperties() - .get("textures"); + ProfileProperty skinProperty = propertiesResolveEvent.getProperties().get("textures"); if (skinProperty != null) { WrappedSignedProperty signedProperty = WrappedSignedProperty .fromValues(skinProperty.getName(), skinProperty.getValue(), skinProperty.getSignature()); @@ -74,12 +74,35 @@ public class ProtcolSupportListener implements Listener { if (player.isOnline()) { //check if it's the same player as we checked before if (session != null && player.getName().equals(session.getUsername()) && session.isVerified()) { - plugin.getLogger().log(Level.FINE, "Logging player {0} in", player.getName()); - plugin.getAuthPlugin().forceLogin(player); + if (session.needsRegistration()) { + plugin.getLogger().log(Level.FINE, "Register player {0}", player.getName()); + + String generatedPassword = plugin.generateStringPassword(); + plugin.getAuthPlugin().forceRegister(player, generatedPassword); + player.sendMessage(ChatColor.DARK_GREEN + "Auto registered with password: " + + generatedPassword); + player.sendMessage(ChatColor.DARK_GREEN + "You may want change it?"); + } else { + plugin.getLogger().log(Level.FINE, "Logging player {0} in", player.getName()); + plugin.getAuthPlugin().forceLogin(player); + player.sendMessage(ChatColor.DARK_GREEN + "Auto logged in"); + } } } } //Wait before auth plugin and we received a message from BungeeCord initializes the player }, DELAY_LOGIN); } + + private void startPremiumSession(String playerName, PlayerLoginStartEvent loginStartEvent, boolean registered) { + if (plugin.getApiConnector().isPremiumName(playerName)) { + loginStartEvent.setOnlineMode(true); + InetSocketAddress address = loginStartEvent.getAddress(); + + PlayerSession playerSession = new PlayerSession(playerName, null, null); + playerSession.setRegistered(registered); + plugin.getSessions().put(address.toString(), playerSession); +// loginStartEvent.setUseOnlineModeUUID(true); + } + } } diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/StartPacketListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/StartPacketListener.java index 114cf4f6..a04355b2 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/StartPacketListener.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/StartPacketListener.java @@ -8,13 +8,10 @@ import com.comphenix.protocol.events.PacketEvent; import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.PlayerSession; -import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.net.HttpURLConnection; import java.security.PublicKey; import java.util.Random; import java.util.logging.Level; -import java.util.regex.Pattern; import org.bukkit.entity.Player; @@ -31,10 +28,6 @@ import org.bukkit.entity.Player; */ public class StartPacketListener extends PacketAdapter { - //only premium (paid account) users have a uuid from here - private static final String UUID_LINK = "https://api.mojang.com/users/profiles/minecraft/"; - //this includes a-zA-Z1-9_ - private static final String VALID_PLAYERNAME = "^\\w{2,16}$"; private static final int VERIFY_TOKEN_LENGTH = 4; private final ProtocolManager protocolManager; @@ -43,8 +36,6 @@ public class StartPacketListener extends PacketAdapter { //just create a new once on plugin enable. This used for verify token generation private final Random random = new Random(); - //compile the pattern on plugin enable - private final Pattern playernameMatcher = Pattern.compile(VALID_PLAYERNAME); public StartPacketListener(FastLoginBukkit plugin, ProtocolManager protocolManger) { //run async in order to not block the server, because we are making api calls to Mojang @@ -80,35 +71,43 @@ public class StartPacketListener extends PacketAdapter { String username = packet.getGameProfiles().read(0).getName(); plugin.getLogger().log(Level.FINER, "Player {0} with {1} connecting to the server" , new Object[]{sessionKey, username}); - if (!plugin.getBungeeCordUsers().containsKey(player) - && plugin.getEnabledPremium().contains(username) && isPremiumName(username)) { + if (!plugin.getBungeeCordUsers().containsKey(player)) { + if (plugin.getEnabledPremium().contains(username)) { + enablePremiumLogin(username, sessionKey, player, packetEvent, true); + } else if (plugin.getConfig().getBoolean("autologin") && !plugin.getAuthPlugin().isRegistered(username)) { + enablePremiumLogin(username, sessionKey, player, packetEvent, false); + plugin.getEnabledPremium().add(username); + } + } + } + + private void enablePremiumLogin(String username, String sessionKey, Player player, PacketEvent packetEvent + , boolean registered) { + if (plugin.getApiConnector().isPremiumName(username)) { + plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username); //minecraft server implementation //https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L161 - sentEncryptionRequest(sessionKey, username, player, packetEvent); - } - } - private boolean isPremiumName(String playerName) { - //check if it's a valid playername - if (playernameMatcher.matcher(playerName).matches()) { - //only make a API call if the name is valid existing mojang account - try { - HttpURLConnection connection = plugin.getConnection(UUID_LINK + playerName); - int responseCode = connection.getResponseCode(); + //randomized server id to make sure the request is for our server + //this could be relevant http://www.sk89q.com/2011/09/minecraft-name-spoofing-exploit/ + String serverId = Long.toString(random.nextLong(), 16); - return responseCode == HttpURLConnection.HTTP_OK; - //204 - no content for not found - } catch (IOException ex) { - plugin.getLogger().log(Level.SEVERE, "Failed to check if player has a paid account", ex); + //generate a random token which should be the same when we receive it from the client + byte[] verifyToken = new byte[VERIFY_TOKEN_LENGTH]; + random.nextBytes(verifyToken); + + boolean success = sentEncryptionRequest(player, serverId, verifyToken); + if (success) { + PlayerSession playerSession = new PlayerSession(username, serverId, verifyToken); + playerSession.setRegistered(registered); + plugin.getSessions().put(sessionKey, playerSession); + //cancel only if the player has a paid account otherwise login as normal offline player + packetEvent.setCancelled(true); } - //this connection doesn't need to be closed. So can make use of keep alive in java } - - return false; } - private void sentEncryptionRequest(String sessionKey, String username, Player player, PacketEvent packetEvent) { - plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username); + private boolean sentEncryptionRequest(Player player, String serverId, byte[] verifyToken) { try { /** * Packet Information: http://wiki.vg/Protocol#Encryption_Request @@ -119,24 +118,18 @@ public class StartPacketListener extends PacketAdapter { */ PacketContainer newPacket = protocolManager.createPacket(PacketType.Login.Server.ENCRYPTION_BEGIN); - //randomized server id to make sure the request is for our server - //this could be relevant http://www.sk89q.com/2011/09/minecraft-name-spoofing-exploit/ - String serverId = Long.toString(random.nextLong(), 16); newPacket.getStrings().write(0, serverId); newPacket.getSpecificModifier(PublicKey.class).write(0, plugin.getServerKey().getPublic()); - //generate a random token which should be the same when we receive it from the client - byte[] verifyToken = new byte[VERIFY_TOKEN_LENGTH]; - random.nextBytes(verifyToken); + newPacket.getByteArrays().write(0, verifyToken); //serverId is a empty string protocolManager.sendServerPacket(player, newPacket); - - //cancel only if the player has a paid account otherwise login as normal offline player - plugin.getSessions().put(sessionKey, new PlayerSession(username, serverId, verifyToken)); - packetEvent.setCancelled(true); + return true; } catch (InvocationTargetException ex) { plugin.getLogger().log(Level.SEVERE, "Cannot send encryption packet. Falling back to normal login", ex); } + + return false; } } diff --git a/bukkit/src/main/resources/config.yml b/bukkit/src/main/resources/config.yml new file mode 100644 index 00000000..1e17f4a4 --- /dev/null +++ b/bukkit/src/main/resources/config.yml @@ -0,0 +1,19 @@ +# FastLogin config +# You can access the newest config here: +# https://github.com/games647/FastLogin/blob/master/bukkit/src/main/resources/config.yml + +# Request a premium login without forcing the player to type a command +# +# If you activate autologin, this plugin will check/do these points on login: +# 1. An existing cracked account shouldn't exist +# -> paid accounts cannot steal the existing account of cracked players +# - (Already registered players could still use the /premium command to activate premium checks) +# 2. Automatically registers an account with a strong random generated password +# -> cracked player cannot register an account for the premium player and so cannot the steal the account +# +# Furthermore the premium player check have to be made based on the player name +# This means if a cracked player connects to the server and we request a paid account login from this player +# the player just disconnect and sees the message: 'bad login' +# There is no way to change this message +# For more information: https://github.com/games647/FastLogin#why-do-players-have-to-invoke-a-command +autologin: false \ No newline at end of file