]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Add a test for a retry during the handshake
authorMatt Caswell <matt@openssl.org>
Wed, 12 Jul 2023 13:54:46 +0000 (14:54 +0100)
committerPauli <pauli@openssl.org>
Sun, 16 Jul 2023 23:39:46 +0000 (09:39 +1000)
Test various scenarios for a write retry occuring during a handshake.

Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/21434)

test/helpers/ssltestlib.c
test/helpers/ssltestlib.h
test/sslapitest.c

index 0c31058b65eaa702b8edef0eba04fa86150df0dd..0c3a076ea91fc0b2221361ead88684f7399d41ea 100644 (file)
@@ -36,6 +36,7 @@ static int tls_dump_puts(BIO *bp, const char *str);
 static BIO_METHOD *method_tls_dump = NULL;
 static BIO_METHOD *meth_mem = NULL;
 static BIO_METHOD *meth_always_retry = NULL;
+static int retry_err = -1;
 
 /* Note: Not thread safe! */
 const BIO_METHOD *bio_f_tls_dump_filter(void)
@@ -754,16 +755,21 @@ static int always_retry_free(BIO *bio)
     return 1;
 }
 
+void set_always_retry_err_val(int err)
+{
+    retry_err = err;
+}
+
 static int always_retry_read(BIO *bio, char *out, int outl)
 {
     BIO_set_retry_read(bio);
-    return -1;
+    return retry_err;
 }
 
 static int always_retry_write(BIO *bio, const char *in, int inl)
 {
     BIO_set_retry_write(bio);
-    return -1;
+    return retry_err;
 }
 
 static long always_retry_ctrl(BIO *bio, int cmd, long num, void *ptr)
@@ -789,13 +795,13 @@ static long always_retry_ctrl(BIO *bio, int cmd, long num, void *ptr)
 static int always_retry_gets(BIO *bio, char *buf, int size)
 {
     BIO_set_retry_read(bio);
-    return -1;
+    return retry_err;
 }
 
 static int always_retry_puts(BIO *bio, const char *str)
 {
     BIO_set_retry_write(bio);
-    return -1;
+    return retry_err;
 }
 
 int create_ssl_ctx_pair(OSSL_LIB_CTX *libctx, const SSL_METHOD *sm,
index 38cf161c868e705d5d06400031343a34b7935513..defcb35115958b72c9cdfeccacf627ce59767d03 100644 (file)
@@ -36,6 +36,7 @@ void bio_s_mempacket_test_free(void);
 
 const BIO_METHOD *bio_s_always_retry(void);
 void bio_s_always_retry_free(void);
+void set_always_retry_err_val(int err);
 
 /* Packet types - value 0 is reserved */
 #define INJECT_PACKET                   1
index 1a5d887531da8092a8b8bae00038dc673700e241..28ed079fd6fc1f8e1c111afe38fbd34b796adc58 100644 (file)
@@ -956,18 +956,13 @@ end:
 }
 #endif
 
-static int execute_test_large_message(const SSL_METHOD *smeth,
-                                      const SSL_METHOD *cmeth,
-                                      int min_version, int max_version,
-                                      int read_ahead)
+static int add_large_cert_chain(SSL_CTX *sctx)
 {
-    SSL_CTX *cctx = NULL, *sctx = NULL;
-    SSL *clientssl = NULL, *serverssl = NULL;
-    int testresult = 0;
-    int i;
     BIO *certbio = NULL;
     X509 *chaincert = NULL;
     int certlen;
+    int ret = 0;
+    int i;
 
     if (!TEST_ptr(certbio = BIO_new_file(cert, "r")))
         goto end;
@@ -980,6 +975,41 @@ static int execute_test_large_message(const SSL_METHOD *smeth,
     BIO_free(certbio);
     certbio = NULL;
 
+    /*
+     * We assume the supplied certificate is big enough so that if we add
+     * NUM_EXTRA_CERTS it will make the overall message large enough. The
+     * default buffer size is requested to be 16k, but due to the way BUF_MEM
+     * works, it ends up allocating a little over 21k (16 * 4/3). So, in this
+     * test we need to have a message larger than that.
+     */
+    certlen = i2d_X509(chaincert, NULL);
+    OPENSSL_assert(certlen * NUM_EXTRA_CERTS >
+                   (SSL3_RT_MAX_PLAIN_LENGTH * 4) / 3);
+    for (i = 0; i < NUM_EXTRA_CERTS; i++) {
+        if (!X509_up_ref(chaincert))
+            goto end;
+        if (!SSL_CTX_add_extra_chain_cert(sctx, chaincert)) {
+            X509_free(chaincert);
+            goto end;
+        }
+    }
+
+    ret = 1;
+ end:
+    BIO_free(certbio);
+    X509_free(chaincert);
+    return ret;
+}
+
+static int execute_test_large_message(const SSL_METHOD *smeth,
+                                      const SSL_METHOD *cmeth,
+                                      int min_version, int max_version,
+                                      int read_ahead)
+{
+    SSL_CTX *cctx = NULL, *sctx = NULL;
+    SSL *clientssl = NULL, *serverssl = NULL;
+    int testresult = 0;
+
     if (!TEST_true(create_ssl_ctx_pair(libctx, smeth, cmeth, min_version,
                                        max_version, &sctx, &cctx, cert,
                                        privkey)))
@@ -1006,24 +1036,8 @@ static int execute_test_large_message(const SSL_METHOD *smeth,
         SSL_CTX_set_read_ahead(cctx, 1);
     }
 
-    /*
-     * We assume the supplied certificate is big enough so that if we add
-     * NUM_EXTRA_CERTS it will make the overall message large enough. The
-     * default buffer size is requested to be 16k, but due to the way BUF_MEM
-     * works, it ends up allocating a little over 21k (16 * 4/3). So, in this
-     * test we need to have a message larger than that.
-     */
-    certlen = i2d_X509(chaincert, NULL);
-    OPENSSL_assert(certlen * NUM_EXTRA_CERTS >
-                   (SSL3_RT_MAX_PLAIN_LENGTH * 4) / 3);
-    for (i = 0; i < NUM_EXTRA_CERTS; i++) {
-        if (!X509_up_ref(chaincert))
-            goto end;
-        if (!SSL_CTX_add_extra_chain_cert(sctx, chaincert)) {
-            X509_free(chaincert);
-            goto end;
-        }
-    }
+    if (!add_large_cert_chain(sctx))
+        goto end;
 
     if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
                                       NULL, NULL))
