mirror of https://github.com/openssl/openssl.git
				
				
				
			Correctly handle a retransmitted ClientHello
If we receive a ClientHello and send back a HelloVerifyRequest, we need to be able to handle the scenario where the HelloVerifyRequest gets lost and we receive another ClientHello with the message sequence number set to 0. Fixes #18635 Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Hugo Landau <hlandau@openssl.org> (Merged from https://github.com/openssl/openssl/pull/18654)
This commit is contained in:
		
							parent
							
								
									0ff9813744
								
							
						
					
					
						commit
						81926c9156
					
				|  | @ -496,23 +496,64 @@ static int dtls1_retrieve_buffered_fragment(SSL_CONNECTION *s, size_t *len) | ||||||
|      * (2) update s->init_num |      * (2) update s->init_num | ||||||
|      */ |      */ | ||||||
|     pitem *item; |     pitem *item; | ||||||
|  |     piterator iter; | ||||||
|     hm_fragment *frag; |     hm_fragment *frag; | ||||||
|     int ret; |     int ret; | ||||||
|  |     int chretran = 0; | ||||||
| 
 | 
 | ||||||
|  |     iter = pqueue_iterator(s->d1->buffered_messages); | ||||||
|     do { |     do { | ||||||
|         item = pqueue_peek(s->d1->buffered_messages); |         item = pqueue_next(&iter); | ||||||
|         if (item == NULL) |         if (item == NULL) | ||||||
|             return 0; |             return 0; | ||||||
| 
 | 
 | ||||||
|         frag = (hm_fragment *)item->data; |         frag = (hm_fragment *)item->data; | ||||||
| 
 | 
 | ||||||
|         if (frag->msg_header.seq < s->d1->handshake_read_seq) { |         if (frag->msg_header.seq < s->d1->handshake_read_seq) { | ||||||
|             /* This is a stale message that has been buffered so clear it */ |             pitem *next; | ||||||
|             pqueue_pop(s->d1->buffered_messages); |             hm_fragment *nextfrag; | ||||||
|             dtls1_hm_fragment_free(frag); | 
 | ||||||
|             pitem_free(item); |             if (!s->server | ||||||
|             item = NULL; |                     || frag->msg_header.seq != 0 | ||||||
|             frag = NULL; |                     || s->d1->handshake_read_seq != 1 | ||||||
|  |                     || s->statem.hand_state != DTLS_ST_SW_HELLO_VERIFY_REQUEST) { | ||||||
|  |                 /*
 | ||||||
|  |                  * This is a stale message that has been buffered so clear it. | ||||||
|  |                  * It is safe to pop this message from the queue even though | ||||||
|  |                  * we have an active iterator | ||||||
|  |                  */ | ||||||
|  |                 pqueue_pop(s->d1->buffered_messages); | ||||||
|  |                 dtls1_hm_fragment_free(frag); | ||||||
|  |                 pitem_free(item); | ||||||
|  |                 item = NULL; | ||||||
|  |                 frag = NULL; | ||||||
|  |             } else { | ||||||
|  |                 /*
 | ||||||
|  |                  * We have fragments for a ClientHello without a cookie, | ||||||
|  |                  * even though we have sent a HelloVerifyRequest. It is possible | ||||||
|  |                  * that the HelloVerifyRequest got lost and this is a | ||||||
|  |                  * retransmission of the original ClientHello | ||||||
|  |                  */ | ||||||
|  |                 next = pqueue_next(&iter); | ||||||
|  |                 if (next != NULL) { | ||||||
|  |                     nextfrag = (hm_fragment *)next->data; | ||||||
|  |                     if (nextfrag->msg_header.seq == s->d1->handshake_read_seq) { | ||||||
|  |                         /*
 | ||||||
|  |                         * We have fragments for both a ClientHello without | ||||||
|  |                         * cookie and one with. Ditch the one without. | ||||||
|  |                         */ | ||||||
|  |                         pqueue_pop(s->d1->buffered_messages); | ||||||
|  |                         dtls1_hm_fragment_free(frag); | ||||||
|  |                         pitem_free(item); | ||||||
|  |                         item = next; | ||||||
|  |                         frag = nextfrag; | ||||||
|  |                     } else { | ||||||
|  |                         chretran = 1; | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     chretran = 1; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } while (item == NULL); |     } while (item == NULL); | ||||||
| 
 | 
 | ||||||
|  | @ -520,7 +561,7 @@ static int dtls1_retrieve_buffered_fragment(SSL_CONNECTION *s, size_t *len) | ||||||
|     if (frag->reassembly != NULL) |     if (frag->reassembly != NULL) | ||||||
|         return 0; |         return 0; | ||||||
| 
 | 
 | ||||||
|     if (s->d1->handshake_read_seq == frag->msg_header.seq) { |     if (s->d1->handshake_read_seq == frag->msg_header.seq || chretran) { | ||||||
|         size_t frag_len = frag->msg_header.frag_len; |         size_t frag_len = frag->msg_header.frag_len; | ||||||
|         pqueue_pop(s->d1->buffered_messages); |         pqueue_pop(s->d1->buffered_messages); | ||||||
| 
 | 
 | ||||||
|  | @ -538,6 +579,16 @@ static int dtls1_retrieve_buffered_fragment(SSL_CONNECTION *s, size_t *len) | ||||||
|         pitem_free(item); |         pitem_free(item); | ||||||
| 
 | 
 | ||||||
|         if (ret) { |         if (ret) { | ||||||
|  |             if (chretran) { | ||||||
|  |                 /*
 | ||||||
|  |                  * We got a new ClientHello with a message sequence of 0. | ||||||
|  |                  * Reset the read/write sequences back to the beginning. | ||||||
|  |                  * We process it like this is the first time we've seen a | ||||||
|  |                  * ClientHello from the client. | ||||||
|  |                  */ | ||||||
|  |                 s->d1->handshake_read_seq = 0; | ||||||
|  |                 s->d1->next_handshake_write_seq = 0; | ||||||
|  |             } | ||||||
|             *len = frag_len; |             *len = frag_len; | ||||||
|             return 1; |             return 1; | ||||||
|         } |         } | ||||||
|  | @ -768,6 +819,7 @@ static int dtls_get_reassembled_message(SSL_CONNECTION *s, int *errtype, | ||||||
|     struct hm_header_st msg_hdr; |     struct hm_header_st msg_hdr; | ||||||
|     size_t readbytes; |     size_t readbytes; | ||||||
|     SSL *ssl = SSL_CONNECTION_GET_SSL(s); |     SSL *ssl = SSL_CONNECTION_GET_SSL(s); | ||||||
|  |     int chretran = 0; | ||||||
| 
 | 
 | ||||||
|     *errtype = 0; |     *errtype = 0; | ||||||
| 
 | 
 | ||||||
|  | @ -837,8 +889,20 @@ static int dtls_get_reassembled_message(SSL_CONNECTION *s, int *errtype, | ||||||
|      * although we're still expecting seq 0 (ClientHello) |      * although we're still expecting seq 0 (ClientHello) | ||||||
|      */ |      */ | ||||||
|     if (msg_hdr.seq != s->d1->handshake_read_seq) { |     if (msg_hdr.seq != s->d1->handshake_read_seq) { | ||||||
|         *errtype = dtls1_process_out_of_seq_message(s, &msg_hdr); |         if (!s->server | ||||||
|         return 0; |                 || msg_hdr.seq != 0 | ||||||
|  |                 || s->d1->handshake_read_seq != 1 | ||||||
|  |                 || wire[0] != SSL3_MT_CLIENT_HELLO | ||||||
|  |                 || s->statem.hand_state != DTLS_ST_SW_HELLO_VERIFY_REQUEST) { | ||||||
|  |             *errtype = dtls1_process_out_of_seq_message(s, &msg_hdr); | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |         /*
 | ||||||
|  |          * We received a ClientHello and sent back a HelloVerifyRequest. We | ||||||
|  |          * now seem to have received a retransmitted initial ClientHello. That | ||||||
|  |          * is allowed (possibly our HelloVerifyRequest got lost). | ||||||
|  |          */ | ||||||
|  |         chretran = 1; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (frag_len && frag_len < mlen) { |     if (frag_len && frag_len < mlen) { | ||||||
|  | @ -904,6 +968,17 @@ static int dtls_get_reassembled_message(SSL_CONNECTION *s, int *errtype, | ||||||
|         goto f_err; |         goto f_err; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (chretran) { | ||||||
|  |         /*
 | ||||||
|  |          * We got a new ClientHello with a message sequence of 0. | ||||||
|  |          * Reset the read/write sequences back to the beginning. | ||||||
|  |          * We process it like this is the first time we've seen a ClientHello | ||||||
|  |          * from the client. | ||||||
|  |          */ | ||||||
|  |         s->d1->handshake_read_seq = 0; | ||||||
|  |         s->d1->next_handshake_write_seq = 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /*
 |     /*
 | ||||||
|      * Note that s->init_num is *not* used as current offset in |      * Note that s->init_num is *not* used as current offset in | ||||||
|      * s->init_buf->data, but as a counter summing up fragments' lengths: as |      * s->init_buf->data, but as a counter summing up fragments' lengths: as | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue