From 800f077be0ea88a43b931079536a71c86c2dc0fd Mon Sep 17 00:00:00 2001 From: games647 Date: Fri, 4 Sep 2015 19:56:58 +0200 Subject: [PATCH] First upload --- .gitignore | 50 +++++- README.md | 9 +- pom.xml | 149 ++++++++++++++++++ .../github/games647/fastlogin/FastLogin.java | 93 +++++++++++ .../github/games647/fastlogin/PlayerData.java | 20 +++ .../listener/EncryptionPacketListener.java | 145 +++++++++++++++++ .../fastlogin/listener/PlayerListener.java | 59 +++++++ .../listener/StartPacketListener.java | 90 +++++++++++ src/main/resources/plugin.yml | 14 ++ 9 files changed, 619 insertions(+), 10 deletions(-) create mode 100644 pom.xml create mode 100644 src/main/java/com/github/games647/fastlogin/FastLogin.java create mode 100644 src/main/java/com/github/games647/fastlogin/PlayerData.java create mode 100644 src/main/java/com/github/games647/fastlogin/listener/EncryptionPacketListener.java create mode 100644 src/main/java/com/github/games647/fastlogin/listener/PlayerListener.java create mode 100644 src/main/java/com/github/games647/fastlogin/listener/StartPacketListener.java create mode 100644 src/main/resources/plugin.yml diff --git a/.gitignore b/.gitignore index 8fffe724..38cd6398 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,42 @@ -target/ -pom.xml.tag -pom.xml.releaseBackup -pom.xml.versionsBackup -pom.xml.next -release.properties -dependency-reduced-pom.xml -buildNumber.properties +# Eclipse stuff +/.classpath +/.project +/.settings + +# netbeans +/nbproject +nb-configuration.xml + +# maven +/target + +# vim +.*.sw[a-p] + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# various other potential build files +/build +/bin +/dist +/manifest.mf +*.log + +# Mac filesystem dust +.DS_Store + +# intellij +*.iml +*.ipr +*.iws +.idea/ + +# Gradle +.gradle + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar diff --git a/README.md b/README.md index a59f4463..46f8e780 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ -# FastLogin -Checks if a minecraft player has a valid paid account. If so, they can skip offline authentification. +# mcMMOExtras + +A visual boss bar Bukkit plugin for mcMMO that keeps people entertained and encourages them to want to level up. + +See +* http://dev.bukkit.org/bukkit-plugins/mcmmoextras/ +* http://www.curse.com/bukkit-plugins/minecraft/mcmmoextras diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..40161e20 --- /dev/null +++ b/pom.xml @@ -0,0 +1,149 @@ + + 4.0.0 + + com.github.games647 + + fastlogin + jar + + FastLogin + 0.1 + 2015 + http://dev.bukkit.org/bukkit-plugins/fastlogin + + Automatically logins premium player on a offline mode server + + + + UTF-8 + + ${basedir}/target + + + + GitHub + https://github.com/games647/FastLogin/issues + + + + https://github.com/games647/FastLogin + scm:git:git://github.com/games647/FastLogin.git + scm:git:ssh://git@github.com:games647/FastLogin.git + + + + install + + ${project.name} + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.2 + + + 1.8 + 1.8 + true + true + + false + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + ${outputDir} + + + + + + + src/main/resources + + true + + + + + ${basedir} + + LICENSE + + + + + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + + dmulloy2-repo + http://repo.dmulloy2.net/content/groups/public/ + + + + + xephi-repo + http://ci.xephi.fr/plugin/repository/everything/ + + + + + luricos.de-repo + http://repo.luricos.de/bukkit-plugins/ + + + + + + + org.spigotmc + spigot + 1.8.8-R0.1-SNAPSHOT + provided + + + + + com.comphenix.protocol + ProtocolLib + 3.6.3-SNAPSHOT + true + + + + + fr.xephi + authme + 5.0-SNAPSHOT + + + + de.luricos.bukkit + xAuth + 2.6 + + + net.gravitydevelopment.updater + updater + + + net.ess3 + EssentialsGroupManager + + + + + diff --git a/src/main/java/com/github/games647/fastlogin/FastLogin.java b/src/main/java/com/github/games647/fastlogin/FastLogin.java new file mode 100644 index 00000000..4b6f7a2c --- /dev/null +++ b/src/main/java/com/github/games647/fastlogin/FastLogin.java @@ -0,0 +1,93 @@ +package com.github.games647.fastlogin; + +import com.github.games647.fastlogin.listener.PlayerListener; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import com.github.games647.fastlogin.listener.EncryptionPacketListener; +import com.github.games647.fastlogin.listener.StartPacketListener; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.util.concurrent.TimeUnit; + +import org.bukkit.plugin.java.JavaPlugin; + +public class FastLogin extends JavaPlugin { + + private final KeyPair keyPair = generateKey(); + private final Cache session = CacheBuilder.newBuilder() + .expireAfterWrite(2, TimeUnit.MINUTES) + .build(); + + @Override + public void onEnable() { + if (!isEnabled()) { + return; + } + + if (!getServer().getPluginManager().isPluginEnabled("AuthMe") + && !getServer().getPluginManager().isPluginEnabled("xAuth")) { + getLogger().warning("No support offline Auth plugin found. "); + getLogger().warning("Disabling this plugin..."); + + setEnabled(false); + return; + } + + ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); + protocolManager.addPacketListener(new EncryptionPacketListener(this, protocolManager)); + protocolManager.addPacketListener(new StartPacketListener(this, protocolManager)); + + getServer().getPluginManager().registerEvents(new PlayerListener(this), this); + } + + @Override + public void onLoad() { + //online mode is only changeable aftter a restart + if (getServer().getOnlineMode()) { + getLogger().severe("Server have to be in offline mode"); + + setEnabled(false); + } + + generateKey(); + } + + private KeyPair generateKey() { + try { + KeyPairGenerator keypairgenerator = KeyPairGenerator.getInstance("RSA"); + + keypairgenerator.initialize(1024); + return keypairgenerator.generateKeyPair(); + } catch (NoSuchAlgorithmException noSuchAlgorithmException) { + //Should be default existing in every vm + } + + return null; + } + + public Cache getSession() { + return session; + } + + public KeyPair getKeyPair() { + return keyPair; + } + + public HttpURLConnection getConnection(String url) throws IOException { + final HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setConnectTimeout(15000); + connection.setReadTimeout(15000); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestProperty("User-Agent", "Premium-Checker"); + + return connection; + } +} diff --git a/src/main/java/com/github/games647/fastlogin/PlayerData.java b/src/main/java/com/github/games647/fastlogin/PlayerData.java new file mode 100644 index 00000000..aabe2ff6 --- /dev/null +++ b/src/main/java/com/github/games647/fastlogin/PlayerData.java @@ -0,0 +1,20 @@ +package com.github.games647.fastlogin; + +public class PlayerData { + + private final byte[] verifyToken; + private final String username; + + public PlayerData(byte[] verifyToken, String username) { + this.username = username; + this.verifyToken = verifyToken; + } + + public byte[] getVerifyToken() { + return verifyToken; + } + + public String getUsername() { + return username; + } +} diff --git a/src/main/java/com/github/games647/fastlogin/listener/EncryptionPacketListener.java b/src/main/java/com/github/games647/fastlogin/listener/EncryptionPacketListener.java new file mode 100644 index 00000000..75b43e60 --- /dev/null +++ b/src/main/java/com/github/games647/fastlogin/listener/EncryptionPacketListener.java @@ -0,0 +1,145 @@ +package com.github.games647.fastlogin.listener; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.events.PacketAdapter; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.events.PacketEvent; +import com.comphenix.protocol.injector.server.SocketInjector; +import com.comphenix.protocol.injector.server.TemporaryPlayerFactory; +import com.comphenix.protocol.wrappers.WrappedGameProfile; +import com.github.games647.fastlogin.FastLogin; +import com.github.games647.fastlogin.PlayerData; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.math.BigInteger; +import java.net.HttpURLConnection; +import java.security.PrivateKey; +import java.util.Arrays; +import java.util.logging.Level; + +import javax.crypto.SecretKey; + +import net.minecraft.server.v1_8_R3.MinecraftEncryption; +import net.minecraft.server.v1_8_R3.NetworkManager; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; + +public class EncryptionPacketListener extends PacketAdapter { + + private static final String HAS_JOINED_URL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?"; + + private final ProtocolManager protocolManager; + private final FastLogin fastLogin; + + public EncryptionPacketListener(FastLogin plugin, ProtocolManager protocolManger) { + super(params(plugin, PacketType.Login.Client.ENCRYPTION_BEGIN).optionAsync()); + + this.fastLogin = plugin; + this.protocolManager = protocolManger; + } + + /* + * C->S : Handshake State=2 + * C->S : Login Start + * S->C : Encryption Key Request + * (Client Auth) + * C->S : Encryption Key Response + * (Server Auth, Both enable encryption) + * S->C : Login Success (*) + */ + @Override + public void onPacketReceiving(PacketEvent event) { + PacketContainer packet = event.getPacket(); + Player player = event.getPlayer(); + + final byte[] sharedSecret = packet.getByteArrays().read(0); + byte[] clientVerify = packet.getByteArrays().read(1); + + PrivateKey privateKey = fastLogin.getKeyPair().getPrivate(); + + String addressString = player.getAddress().toString(); + PlayerData cachedEntry = fastLogin.getSession().asMap().get(addressString); + byte[] serverVerify = cachedEntry.getVerifyToken(); + if (!Arrays.equals(serverVerify, MinecraftEncryption.b(privateKey, clientVerify))) { + player.kickPlayer("Invalid token"); + event.setCancelled(true); + return; + } + + //encrypt all following packets + NetworkManager networkManager = getNetworkManager(event); + SecretKey loginKey = MinecraftEncryption.a(privateKey, sharedSecret); + networkManager.a(loginKey); + String serverId = (new BigInteger(MinecraftEncryption.a("", fastLogin.getKeyPair().getPublic(), loginKey))) + .toString(16); + + String username = cachedEntry.getUsername(); + if (!hasJoinedServer(username, serverId)) { + //user tried to fake a authentification + player.kickPlayer("Invalid session"); + event.setCancelled(true); + return; + } + + //fake a new login packet + PacketContainer startPacket = protocolManager.createPacket(PacketType.Login.Client.START, true); + WrappedGameProfile fakeProfile = WrappedGameProfile.fromOfflinePlayer(Bukkit.getOfflinePlayer(username)); + startPacket.getGameProfiles().write(0, fakeProfile); + try { + protocolManager.recieveClientPacket(event.getPlayer(), startPacket, false); + } catch (InvocationTargetException | IllegalAccessException ex) { + plugin.getLogger().log(Level.WARNING, null, ex); + } + + event.setCancelled(true); + } + + private NetworkManager getNetworkManager(PacketEvent event) throws IllegalArgumentException { + SocketInjector injector = TemporaryPlayerFactory.getInjectorFromPlayer(event.getPlayer()); + NetworkManager networkManager = null; + try { + Field declaredField = injector.getClass().getDeclaredField("injector"); + declaredField.setAccessible(true); + + Object rawInjector = declaredField.get(injector); + + declaredField = rawInjector.getClass().getDeclaredField("networkManager"); + declaredField.setAccessible(true); + networkManager = (NetworkManager) declaredField.get(rawInjector); + } catch (IllegalAccessException | NoSuchFieldException ex) { + plugin.getLogger().log(Level.WARNING, null, ex); + } + + return networkManager; + } + + private boolean hasJoinedServer(String username, String serverId) { + try { + String url = HAS_JOINED_URL + "username=" + username + "&serverId=" + serverId; + + HttpURLConnection conn = fastLogin.getConnection(url); + + BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String line = reader.readLine(); + if (!line.equals("null")) { + JSONObject object = (JSONObject) JSONValue.parse(line); + String uuid = (String) object.get("id"); + String name = (String) object.get("name"); + + return true; + } + } catch (IOException ex) { + plugin.getLogger().log(Level.WARNING, null, ex); + } + + return false; + } +} diff --git a/src/main/java/com/github/games647/fastlogin/listener/PlayerListener.java b/src/main/java/com/github/games647/fastlogin/listener/PlayerListener.java new file mode 100644 index 00000000..91781900 --- /dev/null +++ b/src/main/java/com/github/games647/fastlogin/listener/PlayerListener.java @@ -0,0 +1,59 @@ +package com.github.games647.fastlogin.listener; + +import com.github.games647.fastlogin.FastLogin; + +import de.luricos.bukkit.xAuth.xAuth; +import de.luricos.bukkit.xAuth.xAuthPlayer; +import de.luricos.bukkit.xAuth.xAuthPlayer.Status; + +import fr.xephi.authme.api.NewAPI; +import fr.xephi.authme.cache.limbo.LimboCache; + +import java.sql.Timestamp; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; + +public class PlayerListener implements Listener { + + private final FastLogin plugin; + + public PlayerListener(FastLogin plugin) { + this.plugin = plugin; + } + + @EventHandler(ignoreCancelled = true) + public void onJoin(PlayerJoinEvent joinEvent) { + final Player player = joinEvent.getPlayer(); + String address = player.getAddress().toString(); + if (plugin.getSession().asMap().containsKey(address)) { + Bukkit.getScheduler().runTaskLater(plugin, () -> { + doLogin(player); + }, 1 * 20L); + } + } + + private void doLogin(Player player) { + if (Bukkit.getPluginManager().isPluginEnabled("AuthMe")) { + //add cache entry - otherwise loggin wouldn't work + LimboCache.getInstance().addLimboPlayer(player); + + //skips registration and login + NewAPI.getInstance().forceLogin(player); + } else if (Bukkit.getPluginManager().isPluginEnabled("xAuth")) { + xAuth xAuthPlugin = xAuth.getPlugin(); + + xAuthPlayer xAuthPlayer = xAuthPlugin.getPlayerManager().getPlayer(player); + xAuthPlayer.setPremium(true); + xAuthPlugin.getAuthClass(xAuthPlayer).online(xAuthPlayer.getName()); + xAuthPlayer.setLoginTime(new Timestamp(System.currentTimeMillis())); + + xAuthPlayer.setStatus(Status.AUTHENTICATED); + + xAuthPlugin.getPlayerManager().unprotect(xAuthPlayer); + } + } +} diff --git a/src/main/java/com/github/games647/fastlogin/listener/StartPacketListener.java b/src/main/java/com/github/games647/fastlogin/listener/StartPacketListener.java new file mode 100644 index 00000000..b0f3dfb5 --- /dev/null +++ b/src/main/java/com/github/games647/fastlogin/listener/StartPacketListener.java @@ -0,0 +1,90 @@ +package com.github.games647.fastlogin.listener; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.events.PacketAdapter; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.events.PacketEvent; +import com.github.games647.fastlogin.FastLogin; +import com.github.games647.fastlogin.PlayerData; + +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 org.bukkit.entity.Player; + +public class StartPacketListener extends PacketAdapter { + + //only premium members have a uuid from there + private static final String UUID_LINK = "https://api.mojang.com/users/profiles/minecraft/"; + + private final ProtocolManager protocolManager; + private final FastLogin fastLogin; + + private final Random random = new Random(); + + public StartPacketListener(FastLogin plugin, ProtocolManager protocolManger) { + super(params(plugin, PacketType.Login.Client.START).optionAsync()); + + this.fastLogin = plugin; + this.protocolManager = protocolManger; + } + + /* + * C->S : Handshake State=2 + * C->S : Login Start + * S->C : Encryption Key Request + * (Client Auth) + * C->S : Encryption Key Response + * (Server Auth, Both enable encryption) + * S->C : Login Success (*) + */ + @Override + public void onPacketReceiving(PacketEvent packetEvent) { + PacketContainer packet = packetEvent.getPacket(); + Player player = packetEvent.getPlayer(); + + String username = packet.getGameProfiles().read(0).getName(); + if (isPremium(username)) { + //do premium login process + try { + PacketContainer newPacket = protocolManager.createPacket(PacketType.Login.Server.ENCRYPTION_BEGIN, true); + + //constr ServerID="" + //public key=plugin.getPublic + newPacket.getSpecificModifier(PublicKey.class).write(0, fastLogin.getKeyPair().getPublic()); + byte[] verifyToken = new byte[4]; + random.nextBytes(verifyToken); + newPacket.getByteArrays().write(0, verifyToken); + + String addressString = player.getAddress().toString(); + fastLogin.getSession().asMap().put(addressString, new PlayerData(verifyToken, username)); + + protocolManager.sendServerPacket(player, newPacket, false); + } catch (InvocationTargetException ex) { + plugin.getLogger().log(Level.SEVERE, null, ex); + } + + //cancel only if the player is premium + packetEvent.setCancelled(true); + } + } + + private boolean isPremium(String playerName) { + try { + final HttpURLConnection connection = fastLogin.getConnection(UUID_LINK + playerName); + final int responseCode = connection.getResponseCode(); + + return responseCode == HttpURLConnection.HTTP_OK; + } catch (IOException ex) { + plugin.getLogger().log(Level.SEVERE, null, ex); + } + + return false; + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 00000000..92b3ab2b --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,14 @@ +# project informations for Bukkit in order to register our plugin with all it components +# ${project.name} are variables from Maven (pom.xml) which will be replaced after the build +name: ${project.name} +version: ${project.version} +main: ${project.groupId}.${project.artifactId}.${project.name} + +# meta informations for plugin managers +authors: [Xeroun, games647, 'https://github.com/games647/FastLogin/graphs/contributors'] +description: | + ${project.description} +website: ${project.url} +dev-url: ${project.url} + +depend: [ProtocolLib] \ No newline at end of file