1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.shardingsphere.proxy.frontend.opengauss.authentication;
19
20 import com.google.common.base.Strings;
21 import io.netty.channel.ChannelHandlerContext;
22 import io.netty.handler.ssl.SslHandler;
23 import org.apache.shardingsphere.authentication.Authenticator;
24 import org.apache.shardingsphere.authentication.AuthenticatorFactory;
25 import org.apache.shardingsphere.authentication.result.AuthenticationResult;
26 import org.apache.shardingsphere.authentication.result.AuthenticationResultBuilder;
27 import org.apache.shardingsphere.authority.checker.AuthorityChecker;
28 import org.apache.shardingsphere.authority.rule.AuthorityRule;
29 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
30 import org.apache.shardingsphere.database.exception.core.exception.syntax.database.UnknownDatabaseException;
31 import org.apache.shardingsphere.database.exception.postgresql.exception.authority.EmptyUsernameException;
32 import org.apache.shardingsphere.database.exception.postgresql.exception.authority.InvalidPasswordException;
33 import org.apache.shardingsphere.database.exception.postgresql.exception.authority.PrivilegeNotGrantedException;
34 import org.apache.shardingsphere.database.exception.postgresql.exception.authority.UnknownUsernameException;
35 import org.apache.shardingsphere.database.exception.postgresql.exception.protocol.ProtocolViolationException;
36 import org.apache.shardingsphere.database.protocol.constant.CommonConstants;
37 import org.apache.shardingsphere.database.protocol.constant.DatabaseProtocolServerInfo;
38 import org.apache.shardingsphere.database.protocol.opengauss.constant.OpenGaussProtocolVersion;
39 import org.apache.shardingsphere.database.protocol.opengauss.packet.authentication.OpenGaussAuthenticationHexData;
40 import org.apache.shardingsphere.database.protocol.opengauss.packet.authentication.OpenGaussAuthenticationSCRAMSha256Packet;
41 import org.apache.shardingsphere.database.protocol.payload.PacketPayload;
42 import org.apache.shardingsphere.database.protocol.postgresql.constant.PostgreSQLAuthenticationMethod;
43 import org.apache.shardingsphere.database.protocol.postgresql.packet.generic.PostgreSQLReadyForQueryPacket;
44 import org.apache.shardingsphere.database.protocol.postgresql.packet.handshake.PostgreSQLAuthenticationOKPacket;
45 import org.apache.shardingsphere.database.protocol.postgresql.packet.handshake.PostgreSQLComStartupPacket;
46 import org.apache.shardingsphere.database.protocol.postgresql.packet.handshake.PostgreSQLParameterStatusPacket;
47 import org.apache.shardingsphere.database.protocol.postgresql.packet.handshake.PostgreSQLPasswordMessagePacket;
48 import org.apache.shardingsphere.database.protocol.postgresql.packet.handshake.PostgreSQLRandomGenerator;
49 import org.apache.shardingsphere.database.protocol.postgresql.packet.handshake.PostgreSQLSSLUnwillingPacket;
50 import org.apache.shardingsphere.database.protocol.postgresql.packet.handshake.PostgreSQLSSLWillingPacket;
51 import org.apache.shardingsphere.database.protocol.postgresql.packet.handshake.authentication.PostgreSQLMD5PasswordAuthenticationPacket;
52 import org.apache.shardingsphere.database.protocol.postgresql.packet.identifier.PostgreSQLIdentifierPacket;
53 import org.apache.shardingsphere.database.protocol.postgresql.packet.identifier.PostgreSQLMessagePacketType;
54 import org.apache.shardingsphere.database.protocol.postgresql.payload.PostgreSQLPacketPayload;
55 import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
56 import org.apache.shardingsphere.infra.metadata.user.Grantee;
57 import org.apache.shardingsphere.infra.metadata.user.ShardingSphereUser;
58 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
59 import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
60 import org.apache.shardingsphere.proxy.backend.postgresql.handler.admin.executor.variable.charset.PostgreSQLCharacterSets;
61 import org.apache.shardingsphere.proxy.frontend.authentication.AuthenticationEngine;
62 import org.apache.shardingsphere.proxy.frontend.connection.ConnectionIdGenerator;
63 import org.apache.shardingsphere.proxy.frontend.opengauss.authentication.authenticator.OpenGaussAuthenticatorType;
64 import org.apache.shardingsphere.proxy.frontend.ssl.ProxySSLContext;
65
66 import java.util.Optional;
67
68
69
70
71
72
73 public final class OpenGaussAuthenticationEngine implements AuthenticationEngine {
74
75 private static final int SSL_REQUEST_PAYLOAD_LENGTH = 8;
76
77 private static final int SSL_REQUEST_CODE = 80877103;
78
79 private static final int PROTOCOL_350_SERVER_ITERATOR = 2048;
80
81 private static final int PROTOCOL_351_SERVER_ITERATOR = 10000;
82
83 private final OpenGaussAuthenticationHexData authHexData = new OpenGaussAuthenticationHexData();
84
85 private boolean startupMessageReceived;
86
87 private String clientEncoding;
88
89 private int serverIteration;
90
91 private byte[] md5Salt;
92
93 private AuthenticationResult currentAuthResult;
94
95 @Override
96 public int handshake(final ChannelHandlerContext context) {
97 return ConnectionIdGenerator.getInstance().nextId();
98 }
99
100 @Override
101 public AuthenticationResult authenticate(final ChannelHandlerContext context, final PacketPayload payload) {
102 if (SSL_REQUEST_PAYLOAD_LENGTH == payload.getByteBuf().markReaderIndex().readInt() && SSL_REQUEST_CODE == payload.getByteBuf().readInt()) {
103 if (ProxySSLContext.getInstance().isSSLEnabled()) {
104 SslHandler sslHandler = new SslHandler(ProxySSLContext.getInstance().newSSLEngine(context.alloc()), true);
105 context.pipeline().addFirst(SslHandler.class.getSimpleName(), sslHandler);
106 context.writeAndFlush(new PostgreSQLSSLWillingPacket());
107 } else {
108 context.writeAndFlush(new PostgreSQLSSLUnwillingPacket());
109 }
110 return AuthenticationResultBuilder.continued();
111 }
112 payload.getByteBuf().resetReaderIndex();
113 AuthorityRule rule = ProxyContext.getInstance().getContextManager().getMetaDataContexts().getMetaData().getGlobalRuleMetaData().getSingleRule(AuthorityRule.class);
114 return startupMessageReceived ? processPasswordMessage(context, (PostgreSQLPacketPayload) payload, rule) : processStartupMessage(context, (PostgreSQLPacketPayload) payload, rule);
115 }
116
117 private AuthenticationResult processPasswordMessage(final ChannelHandlerContext context, final PostgreSQLPacketPayload payload, final AuthorityRule rule) {
118 char messageType = (char) payload.readInt1();
119 ShardingSpherePreconditions.checkState(PostgreSQLMessagePacketType.PASSWORD_MESSAGE.getValue() == messageType,
120 () -> new ProtocolViolationException("password", Character.toString(messageType)));
121 PostgreSQLPasswordMessagePacket passwordMessagePacket = new PostgreSQLPasswordMessagePacket(payload);
122 login(rule, passwordMessagePacket.getDigest());
123 context.write(new PostgreSQLAuthenticationOKPacket());
124 context.write(new PostgreSQLParameterStatusPacket("server_version",
125 DatabaseProtocolServerInfo.getProtocolVersion(currentAuthResult.getDatabase(), TypedSPILoader.getService(DatabaseType.class, "openGauss"))));
126 context.write(new PostgreSQLParameterStatusPacket("client_encoding", clientEncoding));
127 context.write(new PostgreSQLParameterStatusPacket("server_encoding", "UTF8"));
128 context.write(new PostgreSQLParameterStatusPacket("integer_datetimes", "on"));
129 context.writeAndFlush(PostgreSQLReadyForQueryPacket.NOT_IN_TRANSACTION);
130 return AuthenticationResultBuilder.finished(currentAuthResult.getUsername(), "", currentAuthResult.getDatabase());
131 }
132
133 private void login(final AuthorityRule rule, final String digest) {
134 String username = currentAuthResult.getUsername();
135 String databaseName = currentAuthResult.getDatabase();
136 ShardingSpherePreconditions.checkState(
137 Strings.isNullOrEmpty(databaseName) || ProxyContext.getInstance().getContextManager().getMetaDataContexts().getMetaData().containsDatabase(databaseName),
138 () -> new UnknownDatabaseException(databaseName));
139 Grantee grantee = new Grantee(username);
140 ShardingSphereUser user = rule.findUser(grantee).orElseThrow(() -> new UnknownUsernameException(username));
141 Authenticator authenticator = new AuthenticatorFactory<>(OpenGaussAuthenticatorType.class, rule).newInstance(user);
142 ShardingSpherePreconditions.checkState(login(authenticator, user, digest), () -> new InvalidPasswordException(username));
143 ShardingSpherePreconditions.checkState(null == databaseName || new AuthorityChecker(rule, grantee).isAuthorized(databaseName), () -> new PrivilegeNotGrantedException(username, databaseName));
144 }
145
146 private boolean login(final Authenticator authenticator, final ShardingSphereUser user, final String digest) {
147 if (PostgreSQLAuthenticationMethod.MD5.getMethodName().equals(authenticator.getAuthenticationMethodName())) {
148 return authenticator.authenticate(user, new Object[]{digest, md5Salt});
149 }
150 return authenticator.authenticate(user, new Object[]{digest, authHexData.getSalt(), authHexData.getNonce(), serverIteration});
151 }
152
153 private AuthenticationResult processStartupMessage(final ChannelHandlerContext context, final PostgreSQLPacketPayload payload, final AuthorityRule rule) {
154 startupMessageReceived = true;
155 PostgreSQLComStartupPacket startupPacket = new PostgreSQLComStartupPacket(payload);
156 clientEncoding = startupPacket.getClientEncoding();
157 context.channel().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY).set(PostgreSQLCharacterSets.findCharacterSet(clientEncoding));
158 String username = startupPacket.getUsername();
159 ShardingSpherePreconditions.checkNotEmpty(username, EmptyUsernameException::new);
160 context.writeAndFlush(getIdentifierPacket(username, rule, startupPacket.getVersion()));
161 currentAuthResult = AuthenticationResultBuilder.continued(username, "", startupPacket.getDatabase());
162 return currentAuthResult;
163 }
164
165 private PostgreSQLIdentifierPacket getIdentifierPacket(final String username, final AuthorityRule rule, final int version) {
166 Optional<Authenticator> authenticator = rule.findUser(new Grantee(username)).map(optional -> new AuthenticatorFactory<>(OpenGaussAuthenticatorType.class, rule).newInstance(optional));
167 if (authenticator.isPresent() && PostgreSQLAuthenticationMethod.MD5.getMethodName().equals(authenticator.get().getAuthenticationMethodName())) {
168 md5Salt = PostgreSQLRandomGenerator.getInstance().generateRandomBytes(4);
169 return new PostgreSQLMD5PasswordAuthenticationPacket(md5Salt);
170 }
171 serverIteration = version == OpenGaussProtocolVersion.PROTOCOL_350.getVersion() ? PROTOCOL_350_SERVER_ITERATOR : PROTOCOL_351_SERVER_ITERATOR;
172 String password = rule.findUser(new Grantee(username)).map(ShardingSphereUser::getPassword).orElse("");
173 return new OpenGaussAuthenticationSCRAMSha256Packet(version, serverIteration, authHexData, password);
174 }
175 }