Hi David On 03/11/15 01:58, David Benjamin via RT wrote: > Hey folks, > > We found a small DTLS bug while writing some tests. I think it affects > 1.0.1 and 1.0.2 too, so I thought I'd send you a note. (Note sure about > master. I'm unfamiliar with the new state machine mechanism.) > > In DTLS, each ClientHello is supposed to reset the handshake hash (in case > of HelloVerifyRequest). The old state machine calls ssl3_init_finished_mac > in the SSL3_ST_CW_CLNT_HELLO_* states. > > https://git.openssl.org/gitweb/?p=openssl.git;a=blob;f=ssl/d1_clnt.c;h=feeaf6d0656f5d0868121852d42b5037b8823111;hb=refs/heads/OpenSSL_1_0_2-stable#l317 > > If the ClientHello is fragmented and takes multiple iterations to write, > the state machine is re-entered in SSL3_ST_CW_CLNT_HELLO_B, which > calls ssl3_init_finished_mac again, and clobbers what was sampled to the > handshake hash/buffer previously. > > This requires the transport return a retriable error on write, which > probably isn't common for DTLS. It came up because WebRTC uses a custom BIO > with a fixed-size buffer, so it can actually do this. Even then, a > ClientHello is unlikely to fill up the buffer. Our tests repro'd it in > BoringSSL by forcing every write to take two iterations.
I can confirm this issue on 1.0.2 (and almost certainly 1.0.1). It does not affect master. Whilst investigating this I noticed another bug which is actually probably more significant. My eyeball only look at the BoringSSL source suggests that it is there too, so I'm not sure why you haven't seen it in the test that you mentioned. If a retry occurs on a second or subsequent fragment of a handshake message then when we do the retry the wrong fragment offset and length is used. The impact isn't that great because the messages got dropped by the peer, and then when they get retransmitted they have the correct values inserted...so the handshake succeeds - but it gets delayed. Perhaps that is why you don't see it in your test. This second issue occurs in all branches. One other related point is that fragmenting ClientHellos is probably a bad idea. The whole ClientHello/HelloVerifyRequest mechanism is meant to be implemented without storing state on the server. That isn't possible if you have to deal with fragment reassembly. In the new DTLSv1_listen implementation in master we drop fragmented ClientHellos. Patch attached for these two issues (patch against 1.0.2). Matt
>From 35b6c161b032bd2e04e54e80120f72b5d586c031 Mon Sep 17 00:00:00 2001 From: Matt Caswell <m...@openssl.org> Date: Tue, 3 Nov 2015 14:45:07 +0000 Subject: [PATCH 1/2] Fix DTLS handshake fragment retries If using DTLS and NBIO then if a second or subsequent handshake message fragment hits a retry, then the retry attempt uses the wrong fragment offset value. This commit restores the fragment offset from the last attempt. --- ssl/d1_both.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/ssl/d1_both.c b/ssl/d1_both.c index c2c8d57..59a79a7 100644 --- a/ssl/d1_both.c +++ b/ssl/d1_both.c @@ -337,9 +337,26 @@ int dtls1_do_write(SSL *s, int type) */ if (type == SSL3_RT_HANDSHAKE) { if (s->init_off != 0) { + /* We must be writing a fragment other than the first one */ OPENSSL_assert(s->init_off > DTLS1_HM_HEADER_LENGTH); - s->init_off -= DTLS1_HM_HEADER_LENGTH; - s->init_num += DTLS1_HM_HEADER_LENGTH; + + if (frag_off > 0) { + /* + * This is the first attempt at writing out this fragment. + * Adjust |init_off| and |init_num| to add a new message + * header. + */ + s->init_off -= DTLS1_HM_HEADER_LENGTH; + s->init_num += DTLS1_HM_HEADER_LENGTH; + } else { + /* + * We must have been called again after a retry so use the + * fragment offset from our last attempt. We do not need + * to adjust |init_off| and |init_num| as above, because + * that should already have been done before the retry. + */ + frag_off = s->d1->w_msg_hdr.frag_off; + } /* * We just checked that s->init_num > 0 so this cast should -- 2.1.4 >From b0ff7bdae8e7ae6b46a1d95d73ef887673684d0a Mon Sep 17 00:00:00 2001 From: Matt Caswell <m...@openssl.org> Date: Tue, 3 Nov 2015 15:49:08 +0000 Subject: [PATCH 2/2] Only call ssl3_init_finished_mac once for DTLS In DTLS if an IO retry occurs during writing of a fragmented ClientHello then we can end up reseting the finish mac variables on the retry, which causes a handshake failure. We should only reset on the first attempt not on retries. --- ssl/d1_clnt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ssl/d1_clnt.c b/ssl/d1_clnt.c index feeaf6d..26f3c99 100644 --- a/ssl/d1_clnt.c +++ b/ssl/d1_clnt.c @@ -315,13 +315,12 @@ int dtls1_connect(SSL *s) #endif case SSL3_ST_CW_CLNT_HELLO_A: - case SSL3_ST_CW_CLNT_HELLO_B: - s->shutdown = 0; /* every DTLS ClientHello resets Finished MAC */ ssl3_init_finished_mac(s); + case SSL3_ST_CW_CLNT_HELLO_B: dtls1_start_timer(s); ret = ssl3_client_hello(s); if (ret <= 0) -- 2.1.4
_______________________________________________ openssl-dev mailing list To unsubscribe: https://mta.openssl.org/mailman/listinfo/openssl-dev