Bug #83347 Server closes SSL Connection when "Client Hello" packet version is TLS 1.2
Submitted: 12 Oct 2016 21:45 Modified: 30 Mar 2017 19:15
Reporter: Caleb Lloyd Email Updates:
Status: Verified Impact on me:
Category:MySQL Server: Security: Encryption Severity:S3 (Non-critical)
Version: OS:Any
Assigned to: CPU Architecture:Any

[12 Oct 2016 21:45] Caleb Lloyd
MySQL Server sends an error when a client initiates a changeover to SSL that sends the Client Hello packet with a version of TLS 1.2.  It sends the message "#08S01 Bad Handshake" and closes the connection.

Windows SSL Connections initiated from Schannel (the native Windows SSL provider) with a maximum allowed protocol of TLS 1.2 will hit this error when the MySQL server does not support TLS 1.2.

It appears that the client code is permitted to send "TLS 1.2" as its record layer version number (as Windows does).  MySQL Server is not compliant with this specification because it doesn't accept any value as the record layer version number.

From RFC 5246 Appendix E:

Earlier versions of the TLS specification were not fully clear on what the record layer version number (TLSPlaintext.version) should contain when sending ClientHello (i.e., before it is known which version of the protocol will be employed). Thus, TLS servers compliant with this specification MUST accept any value {03,XX} as the record layer version number for ClientHello.

TLS clients that wish to negotiate with older servers MAY send any value {03,XX} as the record layer version number. Typical values would be {03,00}, the lowest version number supported by the client, and the value of ClientHello.client_version. No single value will guarantee interoperability with all old servers, but this is a complex topic beyond the scope of this document.

The workaround for now is to make TLS 1.1 the maximum accepted protocol on Windows when connecting to MySQL.  If the server is ever upgraded to support TLS 1.2, Windows will continue to connect over the older TLS 1.1 protocol.

Packet Capture traces are available at: https://github.com/bgrainger/MySqlConnector/files/525350/tls-traces.zip

For more information and pictures view: https://github.com/bgrainger/MySqlConnector/pull/101#issuecomment-253301090

How to repeat:
Open a TCP connection that supports TLS1.2 from Windows to a MySql server.

Suggested fix:
Accept any value {03,XX} as the record layer version number in the Client Hello packet during TLS negotiation.
[30 Mar 2017 16:43] MySQL Verification Team
Thanks for your report. The Linux MySQL server binary that is failing, can you give us more details about it
 - binary is compiled by you or you used binary from Oracle?
 - what is the version of the MySQL server you are using
 - what is the version of the OS you are using

[30 Mar 2017 17:02] Bradley Grainger
The links given in the original bug report are now incorrect because the code was moved to a different GitHub organization. Here are the correct links:

Packet Capture traces are available at: https://github.com/mysql-net/MySqlConnector/files/525350/tls-traces.zip

For more information and pictures view: https://github.com/mysql-net/MySqlConnector/pull/101#issuecomment-253301090
[30 Mar 2017 17:35] MySQL Verification Team

Thanks for the updated links, I see you are writing your own client there. Still I have no info on the version of mysql you are using. As far as I remember this part of handshake is delegated to the SSL library so it might be a bug in YASSL and not MySQL.

Anyhow, wherever the problem is (MySQL or YASSL), the root of the problem is standard that's not really clear on how the versioning should be handled. If you look at the standard: https://tools.ietf.org/html/rfc5246 (especially https://tools.ietf.org/html/rfc5246#appendix-E.1 ) you will see that it implicitly states that version chosen will be based on what client & server support rather then what they agree on. So in theory if one support 1.0 and other 1.2 they both have to use 1.0 as that's what they both support. You can't decide to use 1.2 just because you want to :). 

The Appendix E.1 states that "Whenever a client already knows the highest protocol version known to a server (for example, when resuming a session), it SHOULD initiate the connection in that native protocol." so in this case since MySQL understands 1.1 only your client should drop down to 1.1 and not try to use 1.2.

I don't say that there's no bug in handshake code in mysql/yassl but there might be a problem with your client code as well.

In your own thread you state that when you send version 1.0 mysql recognises it but when you send 1.2 mysql don't. Since it's 1.1 on server side it's kinda expected that it don't know higher versions so you might want to change that on the client. 

I will *verify* this bug, since it's fairly easy to reproduce, and let our team in charge of SSL decide if it's in our or library code and if it should be changed or not.

[30 Mar 2017 18:50] Bradley Grainger
I can reproduce the problem with MySQL Community Server 5.7.15 (64-bit) running on Windows 10 (with a connector that sends TLS 1.2 in the version of the record layer).

Although RFC 5246 says a client "SHOULD initiate the connection in [the version it knows the server supports]" (and that was indeed our workaround), it also says "If a TLS server receives a ClientHello containing a version number greater than the highest version supported by the server, it MUST reply according to the highest version supported by the server" and "Thus, TLS servers compliant with this specification MUST accept any value {03,XX} as the record layer version number for ClientHello."

It seems that MySQL Server's SSL library is not following this latter requirement, in particular.
[30 Mar 2017 19:01] Bradley Grainger
Just updated to MySQL Community Server 5.7.17 64-bit on Windows 10 and can still repro the bug.
[30 Mar 2017 19:02] MySQL Verification Team
Hi Bradley,

It's very "normal" to expect the server to understand any version in this case as this helloconnect communication didn't change and "number is a number" :).. obviously in this case for some reason 1.2 (and from what I see 3.0) are read as error and connection is closed rather then "negotiating" lower version. As I said I think (not sure) this is part of YASSL 3rd party library MySQL uses and not part of MySQL but since I already verified the bug it's up to sustaining team to decide how to proceed.

MySQL do allow you to compile it with a different SSL library so you could recompile with openssl as it might work different/better in that case. Not sure how to check on windows what library it's built against, if you try to connect to linux server you can test this with "ldd mysqld".

Selecting what SSL to build against - use "-DWITH_SSL={ssl_type|path_name} " ( https://dev.mysql.com/doc/mysql-sourcebuild-excerpt/5.7/en/source-configuration-options.ht... )

Now a question - did you try mysqld-community server on linux (connecting from windows)?

all best
[30 Mar 2017 19:07] Bradley Grainger
No, I have not tried connecting to a Linux-based server from Windows yet. And as you said, it probably depends on which SSL library MySQL Server is linked with.

Finally, from a client perspective, even if this is fixed in a newer version of MySQL Server it won't make much practical difference because the connector still needs to be backwards compatible to MySQL Server 5.5 (or earlier) and can't know whether the server has the bugfix until after it's connected. :)
[30 Mar 2017 19:15] Caleb Lloyd
Hi Bogdan,

Thanks for looking into this for us.  My initial bug report was trying to connect from a Windows Client to a Linux Server, the server version was 5.7.15 at that time (I use the latest 5.7 docker image).

@Bradley the Server Greeting comes across unencrypted at the beginning of the conversation, then the connection attempts to upgrade to TLS.  The Client could detect the Server Version and make a decision about which TLS version to send.  This can be seen in the "windows-tls12-failed.pcapng" packet capture.
[30 Mar 2017 19:40] MySQL Verification Team
Hi Caleb,

thanks for the confirmation.

as for 

> The Client could detect the Server Version and make a decision about which TLS version to send.  This can be seen in the "windows-tls12-failed.pcapng" packet capture.

yes, way better then a "dirty" fix I often see in similar cases that would lead to other issues like fail2ban banning your client for too many attempts etc ( "if you lose connection after sending version higher then 1.1, reconnect and try 1.1" :) )

all best