Extend the testing of resetting/clearing an SSL connection
authorMatt Caswell <matt@openssl.org>
Tue, 16 Jan 2024 13:53:30 +0000 (13:53 +0000)
committerMatt Caswell <matt@openssl.org>
Wed, 31 Jan 2024 10:10:55 +0000 (10:10 +0000)
SSL_clear() explicitly clears an SSL object to enable it to be reused.
You can have a similar effect by calling SSL_set_accept_state() or
SSL_set_connect_state(). We extend the testing of SSL_clear() to use these
other methods. We also ensure we test the case where we have unread
bufferred data that needs to be cleared.

Reviewed-by: Hugo Landau <hlandau@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23256)

test/sslapitest.c

index b8c474d524ea9f93a47bfee8a97b43a91b2ce9c9..6ef64740db8f602eec8ee05e7ceff7b2e31e0cae 100644 (file)
@@ -7035,22 +7035,45 @@ static int test_key_update_local_in_read(int tst)
 }
 #endif /* OSSL_NO_USABLE_TLS1_3 */
 
+/*
+ * Test clearing a connection via SSL_clear(), or resetting it via
+ * SSL_set_connect_state()/SSL_set_accept_state()
+ * Test 0: SSL_set_connect_state, TLSv1.3
+ * Test 1: SSL_set_connect_state, TLSv1.2
+ * Test 2: SSL_set_accept_state, TLSv1.3
+ * Test 3: SSL_set_accept_state, TLSv1.2
+ * Test 4: SSL_clear (client), TLSv1.3
+ * Test 5: SSL_clear (client), TLSv1.2
+ * Test 6: SSL_clear (server), TLSv1.3
+ * Test 7: SSL_clear (server), TLSv1.2
+ */
 static int test_ssl_clear(int idx)
 {
     SSL_CTX *cctx = NULL, *sctx = NULL;
     SSL *clientssl = NULL, *serverssl = NULL;
+    SSL *writer, *reader;
     int testresult = 0;
+    int tls12test, servertest, cleartest;
+    size_t written, readbytes;
+    const char *msg = "Hello World";
+    unsigned char buf[5];
+
+    tls12test = idx & 1;
+    idx >>= 1;
+    servertest = idx & 1;
+    idx >>= 1;
+    cleartest = idx & 1;
 
 #ifdef OPENSSL_NO_TLS1_2
-    if (idx == 1)
-        return 1;
+    if (tls12test == 1)
+        return TEST_skip("No TLSv1.2 in this build");
 #endif
 
     /* Create an initial connection */
     if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(),
                                        TLS_client_method(), TLS1_VERSION, 0,
                                        &sctx, &cctx, cert, privkey))
-            || (idx == 1
+            || (tls12test
                 && !TEST_true(SSL_CTX_set_max_proto_version(cctx,
                                                             TLS1_2_VERSION)))
             || !TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
@@ -7059,20 +7082,68 @@ static int test_ssl_clear(int idx)
                                                 SSL_ERROR_NONE)))
         goto end;
 
+    if (servertest) {
+        writer = clientssl;
+        reader = serverssl;
+    } else {
+        writer = serverssl;
+        reader = clientssl;
+    }
+
+    /* Write some data */
+    if (!TEST_true(SSL_write_ex(writer, msg, strlen(msg), &written))
+            || written != strlen(msg))
+        goto end;
+
+    /*
+     * Read a partial record. The remaining buffered data should be cleared by
+     * the subsequent clear/reset
+     */
+    if (!TEST_true(SSL_read_ex(reader, buf, sizeof(buf), &readbytes))
+            || readbytes != sizeof(buf))
+        goto end;
+
     SSL_shutdown(clientssl);
     SSL_shutdown(serverssl);
-    SSL_free(serverssl);
-    serverssl = NULL;
 
-    /* Clear clientssl - we're going to reuse the object */
-    if (!TEST_true(SSL_clear(clientssl)))
-        goto end;
+    /* Reset/clear one SSL object in order to reuse it. We free the other one */
+    if (servertest) {
+        if (cleartest) {
+            if (!TEST_true(SSL_clear(serverssl)))
+                goto end;
+        } else {
+            SSL_set_accept_state(serverssl);
+        }
+        /*
+         * A peculiarity of SSL_clear() is that it does not clear the session.
+         * This is intended behaviour so that a client can create a new
+         * connection and reuse the session. But this doesn't make much sense
+         * on the server side - and causes incorrect behaviour due to the
+         * handshake failing (even though the documentation does say SSL_clear()
+         * is supposed to work on the server side). We clear the session
+         * explicitly - although note that the documentation for
+         * SSL_set_session() says that its only useful for clients!
+         */
+        if (!TEST_true(SSL_set_session(serverssl, NULL)))
+            goto end;
+        SSL_free(clientssl);
+        clientssl = NULL;
+    } else {
+        if (cleartest) {
+            if (!TEST_true(SSL_clear(clientssl)))
+                goto end;
+        } else {
+            SSL_set_connect_state(clientssl);
+        }
+        SSL_free(serverssl);
+        serverssl = NULL;
+    }
 
     if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
                                              NULL, NULL))
             || !TEST_true(create_ssl_connection(serverssl, clientssl,
                                                 SSL_ERROR_NONE))
-            || !TEST_true(SSL_session_reused(clientssl)))
+            || !TEST_true(servertest || SSL_session_reused(clientssl)))
         goto end;
 
     SSL_shutdown(clientssl);
@@ -11637,7 +11708,7 @@ int setup_tests(void)
     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_ssl_clear, 8);
     ADD_ALL_TESTS(test_max_fragment_len_ext, OSSL_NELEM(max_fragment_len_test));
 #if !defined(OPENSSL_NO_SRP) && !defined(OPENSSL_NO_TLS1_2)
     ADD_ALL_TESTS(test_srp, 6);