Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(133)

Unified Diff: webrtc/base/opensslstreamadapter.cc

Issue 2163683003: Relanding: Allow the DTLS fingerprint verification to occur after the handshake. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Fixing comment grammar. Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « webrtc/base/opensslstreamadapter.h ('k') | webrtc/base/sslstreamadapter.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: webrtc/base/opensslstreamadapter.cc
diff --git a/webrtc/base/opensslstreamadapter.cc b/webrtc/base/opensslstreamadapter.cc
index 4b40c389049c326ee5cc4330077ea0cead954e57..6a42003968cf4ff74400818c826b5ff69d069492 100644
--- a/webrtc/base/opensslstreamadapter.cc
+++ b/webrtc/base/opensslstreamadapter.cc
@@ -25,6 +25,7 @@
#include <memory>
#include <vector>
+#include "webrtc/base/checks.h"
#include "webrtc/base/common.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/safe_conversions.h"
@@ -290,11 +291,11 @@ OpenSSLStreamAdapter::OpenSSLStreamAdapter(StreamInterface* stream)
ssl_max_version_(SSL_PROTOCOL_TLS_12) {}
OpenSSLStreamAdapter::~OpenSSLStreamAdapter() {
- Cleanup();
+ Cleanup(0);
}
void OpenSSLStreamAdapter::SetIdentity(SSLIdentity* identity) {
- ASSERT(!identity_);
+ RTC_DCHECK(!identity_);
identity_.reset(static_cast<OpenSSLIdentity*>(identity));
}
@@ -309,25 +310,56 @@ std::unique_ptr<SSLCertificate> OpenSSLStreamAdapter::GetPeerCertificate()
: nullptr;
}
-bool OpenSSLStreamAdapter::SetPeerCertificateDigest(const std::string
- &digest_alg,
- const unsigned char*
- digest_val,
- size_t digest_len) {
- ASSERT(!peer_certificate_);
- ASSERT(peer_certificate_digest_algorithm_.size() == 0);
+bool OpenSSLStreamAdapter::SetPeerCertificateDigest(
+ const std::string& digest_alg,
+ const unsigned char* digest_val,
+ size_t digest_len,
+ SSLPeerCertificateDigestError* error) {
+ RTC_DCHECK(!peer_certificate_verified_);
+ RTC_DCHECK(!has_peer_certificate_digest());
size_t expected_len;
+ if (error) {
+ *error = SSLPeerCertificateDigestError::NONE;
+ }
if (!OpenSSLDigest::GetDigestSize(digest_alg, &expected_len)) {
LOG(LS_WARNING) << "Unknown digest algorithm: " << digest_alg;
+ if (error) {
+ *error = SSLPeerCertificateDigestError::UNKNOWN_ALGORITHM;
+ }
return false;
}
- if (expected_len != digest_len)
+ if (expected_len != digest_len) {
+ if (error) {
+ *error = SSLPeerCertificateDigestError::INVALID_LENGTH;
+ }
return false;
+ }
peer_certificate_digest_value_.SetData(digest_val, digest_len);
peer_certificate_digest_algorithm_ = digest_alg;
+ if (!peer_certificate_) {
+ // Normal case, where the digest is set before we obtain the certificate
+ // from the handshake.
+ return true;
+ }
+
+ if (!VerifyPeerCertificate()) {
+ Error("SetPeerCertificateDigest", -1, SSL_AD_BAD_CERTIFICATE, false);
+ if (error) {
+ *error = SSLPeerCertificateDigestError::VERIFICATION_FAILED;
+ }
+ return false;
+ }
+
+ if (state_ == SSL_CONNECTED) {
+ // Post the event asynchronously to unwind the stack. The caller
+ // of ContinueSSL may be the same object listening for these
+ // events and may not be prepared for reentrancy.
+ PostEvent(SE_OPEN | SE_READ | SE_WRITE, 0);
+ }
+
return true;
}
@@ -450,7 +482,7 @@ bool OpenSSLStreamAdapter::SetDtlsSrtpCryptoSuites(
bool OpenSSLStreamAdapter::GetDtlsSrtpCryptoSuite(int* crypto_suite) {
#ifdef HAVE_DTLS_SRTP
- ASSERT(state_ == SSL_CONNECTED);
+ RTC_DCHECK(state_ == SSL_CONNECTED);
if (state_ != SSL_CONNECTED)
return false;
@@ -461,15 +493,22 @@ bool OpenSSLStreamAdapter::GetDtlsSrtpCryptoSuite(int* crypto_suite) {
return false;
*crypto_suite = srtp_profile->id;
- ASSERT(!SrtpCryptoSuiteToName(*crypto_suite).empty());
+ RTC_DCHECK(!SrtpCryptoSuiteToName(*crypto_suite).empty());
return true;
#else
return false;
#endif
}
+bool OpenSSLStreamAdapter::IsTlsConnected() {
+ return state_ == SSL_CONNECTED;
+}
+
int OpenSSLStreamAdapter::StartSSL() {
- ASSERT(state_ == SSL_NONE);
+ if (state_ != SSL_NONE) {
+ // Don't allow StartSSL to be called twice.
+ return -1;
+ }
if (StreamAdapterInterface::GetState() != SS_OPEN) {
state_ = SSL_WAIT;
@@ -478,7 +517,7 @@ int OpenSSLStreamAdapter::StartSSL() {
state_ = SSL_CONNECTING;
if (int err = BeginSSL()) {
- Error("BeginSSL", err, false);
+ Error("BeginSSL", err, 0, false);
return err;
}
@@ -486,12 +525,12 @@ int OpenSSLStreamAdapter::StartSSL() {
}
void OpenSSLStreamAdapter::SetMode(SSLMode mode) {
- ASSERT(state_ == SSL_NONE);
+ RTC_DCHECK(state_ == SSL_NONE);
ssl_mode_ = mode;
}
void OpenSSLStreamAdapter::SetMaxProtocolVersion(SSLProtocolVersion version) {
- ASSERT(ssl_ctx_ == NULL);
+ RTC_DCHECK(ssl_ctx_ == NULL);
ssl_max_version_ = version;
}
@@ -513,6 +552,9 @@ StreamResult OpenSSLStreamAdapter::Write(const void* data, size_t data_len,
return SR_BLOCK;
case SSL_CONNECTED:
+ if (waiting_to_verify_peer_certificate()) {
+ return SR_BLOCK;
+ }
break;
case SSL_ERROR:
@@ -537,7 +579,7 @@ StreamResult OpenSSLStreamAdapter::Write(const void* data, size_t data_len,
switch (ssl_error) {
case SSL_ERROR_NONE:
LOG(LS_VERBOSE) << " -- success";
- ASSERT(0 < code && static_cast<unsigned>(code) <= data_len);
+ RTC_DCHECK(0 < code && static_cast<unsigned>(code) <= data_len);
if (written)
*written = code;
return SR_SUCCESS;
@@ -551,7 +593,7 @@ StreamResult OpenSSLStreamAdapter::Write(const void* data, size_t data_len,
case SSL_ERROR_ZERO_RETURN:
default:
- Error("SSL_write", (ssl_error ? ssl_error : -1), false);
+ Error("SSL_write", (ssl_error ? ssl_error : -1), 0, false);
if (error)
*error = ssl_error_code_;
return SR_ERROR;
@@ -572,6 +614,9 @@ StreamResult OpenSSLStreamAdapter::Read(void* data, size_t data_len,
return SR_BLOCK;
case SSL_CONNECTED:
+ if (waiting_to_verify_peer_certificate()) {
+ return SR_BLOCK;
+ }
break;
case SSL_CLOSED:
@@ -598,7 +643,7 @@ StreamResult OpenSSLStreamAdapter::Read(void* data, size_t data_len,
switch (ssl_error) {
case SSL_ERROR_NONE:
LOG(LS_VERBOSE) << " -- success";
- ASSERT(0 < code && static_cast<unsigned>(code) <= data_len);
+ RTC_DCHECK(0 < code && static_cast<unsigned>(code) <= data_len);
if (read)
*read = code;
@@ -624,15 +669,12 @@ StreamResult OpenSSLStreamAdapter::Read(void* data, size_t data_len,
return SR_BLOCK;
case SSL_ERROR_ZERO_RETURN:
LOG(LS_VERBOSE) << " -- remote side closed";
- // When we're closed at SSL layer, also close the stream level which
- // performs necessary clean up. Otherwise, a new incoming packet after
- // this could overflow the stream buffer.
- this->stream()->Close();
+ Close();
return SR_EOS;
break;
default:
LOG(LS_VERBOSE) << " -- error " << code;
- Error("SSL_read", (ssl_error ? ssl_error : -1), false);
+ Error("SSL_read", (ssl_error ? ssl_error : -1), 0, false);
if (error)
*error = ssl_error_code_;
return SR_ERROR;
@@ -649,11 +691,11 @@ void OpenSSLStreamAdapter::FlushInput(unsigned int left) {
int code = SSL_read(ssl_, buf, toread);
int ssl_error = SSL_get_error(ssl_, code);
- ASSERT(ssl_error == SSL_ERROR_NONE);
+ RTC_DCHECK(ssl_error == SSL_ERROR_NONE);
if (ssl_error != SSL_ERROR_NONE) {
LOG(LS_VERBOSE) << " -- error " << code;
- Error("SSL_read", (ssl_error ? ssl_error : -1), false);
+ Error("SSL_read", (ssl_error ? ssl_error : -1), 0, false);
return;
}
@@ -663,8 +705,11 @@ void OpenSSLStreamAdapter::FlushInput(unsigned int left) {
}
void OpenSSLStreamAdapter::Close() {
- Cleanup();
- ASSERT(state_ == SSL_CLOSED || state_ == SSL_ERROR);
+ Cleanup(0);
+ RTC_DCHECK(state_ == SSL_CLOSED || state_ == SSL_ERROR);
+ // When we're closed at SSL layer, also close the stream level which
+ // performs necessary clean up. Otherwise, a new incoming packet after
+ // this could overflow the stream buffer.
StreamAdapterInterface::Close();
}
@@ -674,6 +719,9 @@ StreamState OpenSSLStreamAdapter::GetState() const {
case SSL_CONNECTING:
return SS_OPENING;
case SSL_CONNECTED:
+ if (waiting_to_verify_peer_certificate()) {
+ return SS_OPENING;
+ }
return SS_OPEN;
default:
return SS_CLOSED;
@@ -685,16 +733,16 @@ void OpenSSLStreamAdapter::OnEvent(StreamInterface* stream, int events,
int err) {
int events_to_signal = 0;
int signal_error = 0;
- ASSERT(stream == this->stream());
+ RTC_DCHECK(stream == this->stream());
if ((events & SE_OPEN)) {
LOG(LS_VERBOSE) << "OpenSSLStreamAdapter::OnEvent SE_OPEN";
if (state_ != SSL_WAIT) {
- ASSERT(state_ == SSL_NONE);
+ RTC_DCHECK(state_ == SSL_NONE);
events_to_signal |= SE_OPEN;
} else {
state_ = SSL_CONNECTING;
if (int err = BeginSSL()) {
- Error("BeginSSL", err, true);
+ Error("BeginSSL", err, 0, true);
return;
}
}
@@ -707,7 +755,7 @@ void OpenSSLStreamAdapter::OnEvent(StreamInterface* stream, int events,
events_to_signal |= events & (SE_READ|SE_WRITE);
} else if (state_ == SSL_CONNECTING) {
if (int err = ContinueSSL()) {
- Error("ContinueSSL", err, true);
+ Error("ContinueSSL", err, 0, true);
return;
}
} else if (state_ == SSL_CONNECTED) {
@@ -725,10 +773,10 @@ void OpenSSLStreamAdapter::OnEvent(StreamInterface* stream, int events,
}
if ((events & SE_CLOSE)) {
LOG(LS_VERBOSE) << "OpenSSLStreamAdapter::OnEvent(SE_CLOSE, " << err << ")";
- Cleanup();
+ Cleanup(0);
events_to_signal |= SE_CLOSE;
// SE_CLOSE is the only event that uses the final parameter to OnEvent().
- ASSERT(signal_error == 0);
+ RTC_DCHECK(signal_error == 0);
signal_error = err;
}
if (events_to_signal)
@@ -736,16 +784,14 @@ void OpenSSLStreamAdapter::OnEvent(StreamInterface* stream, int events,
}
int OpenSSLStreamAdapter::BeginSSL() {
- ASSERT(state_ == SSL_CONNECTING);
+ RTC_DCHECK(state_ == SSL_CONNECTING);
// The underlying stream has opened.
- // A peer certificate digest must have been specified by now.
- ASSERT(!peer_certificate_digest_algorithm_.empty());
LOG(LS_INFO) << "BeginSSL with peer.";
BIO* bio = NULL;
// First set up the context.
- ASSERT(ssl_ctx_ == NULL);
+ RTC_DCHECK(ssl_ctx_ == NULL);
ssl_ctx_ = SetupSSLContext();
if (!ssl_ctx_)
return -1;
@@ -799,7 +845,7 @@ int OpenSSLStreamAdapter::BeginSSL() {
int OpenSSLStreamAdapter::ContinueSSL() {
LOG(LS_VERBOSE) << "ContinueSSL";
- ASSERT(state_ == SSL_CONNECTING);
+ RTC_DCHECK(state_ == SSL_CONNECTING);
// Clear the DTLS timer
Thread::Current()->Clear(this, MSG_TIMEOUT);
@@ -809,15 +855,21 @@ int OpenSSLStreamAdapter::ContinueSSL() {
switch (ssl_error = SSL_get_error(ssl_, code)) {
case SSL_ERROR_NONE:
LOG(LS_VERBOSE) << " -- success";
-
- if (!SSLPostConnectionCheck(ssl_, NULL,
- peer_certificate_digest_algorithm_)) {
- LOG(LS_ERROR) << "TLS post connection check failed";
- return -1;
- }
+ // By this point, OpenSSL should have given us a certificate, or errored
+ // out if one was missing.
+ RTC_DCHECK(peer_certificate_ || !client_auth_enabled());
state_ = SSL_CONNECTED;
- StreamAdapterInterface::OnEvent(stream(), SE_OPEN|SE_READ|SE_WRITE, 0);
+ if (!waiting_to_verify_peer_certificate()) {
+ // We have everything we need to start the connection, so signal
+ // SE_OPEN. If we need a client certificate fingerprint and don't have
+ // it yet, we'll instead signal SE_OPEN in SetPeerCertificateDigest.
+ //
+ // Post the event asynchronously to unwind the stack. The
+ // caller of ContinueSSL may be the same object listening
+ // for these events and may not be prepared for reentrancy.
+ PostEvent(SE_OPEN | SE_READ | SE_WRITE, 0);
+ }
break;
case SSL_ERROR_WANT_READ: {
@@ -851,17 +903,20 @@ int OpenSSLStreamAdapter::ContinueSSL() {
return 0;
}
-void OpenSSLStreamAdapter::Error(const char* context, int err, bool signal) {
- LOG(LS_WARNING) << "OpenSSLStreamAdapter::Error("
- << context << ", " << err << ")";
+void OpenSSLStreamAdapter::Error(const char* context,
+ int err,
+ uint8_t alert,
+ bool signal) {
+ LOG(LS_WARNING) << "OpenSSLStreamAdapter::Error(" << context << ", " << err
+ << ", " << static_cast<int>(alert) << ")";
state_ = SSL_ERROR;
ssl_error_code_ = err;
- Cleanup();
+ Cleanup(alert);
if (signal)
StreamAdapterInterface::OnEvent(stream(), SE_CLOSE, err);
}
-void OpenSSLStreamAdapter::Cleanup() {
+void OpenSSLStreamAdapter::Cleanup(uint8_t alert) {
LOG(LS_INFO) << "Cleanup";
if (state_ != SSL_ERROR) {
@@ -870,12 +925,25 @@ void OpenSSLStreamAdapter::Cleanup() {
}
if (ssl_) {
- int ret = SSL_shutdown(ssl_);
- if (ret < 0) {
- LOG(LS_WARNING) << "SSL_shutdown failed, error = "
- << SSL_get_error(ssl_, ret);
+ int ret;
+// SSL_send_fatal_alert is only available in BoringSSL.
+#ifdef OPENSSL_IS_BORINGSSL
+ if (alert) {
+ ret = SSL_send_fatal_alert(ssl_, alert);
+ if (ret < 0) {
+ LOG(LS_WARNING) << "SSL_send_fatal_alert failed, error = "
+ << SSL_get_error(ssl_, ret);
+ }
+ } else {
+#endif
+ ret = SSL_shutdown(ssl_);
+ if (ret < 0) {
+ LOG(LS_WARNING) << "SSL_shutdown failed, error = "
+ << SSL_get_error(ssl_, ret);
+ }
+#ifdef OPENSSL_IS_BORINGSSL
}
-
+#endif
SSL_free(ssl_);
ssl_ = NULL;
}
@@ -1033,43 +1101,23 @@ SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() {
return ctx;
}
-int OpenSSLStreamAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) {
- // Get our SSL structure from the store
- SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(
- store,
- SSL_get_ex_data_X509_STORE_CTX_idx()));
- OpenSSLStreamAdapter* stream =
- reinterpret_cast<OpenSSLStreamAdapter*>(SSL_get_app_data(ssl));
-
- if (stream->peer_certificate_digest_algorithm_.empty()) {
- return 0;
- }
- X509* cert = X509_STORE_CTX_get_current_cert(store);
- int depth = X509_STORE_CTX_get_error_depth(store);
-
- // For now We ignore the parent certificates and verify the leaf against
- // the digest.
- //
- // TODO(jiayl): Verify the chain is a proper chain and report the chain to
- // |stream->peer_certificate_|.
- if (depth > 0) {
- LOG(LS_INFO) << "Ignored chained certificate at depth " << depth;
- return 1;
+bool OpenSSLStreamAdapter::VerifyPeerCertificate() {
+ if (!has_peer_certificate_digest() || !peer_certificate_) {
+ LOG(LS_WARNING) << "Missing digest or peer certificate.";
+ return false;
}
unsigned char digest[EVP_MAX_MD_SIZE];
size_t digest_length;
if (!OpenSSLCertificate::ComputeDigest(
- cert,
- stream->peer_certificate_digest_algorithm_,
- digest, sizeof(digest),
- &digest_length)) {
+ peer_certificate_->x509(), peer_certificate_digest_algorithm_, digest,
+ sizeof(digest), &digest_length)) {
LOG(LS_WARNING) << "Failed to compute peer cert digest.";
- return 0;
+ return false;
}
Buffer computed_digest(digest, digest_length);
- if (computed_digest != stream->peer_certificate_digest_value_) {
+ if (computed_digest != peer_certificate_digest_value_) {
LOG(LS_WARNING) << "Rejected peer certificate due to mismatched digest.";
return 0;
}
@@ -1077,18 +1125,41 @@ int OpenSSLStreamAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) {
// value in checking the validity of a self-signed cert issued by untrusted
// sources.
LOG(LS_INFO) << "Accepted peer certificate.";
+ peer_certificate_verified_ = true;
+ return true;
+}
+
+int OpenSSLStreamAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) {
+ // Get our SSL structure from the store
+ SSL* ssl = reinterpret_cast<SSL*>(
+ X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx()));
+ X509* cert = X509_STORE_CTX_get_current_cert(store);
+ int depth = X509_STORE_CTX_get_error_depth(store);
+
+ // For now we ignore the parent certificates and verify the leaf against
+ // the digest.
+ //
+ // TODO(jiayl): Verify the chain is a proper chain and report the chain to
+ // |stream->peer_certificate_|.
+ if (depth > 0) {
+ LOG(LS_INFO) << "Ignored chained certificate at depth " << depth;
+ return 1;
+ }
+
+ OpenSSLStreamAdapter* stream =
+ reinterpret_cast<OpenSSLStreamAdapter*>(SSL_get_app_data(ssl));
// Record the peer's certificate.
stream->peer_certificate_.reset(new OpenSSLCertificate(cert));
- return 1;
-}
-bool OpenSSLStreamAdapter::SSLPostConnectionCheck(SSL* ssl,
- const X509* peer_cert,
- const std::string
- &peer_digest) {
- ASSERT((peer_cert != NULL) || (!peer_digest.empty()));
- return true;
+ // 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()) {
+ LOG(LS_INFO) << "Waiting to verify certificate until digest is known.";
+ return 1;
+ }
+
+ return stream->VerifyPeerCertificate();
}
bool OpenSSLStreamAdapter::HaveDtls() {
« no previous file with comments | « webrtc/base/opensslstreamadapter.h ('k') | webrtc/base/sslstreamadapter.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698