+#define USE_NULL 0
+#define USE_BIO_1 1
+#define USE_BIO_2 2
+
+#define TOTAL_SSL_SET_BIO_TESTS (3 * 3 * 3 * 3)
+
+static void setupbio(BIO **res, BIO *bio1, BIO *bio2, int type)
+{
+ switch (type) {
+ case USE_NULL:
+ *res = NULL;
+ break;
+ case USE_BIO_1:
+ *res = bio1;
+ break;
+ case USE_BIO_2:
+ *res = bio2;
+ break;
+ }
+}
+
+static int test_ssl_set_bio(int idx)
+{
+ SSL_CTX *ctx = SSL_CTX_new(TLS_method());
+ BIO *bio1 = NULL;
+ BIO *bio2 = NULL;
+ BIO *irbio = NULL, *iwbio = NULL, *nrbio = NULL, *nwbio = NULL;
+ SSL *ssl = NULL;
+ int initrbio, initwbio, newrbio, newwbio;
+ int testresult = 0;
+
+ if (ctx == NULL) {
+ printf("Failed to allocate SSL_CTX\n");
+ goto end;
+ }
+
+ ssl = SSL_new(ctx);
+ if (ssl == NULL) {
+ printf("Failed to allocate SSL object\n");
+ goto end;
+ }
+
+ initrbio = idx % 3;
+ idx /= 3;
+ initwbio = idx % 3;
+ idx /= 3;
+ newrbio = idx % 3;
+ idx /= 3;
+ newwbio = idx;
+ OPENSSL_assert(newwbio <= 2);
+
+ if (initrbio == USE_BIO_1 || initwbio == USE_BIO_1 || newrbio == USE_BIO_1
+ || newwbio == USE_BIO_1) {
+ bio1 = BIO_new(BIO_s_mem());
+ if (bio1 == NULL) {
+ printf("Failed to allocate bio1\n");
+ goto end;
+ }
+ }
+
+ if (initrbio == USE_BIO_2 || initwbio == USE_BIO_2 || newrbio == USE_BIO_2
+ || newwbio == USE_BIO_2) {
+ bio2 = BIO_new(BIO_s_mem());
+ if (bio2 == NULL) {
+ printf("Failed to allocate bio2\n");
+ goto end;
+ }
+ }
+
+ setupbio(&irbio, bio1, bio2, initrbio);
+ setupbio(&iwbio, bio1, bio2, initwbio);
+
+ /*
+ * We want to maintain our own refs to these BIO, so do an up ref for each
+ * BIO that will have ownersip transferred in the SSL_set_bio() call
+ */
+ if (irbio != NULL)
+ BIO_up_ref(irbio);
+ if (iwbio != NULL && iwbio != irbio)
+ BIO_up_ref(iwbio);
+
+ SSL_set_bio(ssl, irbio, iwbio);
+
+ setupbio(&nrbio, bio1, bio2, newrbio);
+ setupbio(&nwbio, bio1, bio2, newwbio);
+
+ /*
+ * We will (maybe) transfer ownership again so do more up refs.
+ * SSL_set_bio() has some really complicated ownership rules where BIOs have
+ * already been set!
+ */
+ if (nrbio != NULL && nrbio != irbio && (nwbio != iwbio || nrbio != nwbio))
+ BIO_up_ref(nrbio);
+ if (nwbio != NULL && nwbio != nrbio && (nwbio != iwbio || (nwbio == iwbio && irbio == iwbio)))
+ BIO_up_ref(nwbio);
+
+ SSL_set_bio(ssl, nrbio, nwbio);
+
+ testresult = 1;
+
+ end:
+ SSL_free(ssl);
+ BIO_free(bio1);
+ BIO_free(bio2);
+ /*
+ * This test is checking that the ref counting for SSL_set_bio is correct.
+ * If we get here and we did too many frees then we will fail in the above
+ * functions. If we haven't done enough then this will only be detected in
+ * a crypto-mdebug build
+ */
+ SSL_CTX_free(ctx);
+
+ return testresult;
+}
+
+typedef struct ssl_bio_test_fixture {
+ const char *test_case_name;
+ int pop_ssl;
+ enum { NO_BIO_CHANGE, CHANGE_RBIO, CHANGE_WBIO } change_bio;
+} SSL_BIO_TEST_FIXTURE;
+
+static SSL_BIO_TEST_FIXTURE ssl_bio_set_up(const char *const test_case_name)
+{
+ SSL_BIO_TEST_FIXTURE fixture;
+
+ fixture.test_case_name = test_case_name;
+ fixture.pop_ssl = 0;
+ fixture.change_bio = NO_BIO_CHANGE;
+
+ return fixture;
+}
+
+static void ssl_bio_tear_down(SSL_BIO_TEST_FIXTURE fixture)
+{
+}
+
+static int execute_test_ssl_bio(SSL_BIO_TEST_FIXTURE fix)
+{
+ BIO *sslbio = NULL, *membio1 = NULL, *membio2 = NULL;
+ SSL_CTX *ctx = SSL_CTX_new(TLS_method());
+ SSL *ssl = NULL;
+ int testresult = 0;
+
+ if (ctx == NULL) {
+ printf("Failed to allocate SSL_CTX\n");
+ return 0;
+ }
+
+ ssl = SSL_new(ctx);
+ if (ssl == NULL) {
+ printf("Failed to allocate SSL object\n");
+ goto end;
+ }
+
+ sslbio = BIO_new(BIO_f_ssl());
+ membio1 = BIO_new(BIO_s_mem());
+
+ if (sslbio == NULL || membio1 == NULL) {
+ printf("Malloc failure creating BIOs\n");
+ goto end;
+ }
+
+ BIO_set_ssl(sslbio, ssl, BIO_CLOSE);
+
+ /*
+ * If anything goes wrong here then we could leak memory, so this will
+ * be caught in a crypto-mdebug build
+ */
+ BIO_push(sslbio, membio1);
+
+ /* Verify chaning the rbio/wbio directly does not cause leaks */
+ if (fix.change_bio != NO_BIO_CHANGE) {
+ membio2 = BIO_new(BIO_s_mem());
+ if (membio2 == NULL) {
+ printf("Malloc failure creating membio2\n");
+ goto end;
+ }
+ if (fix.change_bio == CHANGE_RBIO)
+ SSL_set0_rbio(ssl, membio2);
+ else
+ SSL_set0_wbio(ssl, membio2);
+ }
+ ssl = NULL;
+
+ if (fix.pop_ssl)
+ BIO_pop(sslbio);
+ else
+ BIO_pop(membio1);
+
+ testresult = 1;
+ end:
+ BIO_free(membio1);
+ BIO_free(sslbio);
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+
+ return testresult;
+}
+
+static int test_ssl_bio_pop_next_bio(void)
+{
+ SETUP_TEST_FIXTURE(SSL_BIO_TEST_FIXTURE, ssl_bio_set_up);
+
+ EXECUTE_TEST(execute_test_ssl_bio, ssl_bio_tear_down);
+}
+
+static int test_ssl_bio_pop_ssl_bio(void)
+{
+ SETUP_TEST_FIXTURE(SSL_BIO_TEST_FIXTURE, ssl_bio_set_up);
+
+ fixture.pop_ssl = 1;
+
+ EXECUTE_TEST(execute_test_ssl_bio, ssl_bio_tear_down);
+}
+
+static int test_ssl_bio_change_rbio(void)
+{
+ SETUP_TEST_FIXTURE(SSL_BIO_TEST_FIXTURE, ssl_bio_set_up);
+
+ fixture.change_bio = CHANGE_RBIO;
+
+ EXECUTE_TEST(execute_test_ssl_bio, ssl_bio_tear_down);
+}
+
+static int test_ssl_bio_change_wbio(void)
+{
+ SETUP_TEST_FIXTURE(SSL_BIO_TEST_FIXTURE, ssl_bio_set_up);
+
+ fixture.change_bio = CHANGE_WBIO;
+
+ EXECUTE_TEST(execute_test_ssl_bio, ssl_bio_tear_down);
+}
+
+typedef struct {
+ /* The list of sig algs */
+ const int *list;
+ /* The length of the list */
+ size_t listlen;
+ /* A sigalgs list in string format */
+ const char *liststr;
+ /* Whether setting the list should succeed */
+ int valid;
+ /* Whether creating a connection with the list should succeed */
+ int connsuccess;
+} sigalgs_list;
+
+static const int validlist1[] = {NID_sha256, EVP_PKEY_RSA};
+static const int validlist2[] = {NID_sha256, EVP_PKEY_RSA, NID_sha512, EVP_PKEY_EC};
+static const int validlist3[] = {NID_sha512, EVP_PKEY_EC};
+static const int invalidlist1[] = {NID_undef, EVP_PKEY_RSA};
+static const int invalidlist2[] = {NID_sha256, NID_undef};
+static const int invalidlist3[] = {NID_sha256, EVP_PKEY_RSA, NID_sha256};
+static const int invalidlist4[] = {NID_sha256};
+static const sigalgs_list testsigalgs[] = {
+ {validlist1, OSSL_NELEM(validlist1), NULL, 1, 1},
+ {validlist2, OSSL_NELEM(validlist2), NULL, 1, 1},
+ {validlist3, OSSL_NELEM(validlist3), NULL, 1, 0},
+ {NULL, 0, "RSA+SHA256", 1, 1},
+ {NULL, 0, "RSA+SHA256:ECDSA+SHA512", 1, 1},
+ {NULL, 0, "ECDSA+SHA512", 1, 0},
+ {invalidlist1, OSSL_NELEM(invalidlist1), NULL, 0, 0},
+ {invalidlist2, OSSL_NELEM(invalidlist2), NULL, 0, 0},
+ {invalidlist3, OSSL_NELEM(invalidlist3), NULL, 0, 0},
+ {invalidlist4, OSSL_NELEM(invalidlist4), NULL, 0, 0},
+ {NULL, 0, "RSA", 0, 0},
+ {NULL, 0, "SHA256", 0, 0},
+ {NULL, 0, "RSA+SHA256:SHA256", 0, 0},
+ {NULL, 0, "Invalid", 0, 0}};
+
+static int test_set_sigalgs(int idx)
+{
+ SSL_CTX *cctx = NULL, *sctx = NULL;
+ SSL *clientssl = NULL, *serverssl = NULL;
+ int testresult = 0;
+ const sigalgs_list *curr;
+ int testctx;
+
+ /* Should never happen */
+ if ((size_t)idx >= OSSL_NELEM(testsigalgs) * 2)
+ return 0;
+
+ testctx = ((size_t)idx < OSSL_NELEM(testsigalgs));
+ curr = testctx ? &testsigalgs[idx]
+ : &testsigalgs[idx - OSSL_NELEM(testsigalgs)];
+
+ if (!create_ssl_ctx_pair(TLS_server_method(), TLS_client_method(), &sctx,
+ &cctx, cert, privkey)) {
+ printf("Unable to create SSL_CTX pair\n");
+ return 0;
+ }
+
+ /*
+ * TODO(TLS1.3): These APIs cannot set TLSv1.3 sig algs so we just test it
+ * for TLSv1.2 for now until we add a new API.
+ */
+ SSL_CTX_set_max_proto_version(cctx, TLS1_2_VERSION);
+
+ if (testctx) {
+ int ret;
+ if (curr->list != NULL)
+ ret = SSL_CTX_set1_sigalgs(cctx, curr->list, curr->listlen);
+ else
+ ret = SSL_CTX_set1_sigalgs_list(cctx, curr->liststr);
+
+ if (!ret) {
+ if (curr->valid)
+ printf("Unexpected failure setting sigalgs in SSL_CTX (%d)\n",
+ idx);
+ else
+ testresult = 1;
+ goto end;
+ }
+ if (!curr->valid) {
+ printf("Unexpected success setting sigalgs in SSL_CTX (%d)\n", idx);
+ goto end;
+ }
+ }
+
+ if (!create_ssl_objects(sctx, cctx, &serverssl, &clientssl, NULL, NULL)) {
+ printf("Unable to create SSL objects\n");
+ goto end;
+ }
+
+ if (!testctx) {
+ int ret;
+
+ if (curr->list != NULL)
+ ret = SSL_set1_sigalgs(clientssl, curr->list, curr->listlen);
+ else
+ ret = SSL_set1_sigalgs_list(clientssl, curr->liststr);
+ if (!ret) {
+ if (curr->valid)
+ printf("Unexpected failure setting sigalgs in SSL (%d)\n", idx);
+ else
+ testresult = 1;
+ goto end;
+ }
+ if (!curr->valid) {
+ printf("Unexpected success setting sigalgs in SSL (%d)\n", idx);
+ goto end;
+ }
+ }
+
+ if (curr->connsuccess != create_ssl_connection(serverssl, clientssl)) {
+ printf("Unexpected return value creating SSL connection (%d)\n", idx);
+ goto end;
+ }
+
+ testresult = 1;
+
+ end:
+ SSL_free(serverssl);
+ SSL_free(clientssl);
+ SSL_CTX_free(sctx);
+ SSL_CTX_free(cctx);
+
+ return testresult;
+}
+
+int test_main(int argc, char *argv[])