mirror of https://github.com/openssl/openssl.git
				
				
				
			Add testcases for SSL_key_update() corner case calls
Test that SSL_key_update() is not allowed if there are writes pending. Test that there is no reset of the packet pointer in ssl3_setup_read_buffer(). Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> (Merged from https://github.com/openssl/openssl/pull/16085)
This commit is contained in:
		
							parent
							
								
									fd76ee47b9
								
							
						
					
					
						commit
						0c48fda8d3
					
				|  | @ -85,7 +85,9 @@ protocol level. | |||
| It is safe to call SSL_read() or SSL_read_ex() when more data is available | ||||
| even when the call that set this error was an SSL_write() or SSL_write_ex(). | ||||
| However, if the call was an SSL_write() or SSL_write_ex(), it should be called | ||||
| again to continue sending the application data. | ||||
| again to continue sending the application data. If you get B<SSL_ERROR_WANT_WRITE> | ||||
| from SSL_write() or SSL_write_ex() then you should not do any other operation | ||||
| that could trigger B<IO> other than to repeat the previous SSL_write() call. | ||||
| 
 | ||||
| For socket B<BIO>s (e.g. when SSL_set_fd() was used), select() or | ||||
| poll() on the underlying socket can be used to find out when the | ||||
|  |  | |||
|  | @ -32,10 +32,11 @@ peer to additionally update its sending keys. It is an error if B<updatetype> is | |||
| set to B<SSL_KEY_UPDATE_NONE>. | ||||
| 
 | ||||
| SSL_key_update() must only be called after the initial handshake has been | ||||
| completed and TLSv1.3 has been negotiated. The key update will not take place | ||||
| until the next time an IO operation such as SSL_read_ex() or SSL_write_ex() | ||||
| takes place on the connection. Alternatively SSL_do_handshake() can be called to | ||||
| force the update to take place immediately. | ||||
| completed and TLSv1.3 has been negotiated, at the same time, the application | ||||
| needs to ensure that the writing of data has been completed. The key update | ||||
| will not take place until the next time an IO operation such as SSL_read_ex() | ||||
| or SSL_write_ex() takes place on the connection. Alternatively SSL_do_handshake() | ||||
| can be called to force the update to take place immediately. | ||||
| 
 | ||||
| SSL_get_key_update_type() can be used to determine whether a key update | ||||
| operation has been scheduled but not yet performed. The type of the pending key | ||||
|  |  | |||
|  | @ -6109,12 +6109,12 @@ static int test_key_update(void) | |||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Test we can handle a KeyUpdate (update requested) message while write data | ||||
|  * is pending. | ||||
|  * Test we can handle a KeyUpdate (update requested) message while | ||||
|  * write data is pending in peer. | ||||
|  * Test 0: Client sends KeyUpdate while Server is writing | ||||
|  * Test 1: Server sends KeyUpdate while Client is writing | ||||
|  */ | ||||
| static int test_key_update_in_write(int tst) | ||||
| static int test_key_update_peer_in_write(int tst) | ||||
| { | ||||
|     SSL_CTX *cctx = NULL, *sctx = NULL; | ||||
|     SSL *clientssl = NULL, *serverssl = NULL; | ||||
|  | @ -6141,7 +6141,7 @@ static int test_key_update_in_write(int tst) | |||
|     peerwrite = tst == 0 ? serverssl : clientssl; | ||||
| 
 | ||||
|     if (!TEST_true(SSL_key_update(peerupdate, SSL_KEY_UPDATE_REQUESTED)) | ||||
|             || !TEST_true(SSL_do_handshake(peerupdate))) | ||||
|             || !TEST_int_eq(SSL_do_handshake(peerupdate), 1)) | ||||
|         goto end; | ||||
| 
 | ||||
|     /* Swap the writing endpoint's write BIO to force a retry */ | ||||
|  | @ -6192,6 +6192,264 @@ static int test_key_update_in_write(int tst) | |||
| 
 | ||||
|     return testresult; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Test we can handle a KeyUpdate (update requested) message while | ||||
|  * peer read data is pending after peer accepted keyupdate(the msg header | ||||
|  * had been read 5 bytes). | ||||
|  * Test 0: Client sends KeyUpdate while Server is reading | ||||
|  * Test 1: Server sends KeyUpdate while Client is reading | ||||
|  */ | ||||
| static int test_key_update_peer_in_read(int tst) | ||||
| { | ||||
|     SSL_CTX *cctx = NULL, *sctx = NULL; | ||||
|     SSL *clientssl = NULL, *serverssl = NULL; | ||||
|     int testresult = 0; | ||||
|     char prbuf[515], lwbuf[515] = {0}; | ||||
|     static char *mess = "A test message"; | ||||
|     BIO *lbio = NULL, *pbio = NULL; | ||||
|     SSL *local = NULL, *peer = NULL; | ||||
| 
 | ||||
|     if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(), | ||||
|                                               TLS_client_method(), | ||||
|                                               TLS1_3_VERSION, | ||||
|                                               0, | ||||
|                                               &sctx, &cctx, cert, privkey)) | ||||
|             || !TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, | ||||
|                                              NULL, NULL)) | ||||
|             || !TEST_true(create_ssl_connection(serverssl, clientssl, | ||||
|                                                 SSL_ERROR_NONE))) | ||||
|         goto end; | ||||
| 
 | ||||
