This is an automated email from the ASF dual-hosted git repository.

jianglongtao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new 3c51575c030 Add caching_sha2_password authentication for mysql (#31280)
3c51575c030 is described below

commit 3c51575c0303243d088c0e0953f67c9533e9f812
Author: yx9o <[email protected]>
AuthorDate: Sat May 18 17:34:30 2024 +0800

    Add caching_sha2_password authentication for mysql (#31280)
    
    * Add caching_sha2_password authentication for mysql
    
    * Compatible with mismatched authentication plugins
    
    * CheckStyle
    
    * CheckStyle
---
 .../mysql/constant/MySQLAuthenticationMethod.java  |  4 +-
 .../packet/handshake/MySQLHandshakePacket.java     |  2 +-
 .../authentication/MySQLAuthenticationEngine.java  | 10 +--
 .../authenticator/MySQLAuthenticatorType.java      |  5 +-
 .../MySQLCachingSha2PasswordAuthenticator.java     | 80 ++++++++++++++++++++++
 5 files changed, 93 insertions(+), 8 deletions(-)

diff --git 
a/db-protocol/mysql/src/main/java/org/apache/shardingsphere/db/protocol/mysql/constant/MySQLAuthenticationMethod.java
 
b/db-protocol/mysql/src/main/java/org/apache/shardingsphere/db/protocol/mysql/constant/MySQLAuthenticationMethod.java
index ec98feacbbe..e8b42c8a9b6 100644
--- 
a/db-protocol/mysql/src/main/java/org/apache/shardingsphere/db/protocol/mysql/constant/MySQLAuthenticationMethod.java
+++ 
b/db-protocol/mysql/src/main/java/org/apache/shardingsphere/db/protocol/mysql/constant/MySQLAuthenticationMethod.java
@@ -38,7 +38,9 @@ public enum MySQLAuthenticationMethod implements 
AuthenticationMethod {
     
     WINDOWS_NATIVE("authentication_windows_client"),
     
-    SHA256("sha256_password");
+    SHA256("sha256_password"),
+    
+    CACHING_SHA2_PASSWORD("caching_sha2_password");
     
     private final String methodName;
 }
diff --git 
a/db-protocol/mysql/src/main/java/org/apache/shardingsphere/db/protocol/mysql/packet/handshake/MySQLHandshakePacket.java
 
b/db-protocol/mysql/src/main/java/org/apache/shardingsphere/db/protocol/mysql/packet/handshake/MySQLHandshakePacket.java
index 0dd4727333b..07499ad0434 100644
--- 
a/db-protocol/mysql/src/main/java/org/apache/shardingsphere/db/protocol/mysql/packet/handshake/MySQLHandshakePacket.java
+++ 
b/db-protocol/mysql/src/main/java/org/apache/shardingsphere/db/protocol/mysql/packet/handshake/MySQLHandshakePacket.java
@@ -63,7 +63,7 @@ public final class MySQLHandshakePacket extends MySQLPacket {
         statusFlag = MySQLStatusFlag.SERVER_STATUS_AUTOCOMMIT;
         capabilityFlagsUpper = 
MySQLCapabilityFlag.calculateHandshakeCapabilityFlagsUpper();
         this.authPluginData = authPluginData;
-        authPluginName = MySQLAuthenticationMethod.NATIVE.getMethodName();
+        authPluginName = 
MySQLAuthenticationMethod.CACHING_SHA2_PASSWORD.getMethodName();
     }
     
     public MySQLHandshakePacket(final MySQLPacketPayload payload) {
diff --git 
a/proxy/frontend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/MySQLAuthenticationEngine.java
 
b/proxy/frontend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/MySQLAuthenticationEngine.java
index 3afb2c1751b..1e418332892 100644
--- 
a/proxy/frontend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/MySQLAuthenticationEngine.java
+++ 
b/proxy/frontend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/MySQLAuthenticationEngine.java
@@ -22,6 +22,10 @@ import io.netty.buffer.ByteBufUtil;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.epoll.EpollDomainSocketChannel;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.shardingsphere.authentication.Authenticator;
+import org.apache.shardingsphere.authentication.AuthenticatorFactory;
+import org.apache.shardingsphere.authentication.result.AuthenticationResult;
+import 
org.apache.shardingsphere.authentication.result.AuthenticationResultBuilder;
 import org.apache.shardingsphere.authority.checker.AuthorityChecker;
 import org.apache.shardingsphere.authority.rule.AuthorityRule;
 import org.apache.shardingsphere.db.protocol.constant.CommonConstants;
@@ -46,10 +50,6 @@ import org.apache.shardingsphere.infra.metadata.user.Grantee;
 import org.apache.shardingsphere.infra.metadata.user.ShardingSphereUser;
 import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
 import 
org.apache.shardingsphere.proxy.frontend.authentication.AuthenticationEngine;
-import org.apache.shardingsphere.authentication.result.AuthenticationResult;
-import 
org.apache.shardingsphere.authentication.result.AuthenticationResultBuilder;
-import org.apache.shardingsphere.authentication.Authenticator;
-import org.apache.shardingsphere.authentication.AuthenticatorFactory;
 import 
org.apache.shardingsphere.proxy.frontend.connection.ConnectionIdGenerator;
 import 
org.apache.shardingsphere.proxy.frontend.mysql.authentication.authenticator.MySQLAuthenticatorType;
 import 
org.apache.shardingsphere.proxy.frontend.mysql.command.query.binary.MySQLStatementIdGenerator;
@@ -130,7 +130,7 @@ public final class MySQLAuthenticationEngine implements 
AuthenticationEngine {
         String hostname = getHostAddress(context);
         ShardingSphereUser user = rule.findUser(new Grantee(username, 
hostname)).orElseGet(() -> new ShardingSphereUser(username, "", hostname));
         Authenticator authenticator = new 
AuthenticatorFactory<>(MySQLAuthenticatorType.class, rule).newInstance(user);
-        if (isClientPluginAuthenticate(handshakeResponsePacket) && 
!authenticator.getAuthenticationMethodName().equals(handshakeResponsePacket.getAuthPluginName()))
 {
+        if (0 == authResponse.length || 
isClientPluginAuthenticate(handshakeResponsePacket) && 
!authenticator.getAuthenticationMethodName().equals(handshakeResponsePacket.getAuthPluginName()))
 {
             connectionPhase = 
MySQLConnectionPhase.AUTHENTICATION_METHOD_MISMATCH;
             context.writeAndFlush(new 
MySQLAuthSwitchRequestPacket(authenticator.getAuthenticationMethodName(), 
authPluginData));
             return AuthenticationResultBuilder.continued(username, hostname, 
database);
diff --git 
a/proxy/frontend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/authenticator/MySQLAuthenticatorType.java
 
b/proxy/frontend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/authenticator/MySQLAuthenticatorType.java
index a64a5c59dc4..dce43762e0e 100644
--- 
a/proxy/frontend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/authenticator/MySQLAuthenticatorType.java
+++ 
b/proxy/frontend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/authenticator/MySQLAuthenticatorType.java
@@ -20,6 +20,7 @@ package 
org.apache.shardingsphere.proxy.frontend.mysql.authentication.authentica
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
 import org.apache.shardingsphere.authentication.AuthenticatorType;
+import 
org.apache.shardingsphere.proxy.frontend.mysql.authentication.authenticator.impl.MySQLCachingSha2PasswordAuthenticator;
 import 
org.apache.shardingsphere.proxy.frontend.mysql.authentication.authenticator.impl.MySQLClearPasswordAuthenticator;
 import 
org.apache.shardingsphere.proxy.frontend.mysql.authentication.authenticator.impl.MySQLNativePasswordAuthenticator;
 
@@ -41,7 +42,9 @@ public enum MySQLAuthenticatorType implements 
AuthenticatorType {
     WINDOWS_NATIVE(MySQLNativePasswordAuthenticator.class),
     
     // TODO impl SHA256 Authenticator
-    SHA256(MySQLNativePasswordAuthenticator.class);
+    SHA256(MySQLNativePasswordAuthenticator.class),
+    
+    CACHING_SHA2_PASSWORD(MySQLCachingSha2PasswordAuthenticator.class);
     
     private final Class<? extends MySQLAuthenticator> authenticatorClass;
     
diff --git 
a/proxy/frontend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/authenticator/impl/MySQLCachingSha2PasswordAuthenticator.java
 
b/proxy/frontend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/authenticator/impl/MySQLCachingSha2PasswordAuthenticator.java
new file mode 100644
index 00000000000..1b95906ed4a
--- /dev/null
+++ 
b/proxy/frontend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/authenticator/impl/MySQLCachingSha2PasswordAuthenticator.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package 
org.apache.shardingsphere.proxy.frontend.mysql.authentication.authenticator.impl;
+
+import com.google.common.base.Strings;
+import lombok.SneakyThrows;
+import 
org.apache.shardingsphere.db.protocol.mysql.constant.MySQLAuthenticationMethod;
+import 
org.apache.shardingsphere.db.protocol.mysql.packet.handshake.MySQLAuthenticationPluginData;
+import org.apache.shardingsphere.infra.metadata.user.ShardingSphereUser;
+import 
org.apache.shardingsphere.proxy.frontend.mysql.authentication.authenticator.MySQLAuthenticator;
+
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+/**
+ * Caching sha2 password authenticator for MySQL.
+ *
+ * @see <a 
href="https://dev.mysql.com/doc/dev/mysql-server/latest/page_caching_sha2_authentication_exchanges.html";>Caching_sha2_password
 information</a>
+ */
+public final class MySQLCachingSha2PasswordAuthenticator implements 
MySQLAuthenticator {
+    
+    @Override
+    public boolean authenticate(final ShardingSphereUser user, final Object[] 
authInfo) {
+        byte[] authResponse = (byte[]) authInfo[0];
+        MySQLAuthenticationPluginData authPluginData = 
(MySQLAuthenticationPluginData) authInfo[1];
+        return Strings.isNullOrEmpty(user.getPassword()) || 
Arrays.equals(scramble256(user.getPassword().getBytes(), 
authPluginData.getAuthenticationPluginData()), authResponse);
+    }
+    
+    @SneakyThrows({NoSuchAlgorithmException.class, DigestException.class})
+    private byte[] scramble256(final byte[] pass, final byte[] 
authenticationPluginData) {
+        int cachingSha2DigestLength = 32;
+        MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
+        byte[] dig1 = new byte[cachingSha2DigestLength];
+        byte[] dig2 = new byte[cachingSha2DigestLength];
+        messageDigest.update(pass, 0, pass.length);
+        messageDigest.digest(dig1, 0, cachingSha2DigestLength);
+        messageDigest.reset();
+        messageDigest.update(dig1, 0, dig1.length);
+        messageDigest.digest(dig2, 0, cachingSha2DigestLength);
+        messageDigest.reset();
+        messageDigest.update(dig2, 0, dig1.length);
+        messageDigest.update(authenticationPluginData, 0, 
authenticationPluginData.length);
+        byte[] scramble1 = new byte[cachingSha2DigestLength];
+        messageDigest.digest(scramble1, 0, cachingSha2DigestLength);
+        byte[] result = new byte[cachingSha2DigestLength];
+        xorString(dig1, result, scramble1, cachingSha2DigestLength);
+        return result;
+    }
+    
+    private void xorString(final byte[] from, final byte[] to, final byte[] 
scramble, final int length) {
+        int pos = 0;
+        int scrambleLength = scramble.length;
+        while (pos < length) {
+            to[pos] = (byte) (from[pos] ^ scramble[pos % scrambleLength]);
+            pos++;
+        }
+    }
+    
+    @Override
+    public String getAuthenticationMethodName() {
+        return MySQLAuthenticationMethod.CACHING_SHA2_PASSWORD.getMethodName();
+    }
+}

Reply via email to