Skip to content

Commit

Permalink
Fix for Bug#78706 (21947042), Prefer TLS where supported by MySQL
Browse files Browse the repository at this point in the history
Server.
  • Loading branch information
soklakov committed Oct 30, 2015
1 parent b82eed2 commit 808463b
Show file tree
Hide file tree
Showing 14 changed files with 216 additions and 104 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ dd-mm-yy - Version 5.1.38

- Fix for Bug#21978216, GETTYPEINFO REPORT MAXIMUM PRECISION OF 255 FOR VARBINARY.

- Fix for Bug#78706 (21947042), Prefer TLS where supported by MySQL Server.

- Fix for Bug#21934573, FABRIC CODE INVOLVED IN THREAD DEADLOCK.
Duplicate: Bug#78710 (21966391), Deadlock on ReplicationConnection and ReplicationConnectionGroup when failover.

Expand Down
2 changes: 2 additions & 0 deletions src/com/mysql/jdbc/ConnectionProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,8 @@ public interface ConnectionProperties {

public boolean getUseSSL();

boolean isUseSSLExplicit();

public boolean getUseStreamLengthsInPrepStmts();

public boolean getUseTimezone();
Expand Down
21 changes: 21 additions & 0 deletions src/com/mysql/jdbc/ConnectionPropertiesImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ void initializeFrom(String extractedValue, ExceptionInterceptor exceptionInterce
validateStringValues(extractedValue, exceptionInterceptor);

this.valueAsObject = Boolean.valueOf(extractedValue.equalsIgnoreCase("TRUE") || extractedValue.equalsIgnoreCase("YES"));
this.wasExplicitlySet = true;
} else {
this.valueAsObject = this.defaultValue;
}
Expand All @@ -107,6 +108,7 @@ boolean isRangeBased() {

void setValue(boolean valueFlag) {
this.valueAsObject = Boolean.valueOf(valueFlag);
this.wasExplicitlySet = true;
this.updateCount++;
}
}
Expand Down Expand Up @@ -139,6 +141,8 @@ static abstract class ConnectionProperty implements Serializable {

int updateCount = 0;

boolean wasExplicitlySet = false;

public ConnectionProperty() {
}

Expand Down Expand Up @@ -200,6 +204,10 @@ int getUpdateCount() {
return this.updateCount;
}

boolean isExplicitlySet() {
return this.wasExplicitlySet;
}

abstract boolean hasValueConstraints();

void initializeFrom(Properties extractFrom, ExceptionInterceptor exceptionInterceptor) throws SQLException {
Expand Down Expand Up @@ -416,6 +424,7 @@ void setValue(int intValue, String valueAsString, ExceptionInterceptor exception
}

this.valueAsObject = Integer.valueOf(intValue);
this.wasExplicitlySet = true;
this.updateCount++;
}
}
Expand Down Expand Up @@ -448,6 +457,7 @@ void setValue(long longValue, String valueAsString, ExceptionInterceptor excepti
}
}
this.valueAsObject = Long.valueOf(longValue);
this.wasExplicitlySet = true;
this.updateCount++;
}

Expand Down Expand Up @@ -563,6 +573,7 @@ void initializeFrom(String extractedValue, ExceptionInterceptor exceptionInterce
validateStringValues(extractedValue, exceptionInterceptor);

this.valueAsObject = extractedValue;
this.wasExplicitlySet = true;
} else {
this.valueAsObject = this.defaultValue;
}
Expand All @@ -579,6 +590,7 @@ boolean isRangeBased() {

void setValue(String valueFlag) {
this.valueAsObject = valueFlag;
this.wasExplicitlySet = true;
this.updateCount++;
}
}
Expand Down Expand Up @@ -2328,6 +2340,15 @@ public boolean getUseSSL() {
return this.useSSL.getValueAsBoolean();
}

/**
* Was the value of useSSL set explicitly or just got from defaults.
*
* @return
*/
public boolean isUseSSLExplicit() {
return this.useSSL.wasExplicitlySet;
}

