Skip to content

Commit

Permalink
add back SSL_use_certificate_chain_file
Browse files Browse the repository at this point in the history
  • Loading branch information
samuel40791765 committed Nov 17, 2023
1 parent b15b9d3 commit 3cc25c4
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 34 deletions.
6 changes: 6 additions & 0 deletions include/openssl/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1331,6 +1331,12 @@ OPENSSL_EXPORT int SSL_use_PrivateKey_file(SSL *ssl, const char *file,
OPENSSL_EXPORT int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx,
const char *file);

// SSL_CTX_use_certificate_chain_file configures certificates for |ssl|. It
// reads the contents of |file| as a PEM-encoded leaf certificate followed
// optionally by the certificate chain to send to the peer. It returns one on
// success and zero on failure.
OPENSSL_EXPORT int SSL_use_certificate_chain_file(SSL *ssl, const char *file);

// SSL_CTX_set_default_passwd_cb sets the password callback for PEM-based
// convenience functions called on |ctx|.
OPENSSL_EXPORT void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx,
Expand Down
2 changes: 1 addition & 1 deletion ssl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ if(BUILD_TESTING)
span_test.cc
ssl_test.cc
ssl_c_test.c
)
)

target_link_libraries(${SSL_TEST_EXEC} test_support_lib boringssl_gtest_main ssl crypto)

Expand Down
78 changes: 51 additions & 27 deletions ssl/ssl_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -475,32 +475,36 @@ int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) {
// Read a file that contains our certificate in "PEM" format, possibly followed
// by a sequence of CA certificates that should be sent to the peer in the
// Certificate message.
int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file) {
BIO *in;
static int use_certificate_chain_file(SSL_CTX *ctx, SSL *ssl, const char *file) {
int ret = 0;
X509 *x = NULL;

// |parent_ctx| is |ctx| unless |ssl| is defined.
SSL_CTX *parent_ctx = ctx;
if (ssl != nullptr) {
parent_ctx = ssl->ctx.get();
}
ERR_clear_error(); // clear error stack for SSL_CTX_use_certificate()

in = BIO_new(BIO_s_file());
if (in == NULL) {
bssl::UniquePtr<BIO> in(BIO_new(BIO_s_file()));
if (in == nullptr) {
OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
goto end;
}

if (BIO_read_filename(in, file) <= 0) {
if (BIO_read_filename(in.get(), file) <= 0) {
OPENSSL_PUT_ERROR(SSL, ERR_R_SYS_LIB);
goto end;
}

x = PEM_read_bio_X509_AUX(in, NULL, ctx->default_passwd_callback,
ctx->default_passwd_callback_userdata);
if (x == NULL) {
bssl::UniquePtr<X509> x509(PEM_read_bio_X509_AUX(
in.get(), nullptr, parent_ctx->default_passwd_callback,
parent_ctx->default_passwd_callback_userdata));
if (x509 == nullptr) {
OPENSSL_PUT_ERROR(SSL, ERR_R_PEM_LIB);
goto end;
}

ret = SSL_CTX_use_certificate(ctx, x);
if (ctx != nullptr) {
ret = SSL_CTX_use_certificate(ctx, x509.get());
} else {
ret = SSL_use_certificate(ssl, x509.get());
}

if (ERR_peek_error() != 0) {
ret = 0; // Key/certificate mismatch doesn't imply ret==0 ...
Expand All @@ -510,23 +514,31 @@ int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file) {
// If we could set up our certificate, now proceed to the CA
// certificates.
X509 *ca;
int r;
int temp_ret;
uint32_t err;

SSL_CTX_clear_chain_certs(ctx);
if (ctx != nullptr) {
SSL_CTX_clear_chain_certs(ctx);
} else {
SSL_clear_chain_certs(ssl);
}

while ((ca = PEM_read_bio_X509(
in.get(), nullptr, parent_ctx->default_passwd_callback,
parent_ctx->default_passwd_callback_userdata)) != nullptr) {
if (ctx != nullptr) {
temp_ret = SSL_CTX_add0_chain_cert(ctx, ca);
} else {
temp_ret = SSL_add0_chain_cert(ssl, ca);
}

while ((ca = PEM_read_bio_X509(in, NULL, ctx->default_passwd_callback,
ctx->default_passwd_callback_userdata)) !=
NULL) {
r = SSL_CTX_add0_chain_cert(ctx, ca);
if (!r) {
if (temp_ret == 0) {
X509_free(ca);
ret = 0;
goto end;
}
// Note that we must not free r if it was successfully added to the chain
// Note that we must not free ca if it was successfully added to the chain
// (while we must free the main certificate, since its reference count is
// increased by SSL_CTX_use_certificate).
// increased by |SSL_CTX_use_certificate|).
}

// When the while loop ends, it's usually just EOF.
Expand All @@ -539,12 +551,24 @@ int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file) {
}
}

end:
X509_free(x);
BIO_free(in);
return ret;
}


int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file) {
if (ctx == nullptr || file == nullptr) {
return 0;
}
return use_certificate_chain_file(ctx, nullptr, file);
}

int SSL_use_certificate_chain_file(SSL *ssl, const char *file) {
if (ssl == nullptr || file == nullptr) {
return 0;
}
return use_certificate_chain_file(nullptr, ssl, file);
}

void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb) {
ctx->default_passwd_callback = cb;
}
Expand Down
59 changes: 53 additions & 6 deletions ssl/ssl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11772,15 +11772,19 @@ TEST_P(PerformHybridHandshakeTest, PerformHybridHandshake) {
bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
ASSERT_TRUE(client_ctx);
ASSERT_TRUE(SSL_CTX_set1_curves_list(client_ctx.get(), t.client_rule));
ASSERT_TRUE(SSL_CTX_set_max_proto_version(client_ctx.get(), t.client_version));
ASSERT_TRUE(
SSL_CTX_set_max_proto_version(client_ctx.get(), t.client_version));

bssl::UniquePtr<SSL_CTX> server_ctx = CreateContextWithTestCertificate(TLS_method());
bssl::UniquePtr<SSL_CTX> server_ctx =
CreateContextWithTestCertificate(TLS_method());
ASSERT_TRUE(server_ctx);
ASSERT_TRUE(SSL_CTX_set1_curves_list(server_ctx.get(), t.server_rule));
ASSERT_TRUE(SSL_CTX_set_max_proto_version(server_ctx.get(), t.server_version));
ASSERT_TRUE(
SSL_CTX_set_max_proto_version(server_ctx.get(), t.server_version));

bssl::UniquePtr<SSL> client, server;
ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(), server_ctx.get()));
ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(),
server_ctx.get()));