|     local = tst == 0 ? clientssl : serverssl; | ||||
|     peer = tst == 0 ? serverssl : clientssl; | ||||
| 
 | ||||
|     if (!TEST_int_eq(BIO_new_bio_pair(&lbio, 512, &pbio, 512), 1)) | ||||
|         goto end; | ||||
| 
 | ||||
|     SSL_set_bio(local, lbio, lbio); | ||||
|     SSL_set_bio(peer, pbio, pbio); | ||||
| 
 | ||||
|     /*
 | ||||
|      * we first write keyupdate msg then appdata in local | ||||
|      * write data in local will fail with SSL_ERROR_WANT_WRITE,because | ||||
|      * lwbuf app data msg size + key updata msg size > 512(the size of | ||||
|      * the bio pair buffer) | ||||
|      */ | ||||
|     if (!TEST_true(SSL_key_update(local, SSL_KEY_UPDATE_REQUESTED)) | ||||
|             || !TEST_int_eq(SSL_write(local, lwbuf, sizeof(lwbuf)), -1) | ||||
|             || !TEST_int_eq(SSL_get_error(local, -1), SSL_ERROR_WANT_WRITE)) | ||||
|         goto end; | ||||
| 
 | ||||
|     /*
 | ||||
|      * first read keyupdate msg in peer in peer | ||||
|      * then read appdata that we know will fail with SSL_ERROR_WANT_READ | ||||
|      */ | ||||
|     if (!TEST_int_eq(SSL_read(peer, prbuf, sizeof(prbuf)), -1) | ||||
|             || !TEST_int_eq(SSL_get_error(peer, -1), SSL_ERROR_WANT_READ)) | ||||
|         goto end; | ||||
| 
 | ||||
|     /* Now write some data in peer - we will write the key update */ | ||||
|     if (!TEST_int_eq(SSL_write(peer, mess, strlen(mess)), strlen(mess))) | ||||
|         goto end; | ||||
| 
 | ||||
|     /*
 | ||||
|      * write data in local previously that we will complete | ||||
|      * read data in peer previously that we will complete | ||||
|      */ | ||||
|     if (!TEST_int_eq(SSL_write(local, lwbuf, sizeof(lwbuf)), sizeof(lwbuf)) | ||||
|             || !TEST_int_eq(SSL_read(peer, prbuf, sizeof(prbuf)), sizeof(prbuf))) | ||||
|         goto end; | ||||
| 
 | ||||
|     /* check that sending and receiving appdata ok */ | ||||
|     if (!TEST_int_eq(SSL_write(local, mess, strlen(mess)), strlen(mess)) | ||||
|             || !TEST_int_eq(SSL_read(peer, prbuf, sizeof(prbuf)), strlen(mess))) | ||||
|         goto end; | ||||
| 
 | ||||
|     testresult = 1; | ||||
| 
 | ||||
|  end: | ||||
|     SSL_free(serverssl); | ||||
|     SSL_free(clientssl); | ||||
|     SSL_CTX_free(sctx); | ||||
|     SSL_CTX_free(cctx); | ||||
| 
 | ||||
|     return testresult; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Test we can't send a KeyUpdate (update requested) message while | ||||
|  * local write data is pending. | ||||
|  * Test 0: Client sends KeyUpdate while Client is writing | ||||
|  * Test 1: Server sends KeyUpdate while Server is writing | ||||
|  */ | ||||
| static int test_key_update_local_in_write(int tst) | ||||
| { | ||||
|     SSL_CTX *cctx = NULL, *sctx = NULL; | ||||
|     SSL *clientssl = NULL, *serverssl = NULL; | ||||
|     int testresult = 0; | ||||
|     char buf[20]; | ||||
|     static char *mess = "A test message"; | ||||
|     BIO *bretry = BIO_new(bio_s_always_retry()); | ||||
|     BIO *tmp = NULL; | ||||
|     SSL *local = NULL, *peer = NULL; | ||||
| 
 | ||||
|     if (!TEST_ptr(bretry) | ||||
|             || !TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(), | ||||
|                                               TLS_client_method(), | ||||
|                                               TLS1_3_VERSION, | ||||
|                                               0, | ||||
|                                               &sctx, &cctx, cert, privkey)) | ||||
|             || !TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, | ||||
|                                              NULL, NULL)) | ||||
|             || !TEST_true(create_ssl_connection(serverssl, clientssl, | ||||
|                                                 SSL_ERROR_NONE))) | ||||
|         goto end; | ||||
| 
 | ||||
