From 4e63da06698fd217d89a5ec39fd0c7b26dc8d0c0 Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Fri, 5 Jun 2009 14:46:49 +0000 Subject: [PATCH] PR: 1950 Submitted by: Robin Seggelmann Reviewed by: steve@openssl.org DTLS fragment retransmission bug. --- crypto/bio/bss_dgram.c | 11 +++++++++-- ssl/d1_both.c | 8 ++++++-- ssl/d1_clnt.c | 2 +- ssl/d1_pkt.c | 10 ++++++++++ ssl/d1_srvr.c | 2 ++ ssl/dtls1.h | 1 + 6 files changed, 29 insertions(+), 5 deletions(-) diff --git a/crypto/bio/bss_dgram.c b/crypto/bio/bss_dgram.c index 2e1a6fac33..027cdf943b 100644 --- a/crypto/bio/bss_dgram.c +++ b/crypto/bio/bss_dgram.c @@ -217,12 +217,19 @@ static void dgram_adjust_rcv_timeout(BIO *b) timeleft.tv_usec += 1000000; } + if (timeleft.tv_sec < 0) + { + timeleft.tv_sec = 0; + timeleft.tv_usec = 1; + } + /* Adjust socket timeout if next handhake message timer * will expire earlier. */ - if (data->socket_timeout.tv_sec < timeleft.tv_sec || + if ((data->socket_timeout.tv_sec == 0 && data->socket_timeout.tv_usec == 0) || + (data->socket_timeout.tv_sec > timeleft.tv_sec) || (data->socket_timeout.tv_sec == timeleft.tv_sec && - data->socket_timeout.tv_usec <= timeleft.tv_usec)) + data->socket_timeout.tv_usec >= timeleft.tv_usec)) { #ifdef OPENSSL_SYS_WINDOWS timeout = timeleft.tv_sec * 1000 + timeleft.tv_usec / 1000; diff --git a/ssl/d1_both.c b/ssl/d1_both.c index ebc03482f0..7039d05f0e 100644 --- a/ssl/d1_both.c +++ b/ssl/d1_both.c @@ -569,9 +569,13 @@ dtls1_process_out_of_seq_message(SSL *s, struct hm_header_st* msg_hdr, int *ok) item = pqueue_find(s->d1->buffered_messages, seq64be); /* Discard the message if sequence number was already there, is - * too far in the future or the fragment is already in the queue */ + * too far in the future, already in the queue or if we received + * a FINISHED before the SERVER_HELLO, which then must be a stale + * retransmit. + */ if (msg_hdr->seq <= s->d1->handshake_read_seq || - msg_hdr->seq > s->d1->handshake_read_seq + 10 || item != NULL) + msg_hdr->seq > s->d1->handshake_read_seq + 10 || item != NULL || + s->d1->handshake_read_seq == 0 && msg_hdr->type == SSL3_MT_FINISHED) { unsigned char devnull [256]; diff --git a/ssl/d1_clnt.c b/ssl/d1_clnt.c index 2364ad2f8d..a4a438ac79 100644 --- a/ssl/d1_clnt.c +++ b/ssl/d1_clnt.c @@ -442,7 +442,7 @@ int dtls1_connect(SSL *s) case SSL3_ST_CR_FINISHED_A: case SSL3_ST_CR_FINISHED_B: - + s->d1->change_cipher_spec_ok = 1; ret=ssl3_get_finished(s,SSL3_ST_CR_FINISHED_A, SSL3_ST_CR_FINISHED_B); if (ret <= 0) goto end; diff --git a/ssl/d1_pkt.c b/ssl/d1_pkt.c index a89edbc7a7..394daf6258 100644 --- a/ssl/d1_pkt.c +++ b/ssl/d1_pkt.c @@ -1102,6 +1102,16 @@ start: s->msg_callback(0, s->version, SSL3_RT_CHANGE_CIPHER_SPEC, rr->data, 1, s, s->msg_callback_arg); + /* We can't process a CCS now, because previous handshake + * messages are still missing, so just drop it. + */ + if (!s->d1->change_cipher_spec_ok) + { + goto start; + } + + s->d1->change_cipher_spec_ok = 0; + s->s3->change_cipher_spec=1; if (!ssl3_do_change_cipher_spec(s)) goto err; diff --git a/ssl/d1_srvr.c b/ssl/d1_srvr.c index 6d6363f04f..ac09b45ef1 100644 --- a/ssl/d1_srvr.c +++ b/ssl/d1_srvr.c @@ -497,6 +497,7 @@ int dtls1_accept(SSL *s) case SSL3_ST_SR_CERT_VRFY_A: case SSL3_ST_SR_CERT_VRFY_B: + s->d1->change_cipher_spec_ok = 1; /* we should decide if we expected this one */ ret=ssl3_get_cert_verify(s); if (ret <= 0) goto end; @@ -508,6 +509,7 @@ int dtls1_accept(SSL *s) case SSL3_ST_SR_FINISHED_A: case SSL3_ST_SR_FINISHED_B: + s->d1->change_cipher_spec_ok = 1; ret=ssl3_get_finished(s,SSL3_ST_SR_FINISHED_A, SSL3_ST_SR_FINISHED_B); if (ret <= 0) goto end; diff --git a/ssl/dtls1.h b/ssl/dtls1.h index 2fe0405011..73af56d45c 100644 --- a/ssl/dtls1.h +++ b/ssl/dtls1.h @@ -231,6 +231,7 @@ typedef struct dtls1_state_st unsigned int handshake_fragment_len; unsigned int retransmitting; + unsigned int change_cipher_spec_ok; } DTLS1_STATE; -- 2.25.1