com.mysql.cj.protocol.a.NativeAuthenticationProvider Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mysql-connector-java Show documentation
Show all versions of mysql-connector-java Show documentation
JDBC Type 4 driver for MySQL
The newest version!
/*
* Copyright (c) 2015, 2022, Oracle and/or its affiliates.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 2.0, as published by the
* Free Software Foundation.
*
* This program is also distributed with certain software (including but not
* limited to OpenSSL) that is licensed under separate terms, as designated in a
* particular file or component or in included license documentation. The
* authors of MySQL hereby grant you an additional permission to link the
* program and your derivative works with the separately licensed software that
* they have included with MySQL.
*
* Without limiting anything contained in the foregoing, this file, which is
* part of MySQL Connector/J, is also subject to the Universal FOSS Exception,
* version 1.0, a copy of which can be found at
* http://oss.oracle.com/licenses/universal-foss-exception.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
* for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.mysql.cj.protocol.a;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.mysql.cj.Constants;
import com.mysql.cj.Messages;
import com.mysql.cj.callback.MysqlCallbackHandler;
import com.mysql.cj.callback.UsernameCallback;
import com.mysql.cj.conf.PropertyDefinitions.SslMode;
import com.mysql.cj.conf.PropertyKey;
import com.mysql.cj.conf.PropertySet;
import com.mysql.cj.conf.RuntimeProperty;
import com.mysql.cj.exceptions.ExceptionFactory;
import com.mysql.cj.exceptions.ExceptionInterceptor;
import com.mysql.cj.exceptions.UnableToConnectException;
import com.mysql.cj.exceptions.WrongArgumentException;
import com.mysql.cj.protocol.AuthenticationPlugin;
import com.mysql.cj.protocol.AuthenticationProvider;
import com.mysql.cj.protocol.Protocol;
import com.mysql.cj.protocol.ServerSession;
import com.mysql.cj.protocol.a.NativeConstants.IntegerDataType;
import com.mysql.cj.protocol.a.NativeConstants.StringLengthDataType;
import com.mysql.cj.protocol.a.NativeConstants.StringSelfDataType;
import com.mysql.cj.protocol.a.authentication.*;
import com.mysql.cj.protocol.a.result.OkPacket;
import com.mysql.cj.util.StringUtils;
public class NativeAuthenticationProvider implements AuthenticationProvider {
private static final int AUTH_411_OVERHEAD = 33;
private static final String NONE = "none";
private String seed;
private String username;
private String password;
private String database;
private boolean useConnectWithDb;
private ExceptionInterceptor exceptionInterceptor;
private PropertySet propertySet;
private Protocol protocol;
/**
* Contains instances of authentication plugins that implements {@link AuthenticationPlugin} interface. Key values are MySQL protocol plugin names, for
* example "mysql_native_password" and "mysql_old_password" for built-in plugins.
*/
private Map> authenticationPlugins = null;
/**
* Protocol name of default authentication plugin in client
*/
private String clientDefaultAuthenticationPluginName = null;
/**
* Was the client default authentication plugin explicitly set?
*/
private boolean clientDefaultAuthenticationPluginExplicitelySet = false;
/**
* Protocol name of default authentication plugin in server
*/
private String serverDefaultAuthenticationPluginName = null;
/**
* A callback for updating the username from the authentication plugin.
*/
private MysqlCallbackHandler callbackHandler = (cb) -> {
if (cb instanceof UsernameCallback) {
this.username = ((UsernameCallback) cb).getUsername();
}
};
public NativeAuthenticationProvider() {
}
@Override
public void init(Protocol prot, PropertySet propSet, ExceptionInterceptor excInterceptor) {
this.protocol = prot;
this.propertySet = propSet;
this.exceptionInterceptor = excInterceptor;
}
/**
* Initialize communications with the MySQL server. Handles logging on, and
* handling initial connection errors.
*
* @param user
* user name
* @param pass
* password
* @param db
* database name
*/
@Override
public void connect(String user, String pass, String db) {
ServerSession sessState = this.protocol.getServerSession();
this.username = user;
this.password = pass;
this.database = db;
NativeCapabilities capabilities = (NativeCapabilities) sessState.getCapabilities();
NativePacketPayload buf = capabilities.getInitialHandshakePacket();
SslMode sslMode = this.propertySet.getEnumProperty(PropertyKey.sslMode).getValue();
int capabilityFlags = capabilities.getCapabilityFlags();
if (((capabilityFlags & NativeServerSession.CLIENT_SSL) == 0) && sslMode != SslMode.DISABLED && sslMode != SslMode.PREFERRED) {
// check SSL availability
throw ExceptionFactory.createException(UnableToConnectException.class, Messages.getString("MysqlIO.15"), getExceptionInterceptor());
} else if ((capabilityFlags & NativeServerSession.CLIENT_SECURE_CONNECTION) == 0) {
// TODO: better messaging
throw ExceptionFactory.createException(UnableToConnectException.class, "CLIENT_SECURE_CONNECTION is required", getExceptionInterceptor());
} else if ((capabilityFlags & NativeServerSession.CLIENT_PLUGIN_AUTH) == 0) {
// TODO: better messaging
throw ExceptionFactory.createException(UnableToConnectException.class, "CLIENT_PLUGIN_AUTH is required", getExceptionInterceptor());
}
sessState.setStatusFlags(capabilities.getStatusFlags()); // read status flags (2 bytes)
int authPluginDataLength = capabilities.getAuthPluginDataLength();
StringBuilder fullSeed = new StringBuilder(authPluginDataLength > 0 ? authPluginDataLength : NativeConstants.SEED_LENGTH);
fullSeed.append(capabilities.getSeed()); // read auth-plugin-data-part-1 (string[8])
fullSeed.append(authPluginDataLength > 0 ? // read string[$len] auth-plugin-data-part-2 ($len=MAX(13, length of auth-plugin-data - 8))
buf.readString(StringLengthDataType.STRING_FIXED, "ASCII", authPluginDataLength - 8) : buf.readString(StringSelfDataType.STRING_TERM, "ASCII"));
this.seed = fullSeed.toString();
this.useConnectWithDb = (this.database != null) && (this.database.length() > 0)
&& !this.propertySet.getBooleanProperty(PropertyKey.createDatabaseIfNotExist).getValue();
long clientParam = capabilityFlags & NativeServerSession.CLIENT_LONG_PASSWORD //
| (this.propertySet.getBooleanProperty(PropertyKey.useAffectedRows).getValue() ? //
0 : capabilityFlags & NativeServerSession.CLIENT_FOUND_ROWS) //
| capabilityFlags & NativeServerSession.CLIENT_LONG_FLAG //
| (this.useConnectWithDb ? capabilityFlags & NativeServerSession.CLIENT_CONNECT_WITH_DB : 0) //
| (this.propertySet.getBooleanProperty(PropertyKey.useCompression).getValue() ? //
capabilityFlags & NativeServerSession.CLIENT_COMPRESS : 0) //
| (this.propertySet.getBooleanProperty(PropertyKey.allowLoadLocalInfile).getValue()
|| this.propertySet.getStringProperty(PropertyKey.allowLoadLocalInfileInPath).isExplicitlySet() ? //
capabilityFlags & NativeServerSession.CLIENT_LOCAL_FILES : 0) //
| capabilityFlags & NativeServerSession.CLIENT_PROTOCOL_41 //
| (this.propertySet.getBooleanProperty(PropertyKey.interactiveClient).getValue() ? //
capabilityFlags & NativeServerSession.CLIENT_INTERACTIVE : 0) //
| (this.propertySet.getEnumProperty(PropertyKey.sslMode).getValue() != SslMode.DISABLED ? //
capabilityFlags & NativeServerSession.CLIENT_SSL : 0) //
| capabilityFlags & NativeServerSession.CLIENT_TRANSACTIONS // Required to get server status values.
| NativeServerSession.CLIENT_SECURE_CONNECTION //
| (this.propertySet.getBooleanProperty(PropertyKey.allowMultiQueries).getValue() ? //
capabilityFlags & NativeServerSession.CLIENT_MULTI_STATEMENTS : 0) //
| capabilityFlags & NativeServerSession.CLIENT_MULTI_RESULTS // Always allow multiple result sets.
| capabilityFlags & NativeServerSession.CLIENT_PS_MULTI_RESULTS // Always allow multiple result sets for SSPS.
| NativeServerSession.CLIENT_PLUGIN_AUTH //
| (NONE.equals(this.propertySet.getStringProperty(PropertyKey.connectionAttributes).getValue()) ? //
0 : capabilityFlags & NativeServerSession.CLIENT_CONNECT_ATTRS) //
| capabilityFlags & NativeServerSession.CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA //
| (this.propertySet.getBooleanProperty(PropertyKey.disconnectOnExpiredPasswords).getValue() ? //
0 : capabilityFlags & NativeServerSession.CLIENT_CAN_HANDLE_EXPIRED_PASSWORD) //
| (this.propertySet.getBooleanProperty(PropertyKey.trackSessionState).getValue() ? //
capabilityFlags & NativeServerSession.CLIENT_SESSION_TRACK : 0) //
| capabilityFlags & NativeServerSession.CLIENT_DEPRECATE_EOF //
| capabilityFlags & NativeServerSession.CLIENT_QUERY_ATTRIBUTES //
| capabilityFlags & NativeServerSession.CLIENT_MULTI_FACTOR_AUTHENTICATION;
sessState.setClientParam(clientParam);
/* First, negotiate SSL connection */
if ((clientParam & NativeServerSession.CLIENT_SSL) != 0) {
this.protocol.negotiateSSLConnection();
}
if (buf.isOKPacket()) {
throw ExceptionFactory.createException(Messages.getString("AuthenticationProvider.UnexpectedAuthenticationApproval"), getExceptionInterceptor());
}
proceedHandshakeWithPluggableAuthentication(buf);
this.password = null;
}
/**
* Fill the authentication plugins map.
*
* Starts by filling the map with instances of the built-in authentication plugins. Then creates instances of plugins listed in the "authenticationPlugins"
* connection property and adds them to the map too.
*
* The key for the map entry is got by {@link AuthenticationPlugin#getProtocolPluginName()} thus it is possible to replace built-in plugins with custom
* implementations. To do it, the custom plugin should return one of the values "mysql_native_password", "mysql_clear_password", "sha256_password",
* "caching_sha2_password", "mysql_old_password", "authentication_ldap_sasl_client" or "authentication_kerberos_client" from its own getProtocolPluginName()
* method.
*/
@SuppressWarnings("unchecked")
private void loadAuthenticationPlugins() {
// default plugin
RuntimeProperty defaultAuthenticationPluginProp = this.propertySet.getStringProperty(PropertyKey.defaultAuthenticationPlugin);
String defaultAuthenticationPluginValue = defaultAuthenticationPluginProp.getValue();
if (defaultAuthenticationPluginValue == null || "".equals(defaultAuthenticationPluginValue.trim())) {
throw ExceptionFactory.createException(WrongArgumentException.class,
Messages.getString("AuthenticationProvider.BadDefaultAuthenticationPlugin", new Object[] { defaultAuthenticationPluginValue }),
getExceptionInterceptor());
}
// disabled plugins
String disabledPlugins = this.propertySet.getStringProperty(PropertyKey.disabledAuthenticationPlugins).getValue();
List disabledAuthenticationPlugins;
if (disabledPlugins != null && !"".equals(disabledPlugins)) {
disabledAuthenticationPlugins = StringUtils.split(disabledPlugins, ",", true);
} else {
disabledAuthenticationPlugins = Collections.EMPTY_LIST;
}
this.authenticationPlugins = new HashMap<>();
List> pluginsToInit = new LinkedList<>();
// built-in plugins
pluginsToInit.add(new MysqlNativePasswordPlugin());
pluginsToInit.add(new MysqlClearPasswordPlugin());
pluginsToInit.add(new Sha256PasswordPlugin());
pluginsToInit.add(new CachingSha2PasswordPlugin());
pluginsToInit.add(new MysqlOldPasswordPlugin());
pluginsToInit.add(new AuthenticationLdapSaslClientPlugin());
pluginsToInit.add(new AuthenticationKerberosClient());
pluginsToInit.add(new AuthenticationOciClient());
pluginsToInit.add(new AuthenticationFidoClient());
pluginsToInit.add(new TiDBSM3PasswordPlugin());
// plugins from authenticationPluginClasses connection parameter
String authenticationPluginClasses = this.propertySet.getStringProperty(PropertyKey.authenticationPlugins).getValue();
if (authenticationPluginClasses != null && !"".equals(authenticationPluginClasses.trim())) {
List pluginsToCreate = StringUtils.split(authenticationPluginClasses, ",", true);
for (String className : pluginsToCreate) {
try {
pluginsToInit.add((AuthenticationPlugin) Class.forName(className).newInstance());
} catch (Throwable t) {
throw ExceptionFactory.createException(WrongArgumentException.class,
Messages.getString("AuthenticationProvider.BadAuthenticationPlugin", new Object[] { className }), t, this.exceptionInterceptor);
}
}
}
// add plugin instances
boolean defaultFound = false;
for (AuthenticationPlugin plugin : pluginsToInit) {
String pluginProtocolName = plugin.getProtocolPluginName();
String pluginClassName = plugin.getClass().getName();
boolean disabledByProtocolName = disabledAuthenticationPlugins.contains(pluginProtocolName);
boolean disabledByClassName = disabledAuthenticationPlugins.contains(pluginClassName);
if (disabledByProtocolName || disabledByClassName) {
// check if the default plugin is disabled
if (!defaultFound
&& (defaultAuthenticationPluginValue.equals(pluginProtocolName) || defaultAuthenticationPluginValue.equals(pluginClassName))) {
throw ExceptionFactory.createException(WrongArgumentException.class,
Messages.getString("AuthenticationProvider.BadDisabledAuthenticationPlugin",
new Object[] { disabledByClassName ? pluginClassName : pluginProtocolName }),
getExceptionInterceptor());
}
} else {
this.authenticationPlugins.put(pluginProtocolName, plugin);
if (!defaultFound
&& (defaultAuthenticationPluginValue.equals(pluginProtocolName) || defaultAuthenticationPluginValue.equals(pluginClassName))) {
this.clientDefaultAuthenticationPluginName = pluginProtocolName;
this.clientDefaultAuthenticationPluginExplicitelySet = defaultAuthenticationPluginProp.isExplicitlySet();
defaultFound = true;
}
}
}
// check if the default plugin is listed
if (!defaultFound) {
throw ExceptionFactory.createException(WrongArgumentException.class,
Messages.getString("AuthenticationProvider.DefaultAuthenticationPluginIsNotListed", new Object[] { defaultAuthenticationPluginValue }),
getExceptionInterceptor());
}
}
/**
* Get an authentication plugin instance from the authentication plugins map by pluginName key. If such plugin is found, its method
* {@link AuthenticationPlugin#isReusable()} is called and if the value returned is false
then a new instance of the plugin is returned
* otherwise the instance that already exists is returned.
*
* If plugin is not found method returns null, in such case the subsequent behavior
* of handshake process depends on type of last packet received from server:
* if it was Auth Challenge Packet then handshake will proceed with default plugin,
* if it was Auth Method Switch Request Packet then handshake will be interrupted with exception.
*
* @param pluginName
* mysql protocol plugin names, for example "mysql_native_password" and "mysql_old_password" for built-in plugins
* @return null if plugin is not found or authentication plugin instance initialized with current connection properties
*/
@SuppressWarnings("unchecked")
private AuthenticationPlugin getAuthenticationPlugin(String pluginName) {
AuthenticationPlugin plugin = this.authenticationPlugins.get(pluginName);
if (plugin == null) {
return null;
}
if (!plugin.isReusable()) {
try {
plugin = plugin.getClass().newInstance();
} catch (Throwable t) {
throw ExceptionFactory.createException(WrongArgumentException.class,
Messages.getString("AuthenticationProvider.BadAuthenticationPlugin", new Object[] { plugin.getClass().getName() }), t,
getExceptionInterceptor());
}
}
plugin.init(this.protocol, this.callbackHandler);
return plugin;
}
/**
* Check if given plugin requires confidentiality, but connection is without SSL
*
* @param plugin
* {@link AuthenticationPlugin}
*/
private void checkConfidentiality(AuthenticationPlugin> plugin) {
if (plugin.requiresConfidentiality() && !this.protocol.getSocketConnection().isSSLEstablished()) {
throw ExceptionFactory.createException(
Messages.getString("AuthenticationProvider.AuthenticationPluginRequiresSSL", new Object[] { plugin.getProtocolPluginName() }),
getExceptionInterceptor());
}
}
/**
* Performs an authentication handshake to authorize connection to a given database as a given MySQL user.
* This can happen upon initial connection to the server, after receiving Auth Challenge Packet, or
* at any moment during the connection life-time via a Change User request.
*
* This method will use registered authentication plugins as requested by the server.
*
* @param challenge
* the Auth Challenge Packet received from server if
* this method is used during the initial connection.
* Otherwise null.
*/
private void proceedHandshakeWithPluggableAuthentication(final NativePacketPayload challenge) {
ServerSession serverSession = this.protocol.getServerSession();
if (this.authenticationPlugins == null) {
loadAuthenticationPlugins();
}
boolean forChangeUser = true;
if (challenge != null) {
this.serverDefaultAuthenticationPluginName = challenge.readString(StringSelfDataType.STRING_TERM, "ASCII");
forChangeUser = false;
}
serverSession.getCharsetSettings().configurePreHandshake(forChangeUser);
/*
* Select the initial plugin:
* Choose the client-side default authentication plugin, if explicitely specified, otherwise choose the server-side default authentication plugin.
*/
String pluginName;
if (this.clientDefaultAuthenticationPluginExplicitelySet) {
pluginName = this.clientDefaultAuthenticationPluginName;
} else {
pluginName = this.serverDefaultAuthenticationPluginName != null ? this.serverDefaultAuthenticationPluginName
: this.clientDefaultAuthenticationPluginName;
}
AuthenticationPlugin plugin = getAuthenticationPlugin(pluginName);
if (plugin == null) {
/* Use default if there is no plugin for pluginName. */
pluginName = this.clientDefaultAuthenticationPluginName;
plugin = getAuthenticationPlugin(pluginName);
}
boolean skipPassword = false;
if (pluginName.equals(Sha256PasswordPlugin.PLUGIN_NAME) && !pluginName.equals(this.clientDefaultAuthenticationPluginName)
&& !this.protocol.getSocketConnection().isSSLEstablished()
&& this.propertySet.getStringProperty(PropertyKey.serverRSAPublicKeyFile).getValue() == null
&& !this.propertySet.getBooleanProperty(PropertyKey.allowPublicKeyRetrieval).getValue()) {
/*
* Fall back to client default if plugin is 'sha256_password' but required conditions for this to work aren't met.
* If client default is other than 'sha256_password', this will result in an immediate authentication switch request, allowing for other plugins to
* authenticate successfully. If client default is 'sha256_password' then the authentication will fail as expected. In both cases user's password
* won't be sent to avoid subjecting it to lesser security levels.
*/
plugin = getAuthenticationPlugin(this.clientDefaultAuthenticationPluginName);
skipPassword = true;
}
checkConfidentiality(plugin);
// Servers not affected by Bug#70865 expect the Change User Request containing a correct answer to seed sent by the server during the initial handshake,
// thus we reuse it here. Servers affected by Bug#70865 will just ignore it and send the Auth Switch.
NativePacketPayload fromServer = new NativePacketPayload(StringUtils.getBytes(this.seed));
String sourceOfAuthData = this.serverDefaultAuthenticationPluginName;
NativePacketPayload lastSent = null;
NativePacketPayload lastReceived = challenge;
ArrayList toServer = new ArrayList<>();
boolean firstPacket = true;
// MFA authentication factor
int mfaNthFactor = 1;
/* Max iterations number */
int counter = 100;
while (0 < counter--) {
/*
* call plugin
*/
plugin.setAuthenticationParameters(this.username, skipPassword ? null : getNthFactorPassword(mfaNthFactor));
plugin.setSourceOfAuthData(sourceOfAuthData);
plugin.nextAuthenticationStep(fromServer, toServer);
/*
* send response to server
*/
if (firstPacket) {
NativePacketPayload authData = toServer.isEmpty() ? new NativePacketPayload(0) : toServer.get(0);
if (forChangeUser) {
// write COM_CHANGE_USER Packet
lastSent = createChangeUserPacket(serverSession, plugin.getProtocolPluginName(), authData);
this.protocol.send(lastSent, lastSent.getPosition());
} else {
// write HandshakeResponse packet
lastSent = createHandshakeResponsePacket(serverSession, plugin.getProtocolPluginName(), authData);
this.protocol.send(lastSent, lastSent.getPosition());
}
firstPacket = false;
} else if (!toServer.isEmpty()) {
// write AuthSwitchResponse packet or raw packet(s)
toServer.forEach(b -> this.protocol.send(b, b.getPayloadLength()));
}
/*
* read packet from server
*/
lastReceived = this.protocol.checkErrorMessage();
if (lastReceived.isOKPacket()) {
// read OK packet
OkPacket ok = OkPacket.parse(lastReceived, null);
serverSession.setStatusFlags(ok.getStatusFlags(), true);
serverSession.getServerSessionStateController().setSessionStateChanges(ok.getSessionStateChanges());
// authentication complete
plugin.destroy();
break;
} else if (lastReceived.isAuthMethodSwitchRequestPacket()) {
// read AuthSwitchRequest Packet
skipPassword = false;
pluginName = lastReceived.readString(StringSelfDataType.STRING_TERM, "ASCII");
if (plugin.getProtocolPluginName().equals(pluginName)) {
plugin.reset(); // just reset the current one
} else {
// get new plugin
plugin.destroy();
plugin = getAuthenticationPlugin(pluginName);
if (plugin == null) {
throw ExceptionFactory.createException(WrongArgumentException.class,
Messages.getString("AuthenticationProvider.BadAuthenticationPlugin", new Object[] { pluginName }), getExceptionInterceptor());
}
}
checkConfidentiality(plugin);
fromServer = new NativePacketPayload(lastReceived.readBytes(StringSelfDataType.STRING_EOF));
} else if (lastReceived.isAuthNextFactorPacket()) {
// authentication not done yet, there's another MFA iteration
mfaNthFactor++;
skipPassword = false;
pluginName = lastReceived.readString(StringSelfDataType.STRING_TERM, "ASCII");
if (plugin.getProtocolPluginName().equals(pluginName)) {
plugin.reset(); // just reset the current one
} else {
// get new plugin
plugin.destroy();
plugin = getAuthenticationPlugin(pluginName);
if (plugin == null) {
throw ExceptionFactory.createException(WrongArgumentException.class,
Messages.getString("AuthenticationProvider.BadAuthenticationPlugin", new Object[] { pluginName }), getExceptionInterceptor());
}
}
checkConfidentiality(plugin);
fromServer = new NativePacketPayload(lastReceived.readBytes(StringSelfDataType.STRING_EOF));
} else {
// read raw (from AuthMoreData) packet
if (!this.protocol.versionMeetsMinimum(5, 5, 16)) {
lastReceived.setPosition(lastReceived.getPosition() - 1);
}
fromServer = new NativePacketPayload(lastReceived.readBytes(StringSelfDataType.STRING_EOF));
}
sourceOfAuthData = pluginName;
}
if (counter == 0) {
throw ExceptionFactory.createException(WrongArgumentException.class,
Messages.getString("CommunicationsException.TooManyAuthenticationPluginNegotiations"), getExceptionInterceptor());
}
this.protocol.afterHandshake();
if (!this.useConnectWithDb) {
this.protocol.changeDatabase(this.database);
}
}
private String getNthFactorPassword(int nthFactor) {
switch (nthFactor) {
case 1:
return this.password == null ? this.protocol.getPropertySet().getStringProperty(PropertyKey.password1).getValue() : this.password;
case 2:
return this.protocol.getPropertySet().getStringProperty(PropertyKey.password2).getValue();
case 3:
return this.protocol.getPropertySet().getStringProperty(PropertyKey.password3).getValue();
default:
return null;
}
}
private Map getConnectionAttributesMap(String attStr) {
Map attMap = new HashMap<>();
if (attStr != null) {
String[] pairs = attStr.split(",");
for (String pair : pairs) {
int keyEnd = pair.indexOf(":");
if (keyEnd > 0 && (keyEnd + 1) < pair.length()) {
attMap.put(pair.substring(0, keyEnd), pair.substring(keyEnd + 1));
}
}
}
// Leaving disabled until standard values are defined
// props.setProperty("_os", NonRegisteringDriver.OS);
// props.setProperty("_platform", NonRegisteringDriver.PLATFORM);
attMap.put("_client_name", Constants.CJ_NAME);
attMap.put("_client_version", Constants.CJ_VERSION);
attMap.put("_runtime_vendor", Constants.JVM_VENDOR);
attMap.put("_runtime_version", Constants.JVM_VERSION);
attMap.put("_client_license", Constants.CJ_LICENSE);
return attMap;
}
private void appendConnectionAttributes(NativePacketPayload buf, String attributes, String enc) {
NativePacketPayload lb = new NativePacketPayload(100);
Map attMap = getConnectionAttributesMap(attributes);
for (String key : attMap.keySet()) {
lb.writeBytes(StringSelfDataType.STRING_LENENC, StringUtils.getBytes(key, enc));
lb.writeBytes(StringSelfDataType.STRING_LENENC, StringUtils.getBytes(attMap.get(key), enc));
}
buf.writeInteger(IntegerDataType.INT_LENENC, lb.getPosition());
buf.writeBytes(StringLengthDataType.STRING_FIXED, lb.getByteBuffer(), 0, lb.getPosition());
}
public ExceptionInterceptor getExceptionInterceptor() {
return this.exceptionInterceptor;
}
/**
* Re-authenticates as the given user and password
*
* @param user
* user name
* @param pass
* password
* @param db
* database name
*/
@Override
public void changeUser(String user, String pass, String db) {
this.username = user;
this.password = pass;
this.database = db;
proceedHandshakeWithPluggableAuthentication(null);
this.password = null;
}
private NativePacketPayload createHandshakeResponsePacket(ServerSession serverSession, String pluginName, NativePacketPayload authData) {
long clientParam = serverSession.getClientParam();
int collationIndex = serverSession.getCharsetSettings().configurePreHandshake(false);
String enc = serverSession.getCharsetSettings().getPasswordCharacterEncoding();
int userLength = this.username == null ? 0 : this.username.length();
NativePacketPayload last_sent = new NativePacketPayload(AUTH_411_OVERHEAD + 7 //
+ 48 // passwordLength
+ (3 * userLength) // userLength
+ (this.useConnectWithDb ? 3 * this.database.length() : 0) // databaseLength
);
last_sent.writeInteger(IntegerDataType.INT4, clientParam);
last_sent.writeInteger(IntegerDataType.INT4, NativeConstants.MAX_PACKET_SIZE);
last_sent.writeInteger(IntegerDataType.INT1, collationIndex);
last_sent.writeBytes(StringLengthDataType.STRING_FIXED, new byte[23]); // Set of bytes reserved for future use.
// User/Password data
last_sent.writeBytes(StringSelfDataType.STRING_TERM, StringUtils.getBytes(this.username, enc));
if ((clientParam & NativeServerSession.CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA) != 0) {
// send lenenc-int length of auth-response and string[n] auth-response
last_sent.writeBytes(StringSelfDataType.STRING_LENENC, authData.readBytes(StringSelfDataType.STRING_EOF));
} else {
// send 1 byte length of auth-response and string[n] auth-response
last_sent.writeInteger(IntegerDataType.INT1, authData.getPayloadLength());
last_sent.writeBytes(StringSelfDataType.STRING_EOF, authData.getByteBuffer());
}
if (this.useConnectWithDb) {
last_sent.writeBytes(StringSelfDataType.STRING_TERM, StringUtils.getBytes(this.database, enc));
}
last_sent.writeBytes(StringSelfDataType.STRING_TERM, StringUtils.getBytes(pluginName, enc));
// connection attributes
if (((clientParam & NativeServerSession.CLIENT_CONNECT_ATTRS) != 0)) {
appendConnectionAttributes(last_sent, this.propertySet.getStringProperty(PropertyKey.connectionAttributes).getValue(), enc);
}
return last_sent;
}
private NativePacketPayload createChangeUserPacket(ServerSession serverSession, String pluginName, NativePacketPayload authData) {
// write Auth Response Packet
long clientParam = serverSession.getClientParam();
int collationIndex = serverSession.getCharsetSettings().configurePreHandshake(false);
String enc = serverSession.getCharsetSettings().getPasswordCharacterEncoding();
NativePacketPayload last_sent = new NativePacketPayload(AUTH_411_OVERHEAD + 7 //
+ 48 // passwordLength
+ 3 * this.username.length() // userLength
+ (this.useConnectWithDb ? 3 * this.database.length() : 1) // databaseLength
+ 1);
last_sent.writeInteger(IntegerDataType.INT1, NativeConstants.COM_CHANGE_USER);
// User/Password data
last_sent.writeBytes(StringSelfDataType.STRING_TERM, StringUtils.getBytes(this.username, enc));
// 'auth-response-len' is limited to one Byte but, in case of success, COM_CHANGE_USER will be followed by an AuthSwitchRequest anyway
if (authData.getPayloadLength() < 256) {
// non-mysql servers may use this information to authenticate without requiring another round-trip
last_sent.writeInteger(IntegerDataType.INT1, authData.getPayloadLength());
last_sent.writeBytes(StringSelfDataType.STRING_EOF, authData.getByteBuffer(), 0, authData.getPayloadLength());
} else {
last_sent.writeInteger(IntegerDataType.INT1, 0);
}
if (this.useConnectWithDb) {
last_sent.writeBytes(StringSelfDataType.STRING_TERM, StringUtils.getBytes(this.database, enc));
} else {
/* For empty database */
last_sent.writeInteger(IntegerDataType.INT1, 0);
}
last_sent.writeInteger(IntegerDataType.INT1, collationIndex);
last_sent.writeInteger(IntegerDataType.INT1, 0); // two (little-endian) bytes for charset in this packet
// plugin name
last_sent.writeBytes(StringSelfDataType.STRING_TERM, StringUtils.getBytes(pluginName, enc));
// connection attributes
if ((clientParam & NativeServerSession.CLIENT_CONNECT_ATTRS) != 0) {
appendConnectionAttributes(last_sent, this.propertySet.getStringProperty(PropertyKey.connectionAttributes).getValue(), enc);
}
return last_sent;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy