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.

Reviewed-by: Richard Levitte <levitte@openssl.org>
This commit is contained in:
Matt Caswell 2015-11-03 14:45:07 +00:00
parent 02dc0b82ab
commit 2ad226e88b

View file

@ -252,6 +252,40 @@ 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 (frag_off > 0) {
/* This is the first attempt at writing out this fragment */
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 |init_off| must have progressed by at least
* |DTLS1_HM_HEADER_LENGTH + 1| bytes. If not something went
* wrong.
*/
return -1;
}
/*
* Adjust |init_off| and |init_num| to allow room for a new
* message header for this fragment.
*/
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)
@ -291,25 +325,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
@ -397,7 +412,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);