From 96640797994f212d2904fc47f8d811d1559d9213 Mon Sep 17 00:00:00 2001 From: Clercq <50501391+Clercq@users.noreply.github.com> Date: Thu, 5 Nov 2020 05:50:23 +0300 Subject: [PATCH] MFASupport By PC --- LaunchServer/build.gradle | 1 + .../auth/provider/AuthProvider.java | 2 +- .../auth/provider/MySQLAuthProvider.java | 43 +++++++++++++++++-- .../socket/response/auth/AuthResponse.java | 18 ++++++++ .../events/request/AuthRequestEvent.java | 2 + gradle.properties | 1 + 6 files changed, 62 insertions(+), 5 deletions(-) diff --git a/LaunchServer/build.gradle b/LaunchServer/build.gradle index e61c4063..ec59fee7 100644 --- a/LaunchServer/build.gradle +++ b/LaunchServer/build.gradle @@ -71,6 +71,7 @@ task cleanjar(type: Jar, dependsOn: jar) { dependencies { pack project(':LauncherAPI') + compile 'dev.samstevens.totp:totp:1.7' bundle group: 'org.fusesource.jansi', name: 'jansi', version: rootProject['verJansi'] bundle group: 'org.jline', name: 'jline', version: rootProject['verJline'] bundle group: 'org.jline', name: 'jline-reader', version: rootProject['verJline'] diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/provider/AuthProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/provider/AuthProvider.java index ffe549b9..8b7230f5 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/provider/AuthProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/provider/AuthProvider.java @@ -36,7 +36,7 @@ public GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType getFirstAuthTyp } public GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType getSecondAuthType() { - return GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType.NONE; + return GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType.TOTP; } /** diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/provider/MySQLAuthProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/provider/MySQLAuthProvider.java index 1fe7558d..7bca873b 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/provider/MySQLAuthProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/provider/MySQLAuthProvider.java @@ -1,8 +1,17 @@ package pro.gravit.launchserver.auth.provider; +import dev.samstevens.totp.code.CodeGenerator; +import dev.samstevens.totp.code.CodeVerifier; +import dev.samstevens.totp.code.DefaultCodeGenerator; +import dev.samstevens.totp.code.DefaultCodeVerifier; +import dev.samstevens.totp.time.SystemTimeProvider; +import dev.samstevens.totp.time.TimeProvider; import pro.gravit.launcher.ClientPermissions; +import pro.gravit.launcher.events.request.AuthRequestEvent; import pro.gravit.launcher.request.auth.AuthRequest; +import pro.gravit.launcher.request.auth.password.Auth2FAPassword; import pro.gravit.launcher.request.auth.password.AuthPlainPassword; +import pro.gravit.launcher.request.auth.password.AuthTOTPPassword; import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.auth.AuthException; import pro.gravit.launchserver.auth.MySQLSourceConfig; @@ -32,18 +41,44 @@ public void init(LaunchServer srv) { @Override public AuthProviderResult auth(String login, AuthRequest.AuthPasswordInterface password, String ip) throws SQLException, AuthException { - if (!(password instanceof AuthPlainPassword)) throw new AuthException("This password type not supported"); + + if (!(password instanceof Auth2FAPassword || password instanceof AuthPlainPassword)) throw new AuthException("This password type not supported"); + TimeProvider timeProvider = new SystemTimeProvider(); + CodeGenerator codeGenerator = new DefaultCodeGenerator(); + CodeVerifier verifier = new DefaultCodeVerifier(codeGenerator, timeProvider); + AuthPlainPassword first; + AuthTOTPPassword second; + if(password instanceof Auth2FAPassword) { + first = (AuthPlainPassword) ((Auth2FAPassword) password).firstPassword; + second = (AuthTOTPPassword) ((Auth2FAPassword) password).secondPassword; + } else { + first = (AuthPlainPassword) password; + second = null; + } try (Connection c = mySQLHolder.getConnection()) { PreparedStatement s = c.prepareStatement(query); - String[] replaceParams = {"login", login, "password", ((AuthPlainPassword) password).password, "ip", ip}; + String[] replaceParams = {"login", login, "password", first.password, "ip", ip}; for (int i = 0; i < queryParams.length; i++) s.setString(i + 1, CommonHelper.replace(queryParams[i], replaceParams)); // Execute SQL query s.setQueryTimeout(MySQLSourceConfig.TIMEOUT); try (ResultSet set = s.executeQuery()) { - return set.next() ? new AuthProviderResult(set.getString(1), SecurityHelper.randomStringToken(), new ClientPermissions( - set.getLong(2), flagsEnabled ? set.getLong(3) : 0)) : authError(message); + if (set.next()){ + if (set.getBoolean("has_mfa")){ + if (second == null){ + return authError(AuthRequestEvent.TWO_FACTOR_NEED_ERROR_MESSAGE); + }else{ + boolean successful = verifier.isValidCode(set.getString("secret"), second.totp); + return successful ? new AuthProviderResult(set.getString(1), SecurityHelper.randomStringToken(), new ClientPermissions( + set.getLong(2), flagsEnabled ? set.getLong(3) : 0)) : authError(AuthRequestEvent.TWO_FACTOR_BAD_MESSAGE); + } + } else { + return new AuthProviderResult(set.getString(1), SecurityHelper.randomStringToken(), new ClientPermissions( + set.getLong(2), flagsEnabled ? set.getLong(3) : 0)); + } + } + return authError(message); } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/AuthResponse.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/AuthResponse.java index 6465617a..704cad11 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/AuthResponse.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/AuthResponse.java @@ -1,8 +1,10 @@ package pro.gravit.launchserver.socket.response.auth; import io.netty.channel.ChannelHandlerContext; +import net.sf.launch4j.Log; import pro.gravit.launcher.events.request.AuthRequestEvent; import pro.gravit.launcher.request.auth.AuthRequest; +import pro.gravit.launcher.request.auth.password.Auth2FAPassword; import pro.gravit.launcher.request.auth.password.AuthECPassword; import pro.gravit.launcher.request.auth.password.AuthPlainPassword; import pro.gravit.launchserver.auth.AuthException; @@ -57,6 +59,15 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti throw new AuthException("Password decryption error"); } } + if (password instanceof Auth2FAPassword){ + try { + ((Auth2FAPassword) password).firstPassword = new AuthPlainPassword(IOHelper.decode(SecurityHelper.decrypt(server.runtime.passwordEncryptKey + , ((AuthECPassword)(((Auth2FAPassword) password).firstPassword)).password))); + } catch (IllegalBlockSizeException | BadPaddingException ignored) { + throw new AuthException("Password decryption error"); + } + } + if (clientData.isAuth) { if (LogHelper.isDevEnabled()) { LogHelper.warning("Client %s double auth", clientData.username == null ? ip : clientData.username); @@ -64,6 +75,7 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti sendError("You are already logged in"); return; } + AuthProviderPair pair; if (auth_id == null || auth_id.isEmpty()) pair = server.config.getAuthProviderPair(); else pair = server.config.getAuthProviderPair(auth_id); @@ -71,6 +83,7 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti sendError("auth_id incorrect"); return; } + AuthContext context = new AuthContext(clientData, login, client, ip, authType); AuthProvider provider = pair.provider; server.authHookManager.preHook.hook(context, clientData); @@ -80,6 +93,7 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti AuthProvider.authError(String.format("Illegal result: '%s'", aresult.username)); return; } + //if (clientData.profile == null) { // throw new AuthException("You profile not found"); //} @@ -88,6 +102,7 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti clientData.permissions = aresult.permissions; clientData.auth_id = auth_id; clientData.updateAuth(server); + if (aresult.username != null) clientData.username = aresult.username; else @@ -104,6 +119,7 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti } result.session = clientData.session; } + UUID uuid; if (authType == ConnectTypes.CLIENT && server.config.protectHandler.allowGetAccessToken(context)) { uuid = pair.handler.auth(aresult); @@ -114,12 +130,14 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti uuid = pair.handler.usernameToUUID(aresult.username); result.accessToken = null; } + result.playerProfile = ProfileByUUIDResponse.getProfile(uuid, aresult.username, client, clientData.auth.textureProvider); clientData.type = authType; sendResult(result); } catch (AuthException | HookException e) { sendError(e.getMessage()); + } } diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/AuthRequestEvent.java b/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/AuthRequestEvent.java index 08f35b7d..29bcaa0d 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/AuthRequestEvent.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/AuthRequestEvent.java @@ -9,6 +9,8 @@ public class AuthRequestEvent extends RequestEvent { public static final String TWO_FACTOR_NEED_ERROR_MESSAGE = "auth.require2fa"; + public static final String TWO_FACTOR_BAD_MESSAGE = "auth.bad2fa"; + public static final String TWO_FACTOR_NULL = "auth.null2fa"; @LauncherNetworkAPI public ClientPermissions permissions; @LauncherNetworkAPI diff --git a/gradle.properties b/gradle.properties index e1833827..52819dbc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,3 +2,4 @@ org.gradle.parallel=true org.gradle.daemon=false org.gradle.configureondemand=true org.gradle.caching=true +org.gradle.java.home=C:/Users/Clercqer/.jdks/liberica-11.0.8 \ No newline at end of file