On 24/09/15 21:40, Hubert Kario via RT wrote: > I have made the reproducer cleaner and it should use relatively stable > API's of tlsfuzzer now. > > openssl req -x509 -newkey rsa -keyout localhost.key -out localhost.crt\ > -nodes -batch > ~/dev/openssl/apps/openssl s_server -key localhost.key -cert\ > localhost.crt > > pip install --pre tlslite-ng > git clone https://github.com/tomato42/tlsfuzzer.git > > cd tlsfuzzer > PYTHONPATH=. python scripts/test-openssl-3712.py
This is really nice! Thanks Hubert. I've been looking into this issue. The reason this fails is because at some point in the past there has been an explicit design decision to disallow it. The relevant code is here: /* * At this point, we were expecting handshake data, but have * application data. If the library was running inside ssl3_read() * (i.e. in_read_app_data is set) and it makes sense to read * application data at this point (session renegotiation not yet * started), we will indulge it. */ if (s->s3->in_read_app_data && (s->s3->total_renegotiations != 0) && (((s->state & SSL_ST_CONNECT) && (s->state >= SSL3_ST_CW_CLNT_HELLO_A) && (s->state <= SSL3_ST_CR_SRVR_HELLO_A) ) || ((s->state & SSL_ST_ACCEPT) && (s->state <= SSL3_ST_SW_HELLO_REQ_A) && (s->state >= SSL3_ST_SR_CLNT_HELLO_A) ) )) { s->s3->in_read_app_data = 2; return (-1); } else { al = SSL_AD_UNEXPECTED_MESSAGE; SSLerr(SSL_F_SSL3_READ_BYTES, SSL_R_UNEXPECTED_RECORD); goto f_err; } So to pull this conditional apart a bit: s->s3->in_read_app_data : we were originally called by SSL_read AND s->s3->total_renegotiations != 0 : we have started at least one renegotiation at some point (*see below) AND ( ((s->state & SSL_ST_CONNECT) && (s->state >= SSL3_ST_CW_CLNT_HELLO_A) && (s->state <= SSL3_ST_CR_SRVR_HELLO_A) : we are a client and are in a state where we have started to write/have finished writing a ClientHello out but have not yet received a ServerHello. OR (s->state & SSL_ST_ACCEPT) && (s->state <= SSL3_ST_SW_HELLO_REQ_A) && (s->state >= SSL3_ST_SR_CLNT_HELLO_A) : we are a server and are about to write a HelloVerifyRequest, or we are in the process of reading a ClientHello ) Aside from whether this is the correct logic or not I think there is a related bug here in the total_renegotiations value. I believe this is intended to count up the number of renegotiations that have occurred. As far as I can determine this is incremented when: - There is an explicit call to SSL_renegotiate() or SSL_renegotiate_abbreviated() OR - As a client we initiate a renegotiation following receipt of a HelloRequest So it does not seem to get incremented when, as a server and after the initial handshake has completed, we receive an unsolicited ClientHello, i.e. client initiated renegotiation as in your reproducer code. This alone is sufficient for your test case to fail, but even fixing it won't solve the problem due to the intent of the logic above. The above logic can be traced back to this commit: commit b35e9050f282c5ea2164bd5b08ed34d03accf45f Author: Bodo Möller <b...@openssl.org> AuthorDate: Sun Feb 20 23:04:06 2000 +0000 Commit: Bodo Möller <b...@openssl.org> CommitDate: Sun Feb 20 23:04:06 2000 +0000 Tolerate fragmentation and interleaving in the SSL 3/TLS record layer. Note the date of February 2000! The OP correctly cites RFC 5246 (TLS1.2) and RFC 4346 (TLS1.1) as follows: Note: Data of different TLS Record layer content types MAY be interleaved. Application data is generally of lower precedence for transmission than other content types. However, records MUST be delivered to the network in the same order as they are protected by the record layer. Recipients MUST receive and process interleaved application layer traffic during handshakes subsequent to the first one on a connection. As the OP also observes: "The last sentence clearly puts the blame on OpenSSL. This seems to be an addition in TLS 1.1 since it cannot be found in RFC 2246." Clearly the code commit pre-dates the publication of TLS1.1 (April 2006) and is a carry over from the ambiguous situation as it is in TLS1.0. Therefore this does seem to be an OpenSSL bug. However, I have some concerns with the wording of the RFC. It seems to place no limits whatsoever on when it is valid to receive app data in the handshake. By the wording in the RFC it would be valid for app data to be received *after* the ChangeCipherSpec has been received but *before* the Finished has been processed. This seems dangerous to me because it is not until the Finished is processed that we verify the handshake data MAC - and yet we could already have acted upon app data received. I assume the intent was to allow the interleaved app data only up until the point that the CCS is received. I have attached a patch for 1.0.2 that implements that logic. Matt
From 0260f9d34d81f5080bbbd602577f201922fd4062 Mon Sep 17 00:00:00 2001 From: Matt Caswell <m...@openssl.org> Date: Fri, 25 Sep 2015 10:34:27 +0100 Subject: [PATCH] Allow interleaved app data in a renegotiation Prior to TLS1.1 SSL3/TLS1.0 was ambigous about what to do with application data received during a renegotiation handshake. The existing code (which predates TLS1.1) disallows this. TLS1.1 clarifies the situation to explicitly allow this. This patch enables interleaved app data up until the point that the CCS is received. Whilst the RFC does not explicitly say so it seems incorrect to allow it after this point. --- ssl/s3_pkt.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/ssl/s3_pkt.c b/ssl/s3_pkt.c index 3798902..22a27cf 100644 --- a/ssl/s3_pkt.c +++ b/ssl/s3_pkt.c @@ -1608,18 +1608,14 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek) * application data. If the library was running inside ssl3_read() * (i.e. in_read_app_data is set) and it makes sense to read * application data at this point (session renegotiation not yet - * started), we will indulge it. + * started), we will indulge it. RFCs 4346 & 5246 state that app + * data should be allowed during the handshake without any limits + * specified. This seems dangerous so we only allow it up until the CCS + * has been received. */ if (s->s3->in_read_app_data && - (s->s3->total_renegotiations != 0) && - (((s->state & SSL_ST_CONNECT) && - (s->state >= SSL3_ST_CW_CLNT_HELLO_A) && - (s->state <= SSL3_ST_CR_SRVR_HELLO_A) - ) || ((s->state & SSL_ST_ACCEPT) && - (s->state <= SSL3_ST_SW_HELLO_REQ_A) && - (s->state >= SSL3_ST_SR_CLNT_HELLO_A) - ) - )) { + s->renegotiate && + s->s3->change_cipher_spec == 0) { s->s3->in_read_app_data = 2; return (-1); } else { -- 2.1.4
_______________________________________________ openssl-dev mailing list To unsubscribe: https://mta.openssl.org/mailman/listinfo/openssl-dev