On 03/11/15 17:43, David Benjamin via RT wrote:
> I'm not sure that fix quite works though. If BIO_flush completes > asynchronously Ahhh, yes good point. Updated patch attached. > (hrm, it's missing an rwstate update), Yes, just discovered that myself and then came back and reread your email to find out you already pointed it out! Also addressed in updated patch. > then I believe you'll > be in a state where you *do* want to repeat the init_off / init_num adjust. > You might be able to get away with using init_off/init_num with some minor > tweaks? Another problem: because the fragment headers clobber whatever was > already written, msg_callback sees garbage. Yuck. Not addressed that issue yet. I'll deal with that separately. > Yeah, this part of DTLS (like much of it) is woefully underspecified. We > keep stuffing things into ClientHellos, so if one does need to support > fragmented ones, I think the right way to do stateless HelloVerifyRequest > is to silently drop all non-initial ClientHello fragments and require the > initial one be large enough to include the client_random and whatever else > you figure into the cookie. I really like that idea. I'll take a look at doing that in the new DTLSv1_listen code. Matt
>From d973a67f17917869ab2238c041c447996ff94976 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/3] 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 | 63 ++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/ssl/d1_both.c b/ssl/d1_both.c index c2c8d57..dfae56c 100644 --- a/ssl/d1_both.c +++ b/ssl/d1_both.c @@ -297,6 +297,39 @@ int dtls1_do_write(SSL *s, int type) frag_off = 0; /* s->init_num shouldn't ever be < 0...but just in case */ while (s->init_num > 0) { + if (type == SSL3_RT_HANDSHAKE && s->init_off != 0) { + /* We must be writing a fragment other than the first one */ + + if (s->init_off <= DTLS1_HM_HEADER_LENGTH) { + /* + * Each fragment that was already sent must at least have + * contained the message header plus one other byte. Therefore + * if |init_off| is non zero then it must have progressed by at + * least |DTLS1_HM_HEADER_LENGTH + 1| bytes. If not something + * went wrong. + */ + return -1; + } + + 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; + } + } + used_len = BIO_wpending(SSL_get_wbio(s)) + DTLS1_RT_HEADER_LENGTH + mac_size + blocksize; if (s->d1->mtu > used_len) @@ -336,25 +369,6 @@ int dtls1_do_write(SSL *s, int type) * XDTLS: this function is too long. split out the CCS part */ if (type == SSL3_RT_HANDSHAKE) { - if (s->init_off != 0) { - OPENSSL_assert(s->init_off > DTLS1_HM_HEADER_LENGTH); - s->init_off -= DTLS1_HM_HEADER_LENGTH; - s->init_num += DTLS1_HM_HEADER_LENGTH; - - /* - * We just checked that s->init_num > 0 so this cast should - * be safe - */ - if (((unsigned int)s->init_num) > curr_mtu) - len = curr_mtu; - else - len = s->init_num; - } - - /* Shouldn't ever happen */ - if (len > INT_MAX) - len = INT_MAX; - if (len < DTLS1_HM_HEADER_LENGTH) { /* * len is so small that we really can't do anything sensible @@ -442,7 +456,16 @@ int dtls1_do_write(SSL *s, int type) } s->init_off += ret; s->init_num -= ret; - frag_off += (ret -= DTLS1_HM_HEADER_LENGTH); + ret -= DTLS1_HM_HEADER_LENGTH; + frag_off += ret; + + /* + * We save the fragment offset for the next fragment so we have it + * available in case of an IO retry. We don't know the length of the + * next fragment yet so just set that to 0 for now. It will be + * updated again later. + */ + dtls1_fix_message_header(s, frag_off, 0); } } return (0); -- 2.1.4 >From 46b946ada0a918123612d5375838b3b1ed3faa0d Mon Sep 17 00:00:00 2001 From: Matt Caswell <m...@openssl.org> Date: Wed, 4 Nov 2015 11:20:50 +0000 Subject: [PATCH 2/3] Ensure |rwstate| is set correctly on BIO_flush A BIO_flush call in the DTLS code was not correctly setting the |rwstate| variable to SSL_WRITING. This means that SSL_get_error() will not return SSL_ERROR_WANT_WRITE in the event of an IO retry. --- ssl/d1_both.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ssl/d1_both.c b/ssl/d1_both.c index dfae56c..1f0eaac 100644 --- a/ssl/d1_both.c +++ b/ssl/d1_both.c @@ -295,6 +295,8 @@ int dtls1_do_write(SSL *s, int type) blocksize = 0; frag_off = 0; + s->rwstate = SSL_NOTHING; + /* s->init_num shouldn't ever be < 0...but just in case */ while (s->init_num > 0) { if (type == SSL3_RT_HANDSHAKE && s->init_off != 0) { @@ -342,8 +344,10 @@ int dtls1_do_write(SSL *s, int type) * grr.. we could get an error if MTU picked was wrong */ ret = BIO_flush(SSL_get_wbio(s)); - if (ret <= 0) + if (ret <= 0) { + s->rwstate = SSL_WRITING; return ret; + } used_len = DTLS1_RT_HEADER_LENGTH + mac_size + blocksize; if (s->d1->mtu > used_len + DTLS1_HM_HEADER_LENGTH) { curr_mtu = s->d1->mtu - used_len; -- 2.1.4 >From d7ed52adf42de98e582f1b3b0c2a621d45bbcb11 Mon Sep 17 00:00:00 2001 From: Matt Caswell <m...@openssl.org> Date: Tue, 3 Nov 2015 15:49:08 +0000 Subject: [PATCH 3/3] 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