Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add back SSL_use_certificate_chain_file #1312

Merged
merged 2 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
)
)
samuel40791765 marked this conversation as resolved.
Show resolved Hide resolved

target_link_libraries(${SSL_TEST_EXEC} test_support_lib boringssl_gtest_main ssl crypto)

Expand Down
84 changes: 56 additions & 28 deletions ssl/ssl_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -475,32 +475,40 @@ 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;
int ret = 0;
X509 *x = NULL;
static int use_certificate_chain_file(SSL_CTX *ctx, SSL *ssl, const char *file) {
if(ctx == nullptr && ssl == nullptr) {
return 0;
}

int ret = 0;
// |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;
}
WillChilds-Klein marked this conversation as resolved.
Show resolved Hide resolved

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 +518,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;
samuel40791765 marked this conversation as resolved.
Show resolved Hide resolved
}
// Note that we must not free r 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).
// 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|).
}

// When the while loop ends, it's usually just EOF.
Expand All @@ -539,12 +555,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_tests on different threads. This is
// reenabled via all_tests.go where suitable.
WillChilds-Klein marked this conversation as resolved.
Show resolved Hide resolved
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
Loading