Fail safely if BungeeCord implementation specific classes are not found

This commit is contained in:
games647
2021-03-15 13:40:28 +01:00
parent 357430b5ad
commit 4bc328c37c
8 changed files with 69 additions and 37 deletions

View File

@ -1,4 +1,5 @@
# Automatically build, run unit and integration tests to detect errors early (CI provided by GitHub) # Automatically build, run unit and integration tests to detect errors early (CI provided by GitHub)
# including making pull requests review easier
# Human readable name in the actions tab # Human readable name in the actions tab
name: Java CI name: Java CI
@ -45,4 +46,4 @@ jobs:
# Run non-interactive, package (with compile+test), # Run non-interactive, package (with compile+test),
# ignore snapshot updates, because they are likely to have breaking changes, enforce checksums to validate # ignore snapshot updates, because they are likely to have breaking changes, enforce checksums to validate
# possible errors in dependencies # possible errors in dependencies
run: mvn test --batch-mode --no-snapshot-updates --strict-checksums --file pom.xml run: mvn package test --batch-mode --no-snapshot-updates --strict-checksums --file pom.xml

View File

@ -10,7 +10,6 @@ import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.PublicKey; import java.security.PublicKey;
import java.util.Random; import java.util.Random;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
@ -27,7 +26,7 @@ public class EncryptionUtil {
public static final String KEY_PAIR_ALGORITHM = "RSA"; public static final String KEY_PAIR_ALGORITHM = "RSA";
private EncryptionUtil() { private EncryptionUtil() {
//utility // utility
} }
/** /**
@ -43,7 +42,7 @@ public class EncryptionUtil {
keyPairGenerator.initialize(1_024); keyPairGenerator.initialize(1_024);
return keyPairGenerator.generateKeyPair(); return keyPairGenerator.generateKeyPair();
} catch (NoSuchAlgorithmException nosuchalgorithmexception) { } catch (NoSuchAlgorithmException nosuchalgorithmexception) {
//Should be existing in every vm // Should be existing in every vm
throw new ExceptionInInitializerError(nosuchalgorithmexception); throw new ExceptionInInitializerError(nosuchalgorithmexception);
} }
} }
@ -65,9 +64,9 @@ public class EncryptionUtil {
/** /**
* Generate the server id based on client and server data. * Generate the server id based on client and server data.
* *
* @param sessionId session for the current login attempt * @param sessionId session for the current login attempt
* @param sharedSecret shared secret between the client and the server * @param sharedSecret shared secret between the client and the server
* @param publicKey public key of the server * @param publicKey public key of the server
* @return the server id formatted as a hexadecimal string. * @return the server id formatted as a hexadecimal string.
*/ */
public static String getServerIdHashString(String sessionId, SecretKey sharedSecret, PublicKey publicKey) { public static String getServerIdHashString(String sessionId, SecretKey sharedSecret, PublicKey publicKey) {
@ -85,8 +84,8 @@ public class EncryptionUtil {
/** /**
* Decrypts the content and extracts the key spec. * Decrypts the content and extracts the key spec.
* *
* @param cipher decryption cipher initialized with the private key * @param privateKey private server key
* @param sharedKey the encrypted shared key * @param sharedKey the encrypted shared key
* @return shared secret key * @return shared secret key
* @throws GeneralSecurityException if it fails to decrypt the data * @throws GeneralSecurityException if it fails to decrypt the data
*/ */
@ -106,16 +105,18 @@ public class EncryptionUtil {
* Decrypted the given data using the cipher. * Decrypted the given data using the cipher.
* *
* @param cipher decryption cypher initialized with the private key * @param cipher decryption cypher initialized with the private key
* @param data the encrypted data * @param data the encrypted data
* @return clear text data * @return clear text data
* @throws GeneralSecurityException if it fails to decrypt the data * @throws GeneralSecurityException if it fails to decrypt the data
*/ */
private static byte[] decrypt(Cipher cipher, byte[] data) throws GeneralSecurityException { private static byte[] decrypt(Cipher cipher, byte[] data) throws GeneralSecurityException {
// inlined: byte[] a(int var0, Key var1, byte[] var2), Cipher a(int var0, String var1, Key var2) // inlined: byte[] a(int var0, Key var1, byte[] var2), Cipher a(int var0, String var1, Key
// var2)
return cipher.doFinal(data); return cipher.doFinal(data);
} }
private static byte[] getServerIdHash(String sessionId, PublicKey publicKey, SecretKey sharedSecret) private static byte[] getServerIdHash(
String sessionId, PublicKey publicKey, SecretKey sharedSecret)
throws NoSuchAlgorithmException { throws NoSuchAlgorithmException {
// byte[] a(String var0, PublicKey var1, SecretKey var2) // byte[] a(String var0, PublicKey var1, SecretKey var2)
MessageDigest digest = MessageDigest.getInstance("SHA-1"); MessageDigest digest = MessageDigest.getInstance("SHA-1");

View File

@ -21,7 +21,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.2.3</version> <version>3.2.4</version>
<configuration> <configuration>
<createDependencyReducedPom>false</createDependencyReducedPom> <createDependencyReducedPom>false</createDependencyReducedPom>
<shadedArtifactAttached>false</shadedArtifactAttached> <shadedArtifactAttached>false</shadedArtifactAttached>
@ -84,7 +84,7 @@
<dependency> <dependency>
<groupId>net.md-5</groupId> <groupId>net.md-5</groupId>
<artifactId>bungeecord-proxy</artifactId> <artifactId>bungeecord-proxy</artifactId>
<version>1.15-SNAPSHOT</version> <version>1.16-R0.5-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>

View File

@ -31,6 +31,8 @@ import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.event.EventPriority; import net.md_5.bungee.event.EventPriority;
import org.geysermc.floodgate.FloodgateAPI; import org.geysermc.floodgate.FloodgateAPI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* Enables online mode logins for specified users and sends plugin message to the Bukkit version of this plugin in * Enables online mode logins for specified users and sends plugin message to the Bukkit version of this plugin in
@ -38,31 +40,41 @@ import org.geysermc.floodgate.FloodgateAPI;
*/ */
public class ConnectListener implements Listener { public class ConnectListener implements Listener {
private final FastLoginBungee plugin;
private final RateLimiter rateLimiter;
private final Property[] emptyProperties = {};
private final boolean floodGateAvailable;
private static final String UUID_FIELD_NAME = "uniqueId"; private static final String UUID_FIELD_NAME = "uniqueId";
private static final boolean initialHandlerClazzFound;
private static final MethodHandle uniqueIdSetter; private static final MethodHandle uniqueIdSetter;
static { static {
MethodHandle setHandle = null; MethodHandle setHandle = null;
boolean handlerFound = false;
try { try {
Lookup lookup = MethodHandles.lookup(); Lookup lookup = MethodHandles.lookup();
Class.forName("net.md_5.bungee.connection.InitialHandler");
handlerFound = true;
Field uuidField = InitialHandler.class.getDeclaredField(UUID_FIELD_NAME); Field uuidField = InitialHandler.class.getDeclaredField(UUID_FIELD_NAME);
uuidField.setAccessible(true); uuidField.setAccessible(true);
setHandle = lookup.unreflectSetter(uuidField); setHandle = lookup.unreflectSetter(uuidField);
} catch (ClassNotFoundException classNotFoundException) {
Logger logger = LoggerFactory.getLogger(ConnectListener.class);
logger.error(
"Cannot find Bungee initial handler; Disabling premium UUID and skin won't work.",
classNotFoundException
);
} catch (ReflectiveOperationException reflectiveOperationException) { } catch (ReflectiveOperationException reflectiveOperationException) {
reflectiveOperationException.printStackTrace(); reflectiveOperationException.printStackTrace();
} }
initialHandlerClazzFound = handlerFound;
uniqueIdSetter = setHandle; uniqueIdSetter = setHandle;
} }
private final FastLoginBungee plugin;
private final RateLimiter rateLimiter;
private final Property[] emptyProperties = {};
private final boolean floodGateAvailable;
public ConnectListener(FastLoginBungee plugin, RateLimiter rateLimiter, boolean floodgateAvailable) { public ConnectListener(FastLoginBungee plugin, RateLimiter rateLimiter, boolean floodgateAvailable) {
this.plugin = plugin; this.plugin = plugin;
this.rateLimiter = rateLimiter; this.rateLimiter = rateLimiter;
@ -97,29 +109,31 @@ public class ConnectListener implements Listener {
//use the login event instead of the post login event in order to send the login success packet to the client //use the login event instead of the post login event in order to send the login success packet to the client
//with the offline uuid this makes it possible to set the skin then //with the offline uuid this makes it possible to set the skin then
final PendingConnection connection = loginEvent.getConnection(); PendingConnection connection = loginEvent.getConnection();
final InitialHandler initialHandler = (InitialHandler) connection;
if (connection.isOnlineMode()) { if (connection.isOnlineMode()) {
LoginSession session = plugin.getSession().get(connection); LoginSession session = plugin.getSession().get(connection);
LoginResult loginProfile = initialHandler.getLoginProfile();
UUID verifiedUUID = connection.getUniqueId(); UUID verifiedUUID = connection.getUniqueId();
String verifiedUsername = connection.getName();
session.setUuid(verifiedUUID); session.setUuid(verifiedUUID);
session.setVerifiedUsername(loginProfile.getName()); session.setVerifiedUsername(verifiedUsername);
StoredProfile playerProfile = session.getProfile(); StoredProfile playerProfile = session.getProfile();
playerProfile.setId(verifiedUUID); playerProfile.setId(verifiedUUID);
//bungeecord will do this automatically so override it on disabled option // bungeecord will do this automatically so override it on disabled option
if (!plugin.getCore().getConfig().get("premiumUuid", true)) { if (uniqueIdSetter != null) {
String username = loginProfile.getName(); InitialHandler initialHandler = (InitialHandler) connection;
setOfflineId(initialHandler, username);
}
if (!plugin.getCore().getConfig().get("forwardSkin", true)) { if (!plugin.getCore().getConfig().get("premiumUuid", true)) {
// this is null on offline mode setOfflineId(initialHandler, verifiedUsername);
loginProfile.setProperties(emptyProperties); }
if (!plugin.getCore().getConfig().get("forwardSkin", true)) {
// this is null on offline mode
LoginResult loginProfile = initialHandler.getLoginProfile();
loginProfile.setProperties(emptyProperties);
}
} }
} }
} }

View File

@ -206,7 +206,7 @@ public class AuthStorage {
saveStmt.execute(); saveStmt.execute();
try (ResultSet generatedKeys = saveStmt.getGeneratedKeys()) { try (ResultSet generatedKeys = saveStmt.getGeneratedKeys()) {
if (generatedKeys != null && generatedKeys.next()) { if (generatedKeys.next()) {
playerProfile.setRowId(generatedKeys.getInt(1)); playerProfile.setRowId(generatedKeys.getInt(1));
} }
} }

View File

@ -3,6 +3,7 @@ package com.github.games647.fastlogin.core;
import com.github.games647.craftapi.model.Profile; import com.github.games647.craftapi.model.Profile;
import java.time.Instant; import java.time.Instant;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
@ -86,6 +87,21 @@ public class StoredProfile extends Profile {
this.lastLogin = lastLogin; this.lastLogin = lastLogin;
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof StoredProfile)) return false;
if (!super.equals(o)) return false;
StoredProfile that = (StoredProfile) o;
return rowId == that.rowId && premium == that.premium
&& Objects.equals(lastIp, that.lastIp) && lastLogin.equals(that.lastLogin);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), rowId, premium, lastIp, lastLogin);
}
@Override @Override
public synchronized String toString() { public synchronized String toString() {
return this.getClass().getSimpleName() + '{' + return this.getClass().getSimpleName() + '{' +

View File

@ -90,7 +90,7 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
} }
int maxCon = config.getInt("anti-bot.connections", 200); int maxCon = config.getInt("anti-bot.connections", 200);
long expireTime = config.getInt("anti-bot.expire", 5) * 60 * 1_000L; long expireTime = config.getLong("anti-bot.expire", 5) * 60 * 1_000L;
if (expireTime > MAX_EXPIRE_RATE) { if (expireTime > MAX_EXPIRE_RATE) {
expireTime = MAX_EXPIRE_RATE; expireTime = MAX_EXPIRE_RATE;
} }
@ -132,7 +132,7 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
config = configProvider.load(reader, defaults); config = configProvider.load(reader, defaults);
} }
//explicitly add keys here, because Configuration.getKeys doesn't return the keys from the default configuration // explicitly add keys here, because Configuration.getKeys doesn't return the keys from the default configuration
for (String key : defaults.getKeys()) { for (String key : defaults.getKeys()) {
config.set(key, config.get(key)); config.set(key, config.get(key));
} }

View File

@ -52,7 +52,7 @@
<plugin> <plugin>
<groupId>pl.project13.maven</groupId> <groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId> <artifactId>git-commit-id-plugin</artifactId>
<version>4.0.0</version> <version>4.0.3</version>
<configuration> <configuration>
<failOnNoGitDirectory>false</failOnNoGitDirectory> <failOnNoGitDirectory>false</failOnNoGitDirectory>
</configuration> </configuration>