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 | ||||
|      */ | ||||
|     pitem *item; | ||||
|     piterator iter; | ||||
|     hm_fragment *frag; | ||||
|     int ret; | ||||
|     int chretran = 0; | ||||
| 
 | ||||
|     iter = pqueue_iterator(s->d1->buffered_messages); | ||||
|     do { | ||||
|         item = pqueue_peek(s->d1->buffered_messages); | ||||
|         item = pqueue_next(&iter); | ||||
|         if (item == NULL) | ||||
|             return 0; | ||||
| 
 | ||||
|         frag = (hm_fragment *)item->data; | ||||
| 
 | ||||
|         if (frag->msg_header.seq < s->d1->handshake_read_seq) { | ||||
|             /* This is a stale message that has been buffered so clear it */ | ||||
|             pitem *next; | ||||
|             hm_fragment *nextfrag; | ||||
| 
 | ||||
|             if (!s->server | ||||
|                     || frag->msg_header.seq != 0 | ||||
|                     || 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); | ||||
| 
 | ||||
|  | @ -520,7 +561,7 @@ static int dtls1_retrieve_buffered_fragment(SSL_CONNECTION *s, size_t *len) | |||
|     if (frag->reassembly != NULL) | ||||
|         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; | ||||
|         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); | ||||
| 
 | ||||
|         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; | ||||
|             return 1; | ||||
|         } | ||||
|  | @ -768,6 +819,7 @@ static int dtls_get_reassembled_message(SSL_CONNECTION *s, int *errtype, | |||
|     struct hm_header_st msg_hdr; | ||||
|     size_t readbytes; | ||||
|     SSL *ssl = SSL_CONNECTION_GET_SSL(s); | ||||
|     int chretran = 0; | ||||
| 
 | ||||
|     *errtype = 0; | ||||
| 
 | ||||
|  | @ -837,9 +889,21 @@ static int dtls_get_reassembled_message(SSL_CONNECTION *s, int *errtype, | |||
|      * although we're still expecting seq 0 (ClientHello) | ||||
|      */ | ||||
|     if (msg_hdr.seq != s->d1->handshake_read_seq) { | ||||
|         if (!s->server | ||||
|                 || 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) { | ||||
|         *errtype = dtls1_reassemble_fragment(s, &msg_hdr); | ||||
|  | @ -904,6 +968,17 @@ static int dtls_get_reassembled_message(SSL_CONNECTION *s, int *errtype, | |||
|         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 | ||||
|      * s->init_buf->data, but as a counter summing up fragments' lengths: as | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue