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();
+ }
+}