|     local = tst == 0 ? clientssl : serverssl; | ||||
|     peer = tst == 0 ? serverssl : clientssl; | ||||
| 
 | ||||
|     /* Swap the writing endpoint's write BIO to force a retry */ | ||||
|     tmp = SSL_get_wbio(local); | ||||
|     if (!TEST_ptr(tmp) || !TEST_true(BIO_up_ref(tmp))) { | ||||
|         tmp = NULL; | ||||
|         goto end; | ||||
|     } | ||||
|     SSL_set0_wbio(local, bretry); | ||||
|     bretry = NULL; | ||||
| 
 | ||||
|     /* write data in local will fail with SSL_ERROR_WANT_WRITE */ | ||||
|     if (!TEST_int_eq(SSL_write(local, mess, strlen(mess)), -1) | ||||
|             || !TEST_int_eq(SSL_get_error(local, -1), SSL_ERROR_WANT_WRITE)) | ||||
|         goto end; | ||||
| 
 | ||||
|     /* Reinstate the original writing endpoint's write BIO */ | ||||
|     SSL_set0_wbio(local, tmp); | ||||
|     tmp = NULL; | ||||
| 
 | ||||
|     /* SSL_key_update will fail, because writing in local*/ | ||||
|     if (!TEST_false(SSL_key_update(local, SSL_KEY_UPDATE_REQUESTED)) | ||||
|         || !TEST_int_eq(ERR_GET_REASON(ERR_peek_error()), SSL_R_BAD_WRITE_RETRY)) | ||||
|     goto end; | ||||
| 
 | ||||
|     ERR_clear_error(); | ||||
|     /* write data in local previously that we will complete */ | ||||
|     if (!TEST_int_eq(SSL_write(local, mess, strlen(mess)), strlen(mess))) | ||||
|         goto end; | ||||
| 
 | ||||
|     /* SSL_key_update will succeed because there is no pending write data */ | ||||
|     if (!TEST_true(SSL_key_update(local, SSL_KEY_UPDATE_REQUESTED)) | ||||
|         || !TEST_int_eq(SSL_do_handshake(local), 1)) | ||||
|         goto end; | ||||
| 
 | ||||
|     /*
 | ||||
|      * we write some appdata in local | ||||
|      * read data in peer - we will read the keyupdate msg | ||||
|      */ | ||||
|     if (!TEST_int_eq(SSL_write(local, mess, strlen(mess)), strlen(mess)) | ||||
|         || !TEST_int_eq(SSL_read(peer, buf, sizeof(buf)), strlen(mess))) | ||||
|         goto end; | ||||
| 
 | ||||
|     /* Write more peer more data to ensure we send the keyupdate message back */ | ||||
|     if (!TEST_int_eq(SSL_write(peer, mess, strlen(mess)), strlen(mess)) | ||||
|             || !TEST_int_eq(SSL_read(local, buf, sizeof(buf)), strlen(mess))) | ||||
|         goto end; | ||||
| 
 | ||||
|     testresult = 1; | ||||
| 
 | ||||
|  end: | ||||
|     SSL_free(serverssl); | ||||
|     SSL_free(clientssl); | ||||
|     SSL_CTX_free(sctx); | ||||
|     SSL_CTX_free(cctx); | ||||
|     BIO_free(bretry); | ||||
|     BIO_free(tmp); | ||||
| 
 | ||||
|     return testresult; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Test we can handle a KeyUpdate (update requested) message while | ||||
|  * local read data is pending(the msg header had been read 5 bytes). | ||||
|  * Test 0: Client sends KeyUpdate while Client is reading | ||||
|  * Test 1: Server sends KeyUpdate while Server is reading | ||||
|  */ | ||||
| static int test_key_update_local_in_read(int tst) | ||||
| { | ||||
|     SSL_CTX *cctx = NULL, *sctx = NULL; | ||||
|     SSL *clientssl = NULL, *serverssl = NULL; | ||||
|     int testresult = 0; | ||||
|     char lrbuf[515], pwbuf[515] = {0}, prbuf[20]; | ||||
|     static char *mess = "A test message"; | ||||
|     BIO *lbio = NULL, *pbio = NULL; | ||||
|     SSL *local = NULL, *peer = NULL; | ||||
| 
 | ||||
|     if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(), | ||||
|                                               TLS_client_method(), | ||||
|                                               TLS1_3_VERSION, | ||||
|                                               0, | ||||
|                                               &sctx, &cctx, cert, privkey)) | ||||
|             || !TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, | ||||
|                                              NULL, NULL)) | ||||
|             || !TEST_true(create_ssl_connection(serverssl, clientssl, | ||||
|                                                 SSL_ERROR_NONE))) | ||||
|         goto end; | ||||
| 
 | ||||
