diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MySQLCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MySQLCoreProvider.java index 0d5ac115..c89446cc 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MySQLCoreProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MySQLCoreProvider.java @@ -5,22 +5,28 @@ import pro.gravit.launcher.ClientPermissions; import pro.gravit.launcher.request.auth.AuthRequest; import pro.gravit.launcher.request.auth.password.AuthPlainPassword; +import pro.gravit.launcher.request.secure.HardwareReportRequest; import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.auth.AuthException; import pro.gravit.launchserver.auth.MySQLSourceConfig; +import pro.gravit.launchserver.auth.core.interfaces.UserHardware; +import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportHardware; +import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportHardware; import pro.gravit.launchserver.auth.password.PasswordVerifier; +import pro.gravit.launchserver.auth.protect.hwid.HWIDProvider; import pro.gravit.launchserver.manangers.AuthManager; import pro.gravit.launchserver.socket.response.auth.AuthResponse; +import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.SecurityHelper; +import java.io.ByteArrayInputStream; import java.io.IOException; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; +import java.sql.*; +import java.util.LinkedList; +import java.util.List; import java.util.UUID; -public class MySQLCoreProvider extends AuthCoreProvider { +public class MySQLCoreProvider extends AuthCoreProvider implements AuthSupportHardware { private transient final Logger logger = LogManager.getLogger(); public MySQLSourceConfig mySQLHolder; @@ -29,8 +35,23 @@ public class MySQLCoreProvider extends AuthCoreProvider { public String accessTokenColumn; public String passwordColumn; public String serverIDColumn; + public String hardwareIdColumn; public String table; + + public String tableHWID = "hwids"; + public String tableHWIDLog = "hwidLog"; + private String sqlFindHardwareByPublicKey; + private String sqlFindHardwareByData; + private String sqlFindHardwareById; + private String sqlCreateHardware; + private String sqlCreateHWIDLog; + private String sqlUpdateHardwarePublicKey; + private String sqlUpdateHardwareBanned; + private String sqlUpdateUsers; + private String sqlUsersByHwidId; + public PasswordVerifier passwordVerifier; + public double criticalCompareLevel = 1.0; // Prepared SQL queries private transient String queryByUUIDSQL; @@ -100,17 +121,29 @@ public void init(LaunchServer server) { if (usernameColumn == null) logger.error("usernameColumn cannot be null"); if (accessTokenColumn == null) logger.error("accessTokenColumn cannot be null"); if (serverIDColumn == null) logger.error("serverIDColumn cannot be null"); + if (hardwareIdColumn == null) logger.error("hardwareIdColumn cannot be null"); if (table == null) logger.error("table cannot be null"); // Prepare SQL queries - queryByUUIDSQL = String.format("SELECT %s, %s, %s, %s, %s FROM %s WHERE %s=? LIMIT 1", - uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, passwordColumn, table, uuidColumn); - queryByUsernameSQL = String.format("SELECT %s, %s, %s, %s, %s FROM %s WHERE %s=? LIMIT 1", - uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, passwordColumn, table, usernameColumn); + String userInfoCols = String.format("%s, %s, %s, %s, %s, %s", uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, passwordColumn, hardwareIdColumn); + queryByUUIDSQL = String.format("SELECT %s FROM %s WHERE %s=? LIMIT 1", userInfoCols, + table, uuidColumn); + queryByUsernameSQL = String.format("SELECT %s FROM %s WHERE %s=? LIMIT 1", + userInfoCols, table, usernameColumn); updateAuthSQL = String.format("UPDATE %s SET %s=?, %s=NULL WHERE %s=? LIMIT 1", table, accessTokenColumn, serverIDColumn, uuidColumn); updateServerIDSQL = String.format("UPDATE %s SET %s=? WHERE %s=? LIMIT 1", table, serverIDColumn, uuidColumn); + String hardwareInfoCols = "id, hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, graphicCard, banned"; + sqlFindHardwareByPublicKey = String.format("SELECT %s FROM %s WHERE `publicKey` = ?", hardwareInfoCols, tableHWID); + sqlFindHardwareById = String.format("SELECT %s FROM %s WHERE `id` = ?", hardwareInfoCols, tableHWID); + sqlUsersByHwidId = String.format("SELECT %s FROM %s WHERE `%s` = ?", userInfoCols, table, hardwareIdColumn); + sqlFindHardwareByData = String.format("SELECT %s FROM %s", hardwareInfoCols, tableHWID); + sqlCreateHardware = String.format("INSERT INTO `%s` (`publickey`, `hwDiskId`, `baseboardSerialNumber`, `displayId`, `bitness`, `totalMemory`, `logicalProcessors`, `physicalProcessors`, `processorMaxFreq`, `battery`, `graphicCard`, `banned`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '0')", tableHWID); + sqlCreateHWIDLog = String.format("INSERT INTO %s (`hwidId`, `newPublicKey`) VALUES (?, ?)", tableHWIDLog); + sqlUpdateHardwarePublicKey = String.format("UPDATE %s SET `publicKey` = ? WHERE `id` = ?", tableHWID); + sqlUpdateHardwareBanned = String.format("UPDATE %s SET `banned` = ? WHERE `id` = ?", tableHWID); + sqlUpdateUsers = String.format("UPDATE %s SET `%s` = ? WHERE `%s` = ?", table, hardwareIdColumn, uuidColumn); } protected boolean updateAuth(User user, String accessToken) throws IOException { @@ -145,7 +178,33 @@ public void close() throws IOException { private MySQLUser constructUser(ResultSet set) throws SQLException { return set.next() ? new MySQLUser(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn), - set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), new ClientPermissions()) : null; + set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), new ClientPermissions(), set.getLong(hardwareIdColumn)) : null; + } + + private MySQLUserHardware fetchHardwareInfo(ResultSet set) throws SQLException, IOException { + HardwareReportRequest.HardwareInfo hardwareInfo = new HardwareReportRequest.HardwareInfo(); + hardwareInfo.hwDiskId = set.getString("hwDiskId"); + hardwareInfo.baseboardSerialNumber = set.getString("baseboardSerialNumber"); + Blob displayId = set.getBlob("displayId"); + hardwareInfo.displayId = displayId == null ? null : IOHelper.read(displayId.getBinaryStream()); + hardwareInfo.bitness = set.getInt("bitness"); + hardwareInfo.totalMemory = set.getLong("totalMemory"); + hardwareInfo.logicalProcessors = set.getInt("logicalProcessors"); + hardwareInfo.physicalProcessors = set.getInt("physicalProcessors"); + hardwareInfo.processorMaxFreq = set.getLong("processorMaxFreq"); + hardwareInfo.battery = set.getBoolean("battery"); + hardwareInfo.graphicCard = set.getString("graphicCard"); + Blob publicKey = set.getBlob("publicKey"); + long id = set.getLong("id"); + boolean banned = set.getBoolean("banned"); + return new MySQLUserHardware(hardwareInfo, publicKey == null ? null : IOHelper.read(publicKey.getBinaryStream()), id, banned); + } + + private void setUserHardwareId(Connection connection, UUID uuid, long hwidId) throws SQLException { + PreparedStatement s = connection.prepareStatement(sqlUpdateUsers); + s.setLong(1, hwidId); + s.setString(2, uuid.toString()); + s.executeUpdate(); } private User query(String sql, String value) throws IOException { @@ -161,21 +220,183 @@ private User query(String sql, String value) throws IOException { } } - public static class MySQLUser implements User { - private final UUID uuid; - private final String username; - private final String accessToken; - private final String serverId; - private final String password; - private final ClientPermissions permissions; + @Override + public UserHardware getHardwareInfoByPublicKey(byte[] publicKey) { + try (Connection connection = mySQLHolder.getConnection()) { + PreparedStatement s = connection.prepareStatement(sqlFindHardwareByPublicKey); + s.setBlob(1, new ByteArrayInputStream(publicKey)); + try (ResultSet set = s.executeQuery()) { + if (set.next()) { + return fetchHardwareInfo(set); + } else { + return null; + } + } + } catch (SQLException | IOException throwables) { + logger.error(throwables); + return null; + } + } - public MySQLUser(UUID uuid, String username, String accessToken, String serverId, String password, ClientPermissions permissions) { + @Override + public UserHardware getHardwareInfoByData(HardwareReportRequest.HardwareInfo info) { + try (Connection connection = mySQLHolder.getConnection()) { + PreparedStatement s = connection.prepareStatement(sqlFindHardwareByData); + try (ResultSet set = s.executeQuery()) { + while (set.next()) { + MySQLUserHardware hw = fetchHardwareInfo(set); + HWIDProvider.HardwareInfoCompareResult result = compareHardwareInfo(hw.getHardwareInfo(), info); + if (result.compareLevel > criticalCompareLevel) { + return hw; + } + } + } + } catch (SQLException | IOException throwables) { + logger.error(throwables); + } + return null; + } + + @Override + public UserHardware getHardwareInfoById(String id) { + try (Connection connection = mySQLHolder.getConnection()) { + PreparedStatement s = connection.prepareStatement(sqlFindHardwareById); + s.setLong(1, Long.parseLong(id)); + try (ResultSet set = s.executeQuery()) { + if (set.next()) { + return fetchHardwareInfo(set); + } else { + return null; + } + } + } catch (SQLException | IOException throwables) { + logger.error(throwables); + return null; + } + } + + @Override + public UserHardware createHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey) { + try (Connection connection = mySQLHolder.getConnection()) { + PreparedStatement s = connection.prepareStatement(sqlCreateHardware, Statement.RETURN_GENERATED_KEYS); + s.setBlob(1, new ByteArrayInputStream(publicKey)); + s.setString(2, hardwareInfo.hwDiskId); + s.setString(3, hardwareInfo.baseboardSerialNumber); + s.setBlob(4, hardwareInfo.displayId == null ? null : new ByteArrayInputStream(hardwareInfo.displayId)); + s.setInt(5, hardwareInfo.bitness); + s.setLong(6, hardwareInfo.totalMemory); + s.setInt(7, hardwareInfo.logicalProcessors); + s.setInt(8, hardwareInfo.physicalProcessors); + s.setLong(9, hardwareInfo.processorMaxFreq); + s.setString(10, hardwareInfo.graphicCard); + s.setBoolean(11, hardwareInfo.battery); + s.executeUpdate(); + try (ResultSet generatedKeys = s.getGeneratedKeys()) { + if (generatedKeys.next()) { + //writeHwidLog(connection, generatedKeys.getLong(1), publicKey); + long id = generatedKeys.getLong(1); + return new MySQLUserHardware(hardwareInfo, publicKey, id, false); + } + } + return null; + } catch (SQLException throwables) { + logger.error(throwables); + return null; + } + } + + @Override + public void connectUserAndHardware(User user, UserHardware hardware) { + MySQLUser mySQLUser = (MySQLUser) user; + MySQLUserHardware mySQLUserHardware = (MySQLUserHardware) hardware; + if (mySQLUser.hwidId == mySQLUserHardware.id) return; + mySQLUser.hwidId = mySQLUserHardware.id; + try (Connection connection = mySQLHolder.getConnection()) { + setUserHardwareId(connection, user.getUUID(), mySQLUserHardware.id); + } catch (SQLException throwables) { + logger.error(throwables); + } + } + + @Override + public void addPublicKeyToHardwareInfo(UserHardware hardware, byte[] publicKey) { + MySQLUserHardware mySQLUserHardware = (MySQLUserHardware) hardware; + mySQLUserHardware.publicKey = publicKey; + try (Connection connection = mySQLHolder.getConnection()) { + PreparedStatement s = connection.prepareStatement(sqlUpdateHardwarePublicKey); + s.setBlob(1, new ByteArrayInputStream(publicKey)); + s.setLong(2, mySQLUserHardware.id); + s.executeUpdate(); + } catch (SQLException e) { + logger.error(e); + } + } + + @Override + public Iterable getUsersByHardwareInfo(UserHardware hardware) { + List users = new LinkedList<>(); + try (Connection c = mySQLHolder.getConnection()) { + PreparedStatement s = c.prepareStatement(sqlUsersByHwidId); + s.setLong(1, Long.parseLong(hardware.getId())); + s.setQueryTimeout(MySQLSourceConfig.TIMEOUT); + try (ResultSet set = s.executeQuery()) { + while (set.next()) { + users.add(constructUser(set)); + } + } + } catch (SQLException e) { + logger.error(e); + return null; + } + return users; + } + + @Override + public void banHardware(UserHardware hardware) { + MySQLUserHardware mySQLUserHardware = (MySQLUserHardware) hardware; + mySQLUserHardware.banned = true; + try (Connection connection = mySQLHolder.getConnection()) { + PreparedStatement s = connection.prepareStatement(sqlUpdateHardwareBanned); + s.setBoolean(1, true); + s.setLong(2, mySQLUserHardware.id); + s.executeUpdate(); + } catch (SQLException e) { + logger.error(e); + } + } + + @Override + public void unbanHardware(UserHardware hardware) { + MySQLUserHardware mySQLUserHardware = (MySQLUserHardware) hardware; + mySQLUserHardware.banned = false; + try (Connection connection = mySQLHolder.getConnection()) { + PreparedStatement s = connection.prepareStatement(sqlUpdateHardwareBanned); + s.setBoolean(1, false); + s.setLong(2, mySQLUserHardware.id); + s.executeUpdate(); + } catch (SQLException e) { + logger.error(e); + } + } + + public class MySQLUser implements User, UserSupportHardware { + protected UUID uuid; + protected String username; + protected String accessToken; + protected String serverId; + protected String password; + protected ClientPermissions permissions; + protected long hwidId; + protected transient MySQLUserHardware hardware; + + public MySQLUser(UUID uuid, String username, String accessToken, String serverId, String password, ClientPermissions permissions, long hwidId) { this.uuid = uuid; this.username = username; this.accessToken = accessToken; this.serverId = serverId; this.password = password; this.permissions = permissions; + this.hwidId = hwidId; } @Override @@ -202,5 +423,47 @@ public String getAccessToken() { public ClientPermissions getPermissions() { return permissions; } + + @Override + public UserHardware getHardware() { + if (hardware != null) return hardware; + MySQLUserHardware result = (MySQLUserHardware) getHardwareInfoById(String.valueOf(hwidId)); + hardware = result; + return result; + } + } + + public static class MySQLUserHardware implements UserHardware { + private HardwareReportRequest.HardwareInfo hardwareInfo; + private byte[] publicKey; + private long id; + private boolean banned; + + public MySQLUserHardware(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, long id, boolean banned) { + this.hardwareInfo = hardwareInfo; + this.publicKey = publicKey; + this.id = id; + this.banned = banned; + } + + @Override + public HardwareReportRequest.HardwareInfo getHardwareInfo() { + return hardwareInfo; + } + + @Override + public byte[] getPublicKey() { + return publicKey; + } + + @Override + public String getId() { + return String.valueOf(id); + } + + @Override + public boolean isBanned() { + return banned; + } } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/provider/AuthSupportHardware.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/provider/AuthSupportHardware.java index 6e433d09..c30491c0 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/provider/AuthSupportHardware.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/provider/AuthSupportHardware.java @@ -18,6 +18,8 @@ public interface AuthSupportHardware { UserHardware createHardwareInfo(HardwareReportRequest.HardwareInfo info, byte[] publicKey); + void connectUserAndHardware(User user, UserHardware hardware); + void addPublicKeyToHardwareInfo(UserHardware hardware, byte[] publicKey); Iterable getUsersByHardwareInfo(UserHardware hardware); diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/protect/AdvancedProtectHandler.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/protect/AdvancedProtectHandler.java index 8d95813c..e50d3322 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/protect/AdvancedProtectHandler.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/protect/AdvancedProtectHandler.java @@ -67,7 +67,7 @@ public void onHardwareReport(HardwareReportResponse response, Client client) { return; } logger.debug("HardwareInfo received"); - if (client.auth instanceof AuthSupportHardware) { + if (client.auth.core instanceof AuthSupportHardware) { AuthSupportHardware authSupportHardware = (AuthSupportHardware) client.auth; UserHardware hardware = authSupportHardware.getHardwareInfoByData(response.hardware); if (hardware == null) { @@ -75,6 +75,7 @@ public void onHardwareReport(HardwareReportResponse response, Client client) { } else { authSupportHardware.addPublicKeyToHardwareInfo(hardware, client.trustLevel.publicKey); } + authSupportHardware.connectUserAndHardware(client.getUser(), hardware); if (hardware.isBanned()) { throw new SecurityException("Your hardware banned"); } @@ -108,7 +109,16 @@ public VerifySecureLevelKeyRequestEvent onSuccessVerify(Client client) { logger.warn("HWIDProvider null. HardwareInfo not checked!"); } else { try { - client.trustLevel.hardwareInfo = provider.findHardwareInfoByPublicKey(client.trustLevel.publicKey, client); + if (client.auth.core instanceof AuthSupportHardware) { + AuthSupportHardware authSupportHardware = (AuthSupportHardware) client.auth; + UserHardware hardware = authSupportHardware.getHardwareInfoByPublicKey(client.trustLevel.publicKey); + if (hardware != null) { + client.trustLevel.hardwareInfo = hardware.getHardwareInfo(); + authSupportHardware.connectUserAndHardware(client.getUser(), hardware); + } + } else { + client.trustLevel.hardwareInfo = provider.findHardwareInfoByPublicKey(client.trustLevel.publicKey, client); + } if (client.trustLevel.hardwareInfo == null) //HWID not found? return new VerifySecureLevelKeyRequestEvent(true, false, createPublicKeyToken(client.username, client.trustLevel.publicKey)); } catch (HWIDException e) { diff --git a/Launcher/src/main/java/pro/gravit/launcher/utils/HWIDProvider.java b/Launcher/src/main/java/pro/gravit/launcher/utils/HWIDProvider.java index b2d1062f..2cc8dd06 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/utils/HWIDProvider.java +++ b/Launcher/src/main/java/pro/gravit/launcher/utils/HWIDProvider.java @@ -112,6 +112,7 @@ public HardwareReportRequest.HardwareInfo getHardwareInfo(boolean needSerial) { info.processorMaxFreq = getProcessorMaxFreq(); info.totalMemory = getTotalMemory(); info.battery = isBattery(); + info.graphicCard = getGraphicCardName(); if (needSerial) { info.hwDiskId = getHWDiskID(); info.displayId = getDisplayID(); diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/request/secure/HardwareReportRequest.java b/LauncherAPI/src/main/java/pro/gravit/launcher/request/secure/HardwareReportRequest.java index 6e41781f..d919eb44 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/request/secure/HardwareReportRequest.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/request/secure/HardwareReportRequest.java @@ -21,5 +21,6 @@ public static class HardwareInfo { public String hwDiskId; public byte[] displayId; public String baseboardSerialNumber; + public String graphicCard; } }