[gnutls-devel] BUG: Cannot connect with non-blocking OS to OCSP stapling-enabled (CERTIFICATE STATUS) server

Nils Maier testnutzer123 at gmail.com
Mon Jan 6 15:30:26 CET 2014


Affected: Likely all GnuTLS versions supporting OCSP stapling. Tested
with 3.1.18 and 3.2.8.

STR:
- Program client using non-blocking sockets. Or if you're lazy, use
aria2, where we discovered this.
http://aria2.sourceforge.net/
https://github.com/tatsuhiro-t/aria2/issues/179
Or wget master, which is affected as well, or something like that.

- Try to connect to a TLS server that uses stapling. E.g. https://tn123.org/

- The connection will fail (more often than not), right after the full
CERTIFICATE STATUS packet is received, with:
"An unexpected TLS handshake packet was received."

- It fails in STATE8 of the handshake (kx) not STATE6 (CERTIFICATE STATUS).

Reasons:
https://gitorious.org/gnutls/gnutls/source/1b5fce89c569e4abf9784e6a2944b3ccf9dd61f8:lib/ext/status_request.c#L581

_gnutls_recv_server_certificate_status() does not handle incomplete
packets (EGAIN) correctly.
It sets |priv->expect_cstatus = 0;| before actually checking if there
was already a full packet received. If there wasn't (and this is likely
with non-blocking I/O), the method will later exit, to be called again
when additional data is received.
However, in that subsequent call the function will bail without further
processing as |priv->expect_cstatus| was already set in the first call,
leaving the unprocessed CERTIFICATE STATUS packet in the buffers.
After bailing, the STATE in _gnutls_recv_server_certificate_status()
transitions from STATE6 -> STATE7 -> STATE8. In STATE8,
_gnutls_recv_server_kx_message() is called, with the CERTIFICATE STATUS
still in the buffer, and therefore fails as it rightfully didn't expect
that packet, aborting the connection attempt entirely.

Fix:
A preliminary test indicates the attached patch (against 3_1_x, because
I'm to lazy to build a nettle-2.7 right now) fixes the problem, by
moving |priv->expect_cstatus = 0;| after the |_gnutls_recv_handshake|
call. I'm not a GnuTLS expert, so I have no idea what it might break in
turn.

Cheers
Nils

PS: Please CC me if you need something, as I'm not watching the lists.
-------------- next part --------------
>From d6d4a177e9e705c4bbac0eaa689fa80025f52f71 Mon Sep 17 00:00:00 2001
From: Nils Maier <maierman at web.de>
Date: Mon, 6 Jan 2014 15:15:58 +0100
Subject: [PATCH] Fix CERTIFICATE STATUS processing when using non-blocking I/O

_gnutls_recv_server_certificate_status() must wait for the first full
packet before setting priv->expect_cstatus = 0, or else CERTIFCATE
STATUS packets won't be processed in subsequent calls at all, leaving
them in the buffer and therefore causing later connection aborts.
---
 lib/ext/status_request.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/ext/status_request.c b/lib/ext/status_request.c
index ac512c2..267ba5d 100644
--- a/lib/ext/status_request.c
+++ b/lib/ext/status_request.c
@@ -574,28 +574,28 @@ _gnutls_recv_server_certificate_status (gnutls_session_t session)
     _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_STATUS_REQUEST,
                                   &epriv);
   if (ret < 0)
     return 0;
 
   priv = epriv.ptr;
 
   if (!priv->expect_cstatus)
     return 0;
 
-  priv->expect_cstatus = 0;
-
   ret = _gnutls_recv_handshake (session, 
                                 GNUTLS_HANDSHAKE_CERTIFICATE_STATUS,
                                 0, &buf);
   if (ret < 0)
     return gnutls_assert_val_fatal(ret);
 
+  priv->expect_cstatus = 0;
+
   data = buf.data;
   data_size = buf.length;
 
   /* minimum message is type (1) + response (3) + data */
   if (data_size == 0)
     return 0;
   else if (data_size < 4)
     return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
 
   if (data[0] != 0x01)
-- 
1.8.5.2



More information about the Gnutls-devel mailing list