if (t.expected_group != 0) {
// In this case, assert that the handshake completes as expected.
Expand All @@ -11790,13 +11794,15 @@ TEST_P(PerformHybridHandshakeTest, PerformHybridHandshake) {
ASSERT_TRUE(client_session);
EXPECT_EQ(t.expected_group, client_session->group_id);
EXPECT_EQ(t.is_hrr_expected, SSL_used_hello_retry_request(client.get()));
EXPECT_EQ(std::min(t.client_version, t.server_version), client_session->ssl_version);
EXPECT_EQ(std::min(t.client_version, t.server_version),
client_session->ssl_version);

SSL_SESSION *server_session = SSL_get_session(server.get());
ASSERT_TRUE(server_session);
EXPECT_EQ(t.expected_group, server_session->group_id);
EXPECT_EQ(t.is_hrr_expected, SSL_used_hello_retry_request(server.get()));
EXPECT_EQ(std::min(t.client_version, t.server_version), server_session->ssl_version);
EXPECT_EQ(std::min(t.client_version, t.server_version),
server_session->ssl_version);
} else {
// In this case, we expect the handshake to fail because client and
// server configurations are not compatible.
Expand All @@ -11810,5 +11816,46 @@ TEST_P(PerformHybridHandshakeTest, PerformHybridHandshake) {
}
}

// This test opens and writes a temporary pem file to test certificate file
// loading. It is disabled by default since we may not always have permissions
// to create files on some platforms or the generated files may encounter race
// conditions when running multiple ssl_test on different threads. This is
// reenabled via all_tests.go where suitable.
TEST(SSLTest, DISABLED_SSLFileTests) {
struct fclose_deleter {
void operator()(FILE *f) const { fclose(f); }
};

using ScopedFILE = std::unique_ptr<FILE, fclose_deleter>;
ScopedFILE rsa_pem(fopen("rsa.pem", "w"));
ScopedFILE ecdsa_pem(fopen("ecdsa.pem", "w"));
ASSERT_TRUE(rsa_pem);
ASSERT_TRUE(ecdsa_pem);

bssl::UniquePtr<X509> rsa_leaf = GetChainTestCertificate();
bssl::UniquePtr<X509> rsa_intermediate = GetChainTestIntermediate();
bssl::UniquePtr<X509> ecdsa_leaf = GetECDSATestCertificate();
ASSERT_TRUE(PEM_write_X509(rsa_pem.get(), rsa_leaf.get()));
ASSERT_TRUE(PEM_write_X509(rsa_pem.get(), rsa_intermediate.get()));
ASSERT_TRUE(PEM_write_X509(ecdsa_pem.get(), ecdsa_leaf.get()));
rsa_pem.reset();
ecdsa_pem.reset();

bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
ASSERT_TRUE(ctx);
// Load a certificate into |ctx| and verify that |ssl| inherits it.
EXPECT_TRUE(SSL_CTX_use_certificate_chain_file(ctx.get(), "rsa.pem"));
bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get()));
ASSERT_TRUE(ssl);
EXPECT_EQ(X509_cmp(SSL_get_certificate(ssl.get()), rsa_leaf.get()), 0);

// Load a new cert into |ssl| and verify that it's correctly loaded.
EXPECT_TRUE(SSL_use_certificate_chain_file(ssl.get(), "ecdsa.pem"));
EXPECT_EQ(X509_cmp(SSL_get_certificate(ssl.get()), ecdsa_leaf.get()), 0);

ASSERT_EQ(remove("rsa.pem"), 0);
ASSERT_EQ(remove("ecdsa.pem"), 0);
}

} // namespace
BSSL_NAMESPACE_END
4 changes: 4 additions & 0 deletions util/all_tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@
"cmd": ["ssl/ssl_test"],
"shard": true
},
{
"cmd": ["ssl/ssl_test", "--gtest_also_run_disabled_tests", "--gtest_filter=SSLTest.DISABLED_SSLFileTests"],
"skip_sde": true
},
{
"cmd": ["ssl/integration_test"]
},
Expand Down

0 comments on commit 3cc25c4

Please sign in to comment.