|     local = tst == 0 ? clientssl : serverssl; | ||||
|     peer = tst == 0 ? serverssl : clientssl; | ||||
| 
 | ||||
|     if (!TEST_int_eq(BIO_new_bio_pair(&lbio, 512, &pbio, 512), 1)) | ||||
|         goto end; | ||||
| 
 | ||||
|     SSL_set_bio(local, lbio, lbio); | ||||
|     SSL_set_bio(peer, pbio, pbio); | ||||
| 
 | ||||
|     /* write app data in peer will fail with SSL_ERROR_WANT_WRITE */ | ||||
|     if (!TEST_int_eq(SSL_write(peer, pwbuf, sizeof(pwbuf)), -1) | ||||
|         || !TEST_int_eq(SSL_get_error(peer, -1), SSL_ERROR_WANT_WRITE)) | ||||
|         goto end; | ||||
| 
 | ||||
|     /* read appdata in local will fail with SSL_ERROR_WANT_READ */ | ||||
|     if (!TEST_int_eq(SSL_read(local, lrbuf, sizeof(lrbuf)), -1) | ||||
|             || !TEST_int_eq(SSL_get_error(local, -1), SSL_ERROR_WANT_READ)) | ||||
|         goto end; | ||||
| 
 | ||||
|     /* SSL_do_handshake will send keyupdate msg */ | ||||
|     if (!TEST_true(SSL_key_update(local, SSL_KEY_UPDATE_REQUESTED)) | ||||
|             || !TEST_int_eq(SSL_do_handshake(local), 1)) | ||||
|         goto end; | ||||
| 
 | ||||
|     /*
 | ||||
|      * write data in peer previously that we will complete | ||||
|      * read data in local previously that we will complete | ||||
|      */ | ||||
|     if (!TEST_int_eq(SSL_write(peer, pwbuf, sizeof(pwbuf)), sizeof(pwbuf)) | ||||
|         || !TEST_int_eq(SSL_read(local, lrbuf, sizeof(lrbuf)), sizeof(lrbuf))) | ||||
|         goto end; | ||||
| 
 | ||||
|     /*
 | ||||
|      * write data in local | ||||
|      * read data in peer - we will read the key update | ||||
|      */ | ||||
|     if (!TEST_int_eq(SSL_write(local, mess, strlen(mess)), strlen(mess)) | ||||
|         || !TEST_int_eq(SSL_read(peer, prbuf, sizeof(prbuf)), strlen(mess))) | ||||
|         goto end; | ||||
| 
 | ||||
|   /* Write more peer data to ensure we send the keyupdate message back */ | ||||
|     if (!TEST_int_eq(SSL_write(peer, mess, strlen(mess)), strlen(mess)) | ||||
|             || !TEST_int_eq(SSL_read(local, lrbuf, sizeof(lrbuf)), strlen(mess))) | ||||
|         goto end; | ||||
| 
 | ||||
|     testresult = 1; | ||||
| 
 | ||||
|  end: | ||||
|     SSL_free(serverssl); | ||||
|     SSL_free(clientssl); | ||||
|     SSL_CTX_free(sctx); | ||||
|     SSL_CTX_free(cctx); | ||||
| 
 | ||||
|     return testresult; | ||||
| } | ||||
| #endif /* OSSL_NO_USABLE_TLS1_3 */ | ||||
| 
 | ||||
| static int test_ssl_clear(int idx) | ||||
|  | @ -9402,7 +9660,10 @@ int setup_tests(void) | |||
| #ifndef OSSL_NO_USABLE_TLS1_3 | ||||
|     ADD_ALL_TESTS(test_export_key_mat_early, 3); | ||||
|     ADD_TEST(test_key_update); | ||||
|     ADD_ALL_TESTS(test_key_update_in_write, 2); | ||||
|     ADD_ALL_TESTS(test_key_update_peer_in_write, 2); | ||||
|     ADD_ALL_TESTS(test_key_update_peer_in_read, 2); | ||||
|     ADD_ALL_TESTS(test_key_update_local_in_write, 2); | ||||
|     ADD_ALL_TESTS(test_key_update_local_in_read, 2); | ||||
| #endif | ||||
|     ADD_ALL_TESTS(test_ssl_clear, 2); | ||||
|     ADD_ALL_TESTS(test_max_fragment_len_ext, OSSL_NELEM(max_fragment_len_test)); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue