Skip to content

Commit

Permalink
Fix for Bug#87379 (26646676), Perform actual TLS capabilities check when
Browse files Browse the repository at this point in the history
restricting TLSv1.2.
  • Loading branch information
soklakov committed Aug 21, 2017
1 parent 7e0c645 commit 11102ad
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 5 deletions.
3 changes: 3 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

Version 5.1.44

- Fix for Bug#87379 (26646676), Perform actual TLS capabilities check when restricting TLSv1.2.
Thanks to Todd Farmer for his contribution.

- Fix for Bug#85601 (25777822), Unit notation is missing in the description of the property involved in the time.

- Fix for Bug#87153 (26501245), INCORRECT RESULT OF DBMD.GETVERSIONCOLUMNS() AGAINST MYSQL 8.0.2+.
Expand Down
4 changes: 4 additions & 0 deletions src/com/mysql/jdbc/ConnectionProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -1426,6 +1426,10 @@ public interface ConnectionProperties {

public void setEnabledSSLCipherSuites(String cipherSuites);

public String getEnabledTLSProtocols();

public void setEnabledTLSProtocols(String protocols);

public boolean getEnableEscapeProcessing();

public void setEnableEscapeProcessing(boolean flag);
Expand Down
11 changes: 11 additions & 0 deletions src/com/mysql/jdbc/ConnectionPropertiesImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -1334,6 +1334,9 @@ protected static DriverPropertyInfo[] exposeAsDriverPropertyInfo(Properties info
private StringConnectionProperty enabledSSLCipherSuites = new StringConnectionProperty("enabledSSLCipherSuites", null,
Messages.getString("ConnectionProperties.enabledSSLCipherSuites"), "5.1.35", SECURITY_CATEGORY, 11);

private StringConnectionProperty enabledTLSProtocols = new StringConnectionProperty("enabledTLSProtocols", null,
Messages.getString("ConnectionProperties.enabledTLSProtocols"), "5.1.44", SECURITY_CATEGORY, 12);

private BooleanConnectionProperty enableEscapeProcessing = new BooleanConnectionProperty("enableEscapeProcessing", true,
Messages.getString("ConnectionProperties.enableEscapeProcessing"), "5.1.37", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);

Expand Down Expand Up @@ -4948,6 +4951,14 @@ public void setEnabledSSLCipherSuites(String cipherSuites) {
this.enabledSSLCipherSuites.setValue(cipherSuites);
}

public String getEnabledTLSProtocols() {
return this.enabledTLSProtocols.getValueAsString();
}

public void setEnabledTLSProtocols(String protocols) {
this.enabledTLSProtocols.setValue(protocols);
}

public boolean getEnableEscapeProcessing() {
return this.enableEscapeProcessing.getValueAsBoolean();
}
Expand Down
33 changes: 29 additions & 4 deletions src/com/mysql/jdbc/ExportControlled.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@
*/
public class ExportControlled {
private static final String SQL_STATE_BAD_SSL_PARAMS = "08000";
private static final String TLSv1 = "TLSv1";
private static final String TLSv1_1 = "TLSv1.1";
private static final String TLSv1_2 = "TLSv1.2";
private static final String[] TLS_PROTOCOLS = new String[] { TLSv1_2, TLSv1_1, TLSv1 };

protected static boolean enabled() {
// we may wish to un-static-ify this class this static method call may be removed entirely by the compiler
Expand All @@ -101,11 +105,32 @@ protected static void transformSocketToSSLSocket(MysqlIO mysqlIO) throws SQLExce
try {
mysqlIO.mysqlConnection = sslFact.connect(mysqlIO.host, mysqlIO.port, null);

String[] tryProtocols = null;

// If enabledTLSProtocols configuration option is set, overriding the default TLS version restrictions.
// This allows enabling TLSv1.2 for self-compiled MySQL versions supporting it, as well as the ability
// for users to restrict TLS connections to approved protocols (e.g., prohibiting TLSv1) on the client side.
String enabledTLSProtocols = mysqlIO.connection.getEnabledTLSProtocols();
if (enabledTLSProtocols != null && enabledTLSProtocols.length() > 0) {
tryProtocols = enabledTLSProtocols.split("\\s*,\\s*");
} else {
// Note that it is problematic to enable TLSv1.2 on the client side when the server is compiled with yaSSL.
// When client attempts to connect with TLSv1.2 yaSSL just closes the socket instead of re-attempting handshake with
// lower TLS version.
if (mysqlIO.versionMeetsMinimum(5, 6, 0) && Util.isEnterpriseEdition(mysqlIO.getServerVersion())) {
tryProtocols = new String[] { TLSv1_2, TLSv1_1, TLSv1 };
}
}
// allow TLSv1 and TLSv1.1 for all server versions by default
if (tryProtocols == null) {
tryProtocols = new String[] { TLSv1_1, TLSv1 };
}
List<String> configuredProtocols = new ArrayList<String>(Arrays.asList(tryProtocols));
List<String> jvmSupportedProtocols = Arrays.asList(((SSLSocket) mysqlIO.mysqlConnection).getSupportedProtocols());

List<String> allowedProtocols = new ArrayList<String>();
List<String> supportedProtocols = Arrays.asList(((SSLSocket) mysqlIO.mysqlConnection).getSupportedProtocols());
for (String protocol : (mysqlIO.versionMeetsMinimum(5, 6, 0) && Util.isEnterpriseEdition(mysqlIO.getServerVersion())
? new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" } : new String[] { "TLSv1.1", "TLSv1" })) {
if (supportedProtocols.contains(protocol)) {
for (String protocol : TLS_PROTOCOLS) {
if (jvmSupportedProtocols.contains(protocol) && configuredProtocols.contains(protocol)) {
allowedProtocols.add(protocol);
}
}
Expand Down
1 change: 1 addition & 0 deletions src/com/mysql/jdbc/LocalizedErrorMessages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,7 @@ ConnectionProperties.detectCustomCollations=Should the driver detect custom char
ConnectionProperties.dontCheckOnDuplicateKeyUpdateInSQL=Stops checking if every INSERT statement contains the "ON DUPLICATE KEY UPDATE" clause. As a side effect, obtaining the statement's generated keys information will return a list where normally it wouldn't. Also be aware that, in this case, the list of generated keys returned may not be accurate. The effect of this property is canceled if set simultaneously with 'rewriteBatchedStatements=true'.
ConnectionProperties.readOnlyPropagatesToServer=Should the driver issue appropriate statements to implicitly set the transaction access mode on server side when Connection.setReadOnly() is called? Setting this property to 'true' enables InnoDB read-only potential optimizations but also requires an extra roundtrip to set the right transaction state. Even if this property is set to 'false', the driver will do its best effort to prevent the execution of database-state-changing queries. Requires minimum of MySQL 5.6.
ConnectionProperties.enabledSSLCipherSuites=If "useSSL" is set to "true", overrides the cipher suites enabled for use on the underlying SSL sockets. This may be required when using external JSSE providers or to specify cipher suites compatible with both MySQL server and used JVM.
ConnectionProperties.enabledTLSProtocols=If "useSSL" is set to "true", overrides the TLS protocols enabled for use on the underlying SSL sockets. This may be used to restrict connections to specific TLS versions.
ConnectionProperties.enableEscapeProcessing=Sets the default escape processing behavior for Statement objects. The method Statement.setEscapeProcessing() can be used to specify the escape processing behavior for an individual Statement object. Default escape processing behavior in prepared statements must be defined with the property 'processEscapeCodesForPrepStmts'.

#
Expand Down
8 changes: 8 additions & 0 deletions src/com/mysql/jdbc/MultiHostMySQLConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -2479,6 +2479,14 @@ public void setEnabledSSLCipherSuites(String cipherSuites) {
getActiveMySQLConnection().setEnabledSSLCipherSuites(cipherSuites);
}

public String getEnabledTLSProtocols() {
return getActiveMySQLConnection().getEnabledTLSProtocols();
}

public void setEnabledTLSProtocols(String protocols) {
getActiveMySQLConnection().setEnabledTLSProtocols(protocols);
}

public boolean getEnableEscapeProcessing() {
return getActiveMySQLConnection().getEnableEscapeProcessing();
}
Expand Down
8 changes: 8 additions & 0 deletions src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -2882,6 +2882,14 @@ public void setEnabledSSLCipherSuites(String cipherSuites) {
this.mc.setEnabledSSLCipherSuites(cipherSuites);
}

public String getEnabledTLSProtocols() {
return this.mc.getEnabledTLSProtocols();
}

public void setEnabledTLSProtocols(String protocols) {
this.mc.setEnabledTLSProtocols(protocols);
}

public boolean getEnableEscapeProcessing() {
return this.mc.getEnableEscapeProcessing();
}
Expand Down
82 changes: 81 additions & 1 deletion src/testsuite/regression/ConnectionRegressionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
Expand Down Expand Up @@ -8467,7 +8468,13 @@ public void testTLSVersion() throws Exception {
ResultSet rset = sslConn.createStatement().executeQuery("SHOW STATUS LIKE 'ssl_version'");
assertTrue(rset.next());
String tlsVersion = rset.getString(2);
System.out.println(tlsVersion);
System.out.println("TLS version: " + tlsVersion);
System.out.println();
System.out.println("MySQL version: " + ((MySQLConnection) sslConn).getServerVersion());
String etp = ((MySQLConnection) sslConn).getEnabledTLSProtocols();
System.out.println("enabledTLSProtocols: " + etp);
System.out.println();
System.out.println("JVM version: " + Util.getJVMVersion());
System.out.println();

if (((MySQLConnection) sslConn).versionMeetsMinimum(5, 7, 10) && Util.getJVMVersion() > 6) {
Expand All @@ -8484,6 +8491,79 @@ public void testTLSVersion() throws Exception {
}
}

/**
* Tests fix for Bug#87379. This allows TLS version to be overridden through a new configuration
* option - enabledTLSProtocols. When set to some combination of TLSv1, TLSv1.1, or TLSv1.2 (comma-
* separated, no spaces), the default behaviour restricting the TLS version based on JRE and MySQL
* Server version is bypassed to enable or restrict specific TLS versions.
*
* This test requires community server (with yaSSL) in -Dcom.mysql.jdbc.testsuite.url and
* commercial server (with OpenSSL) in -Dcom.mysql.jdbc.testsuite.url.sha256default
*
* Test certificates from testsuite/ssl-test-certs must be installed on both servers.
*
* @throws Exception
* if the test fails.
*/
public void testEnableTLSVersion() throws Exception {

final String[] testDbUrls;
Properties props = new Properties();
props.setProperty("allowPublicKeyRetrieval", "true");
props.setProperty("useSSL", "true");
props.setProperty("requireSSL", "true");
props.setProperty("trustCertificateKeyStoreUrl", "file:src/testsuite/ssl-test-certs/ca-truststore");
props.setProperty("trustCertificateKeyStoreType", "JKS");
props.setProperty("trustCertificateKeyStorePassword", "password");

if (this.sha256Conn != null && ((MySQLConnection) this.sha256Conn).versionMeetsMinimum(5, 5, 7)) {
testDbUrls = new String[] { BaseTestCase.dbUrl, sha256Url };
} else {
testDbUrls = new String[] { BaseTestCase.dbUrl };
}

for (String testDbUrl : testDbUrls) {
System.out.println(testDbUrl);
System.out.println(System.getProperty("java.version"));
Connection sslConn = getConnectionWithProps(testDbUrl, props);
assertTrue(((MySQLConnection) sslConn).getIO().isSSLEstablished());
List<String> expectedProtocols = new ArrayList<String>();
expectedProtocols.add("TLSv1");
if (Util.getJVMVersion() > 6 && ((MySQLConnection) sslConn).versionMeetsMinimum(5, 7, 10)) {
ResultSet rs1 = sslConn.createStatement().executeQuery("SELECT @@global.tls_version");
assertTrue(rs1.next());
String supportedTLSVersions = rs1.getString(1);
System.out.println("Server reported TLS version support: " + supportedTLSVersions);
expectedProtocols.addAll(Arrays.asList(supportedTLSVersions.split("\\s*,\\s*")));
}

String[] testingProtocols = { "TLSv1.2", "TLSv1.1", "TLSv1" };
for (String protocol : testingProtocols) {
Properties testProps = new Properties();
testProps.putAll(props);
testProps.put("enabledTLSProtocols", protocol);
System.out.println("Testing " + protocol + " expecting connection: " + expectedProtocols.contains(protocol));
try {
Connection tlsConn = getConnectionWithProps(testDbUrl, testProps);
if (!expectedProtocols.contains(protocol)) {
fail("Expected to fail connection with " + protocol + " due to lack of server support.");
}
ResultSet rset = tlsConn.createStatement().executeQuery("SHOW STATUS LIKE 'ssl_version'");
assertTrue(rset.next());
String tlsVersion = rset.getString(2);
assertEquals(protocol, tlsVersion);
tlsConn.close();
} catch (Exception e) {
if (expectedProtocols.contains(protocol)) {
e.printStackTrace();
fail("Expected to be able to connect with " + protocol + " protocol, but failed.");
}
}
}
sslConn.close();
}
}

/**
* Tests fix for Bug#21286268 - CONNECTOR/J REPLICATION USE MASTER IF SLAVE IS UNAVAILABLE.
*/
Expand Down

0 comments on commit 11102ad

Please sign in to comment.