Problem with TLS 1.1 client connecting to TLS 1.0 server

Simon Josefsson simon at
Thu Aug 27 15:24:49 CEST 2009

Roland Dreier <roland at> writes:

> I found what appears to be a small bug in how gnutls handles the case
> where a TLS 1.1 client connects to a server that only supports TLS
> 1.0.  My particular case is using empathy, an IM client built on
> telepathy-gabble, which in turn uses libloudmouth and finally uses
> gnutls to talk to a jabber/XMPP server.  This setup defaults to trying
> TLS 1.1, and in my case I'm using the client to talk to Webex Connect,
> where there server seems to support only TLS 1.0.
> So looking at the trace, I see a client hello sent with version 03 02,
> and the server responds with a hello with version 03 01.  gnutls then
> falls back to TLS 1.0 and future messages have version 03 01, but the
> handshake fails after gnutls sends the client finished message -- the
> server responds with a bad record MAC alert.
> After a bunch of tracing through, I found the code in
> _gnutls_gen_rsa_client_kx() in auth_rsa.c:
>   ver = _gnutls_get_adv_version (session);
>   if (session->internals.rsa_pms_version[0] == 0)
>     {
>       session->key->[0] = _gnutls_version_get_major (ver);
>       session->key->[1] = _gnutls_version_get_minor (ver);
> and when a client starts a TLS 1.1 session but falls back to TLS 1.0,
> _gnutls_get_adv_version() still returns TLS 1.1, so the client uses
> minor version 2 here, even though it is in the process of negotiating
> a TLS 1.0 (minor 1) connection.
> If I add a hack to overwrite session->key->[1] with 1 after
> the assignment of the minor, then the connection goes through OK.
> So it seems that _gnutls_gen_rsa_client_kx() should be using the
> active version here, but I'm not sure what the correct real fix within
> the gnutls design is.  Can someone provide guidance on how to fix this?

RFC 5246 says the PMS should contain the latest (newest) version
supported by the client:

         The latest (newest) version supported by the client.  This is
         used to detect version rollback attacks.
   Note: The version number in the PreMasterSecret is the version
   offered by the client in the ClientHello.client_version, not the
   version negotiated for the connection.  This feature is designed to
   prevent rollback attacks.  Unfortunately, some old implementations
   use the negotiated version instead, and therefore checking the
   version number may lead to failure to interoperate with such
   incorrect client implementations.

   Client implementations MUST always send the correct version number in
   PreMasterSecret.  If ClientHello.client_version is TLS 1.1 or higher,
   server implementations MUST check the version number as described in
   the note below.  If the version number is TLS 1.0 or earlier, server
   implementations SHOULD check the version number, but MAY have a
   configuration option to disable the check.  Note that if the check
   fails, the PreMasterSecret SHOULD be randomized as described below.

Even RFC 2246, TLS 1.0, is clear on this:

           The latest (newest) version supported by the client. This is
           used to detect version roll-back attacks. Upon receiving the
           premaster secret, the server should check that this value
           matches the value transmitted by the client in the client
           hello message.

I believe this means your server is broken and doesn't implement TLS 1.0

Changing how GnuTLS works here by default doesn't work: it would make
the default break with working servers, which isn't a good idea.

So the next-best solution appears to allow applications say to GnuTLS to
work better with this particular server.  This is already supported, and
you can tell GnuTLS to only attempt to talk TLS 1.0 against the server.
This appears to be the simplest solution since your server doesn't
support anything more than TLS 1.0 anyway.  This should be a knob
somewhere in your application, and have to be propagated through the
libraries down into GnuTLS.  The following disables TLS 1.1:

  gnutls_priority_set_direct (session, "NORMAL:-VERS-TLS1.1", NULL);

There are other alternatives to solving this too (i.e., add a knob to
use another client_version value), but I'm not sure it is worth it as it
adds other problems and the problem here is really a broken server.


More information about the Gnutls-devel mailing list