Skip to content

Commit 2ad747a

Browse files
committed
WL#14707, Support OCI IAM authentication.
1 parent 4993d57 commit 2ad747a

File tree

14 files changed

+285
-36
lines changed

14 files changed

+285
-36
lines changed

CHANGES

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
Version 8.0.27
55

6+
- WL#14707, Support OCI IAM authentication.
7+
68
- WL#14660, Testsuite with support for single MySQL server instance.
79

810
- Fix for Bug#103878 (32954449), CONNECTOR/J 8 : QUERY WITH 'SHOW XXX' WILL GET EXCEPTION WHEN USE CURSOR.

src/build/misc/pom.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8" ?>
22
<!--
3-
Copyright (c) 2006, 2020, Oracle and/or its affiliates.
3+
Copyright (c) 2006, 2021, Oracle and/or its affiliates.
44
55
This program is free software; you can redistribute it and/or modify it under
66
the terms of the GNU General Public License, version 2.0, as published by the
@@ -63,5 +63,12 @@
6363
<artifactId>protobuf-java</artifactId>
6464
<version>3.11.4</version>
6565
</dependency>
66+
67+
<dependency>
68+
<groupId>com.oracle.oci.sdk</groupId>
69+
<artifactId>oci-java-sdk-common</artifactId>
70+
<version>2.3.0</version>
71+
<optional>true</optional>
72+
</dependency>
6673
</dependencies>
6774
</project>

src/main/core-api/java/com/mysql/cj/conf/PropertyDefinitions.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ public enum DatabaseTerm {
199199
new StringPropertyDefinition(PropertyKey.ldapServerHostname, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE,
200200
Messages.getString("ConnectionProperties.ldapServerHostname"), "8.0.23", CATEGORY_AUTH, Integer.MIN_VALUE + 6),
201201

202+
new StringPropertyDefinition(PropertyKey.ociConfigFile, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE,
203+
Messages.getString("ConnectionProperties.ociConfigFile"), "8.0.27", CATEGORY_AUTH, Integer.MIN_VALUE + 7),
204+
202205
//
203206
// CATEGORY_CONNECTION
204207
//

src/main/core-api/java/com/mysql/cj/conf/PropertyKey.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ public enum PropertyKey {
166166
noAccessToProcedureBodies("noAccessToProcedureBodies", true), //
167167
noDatetimeStringSync("noDatetimeStringSync", true), //
168168
nullDatabaseMeansCurrent("nullDatabaseMeansCurrent", "nullCatalogMeansCurrent", true), //
169+
ociConfigFile("ociConfigFile", true), //
169170
overrideSupportsIntegrityEnhancementFacility("overrideSupportsIntegrityEnhancementFacility", true), //
170171
packetDebugBufferSize("packetDebugBufferSize", true), //
171172
padCharsWithSpace("padCharsWithSpace", true), //

src/main/core-impl/java/com/mysql/cj/protocol/ExportControlled.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
import java.security.KeyStore;
4242
import java.security.KeyStoreException;
4343
import java.security.NoSuchAlgorithmException;
44+
import java.security.Signature;
45+
import java.security.SignatureException;
4446
import java.security.UnrecoverableKeyException;
4547
import java.security.cert.CertPath;
4648
import java.security.cert.CertPathValidator;
@@ -53,11 +55,14 @@
5355
import java.security.cert.TrustAnchor;
5456
import java.security.cert.X509CertSelector;
5557
import java.security.cert.X509Certificate;
58+
import java.security.interfaces.RSAPrivateKey;
5659
import java.security.interfaces.RSAPublicKey;
5760
import java.security.spec.InvalidKeySpecException;
61+
import java.security.spec.PKCS8EncodedKeySpec;
5862
import java.security.spec.X509EncodedKeySpec;
5963
import java.util.ArrayList;
6064
import java.util.Arrays;
65+
import java.util.Base64;
6166
import java.util.Collection;
6267
import java.util.List;
6368
import java.util.Properties;
@@ -676,4 +681,31 @@ public static byte[] encryptWithRSAPublicKey(byte[] source, RSAPublicKey key, St
676681
public static byte[] encryptWithRSAPublicKey(byte[] source, RSAPublicKey key) throws RSAException {
677682
return encryptWithRSAPublicKey(source, key, "RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
678683
}
684+
685+
public static RSAPrivateKey decodeRSAPrivateKey(String key) throws RSAException {
686+
if (key == null) {
687+
throw ExceptionFactory.createException(RSAException.class, "Key parameter is null");
688+
}
689+
690+
String keyData = key.replace("-----BEGIN PRIVATE KEY-----", "").replaceAll("\\R", "").replace("-----END PRIVATE KEY-----", "");
691+
byte[] decodedKeyData = Base64.getDecoder().decode(keyData);
692+
693+
try {
694+
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
695+
return (RSAPrivateKey) keyFactory.generatePrivate(new PKCS8EncodedKeySpec(decodedKeyData));
696+
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
697+
throw ExceptionFactory.createException(RSAException.class, "Unable to decode private key", e);
698+
}
699+
}
700+
701+
public static byte[] sign(byte[] source, RSAPrivateKey privateKey) throws RSAException {
702+
try {
703+
Signature signature = Signature.getInstance("SHA256withRSA");
704+
signature.initSign(privateKey);
705+
signature.update(source);
706+
return signature.sign();
707+
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
708+
throw ExceptionFactory.createException(RSAException.class, e.getMessage(), e);
709+
}
710+
}
679711
}

src/main/protocol-impl/java/com/mysql/cj/protocol/a/NativeAuthenticationProvider.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import com.mysql.cj.protocol.a.NativeConstants.IntegerDataType;
5656
import com.mysql.cj.protocol.a.NativeConstants.StringLengthDataType;
5757
import com.mysql.cj.protocol.a.NativeConstants.StringSelfDataType;
58+
import com.mysql.cj.protocol.a.authentication.AuthenticationOciClient;
5859
import com.mysql.cj.protocol.a.authentication.AuthenticationKerberosClient;
5960
import com.mysql.cj.protocol.a.authentication.AuthenticationLdapSaslClientPlugin;
6061
import com.mysql.cj.protocol.a.authentication.CachingSha2PasswordPlugin;
@@ -253,6 +254,7 @@ private void loadAuthenticationPlugins() {
253254
pluginsToInit.add(new MysqlOldPasswordPlugin());
254255
pluginsToInit.add(new AuthenticationLdapSaslClientPlugin());
255256
pluginsToInit.add(new AuthenticationKerberosClient());
257+
pluginsToInit.add(new AuthenticationOciClient());
256258

257259
// plugins from authenticationPluginClasses connection parameter
258260
String authenticationPluginClasses = this.propertySet.getStringProperty(PropertyKey.authenticationPlugins).getValue();
@@ -322,7 +324,11 @@ private void loadAuthenticationPlugins() {
322324
private AuthenticationPlugin<NativePacketPayload> getAuthenticationPlugin(String pluginName) {
323325
AuthenticationPlugin<NativePacketPayload> plugin = this.authenticationPlugins.get(pluginName);
324326

325-
if (plugin != null && !plugin.isReusable()) {
327+
if (plugin == null) {
328+
return null;
329+
}
330+
331+
if (!plugin.isReusable()) {
326332
try {
327333
plugin = plugin.getClass().newInstance();
328334
} catch (Throwable t) {
@@ -490,7 +496,7 @@ private void proceedHandshakeWithPluggableAuthentication(final NativePacketPaylo
490496
}
491497

492498
checkConfidentiality(plugin);
493-
fromServer = new NativePacketPayload(StringUtils.getBytes(last_received.readString(StringSelfDataType.STRING_EOF, "ASCII")));
499+
fromServer = new NativePacketPayload(last_received.readBytes(StringSelfDataType.STRING_EOF));
494500

495501
} else {
496502
// read raw packet

src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/AuthenticationKerberosClient.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ public class AuthenticationKerberosClient implements AuthenticationPlugin<Native
7373

7474
private String sourceOfAuthData = PLUGIN_NAME;
7575

76-
private MysqlCallbackHandler usernameCallbackHandler;
77-
private String user;
78-
private String password;
76+
private MysqlCallbackHandler usernameCallbackHandler = null;
77+
private String user = null;
78+
private String password = null;
7979
private String userPrincipalName = null;
8080

8181
private Subject subject = null;
@@ -93,7 +93,7 @@ public class AuthenticationKerberosClient implements AuthenticationPlugin<Native
9393
}
9494
};
9595

96-
private SaslClient saslClient;
96+
private SaslClient saslClient = null;
9797

9898
@Override
9999
public void init(Protocol<NativePacketPayload> prot, MysqlCallbackHandler cbh) {
@@ -156,7 +156,9 @@ public void setAuthenticationParameters(String user, String password) {
156156
// Fall-back to system login user.
157157
this.user = System.getProperty("user.name");
158158
}
159-
this.usernameCallbackHandler.handle(new UsernameCallback(this.user));
159+
if (this.usernameCallbackHandler != null) {
160+
this.usernameCallbackHandler.handle(new UsernameCallback(this.user));
161+
}
160162
}
161163
}
162164

@@ -170,7 +172,7 @@ public boolean nextAuthenticationStep(NativePacketPayload fromServer, List<Nativ
170172
toServer.clear();
171173

172174
if (!this.sourceOfAuthData.equals(PLUGIN_NAME)) {
173-
// Couldn't do anything with whatever payload comes from the server, so just skip this iteration and wait for a Protocol::AuthSwitchRequest.
175+
// Cannot do anything with whatever payload comes from the server, so just skip this iteration and wait for a Protocol::AuthSwitchRequest.
174176
toServer.add(new NativePacketPayload(new byte[0]));
175177
return true;
176178
}

src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication/AuthenticationLdapSaslClientPlugin.java

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
import javax.security.sasl.SaslException;
5353

5454
import com.mysql.cj.Messages;
55+
import com.mysql.cj.callback.MysqlCallbackHandler;
56+
import com.mysql.cj.callback.UsernameCallback;
5557
import com.mysql.cj.conf.PropertyKey;
5658
import com.mysql.cj.exceptions.CJException;
5759
import com.mysql.cj.exceptions.ExceptionFactory;
@@ -78,8 +80,8 @@ private enum AuthenticationMechanisms {
7880
SCRAM_SHA_256(ScramSha256SaslClient.IANA_MECHANISM_NAME, ScramSha256SaslClient.MECHANISM_NAME), //
7981
GSSAPI("GSSAPI", "GSSAPI");
8082

81-
private String mechName;
82-
private String saslServiceName;
83+
private String mechName = null;
84+
private String saslServiceName = null;
8385

8486
private AuthenticationMechanisms(String mechName, String serviceName) {
8587
this.mechName = mechName;
@@ -105,11 +107,12 @@ String getSaslServiceName() {
105107
}
106108

107109
private Protocol<?> protocol = null;
108-
private String user;
109-
private String password;
110+
private MysqlCallbackHandler usernameCallbackHandler = null;
111+
private String user = null;
112+
private String password = null;
110113

111-
private AuthenticationMechanisms authMech;
112-
private SaslClient saslClient;
114+
private AuthenticationMechanisms authMech = null;
115+
private SaslClient saslClient = null;
113116
private Subject subject = null;
114117

115118
private boolean firstPass = true;
@@ -135,6 +138,12 @@ public void init(Protocol<NativePacketPayload> prot) {
135138
Security.addProvider(new ScramShaSaslProvider());
136139
}
137140

141+
@Override
142+
public void init(Protocol<NativePacketPayload> prot, MysqlCallbackHandler cbh) {
143+
init(prot);
144+
this.usernameCallbackHandler = cbh;
145+
}
146+
138147
@Override
139148
public void reset() {
140149
if (this.saslClient != null) {
@@ -177,6 +186,14 @@ public boolean isReusable() {
177186
public void setAuthenticationParameters(String user, String password) {
178187
this.user = user;
179188
this.password = password;
189+
190+
if (this.user == null) {
191+
// Fall-back to system login user.
192+
this.user = System.getProperty("user.name");
193+
if (this.usernameCallbackHandler != null) {
194+
this.usernameCallbackHandler.handle(new UsernameCallback(this.user));
195+
}
196+
}
180197
}
181198

182199
@Override
@@ -192,7 +209,7 @@ public boolean nextAuthenticationStep(NativePacketPayload fromServer, List<Nativ
192209
if (this.firstPass) {
193210
this.firstPass = false;
194211
// Payload could be a salt (auth-plugin-data) value instead of an authentication mechanism identifier.
195-
// Give it another try in the hope of receiving a AuthSwitchRequest next time.
212+
// Give it another try in the expectation of receiving an AuthSwitchRequest next time.
196213
toServer.add(new NativePacketPayload(new byte[0]));
197214
return true;
198215
}

0 commit comments

Comments
 (0)