@@ -1040,8 +1054,6 @@ static int execute_test_large_message(const SSL_METHOD *smeth,
 
     testresult = 1;
  end:
-    BIO_free(certbio);
-    X509_free(chaincert);
     SSL_free(serverssl);
     SSL_free(clientssl);
     SSL_CTX_free(sctx);
@@ -11064,6 +11076,102 @@ end:
     return testresult;
 }
 
+/*
+ * Force a write retry during handshaking. We test various combinations of
+ * scenarios. We test a large certificate message which will fill the buffering
+ * BIO used in the handshake. We try with client auth on and off. Finally we
+ * also try a BIO that indicates retry via a 0 return. BIO_write() is documented
+ * to indicate retry via -1 - but sometimes BIOs don't do that.
+ *
+ * Test 0: Standard certificate message
+ * Test 1: Large certificate message
+ * Test 2: Standard cert, verify peer
+ * Test 3: Large cert, verify peer
+ * Test 4: Standard cert, BIO returns 0 on retry
+ * Test 5: Large cert, BIO returns 0 on retry
+ * Test 6: Standard cert, verify peer, BIO returns 0 on retry
+ * Test 7: Large cert, verify peer, BIO returns 0 on retry
+ * Test 8-15: Repeat of above with TLSv1.2
+ */
+static int test_handshake_retry(int idx)
+{
+    SSL_CTX *cctx = NULL, *sctx = NULL;
+    SSL *clientssl = NULL, *serverssl = NULL;
+    int testresult = 0;
+    BIO *tmp = NULL, *bretry = BIO_new(bio_s_always_retry());
+    int maxversion = 0;
+
+    if (!TEST_ptr(bretry))
+        goto end;
+
+#ifndef OPENSSL_NO_TLS1_2
+    if ((idx & 8) == 8)
+        maxversion = TLS1_2_VERSION;
+#else
+    if ((idx & 8) == 8)
+        return TEST_skip("No TLSv1.2");
+#endif
+
+    if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(),
+                                       TLS_client_method(), 0, maxversion,
+                                       &sctx, &cctx, cert, privkey)))
+        goto end;
+
+    /*
+     * Add a large amount of data to fill the buffering BIO used by the SSL
+     * object
+     */
+    if ((idx & 1) == 1 && !add_large_cert_chain(sctx))
+        goto end;
+
+    /*
+     * We don't actually configure a client cert, but neither do we fail if one
+     * isn't present.
+     */
+    if ((idx & 2) == 2)
+        SSL_CTX_set_verify(sctx, SSL_VERIFY_PEER, NULL);
+
+    if ((idx & 4) == 4)
+        set_always_retry_err_val(0);
+
+    if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
+                                      &clientssl, NULL, NULL)))
+        goto end;
+
+    tmp = SSL_get_wbio(serverssl);
+    if (!TEST_ptr(tmp) || !TEST_true(BIO_up_ref(tmp))) {
+        tmp = NULL;
+        goto end;
+    }
+    SSL_set0_wbio(serverssl, bretry);
+    bretry = NULL;
+
+    if (!TEST_int_eq(SSL_connect(clientssl), -1))
+        goto end;
+
+    if (!TEST_int_eq(SSL_accept(serverssl), -1)
+            || !TEST_int_eq(SSL_get_error(serverssl, -1), SSL_ERROR_WANT_WRITE))
+        goto end;
+
+    /* Restore a BIO that will let the write succeed */
+    SSL_set0_wbio(serverssl, tmp);
+    tmp = NULL;
+
+    if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)))
+        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);
+    set_always_retry_err_val(-1);
+    return testresult;
+}
+
 OPT_TEST_DECLARE_USAGE("certfile privkeyfile srpvfile tmpfile provider config dhfile\n")
 
 int setup_tests(void)
@@ -11369,6 +11477,7 @@ int setup_tests(void)
 #endif
     ADD_ALL_TESTS(test_version, 6);
     ADD_TEST(test_rstate_string);
+    ADD_ALL_TESTS(test_handshake_retry, 16);
     return 1;
 
  err: