Test SSL_add_client_CA. That function actually got a little complicated after the CRYPTO_BUFFER work. Change-Id: Ib679a9f2bcc2c974fe059af49805b8200e77bd03 Reviewed-on: https://boringssl-review.googlesource.com/19764 Commit-Queue: David Benjamin <davidben@google.com> Commit-Queue: Steven Valdez <svaldez@google.com> Reviewed-by: Steven Valdez <svaldez@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index 5c04797..ddac86a 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc
@@ -1104,149 +1104,6 @@ return true; } -// Test that |SSL_get_client_CA_list| echoes back the configured parameter even -// before configuring as a server. -TEST(SSLTest, ClientCAList) { - bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); - ASSERT_TRUE(ctx); - bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get())); - ASSERT_TRUE(ssl); - - bssl::UniquePtr<X509_NAME> name(X509_NAME_new()); - ASSERT_TRUE(name); - - bssl::UniquePtr<X509_NAME> name_dup(X509_NAME_dup(name.get())); - ASSERT_TRUE(name_dup); - - bssl::UniquePtr<STACK_OF(X509_NAME)> stack(sk_X509_NAME_new_null()); - ASSERT_TRUE(stack); - - ASSERT_TRUE(sk_X509_NAME_push(stack.get(), name_dup.get())); - name_dup.release(); - - // |SSL_set_client_CA_list| takes ownership. - SSL_set_client_CA_list(ssl.get(), stack.release()); - - STACK_OF(X509_NAME) *result = SSL_get_client_CA_list(ssl.get()); - ASSERT_TRUE(result); - ASSERT_EQ(1u, sk_X509_NAME_num(result)); - EXPECT_EQ(0, X509_NAME_cmp(sk_X509_NAME_value(result, 0), name.get())); -} - -static void AppendSession(SSL_SESSION *session, void *arg) { - std::vector<SSL_SESSION*> *out = - reinterpret_cast<std::vector<SSL_SESSION*>*>(arg); - out->push_back(session); -} - -// CacheEquals returns true if |ctx|'s session cache consists of |expected|, in -// order. -static bool CacheEquals(SSL_CTX *ctx, - const std::vector<SSL_SESSION*> &expected) { - // Check the linked list. - SSL_SESSION *ptr = ctx->session_cache_head; - for (SSL_SESSION *session : expected) { - if (ptr != session) { - return false; - } - // TODO(davidben): This is an absurd way to denote the end of the list. - if (ptr->next == - reinterpret_cast<SSL_SESSION *>(&ctx->session_cache_tail)) { - ptr = nullptr; - } else { - ptr = ptr->next; - } - } - if (ptr != nullptr) { - return false; - } - - // Check the hash table. - std::vector<SSL_SESSION*> actual, expected_copy; - lh_SSL_SESSION_doall_arg(SSL_CTX_sessions(ctx), AppendSession, &actual); - expected_copy = expected; - - std::sort(actual.begin(), actual.end()); - std::sort(expected_copy.begin(), expected_copy.end()); - - return actual == expected_copy; -} - -static bssl::UniquePtr<SSL_SESSION> CreateTestSession(uint32_t number) { - bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_method())); - if (!ssl_ctx) { - return nullptr; - } - bssl::UniquePtr<SSL_SESSION> ret(SSL_SESSION_new(ssl_ctx.get())); - if (!ret) { - return nullptr; - } - - ret->session_id_length = SSL3_SSL_SESSION_ID_LENGTH; - OPENSSL_memset(ret->session_id, 0, ret->session_id_length); - OPENSSL_memcpy(ret->session_id, &number, sizeof(number)); - return ret; -} - -// Test that the internal session cache behaves as expected. -TEST(SSLTest, InternalSessionCache) { - bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); - ASSERT_TRUE(ctx); - - // Prepare 10 test sessions. - std::vector<bssl::UniquePtr<SSL_SESSION>> sessions; - for (int i = 0; i < 10; i++) { - bssl::UniquePtr<SSL_SESSION> session = CreateTestSession(i); - ASSERT_TRUE(session); - sessions.push_back(std::move(session)); - } - - SSL_CTX_sess_set_cache_size(ctx.get(), 5); - - // Insert all the test sessions. - for (const auto &session : sessions) { - ASSERT_TRUE(SSL_CTX_add_session(ctx.get(), session.get())); - } - - // Only the last five should be in the list. - ASSERT_TRUE(CacheEquals( - ctx.get(), {sessions[9].get(), sessions[8].get(), sessions[7].get(), - sessions[6].get(), sessions[5].get()})); - - // Inserting an element already in the cache should fail and leave the cache - // unchanged. - ASSERT_FALSE(SSL_CTX_add_session(ctx.get(), sessions[7].get())); - ASSERT_TRUE(CacheEquals( - ctx.get(), {sessions[9].get(), sessions[8].get(), sessions[7].get(), - sessions[6].get(), sessions[5].get()})); - - // Although collisions should be impossible (256-bit session IDs), the cache - // must handle them gracefully. - bssl::UniquePtr<SSL_SESSION> collision(CreateTestSession(7)); - ASSERT_TRUE(collision); - ASSERT_TRUE(SSL_CTX_add_session(ctx.get(), collision.get())); - ASSERT_TRUE(CacheEquals( - ctx.get(), {collision.get(), sessions[9].get(), sessions[8].get(), - sessions[6].get(), sessions[5].get()})); - - // Removing sessions behaves correctly. - ASSERT_TRUE(SSL_CTX_remove_session(ctx.get(), sessions[6].get())); - ASSERT_TRUE(CacheEquals(ctx.get(), {collision.get(), sessions[9].get(), - sessions[8].get(), sessions[5].get()})); - - // Removing sessions requires an exact match. - ASSERT_FALSE(SSL_CTX_remove_session(ctx.get(), sessions[0].get())); - ASSERT_FALSE(SSL_CTX_remove_session(ctx.get(), sessions[7].get())); - - // The cache remains unchanged. - ASSERT_TRUE(CacheEquals(ctx.get(), {collision.get(), sessions[9].get(), - sessions[8].get(), sessions[5].get()})); -} - -static uint16_t EpochFromSequence(uint64_t seq) { - return static_cast<uint16_t>(seq >> 48); -} - static bssl::UniquePtr<X509> GetTestCertificate() { static const char kCertPEM[] = "-----BEGIN CERTIFICATE-----\n" @@ -1437,6 +1294,180 @@ PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); } +// Test that |SSL_get_client_CA_list| echoes back the configured parameter even +// before configuring as a server. +TEST(SSLTest, ClientCAList) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(ctx); + bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get())); + ASSERT_TRUE(ssl); + + bssl::UniquePtr<X509_NAME> name(X509_NAME_new()); + ASSERT_TRUE(name); + + bssl::UniquePtr<X509_NAME> name_dup(X509_NAME_dup(name.get())); + ASSERT_TRUE(name_dup); + + bssl::UniquePtr<STACK_OF(X509_NAME)> stack(sk_X509_NAME_new_null()); + ASSERT_TRUE(stack); + + ASSERT_TRUE(sk_X509_NAME_push(stack.get(), name_dup.get())); + name_dup.release(); + + // |SSL_set_client_CA_list| takes ownership. + SSL_set_client_CA_list(ssl.get(), stack.release()); + + STACK_OF(X509_NAME) *result = SSL_get_client_CA_list(ssl.get()); + ASSERT_TRUE(result); + ASSERT_EQ(1u, sk_X509_NAME_num(result)); + EXPECT_EQ(0, X509_NAME_cmp(sk_X509_NAME_value(result, 0), name.get())); +} + +TEST(SSLTest, AddClientCA) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(ctx); + bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get())); + ASSERT_TRUE(ssl); + + bssl::UniquePtr<X509> cert1 = GetTestCertificate(); + bssl::UniquePtr<X509> cert2 = GetChainTestCertificate(); + ASSERT_TRUE(cert1 && cert2); + X509_NAME *name1 = X509_get_subject_name(cert1.get()); + X509_NAME *name2 = X509_get_subject_name(cert2.get()); + + EXPECT_EQ(0u, sk_X509_NAME_num(SSL_get_client_CA_list(ssl.get()))); + + ASSERT_TRUE(SSL_add_client_CA(ssl.get(), cert1.get())); + ASSERT_TRUE(SSL_add_client_CA(ssl.get(), cert2.get())); + + STACK_OF(X509_NAME) *list = SSL_get_client_CA_list(ssl.get()); + ASSERT_EQ(2u, sk_X509_NAME_num(list)); + EXPECT_EQ(0, X509_NAME_cmp(sk_X509_NAME_value(list, 0), name1)); + EXPECT_EQ(0, X509_NAME_cmp(sk_X509_NAME_value(list, 1), name2)); + + ASSERT_TRUE(SSL_add_client_CA(ssl.get(), cert1.get())); + + list = SSL_get_client_CA_list(ssl.get()); + ASSERT_EQ(3u, sk_X509_NAME_num(list)); + EXPECT_EQ(0, X509_NAME_cmp(sk_X509_NAME_value(list, 0), name1)); + EXPECT_EQ(0, X509_NAME_cmp(sk_X509_NAME_value(list, 1), name2)); + EXPECT_EQ(0, X509_NAME_cmp(sk_X509_NAME_value(list, 2), name1)); +} + +static void AppendSession(SSL_SESSION *session, void *arg) { + std::vector<SSL_SESSION*> *out = + reinterpret_cast<std::vector<SSL_SESSION*>*>(arg); + out->push_back(session); +} + +// CacheEquals returns true if |ctx|'s session cache consists of |expected|, in +// order. +static bool CacheEquals(SSL_CTX *ctx, + const std::vector<SSL_SESSION*> &expected) { + // Check the linked list. + SSL_SESSION *ptr = ctx->session_cache_head; + for (SSL_SESSION *session : expected) { + if (ptr != session) { + return false; + } + // TODO(davidben): This is an absurd way to denote the end of the list. + if (ptr->next == + reinterpret_cast<SSL_SESSION *>(&ctx->session_cache_tail)) { + ptr = nullptr; + } else { + ptr = ptr->next; + } + } + if (ptr != nullptr) { + return false; + } + + // Check the hash table. + std::vector<SSL_SESSION*> actual, expected_copy; + lh_SSL_SESSION_doall_arg(SSL_CTX_sessions(ctx), AppendSession, &actual); + expected_copy = expected; + + std::sort(actual.begin(), actual.end()); + std::sort(expected_copy.begin(), expected_copy.end()); + + return actual == expected_copy; +} + +static bssl::UniquePtr<SSL_SESSION> CreateTestSession(uint32_t number) { + bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_method())); + if (!ssl_ctx) { + return nullptr; + } + bssl::UniquePtr<SSL_SESSION> ret(SSL_SESSION_new(ssl_ctx.get())); + if (!ret) { + return nullptr; + } + + ret->session_id_length = SSL3_SSL_SESSION_ID_LENGTH; + OPENSSL_memset(ret->session_id, 0, ret->session_id_length); + OPENSSL_memcpy(ret->session_id, &number, sizeof(number)); + return ret; +} + +// Test that the internal session cache behaves as expected. +TEST(SSLTest, InternalSessionCache) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(ctx); + + // Prepare 10 test sessions. + std::vector<bssl::UniquePtr<SSL_SESSION>> sessions; + for (int i = 0; i < 10; i++) { + bssl::UniquePtr<SSL_SESSION> session = CreateTestSession(i); + ASSERT_TRUE(session); + sessions.push_back(std::move(session)); + } + + SSL_CTX_sess_set_cache_size(ctx.get(), 5); + + // Insert all the test sessions. + for (const auto &session : sessions) { + ASSERT_TRUE(SSL_CTX_add_session(ctx.get(), session.get())); + } + + // Only the last five should be in the list. + ASSERT_TRUE(CacheEquals( + ctx.get(), {sessions[9].get(), sessions[8].get(), sessions[7].get(), + sessions[6].get(), sessions[5].get()})); + + // Inserting an element already in the cache should fail and leave the cache + // unchanged. + ASSERT_FALSE(SSL_CTX_add_session(ctx.get(), sessions[7].get())); + ASSERT_TRUE(CacheEquals( + ctx.get(), {sessions[9].get(), sessions[8].get(), sessions[7].get(), + sessions[6].get(), sessions[5].get()})); + + // Although collisions should be impossible (256-bit session IDs), the cache + // must handle them gracefully. + bssl::UniquePtr<SSL_SESSION> collision(CreateTestSession(7)); + ASSERT_TRUE(collision); + ASSERT_TRUE(SSL_CTX_add_session(ctx.get(), collision.get())); + ASSERT_TRUE(CacheEquals( + ctx.get(), {collision.get(), sessions[9].get(), sessions[8].get(), + sessions[6].get(), sessions[5].get()})); + + // Removing sessions behaves correctly. + ASSERT_TRUE(SSL_CTX_remove_session(ctx.get(), sessions[6].get())); + ASSERT_TRUE(CacheEquals(ctx.get(), {collision.get(), sessions[9].get(), + sessions[8].get(), sessions[5].get()})); + + // Removing sessions requires an exact match. + ASSERT_FALSE(SSL_CTX_remove_session(ctx.get(), sessions[0].get())); + ASSERT_FALSE(SSL_CTX_remove_session(ctx.get(), sessions[7].get())); + + // The cache remains unchanged. + ASSERT_TRUE(CacheEquals(ctx.get(), {collision.get(), sessions[9].get(), + sessions[8].get(), sessions[5].get()})); +} + +static uint16_t EpochFromSequence(uint64_t seq) { + return static_cast<uint16_t>(seq >> 48); +} + static const uint8_t kTestName[] = { 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08,