/*
* (non-Javadoc)
*
Expand Down
53 changes: 50 additions & 3 deletions src/com/mysql/jdbc/ExportControlled.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

import javax.crypto.Cipher;
Expand Down Expand Up @@ -75,7 +78,7 @@ protected static boolean enabled() {
*
* @throws CommunicationsException
* if the handshake fails, or if this distribution of
* Connector/J doesn't contain the SSL crytpo hooks needed to
* Connector/J doesn't contain the SSL crypto hooks needed to
* perform the handshake.
*/
protected static void transformSocketToSSLSocket(MysqlIO mysqlIO) throws SQLException {
Expand All @@ -87,9 +90,53 @@ protected static void transformSocketToSSLSocket(MysqlIO mysqlIO) throws SQLExce
// need to force TLSv1, or else JSSE tries to do a SSLv2 handshake which MySQL doesn't understand
((SSLSocket) mysqlIO.mysqlConnection).setEnabledProtocols(new String[] { "TLSv1" });

// check allowed cipher suites
String enabledSSLCipherSuites = mysqlIO.connection.getEnabledSSLCipherSuites();
if (enabledSSLCipherSuites != null && enabledSSLCipherSuites.length() > 0) {
((SSLSocket) mysqlIO.mysqlConnection).setEnabledCipherSuites(enabledSSLCipherSuites.split("\\s*,\\s*"));
boolean overrideCiphers = enabledSSLCipherSuites != null && enabledSSLCipherSuites.length() > 0;

List<String> allowedCiphers = null;
if (overrideCiphers) {
// If "enabledSSLCipherSuites" is set we just check that JVM allows provided values,
// we don't disable DH algorithm, that allows c/J to deal with custom server builds with different security restrictions
allowedCiphers = new ArrayList<String>();
List<String> availableCiphers = Arrays.asList(((SSLSocket) mysqlIO.mysqlConnection).getEnabledCipherSuites());
for (String cipher : enabledSSLCipherSuites.split("\\s*,\\s*")) {
if (availableCiphers.contains(cipher)) {
allowedCiphers.add(cipher);
}
}

} else {
// If we don't override ciphers, then we check for known restrictions
boolean disableDHAlgorithm = false;
if (mysqlIO.versionMeetsMinimum(5, 5, 45) && !mysqlIO.versionMeetsMinimum(5, 6, 0) || mysqlIO.versionMeetsMinimum(5, 6, 26)
&& !mysqlIO.versionMeetsMinimum(5, 7, 0) || mysqlIO.versionMeetsMinimum(5, 7, 6)) {
// Workaround for JVM bug http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6521495
// Starting from 5.5.45, 5.6.26 and 5.7.6 server the key length used for creating Diffie-Hellman keys has been
// increased from 512 to 2048 bits, while JVMs affected by this bug allow only range from 512 to 1024 (inclusive).
// Bug is fixed in Java 8.
if (Util.getJVMVersion() < 8) {
disableDHAlgorithm = true;
}
} else if (Util.getJVMVersion() >= 8) { // TODO check later for Java 9 behaviour
// Java 8 default java.security contains jdk.tls.disabledAlgorithms=DH keySize < 768
// That causes handshake failures with older MySQL servers, eg 5.6.11. Thus we have to disable DH for them when running on Java 8+
disableDHAlgorithm = true;
}

if (disableDHAlgorithm) {
allowedCiphers = new ArrayList<String>();
for (String cipher : ((SSLSocket) mysqlIO.mysqlConnection).getEnabledCipherSuites()) {
if (!(disableDHAlgorithm && (cipher.indexOf("_DHE_") > -1 || cipher.indexOf("_DH_") > -1))) {
allowedCiphers.add(cipher);
}
}
}
}

// if some ciphers were filtered into allowedCiphers
if (allowedCiphers != null) {
((SSLSocket) mysqlIO.mysqlConnection).setEnabledCipherSuites(allowedCiphers.toArray(new String[] {}));
}

((SSLSocket) mysqlIO.mysqlConnection).startHandshake();
Expand Down
8 changes: 4 additions & 4 deletions src/com/mysql/jdbc/LocalizedErrorMessages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@ CommunicationsException.12=before use in your application, increasing the server
CommunicationsException.13=or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.
CommunicationsException.TooManyClientConnections=The driver was unable to create a connection due to an inability to establish the client portion of a socket.\n\nThis is usually caused by a limit on the number of sockets imposed by the operating system. This limit is usually configurable. \n\nFor Unix-based platforms, see the manual page for the 'ulimit' command. Kernel or system reconfiguration may also be required.\n\nFor Windows-based platforms, see Microsoft Knowledge Base Article 196271 (Q196271).
CommunicationsException.LocalSocketAddressNotAvailable=The configuration parameter \"localSocketAddress\" has been set to a network interface not available for use by the JVM.
CommunicationsException.incompatibleSSLCipherSuites=The driver was unable to create a connection due to a possible incompatibility between the default enabled cipher suites in this JVM and the security layer provided by the server.\n\nMySQL Server 5.5.45, MySQL Community Server 5.6.26, MySQL Community Server 5.7.6 and versions above require a 2048 bit key during DH key exchange. This was not supported in Java before v8. Setting the connection property 'enabledSSLCipherSuites=TLS_RSA_WITH_AES_128_CBC_SHA,SSL_RSA_WITH_RC4_128_SHA,SSL_RSA_WITH_3DES_EDE_CBC_SHA,SSL_RSA_WITH_RC4_128_MD5,SSL_RSA_WITH_DES_CBC_SHA' uses RSA key exchange instead.
CommunicationsException.20=Communications link failure
CommunicationsException.ClientWasStreaming=Application was streaming results when the connection failed. Consider raising value of 'net_write_timeout' on the server.
CommunicationsException.ServerPacketTimingInfoNoRecv=The last packet sent successfully to the server was {0} milliseconds ago. The driver has not received any packets from the server.
Expand Down Expand Up @@ -346,6 +345,7 @@ MysqlIO.EOF=Can not read response from server. Expected to read {0} bytes, read
MysqlIO.NoInnoDBStatusFound=No InnoDB status output returned by server.
MysqlIO.InnoDBStatusFailed=Couldn't retrieve InnoDB status due to underlying exception:
MysqlIO.LoadDataLocalNotAllowed=Server asked for stream in response to LOAD DATA LOCAL INFILE but functionality is disabled at client by 'allowLoadLocalInfile' being set to 'false'.
MysqlIO.SSLWarning=Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
NotImplemented.0=Feature not implemented
UnsupportedSQLType.0=Unsupported SQL type:
PreparedStatement.0=SQL String can not be NULL
Expand Down Expand Up @@ -618,15 +618,15 @@ ConnectionProperties.useOldUtf8Behavior=Use the UTF-8 behavior the driver did wh
ConnectionProperties.useOnlyServerErrorMessages=Don't prepend 'standard' SQLState error messages to error messages returned by the server.
ConnectionProperties.useReadAheadInput=Use newer, optimized non-blocking, buffered input stream when reading from the server?
ConnectionProperties.useSqlStateCodes=Use SQL Standard state codes instead of 'legacy' X/Open/SQL state codes (true/false), default is 'true'
ConnectionProperties.useSSL=Use SSL when communicating with the server (true/false), defaults to 'false'
ConnectionProperties.useSSL=Use SSL when communicating with the server (true/false), default is 'true' when connecting to MySQL 5.5.45+, 5.6.26+ or 5.7.6+, otherwise default is 'false'
ConnectionProperties.useSSPSCompatibleTimezoneShift=If migrating from an environment that was using server-side prepared statements, and the configuration property "useJDBCCompliantTimeZoneShift" set to "true", use compatible behavior when not using server-side prepared statements when sending TIMESTAMP values to the MySQL server.
ConnectionProperties.useStreamLengthsInPrepStmts=Honor stream length parameter in PreparedStatement/ResultSet.setXXXStream() method calls (true/false, defaults to 'true')?
ConnectionProperties.useTimezone=Convert time/date types between client and server time zones (true/false, defaults to 'false')? This is part of the legacy date-time code, thus the property has an effect only when "useLegacyDatetimeCode=true."
ConnectionProperties.ultraDevHack=Create PreparedStatements for prepareCall() when required, because UltraDev is broken and issues a prepareCall() for _all_ statements? (true/false, defaults to 'false')
ConnectionProperties.useUnbufferedInput=Don't use BufferedInputStream for reading data from the server
ConnectionProperties.useUnicode=Should the driver use Unicode character encodings when handling strings? Should only be used when the driver can't determine the character set mapping, or you are trying to 'force' the driver to use a character set that MySQL either doesn't natively support (such as UTF-8), true/false, defaults to 'true'
ConnectionProperties.useUsageAdvisor=Should the driver issue 'usage' warnings advising proper and efficient usage of JDBC and MySQL Connector/J to the log (true/false, defaults to 'false')?
ConnectionProperties.verifyServerCertificate=If "useSSL" is set to "true", should the driver verify the server's certificate? When using this feature, the keystore parameters should be specified by the "clientCertificateKeyStore*" properties, rather than system properties.
ConnectionProperties.verifyServerCertificate=If "useSSL" is set to "true", should the driver verify the server's certificate? When using this feature, the keystore parameters should be specified by the "clientCertificateKeyStore*" properties, rather than system properties. Default is 'false' when connecting to MySQL 5.5.45+, 5.6.26+ or 5.7.6+ and "useSSL" was not explicitly set to "true". Otherwise default is 'true'
ConnectionProperties.yearIsDateType=Should the JDBC driver treat the MySQL type "YEAR" as a java.sql.Date, or as a SHORT?
ConnectionProperties.zeroDateTimeBehavior=What should happen when the driver encounters DATETIME values that are composed entirely of zeros (used by MySQL to represent invalid dates)? Valid values are \"{0}\", \"{1}\" and \"{2}\".
ConnectionProperties.useJvmCharsetConverters=Always use the character encoding routines built into the JVM, rather than using lookup tables for single-byte character sets?
Expand Down Expand Up @@ -664,7 +664,7 @@ ConnectionProperties.getProceduresReturnsFunctions=Pre-JDBC4 DatabaseMetaData AP
ConnectionProperties.detectCustomCollations=Should the driver detect custom charsets/collations installed on server (true/false, defaults to 'false'). If this option set to 'true' driver gets actual charsets/collations from server each time connection establishes. This could slow down connection initialization significantly.
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", sets the cipher suites enabled for use on the underlying SSL sockets. This may be required when using external JSSE providers or to specify compatible cipher suites when connecting to newer, more secure, MySQL server versions and running the driver with older JVMs.
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.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
4 changes: 4 additions & 0 deletions src/com/mysql/jdbc/MultiHostMySQLConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -2427,4 +2427,8 @@ public boolean getEnableEscapeProcessing() {
public void setEnableEscapeProcessing(boolean flag) {
getActiveMySQLConnection().setEnableEscapeProcessing(flag);
}

public boolean isUseSSLExplicit() {
return getActiveMySQLConnection().isUseSSLExplicit();
}
}
8 changes: 8 additions & 0 deletions src/com/mysql/jdbc/MysqlIO.java
Original file line number Diff line number Diff line change
Expand Up @@ -1170,6 +1170,14 @@ void doHandshake(String user, String password, String database) throws SQLExcept
this.clientParam |= CLIENT_CONNECT_WITH_DB;
}

// Changing SSL defaults for 5.7+ server: useSSL=true, requireSSL=false, verifyServerCertificate=false
if (versionMeetsMinimum(5, 7, 0) && !this.connection.getUseSSL() && !this.connection.isUseSSLExplicit()) {
this.connection.setUseSSL(true);
this.connection.setVerifyServerCertificate(false);
this.connection.getLog().logWarn(Messages.getString("MysqlIO.SSLWarning"));
}

// check SSL availability
if (((this.serverCapabilities & CLIENT_SSL) == 0) && this.connection.getUseSSL()) {
if (this.connection.getRequireSSL()) {
this.connection.close();
Expand Down
4 changes: 4 additions & 0 deletions src/com/mysql/jdbc/ReplicationConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -3022,4 +3022,8 @@ public boolean getEnableEscapeProcessing() {
public void setEnableEscapeProcessing(boolean flag) {
getCurrentConnection().setEnableEscapeProcessing(flag);
}

public boolean isUseSSLExplicit() {
return getCurrentConnection().isUseSSLExplicit();
}
}
6 changes: 0 additions & 6 deletions src/com/mysql/jdbc/SQLError.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@
import java.util.Map;
import java.util.TreeMap;

import javax.net.ssl.SSLException;

import com.mysql.jdbc.exceptions.MySQLDataException;
import com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException;
import com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException;
Expand Down Expand Up @@ -1100,10 +1098,6 @@ public static String createLinkFailureMessageBasedOnHeuristics(MySQLConnection c
// too many client connections???
exceptionMessageBuf.append(Messages.getString("CommunicationsException.TooManyClientConnections"));
}
} else if (Util.getJVMVersion() < 8 && underlyingException instanceof SSLException && (cause = underlyingException.getCause()) != null
&& cause.getMessage().equals("Could not generate DH keypair") && (cause = cause.getCause()) != null
&& cause.getMessage().equals("Prime size must be multiple of 64, and can only range from 512 to 1024 (inclusive)")) {
exceptionMessageBuf.append(Messages.getString("CommunicationsException.incompatibleSSLCipherSuites"));
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -2852,4 +2852,8 @@ public boolean getEnableEscapeProcessing() {
public void setEnableEscapeProcessing(boolean flag) {
this.mc.setEnableEscapeProcessing(flag);
}

public boolean isUseSSLExplicit() {
return this.mc.isUseSSLExplicit();
}
}
Loading

0 comments on commit 808463b

Please sign in to comment.