diff --git a/AltAuth.png b/AltAuth.png new file mode 100644 index 0000000..ecf8673 Binary files /dev/null and b/AltAuth.png differ diff --git a/README.md b/README.md index 7dba0a6..cf7c930 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,24 @@ # AltAuth +AltAuth is a solution to allow players with Mojang account, (untested) banned accounts and (untested) Multiplayer +disabled accounts to join online mode Minecraft servers depending on client and server support. +This works by redirecting the session server requests to a proxy specified by the server. + +In comparison to the trustless authentication method proposed by Aizistral +https://github.com/Aizistral-Studios/Trustless-Authentication this authentication method enables joining of Mojang +account players, prevents man in the middle attacks (like vanilla authentication) and is compatible to clients without +AltAuth, but requires a trusted third/second party. + +## Usage + +The build/libs/AltAuth-*-all.jar (produced by running `./gradlew build`) is a valid Fabric (client and server 1.19.2), +Bukkit/Spigot/Paper (1.8 - 1.19.2) and BungeeCord/Waterfall mod/plugin. The server needs to be in online mode and +specify the domain or ip address of an AltAuth web proxy. + +Since https is a requirement and the example proxy `proxy.py` not yet supporting https the proxy has to run behind a +https proxy currently. Configuration of the proxy can be done in the first lines of the proxy script. +Banned and multiplayer disabled user support has not been tested yet and is therefore disabled by default. + +## AltAuth Protocol and Proxy behaviour + +![Sequence diagram describing protocol and principle](AltAuth.png "Sequence diagram describing protocol and principle") \ No newline at end of file diff --git a/bukkit/src/de/lixfel/altauth/bukkit/ProtocolInjector.java b/bukkit/src/de/lixfel/altauth/bukkit/ProtocolInjector.java index 6e0400e..d038ea7 100644 --- a/bukkit/src/de/lixfel/altauth/bukkit/ProtocolInjector.java +++ b/bukkit/src/de/lixfel/altauth/bukkit/ProtocolInjector.java @@ -8,6 +8,7 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; +import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -29,7 +30,7 @@ public class ProtocolInjector implements Listener { private static final Class dedicatedPlayerList = ReflectionUtil.getClass("net.minecraft.server.dedicated.DedicatedPlayerList"); private static final FieldWrapper getPlayerList = ReflectionUtil.getField(craftServer, dedicatedPlayerList, 0); private static final Class playerList = ReflectionUtil.getClass("net.minecraft.server.players.PlayerList"); - private static final Class minecraftServer = ReflectionUtil.getClass("net.minecraft.server.MinecraftServer"); + public static final Class minecraftServer = ReflectionUtil.getClass("net.minecraft.server.MinecraftServer"); private static final FieldWrapper getMinecraftServer = ReflectionUtil.getField(playerList, minecraftServer, 0); private static final Class serverConnection = ReflectionUtil.getClass("net.minecraft.server.network.ServerConnection"); private static final FieldWrapper getServerConnection = ReflectionUtil.getField(minecraftServer, serverConnection, 0); @@ -44,6 +45,7 @@ public class ProtocolInjector implements Listener { private final Plugin plugin; private final String handlerName; + private final Object minecraftServerInstance; private final List connections; private boolean closed; @@ -53,7 +55,8 @@ public class ProtocolInjector implements Listener { private ProtocolInjector(final Plugin plugin) { this.plugin = plugin; this.handlerName = "altauth"; - this.connections = getConnections.get(getServerConnection.get(getMinecraftServer.get(getPlayerList.get(plugin.getServer())))); + this.minecraftServerInstance = getMinecraftServer.get(getPlayerList.get(plugin.getServer())); + this.connections = getConnections.get(getServerConnection.get(minecraftServerInstance)); plugin.getServer().getPluginManager().registerEvents(this, plugin); @@ -62,6 +65,10 @@ public class ProtocolInjector implements Listener { } } + public Object minecraftServer() { + return minecraftServerInstance; + } + @EventHandler(priority = EventPriority.LOWEST) public void onPlayerLogin(PlayerLoginEvent e) { if(closed) diff --git a/bukkit/src/de/lixfel/altauth/bukkit/SessionServiceInjector.java b/bukkit/src/de/lixfel/altauth/bukkit/SessionServiceInjector.java index 2161d80..d88b3d0 100644 --- a/bukkit/src/de/lixfel/altauth/bukkit/SessionServiceInjector.java +++ b/bukkit/src/de/lixfel/altauth/bukkit/SessionServiceInjector.java @@ -10,29 +10,47 @@ import java.util.logging.Level; public class SessionServiceInjector { - private static final Class SESSION_SERVICE = ReflectionUtil.getClass("com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService"); + private static final Class MINECRAFT_SESSION_SERVICE = ReflectionUtil.getClass("com.mojang.authlib.minecraft.MinecraftSessionService"); + private static final Class YGGDRASIL_SESSION_SERVICE = ReflectionUtil.getClass("com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService"); - private static final ReflectionUtil.FieldWrapper JOIN_URL = ReflectionUtil.getField(SESSION_SERVICE, URL.class, "JOIN_URL"); - private static final ReflectionUtil.FieldWrapper CHECK_URL = ReflectionUtil.getField(SESSION_SERVICE, URL.class, "CHECK_URL"); + private static final ReflectionUtil.FieldWrapper JOIN_URL = ReflectionUtil.getField(YGGDRASIL_SESSION_SERVICE, URL.class, 0); + private static final ReflectionUtil.FieldWrapper CHECK_URL = ReflectionUtil.getField(YGGDRASIL_SESSION_SERVICE, URL.class, 1); + private final Object sessionService; private final URL joinUrlBackup; private final URL checkUrlBackup; public SessionServiceInjector(String altAuthServer) { - joinUrlBackup = JOIN_URL.get(null); - checkUrlBackup = CHECK_URL.get(null); - altAuthServer = "https://" + altAuthServer + "/session/minecraft/"; + + if(ReflectionUtil.MINECRAFT_VERSION < 17) { //TODO maybe 16 (untested) + sessionService = null; + } else if (ReflectionUtil.MINECRAFT_VERSION < 19) { + sessionService = ReflectionUtil.getField(ProtocolInjector.minecraftServer, MINECRAFT_SESSION_SERVICE, 0).get( + ProtocolInjector.instance.minecraftServer() + ); + } else { + Class services = ReflectionUtil.getClass("net.minecraft.server.Services"); + sessionService = ReflectionUtil.getField(services, MINECRAFT_SESSION_SERVICE, 0).get( + ReflectionUtil.getField(ProtocolInjector.minecraftServer, services, 0).get( + ProtocolInjector.instance.minecraftServer() + ) + ); + } + + joinUrlBackup = JOIN_URL.get(sessionService); + checkUrlBackup = CHECK_URL.get(sessionService); + try { - JOIN_URL.set(null, new URL(altAuthServer + "join")); - CHECK_URL.set(null, new URL(altAuthServer + "hasJoined")); + JOIN_URL.set(sessionService, new URL(altAuthServer + "join")); + CHECK_URL.set(sessionService, new URL(altAuthServer + "hasJoined")); } catch (MalformedURLException e) { AltAuthBukkit.getInstance().getLogger().log(Level.SEVERE, "Could not create AltAuth URLs", e); } } public void revert() { - JOIN_URL.set(null, joinUrlBackup); - CHECK_URL.set(null, checkUrlBackup); + JOIN_URL.set(sessionService, joinUrlBackup); + CHECK_URL.set(sessionService, checkUrlBackup); } } diff --git a/common/src/de/lixfel/ReflectionUtil.java b/common/src/de/lixfel/ReflectionUtil.java index bc15be9..d422508 100644 --- a/common/src/de/lixfel/ReflectionUtil.java +++ b/common/src/de/lixfel/ReflectionUtil.java @@ -12,19 +12,19 @@ public final class ReflectionUtil { private ReflectionUtil() {} private static final String ORG_BUKKIT_CRAFTBUKKIT; - private static final boolean REPLACE_NET_MINECRAFT; + public static final int MINECRAFT_VERSION; static { String craftbukkitPackage; - boolean legacyNms; + int minecraftVersion; try { craftbukkitPackage = Bukkit.getServer().getClass().getPackage().getName(); - legacyNms = Integer.parseInt(craftbukkitPackage.split("[.](?=[^.]*$)")[1].split("_")[1]) < 17; + minecraftVersion = Integer.parseInt(craftbukkitPackage.split("[.](?=[^.]*$)")[1].split("_")[1]); } catch (NoClassDefFoundError e) { craftbukkitPackage = ""; - legacyNms = false; + minecraftVersion = 0; } ORG_BUKKIT_CRAFTBUKKIT = craftbukkitPackage; - REPLACE_NET_MINECRAFT = legacyNms; + MINECRAFT_VERSION = minecraftVersion; } private static final String LEGACY_NET_MINECRAFT_SERVER = ORG_BUKKIT_CRAFTBUKKIT.replace("org.bukkit.craftbukkit", "net.minecraft.server"); @@ -120,7 +120,7 @@ public final class ReflectionUtil { try { if(name.startsWith("org.bukkit.craftbukkit")) { return Class.forName(ORG_BUKKIT_CRAFTBUKKIT + name.substring(22)); - } else if(REPLACE_NET_MINECRAFT && name.startsWith("net.minecraft")) { + } else if(MINECRAFT_VERSION < 17 && name.startsWith("net.minecraft")) { return Class.forName(LEGACY_NET_MINECRAFT_SERVER + "." + name.split("[.](?=[^.]*$)")[1]); } else { return Class.forName(name); diff --git a/proxy.py b/proxy.py index f88eff1..db11d74 100755 --- a/proxy.py +++ b/proxy.py @@ -22,7 +22,8 @@ bind_to = ('127.0.0.1', 8080) allow_mojang_accounts = True # Should banned microsoft accounts ("UserBannedException") be allowed to join? # Should accounts with disabled multiplayer ("InsufficientPrivilegesException") be allowed to join? -allowed_microsoft_accounts = ("InsufficientPrivilegesException", "UserBannedException") +allowed_microsoft_accounts = () +#allowed_microsoft_accounts = ("InsufficientPrivilegesException", "UserBannedException") def moj_request(url, data=None):