Reland: Use CRYPTO_BUFFER APIs instead of X509 when building with BoringSSL.

Using CRYPTO_BUFFERs instead of legacy X509 objects offers memory and
security gains, and will provide binary size improvements as well once
the default list of built-in certificates can be removed; the code
dealing with them still depends on the X509 API.

Implemented by splitting openssl_identity and openssl_certificate
into BoringSSL and vanilla OpenSSL implementations.

No-Try: True
Bug: webrtc:11410
Change-Id: I86ddb361b94ad85b15ebb8743490de83632ca53f
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/196941
Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#32818}
This commit is contained in:
Taylor Brandstetter
2020-12-10 16:23:03 -08:00
committed by Commit Bot
parent c1ad1ff178
commit 165c618bb9
24 changed files with 1620 additions and 279 deletions

View File

@ -32,7 +32,12 @@
#include "rtc_base/openssl.h"
#include "rtc_base/openssl_adapter.h"
#include "rtc_base/openssl_digest.h"
#ifdef OPENSSL_IS_BORINGSSL
#include "rtc_base/boringssl_identity.h"
#else
#include "rtc_base/openssl_identity.h"
#endif
#include "rtc_base/openssl_utility.h"
#include "rtc_base/ssl_certificate.h"
#include "rtc_base/stream.h"
#include "rtc_base/task_utils/to_queued_task.h"
@ -304,10 +309,14 @@ OpenSSLStreamAdapter::~OpenSSLStreamAdapter() {
void OpenSSLStreamAdapter::SetIdentity(std::unique_ptr<SSLIdentity> identity) {
RTC_DCHECK(!identity_);
#ifdef OPENSSL_IS_BORINGSSL
identity_.reset(static_cast<BoringSSLIdentity*>(identity.release()));
#else
identity_.reset(static_cast<OpenSSLIdentity*>(identity.release()));
#endif
}
OpenSSLIdentity* OpenSSLStreamAdapter::GetIdentityForTesting() const {
SSLIdentity* OpenSSLStreamAdapter::GetIdentityForTesting() const {
return identity_.get();
}
@ -994,8 +1003,16 @@ void OpenSSLStreamAdapter::Cleanup(uint8_t alert) {
}
SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() {
#ifdef OPENSSL_IS_BORINGSSL
// If X509 objects aren't used, we can use these methods to avoid
// linking the sizable crypto/x509 code, using CRYPTO_BUFFER instead.
SSL_CTX* ctx =
SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ? DTLS_with_buffers_method()
: TLS_with_buffers_method());
#else
SSL_CTX* ctx =
SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ? DTLS_method() : TLS_method());
#endif
if (ctx == nullptr) {
return nullptr;
}
@ -1033,6 +1050,7 @@ SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() {
if (g_use_time_callback_for_testing) {
SSL_CTX_set_current_time_cb(ctx, &TimeCallbackForTesting);
}
SSL_CTX_set0_buffer_pool(ctx, openssl::GetBufferPool());
#endif
if (identity_ && !identity_->ConfigureIdentity(ctx)) {
@ -1053,11 +1071,16 @@ SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() {
}
// Configure a custom certificate verification callback to check the peer
// certificate digest. Note the second argument to SSL_CTX_set_verify is to
// override individual errors in the default verification logic, which is not
// what we want here.
// certificate digest.
#ifdef OPENSSL_IS_BORINGSSL
// Use CRYPTO_BUFFER version of the callback if building with BoringSSL.
SSL_CTX_set_custom_verify(ctx, mode, SSLVerifyCallback);
#else
// Note the second argument to SSL_CTX_set_verify is to override individual
// errors in the default verification logic, which is not what we want here.
SSL_CTX_set_verify(ctx, mode, nullptr);
SSL_CTX_set_cert_verify_callback(ctx, SSLVerifyCallback, nullptr);
#endif
// Select list of available ciphers. Note that !SHA256 and !SHA384 only
// remove HMAC-SHA256 and HMAC-SHA384 cipher suites, not GCM cipher suites
@ -1082,14 +1105,12 @@ bool OpenSSLStreamAdapter::VerifyPeerCertificate() {
RTC_LOG(LS_WARNING) << "Missing digest or peer certificate.";
return false;
}
const OpenSSLCertificate* leaf_cert =
static_cast<const OpenSSLCertificate*>(&peer_cert_chain_->Get(0));
unsigned char digest[EVP_MAX_MD_SIZE];
size_t digest_length;
if (!OpenSSLCertificate::ComputeDigest(
leaf_cert->x509(), peer_certificate_digest_algorithm_, digest,
sizeof(digest), &digest_length)) {
if (!peer_cert_chain_->Get(0).ComputeDigest(
peer_certificate_digest_algorithm_, digest, sizeof(digest),
&digest_length)) {
RTC_LOG(LS_WARNING) << "Failed to compute peer cert digest.";
return false;
}
@ -1113,6 +1134,36 @@ std::unique_ptr<SSLCertChain> OpenSSLStreamAdapter::GetPeerSSLCertChain()
return peer_cert_chain_ ? peer_cert_chain_->Clone() : nullptr;
}
#ifdef OPENSSL_IS_BORINGSSL
enum ssl_verify_result_t OpenSSLStreamAdapter::SSLVerifyCallback(
SSL* ssl,
uint8_t* out_alert) {
// Get our OpenSSLStreamAdapter from the context.
OpenSSLStreamAdapter* stream =
reinterpret_cast<OpenSSLStreamAdapter*>(SSL_get_app_data(ssl));
const STACK_OF(CRYPTO_BUFFER)* chain = SSL_get0_peer_certificates(ssl);
// Creates certificate chain.
std::vector<std::unique_ptr<SSLCertificate>> cert_chain;
for (CRYPTO_BUFFER* cert : chain) {
cert_chain.emplace_back(new BoringSSLCertificate(bssl::UpRef(cert)));
}
stream->peer_cert_chain_.reset(new SSLCertChain(std::move(cert_chain)));
// If the peer certificate digest isn't known yet, we'll wait to verify
// until it's known, and for now just return a success status.
if (stream->peer_certificate_digest_algorithm_.empty()) {
RTC_LOG(LS_INFO) << "Waiting to verify certificate until digest is known.";
// TODO(deadbeef): Use ssl_verify_retry?
return ssl_verify_ok;
}
if (!stream->VerifyPeerCertificate()) {
return ssl_verify_invalid;
}
return ssl_verify_ok;
}
#else // OPENSSL_IS_BORINGSSL
int OpenSSLStreamAdapter::SSLVerifyCallback(X509_STORE_CTX* store, void* arg) {
// Get our SSL structure and OpenSSLStreamAdapter from the store.
SSL* ssl = reinterpret_cast<SSL*>(
@ -1120,20 +1171,10 @@ int OpenSSLStreamAdapter::SSLVerifyCallback(X509_STORE_CTX* store, void* arg) {
OpenSSLStreamAdapter* stream =
reinterpret_cast<OpenSSLStreamAdapter*>(SSL_get_app_data(ssl));
#if defined(OPENSSL_IS_BORINGSSL)
STACK_OF(X509)* chain = SSL_get_peer_full_cert_chain(ssl);
// Creates certificate chain.
std::vector<std::unique_ptr<SSLCertificate>> cert_chain;
for (X509* cert : chain) {
cert_chain.emplace_back(new OpenSSLCertificate(cert));
}
stream->peer_cert_chain_.reset(new SSLCertChain(std::move(cert_chain)));
#else
// Record the peer's certificate.
X509* cert = X509_STORE_CTX_get0_cert(store);
stream->peer_cert_chain_.reset(
new SSLCertChain(std::make_unique<OpenSSLCertificate>(cert)));
#endif
// If the peer certificate digest isn't known yet, we'll wait to verify
// until it's known, and for now just return a success status.
@ -1149,6 +1190,7 @@ int OpenSSLStreamAdapter::SSLVerifyCallback(X509_STORE_CTX* store, void* arg) {
return 1;
}
#endif // !OPENSSL_IS_BORINGSSL
bool OpenSSLStreamAdapter::IsBoringSsl() {
#ifdef OPENSSL_IS_BORINGSSL