| /* | |
| * Copyright 2004 The WebRTC Project Authors. All rights reserved. | |
| * | |
| * Use of this source code is governed by a BSD-style license | |
| * that can be found in the LICENSE file in the root of the source | |
| * tree. An additional intellectual property rights grant can be found | |
| * in the file PATENTS. All contributing project authors may | |
| * be found in the AUTHORS file in the root of the source tree. | |
| */ | |
| #include "rtc_base/openssl_stream_adapter.h" | |
| #include <openssl/bio.h> | |
| #include <openssl/err.h> | |
| #include <openssl/ssl.h> | |
| #include <openssl/stack.h> | |
| #include <openssl/tls1.h> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <memory> | |
| #include <optional> | |
| #include <span> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include "absl/functional/any_invocable.h" | |
| #include "absl/strings/str_format.h" | |
| #include "absl/strings/string_view.h" | |
| #include "api/field_trials_view.h" | |
| #include "api/sequence_checker.h" | |
| #include "api/task_queue/pending_task_safety_flag.h" | |
| #include "api/task_queue/task_queue_base.h" | |
| #include "api/units/time_delta.h" | |
| #include "rtc_base/buffer.h" | |
| #include "rtc_base/checks.h" | |
| #include "rtc_base/logging.h" | |
| #include "rtc_base/numerics/safe_conversions.h" | |
| #include "rtc_base/openssl_adapter.h" | |
| #include "rtc_base/openssl_digest.h" | |
| #include "rtc_base/openssl_utility.h" | |
| #include "rtc_base/ssl_certificate.h" | |
| #include "rtc_base/ssl_identity.h" | |
| #include "rtc_base/ssl_stream_adapter.h" | |
| #include "rtc_base/stream.h" | |
| #include "rtc_base/string_encode.h" | |
| #include "rtc_base/task_utils/repeating_task.h" | |
| #include "rtc_base/time_utils.h" | |
| #ifdef OPENSSL_IS_BORINGSSL | |
| #include <openssl/digest.h> | |
| #include <openssl/dtls1.h> | |
| #include <openssl/pool.h> | |
| #include <openssl/ssl.h> | |
| #include "rtc_base/boringssl_certificate.h" | |
| #include "rtc_base/boringssl_identity.h" | |
| #include "rtc_base/openssl.h" | |
| #else | |
| #include "rtc_base/openssl_identity.h" | |
| #endif | |
| #if (OPENSSL_VERSION_NUMBER < 0x10100000L) | |
| #error "webrtc requires at least OpenSSL version 1.1.0, to support DTLS-SRTP" | |
| #endif | |
| namespace { | |
| // Value specified in RFC 5764. | |
| constexpr absl::string_view kDtlsSrtpExporterLabel = "EXTRACTOR-dtls_srtp"; | |
| } // namespace | |
| namespace webrtc { | |
| namespace { | |
| // SRTP cipher suite table. `internal_name` is used to construct a | |
| // colon-separated profile strings which is needed by | |
| // SSL_CTX_set_tlsext_use_srtp(). | |
| struct SrtpCipherMapEntry { | |
| const char* internal_name; | |
| const int id; | |
| }; | |
| // This isn't elegant, but it's better than an external reference | |
| constexpr SrtpCipherMapEntry kSrtpCipherMap[] = { | |
| {.internal_name = "SRTP_AES128_CM_SHA1_80", .id = kSrtpAes128CmSha1_80}, | |
| {.internal_name = "SRTP_AES128_CM_SHA1_32", .id = kSrtpAes128CmSha1_32}, | |
| {.internal_name = "SRTP_AEAD_AES_128_GCM", .id = kSrtpAeadAes128Gcm}, | |
| {.internal_name = "SRTP_AEAD_AES_256_GCM", .id = kSrtpAeadAes256Gcm}}; | |
| #ifdef OPENSSL_IS_BORINGSSL | |
| // Enabled by EnableTimeCallbackForTesting. Should never be set in production | |
| // code. | |
| bool g_use_time_callback_for_testing = false; | |
| // Not used in production code. Actual time should be relative to Jan 1, 1970. | |
| void TimeCallbackForTesting(const SSL* ssl, struct timeval* out_clock) { | |
| int64_t time = TimeNanos(); | |
| out_clock->tv_sec = time / kNumNanosecsPerSec; | |
| out_clock->tv_usec = (time % kNumNanosecsPerSec) / kNumNanosecsPerMicrosec; | |
| } | |
| #endif | |
| uint16_t GetMaxVersion(SSLMode ssl_mode, SSLProtocolVersion version) { | |
| switch (ssl_mode) { | |
| case SSL_MODE_TLS: | |
| switch (version) { | |
| default: | |
| case SSL_PROTOCOL_NOT_GIVEN: | |
| case SSL_PROTOCOL_TLS_10: | |
| case SSL_PROTOCOL_TLS_11: | |
| case SSL_PROTOCOL_TLS_12: | |
| return TLS1_2_VERSION; | |
| case SSL_PROTOCOL_TLS_13: | |
| #ifdef TLS1_3_VERSION | |
| return TLS1_3_VERSION; | |
| #else | |
| return TLS1_2_VERSION; | |
| #endif | |
| } | |
| case SSL_MODE_DTLS: | |
| switch (version) { | |
| default: | |
| case SSL_PROTOCOL_NOT_GIVEN: | |
| case SSL_PROTOCOL_DTLS_10: | |
| case SSL_PROTOCOL_DTLS_12: | |
| return DTLS1_2_VERSION; | |
| case SSL_PROTOCOL_DTLS_13: | |
| #ifdef DTLS1_3_VERSION | |
| return DTLS1_3_VERSION; | |
| #else | |
| return DTLS1_2_VERSION; | |
| #endif | |
| } | |
| } | |
| } | |
| constexpr int kForceDtls13Off = 0; | |
| #ifdef DTLS1_3_VERSION | |
| constexpr int kForceDtls13Enabled = 1; | |
| constexpr int kForceDtls13Only = 2; | |
| #endif | |
| int GetForceDtls13(const FieldTrialsView* field_trials) { | |
| #ifdef DTLS1_3_VERSION | |
| if (field_trials) { | |
| #if defined(WEBRTC_CHROMIUM_BUILD) | |
| if (field_trials->IsDisabled("WebRTC-ForceDtls13")) { | |
| RTC_LOG(LS_WARNING) << "WebRTC-ForceDtls13 Disabled"; | |
| return kForceDtls13Off; | |
| } | |
| #else | |
| if (field_trials->IsEnabled("WebRTC-ForceDtls13")) { | |
| RTC_LOG(LS_WARNING) << "WebRTC-ForceDtls13 Enabled"; | |
| return kForceDtls13Enabled; | |
| } | |
| #endif // defined(WEBRTC_CHROMIUM_BUILD) | |
| if (field_trials->Lookup("WebRTC-ForceDtls13") == "Only") { | |
| RTC_LOG(LS_WARNING) << "WebRTC-ForceDtls13 Only"; | |
| return kForceDtls13Only; | |
| } | |
| } | |
| // Default behavior: | |
| #if defined(WEBRTC_CHROMIUM_BUILD) | |
| RTC_LOG(LS_WARNING) << "WebRTC-ForceDtls13 Enabled"; | |
| return kForceDtls13Enabled; | |
| #else | |
| RTC_LOG(LS_WARNING) << "WebRTC-ForceDtls13 Disabled"; | |
| return kForceDtls13Off; | |
| #endif // defined(WEBRTC_CHROMIUM_BUILD) | |
| #else | |
| return kForceDtls13Off; | |
| #endif // DTLS1_3_VERSION | |
| } | |
| #ifdef OPENSSL_IS_BORINGSSL | |
| std::string GetOpenSslError() { | |
| std::string error; | |
| int64_t error_id; | |
| const char* error_file; | |
| int error_line; | |
| while ((error_id = ERR_get_error_line(&error_file, &error_line)) != 0) { | |
| char buf[ERR_ERROR_STRING_BUF_LEN]; | |
| ERR_error_string_n(error_id, buf, sizeof(buf)); | |
| absl::StrAppendFormat(&error, "\nOpenSSL error (file: %s, line: %d): %s", | |
| error_file, error_line, buf); | |
| } | |
| return error; | |
| } | |
| #endif | |
| ////////////////////////////////////////////////////////////////////// | |
| // StreamBIO | |
| ////////////////////////////////////////////////////////////////////// | |
| int stream_write(BIO* h, const char* buf, int num); | |
| int stream_read(BIO* h, char* buf, int size); | |
| int stream_puts(BIO* h, const char* str); | |
| long stream_ctrl(BIO* h, int cmd, long arg1, void* arg2); | |
| int stream_new(BIO* h); | |
| int stream_free(BIO* data); | |
| BIO_METHOD* BIO_stream_method() { | |
| static BIO_METHOD* method = [] { | |
| BIO_METHOD* method = BIO_meth_new(BIO_TYPE_BIO, "stream"); | |
| BIO_meth_set_write(method, stream_write); | |
| BIO_meth_set_read(method, stream_read); | |
| BIO_meth_set_puts(method, stream_puts); | |
| BIO_meth_set_ctrl(method, stream_ctrl); | |
| BIO_meth_set_create(method, stream_new); | |
| BIO_meth_set_destroy(method, stream_free); | |
| return method; | |
| }(); | |
| return method; | |
| } | |
| BIO* BIO_new_stream(StreamInterface* stream) { | |
| BIO* ret = BIO_new(BIO_stream_method()); | |
| if (ret == nullptr) { | |
| return nullptr; | |
| } | |
| BIO_set_data(ret, stream); | |
| return ret; | |
| } | |
| // bio methods return 1 (or at least non-zero) on success and 0 on failure. | |
| int stream_new(BIO* b) { | |
| BIO_set_shutdown(b, 0); | |
| BIO_set_init(b, 1); | |
| BIO_set_data(b, nullptr); | |
| return 1; | |
| } | |
| int stream_free(BIO* b) { | |
| if (b == nullptr) { | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| int stream_read(BIO* b, char* out, int outl) { | |
| if (!out) { | |
| return -1; | |
| } | |
| StreamInterface* stream = static_cast<StreamInterface*>(BIO_get_data(b)); | |
| BIO_clear_retry_flags(b); | |
| size_t read; | |
| int error; | |
| StreamResult result = stream->Read( | |
| std::span(reinterpret_cast<uint8_t*>(out), outl), read, error); | |
| if (result == SR_SUCCESS) { | |
| return checked_cast<int>(read); | |
| } else if (result == SR_BLOCK) { | |
| BIO_set_retry_read(b); | |
| } | |
| return -1; | |
| } | |
| int stream_write(BIO* b, const char* in, int inl) { | |
| if (!in) { | |
| return -1; | |
| } | |
| StreamInterface* stream = static_cast<StreamInterface*>(BIO_get_data(b)); | |
| BIO_clear_retry_flags(b); | |
| size_t written; | |
| int error; | |
| StreamResult result = stream->Write( | |
| std::span(reinterpret_cast<const uint8_t*>(in), inl), written, error); | |
| if (result == SR_SUCCESS) { | |
| return checked_cast<int>(written); | |
| } else if (result == SR_BLOCK) { | |
| BIO_set_retry_write(b); | |
| } | |
| return -1; | |
| } | |
| int stream_puts(BIO* b, const char* str) { | |
| return stream_write(b, str, checked_cast<int>(strlen(str))); | |
| } | |
| long stream_ctrl(BIO* b, int cmd, long num, void* ptr) { | |
| switch (cmd) { | |
| case BIO_CTRL_RESET: | |
| return 0; | |
| case BIO_CTRL_EOF: { | |
| StreamInterface* stream = static_cast<StreamInterface*>(ptr); | |
| // 1 means end-of-stream. | |
| return (stream->GetState() == SS_CLOSED) ? 1 : 0; | |
| } | |
| case BIO_CTRL_WPENDING: | |
| case BIO_CTRL_PENDING: | |
| return 0; | |
| case BIO_CTRL_FLUSH: { | |
| // Flush signals the end of a flight of handshake packets. | |
| StreamInterface* stream = static_cast<StreamInterface*>(BIO_get_data(b)); | |
| RTC_DCHECK(stream); | |
| if (stream->Flush()) { | |
| RTC_LOG(LS_WARNING) << "Failed to flush stream"; | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| case BIO_CTRL_DGRAM_QUERY_MTU: | |
| // openssl defaults to mtu=256 unless we return something here. | |
| // The handshake doesn't actually need to send packets above 1k, | |
| // so this seems like a sensible value that should work in most cases. | |
| // Webrtc uses the same value for video packets. | |
| RTC_DCHECK_NOTREACHED() | |
| << "We should be using SSL_set_mtu instead of this!"; | |
| return 1200; | |
| default: | |
| return 0; | |
| } | |
| } | |
| } // namespace | |
| ///////////////////////////////////////////////////////////////////////////// | |
| // OpenSSLStreamAdapter | |
| ///////////////////////////////////////////////////////////////////////////// | |
| OpenSSLStreamAdapter::OpenSSLStreamAdapter( | |
| std::unique_ptr<StreamInterface> stream, | |
| absl::AnyInvocable<void(SSLHandshakeError)> handshake_error, | |
| const FieldTrialsView* field_trials) | |
| : stream_(std::move(stream)), | |
| handshake_error_(std::move(handshake_error)), | |
| owner_(TaskQueueBase::Current()), | |
| state_(SSL_NONE), | |
| role_(SSL_CLIENT), | |
| ssl_read_needs_write_(false), | |
| ssl_write_needs_read_(false), | |
| ssl_(nullptr), | |
| ssl_ctx_(nullptr), | |
| ssl_mode_(SSL_MODE_DTLS), | |
| ssl_max_version_(SSL_PROTOCOL_DTLS_12), | |
| force_dtls_13_(GetForceDtls13(field_trials)), | |
| disable_ssl_group_ids_(field_trials && field_trials->IsEnabled( | |
| "WebRTC-DisableSslGroupIds")) { | |
| stream_->SetEventCallback( | |
| [this](int events, int err) { OnEvent(events, err); }); | |
| } | |
| OpenSSLStreamAdapter::~OpenSSLStreamAdapter() { | |
| timeout_task_.Stop(); | |
| Cleanup(0); | |
| } | |
| 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 | |
| } | |
| SSLIdentity* OpenSSLStreamAdapter::GetIdentityForTesting() const { | |
| return identity_.get(); | |
| } | |
| void OpenSSLStreamAdapter::SetServerRole(SSLRole role) { | |
| role_ = role; | |
| } | |
| SSLPeerCertificateDigestError OpenSSLStreamAdapter::SetPeerCertificateDigest( | |
| absl::string_view digest_alg, | |
| std::span<const uint8_t> digest_val) { | |
| RTC_DCHECK(!peer_certificate_verified_); | |
| RTC_DCHECK(!HasPeerCertificateDigest()); | |
| size_t expected_len; | |
| if (!OpenSSLDigest::GetDigestSize(digest_alg, &expected_len)) { | |
| RTC_LOG(LS_WARNING) << "Unknown digest algorithm: " << digest_alg; | |
| return SSLPeerCertificateDigestError::UNKNOWN_ALGORITHM; | |
| } | |
| if (expected_len != digest_val.size()) { | |
| return SSLPeerCertificateDigestError::INVALID_LENGTH; | |
| } | |
| peer_certificate_digest_value_.SetData(digest_val); | |
| peer_certificate_digest_algorithm_ = std::string(digest_alg); | |
| if (!peer_cert_chain_) { | |
| // Normal case, where the digest is set before we obtain the certificate | |
| // from the handshake. | |
| return SSLPeerCertificateDigestError::NONE; | |
| } | |
| if (!VerifyPeerCertificate()) { | |
| Error("SetPeerCertificateDigest", -1, SSL_AD_BAD_CERTIFICATE, false); | |
| return SSLPeerCertificateDigestError::VERIFICATION_FAILED; | |
| } | |
| 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 SSLPeerCertificateDigestError::NONE; | |
| } | |
| std::optional<absl::string_view> OpenSSLStreamAdapter::GetTlsCipherSuiteName() | |
| const { | |
| if (state_ != SSL_CONNECTED) { | |
| return std::nullopt; | |
| } | |
| const SSL_CIPHER* current_cipher = SSL_get_current_cipher(ssl_); | |
| return SSL_CIPHER_standard_name(current_cipher); | |
| } | |
| bool OpenSSLStreamAdapter::GetSslCipherSuite(int* cipher_suite) const { | |
| if (state_ != SSL_CONNECTED) { | |
| return false; | |
| } | |
| const SSL_CIPHER* current_cipher = SSL_get_current_cipher(ssl_); | |
| if (current_cipher == nullptr) { | |
| return false; | |
| } | |
| *cipher_suite = static_cast<uint16_t>(SSL_CIPHER_get_id(current_cipher)); | |
| return true; | |
| } | |
| SSLProtocolVersion OpenSSLStreamAdapter::GetSslVersion() const { | |
| if (state_ != SSL_CONNECTED) { | |
| return SSL_PROTOCOL_NOT_GIVEN; | |
| } | |
| int ssl_version = SSL_version(ssl_); | |
| if (ssl_mode_ == SSL_MODE_DTLS) { | |
| if (ssl_version == DTLS1_VERSION) { | |
| return SSL_PROTOCOL_DTLS_10; | |
| } else if (ssl_version == DTLS1_2_VERSION) { | |
| return SSL_PROTOCOL_DTLS_12; | |
| } | |
| #ifdef DTLS1_3_VERSION | |
| if (ssl_version == DTLS1_3_VERSION) { | |
| return SSL_PROTOCOL_DTLS_13; | |
| } | |
| #endif | |
| } else { | |
| if (ssl_version == TLS1_VERSION) { | |
| return SSL_PROTOCOL_TLS_10; | |
| } else if (ssl_version == TLS1_1_VERSION) { | |
| return SSL_PROTOCOL_TLS_11; | |
| } else if (ssl_version == TLS1_2_VERSION) { | |
| return SSL_PROTOCOL_TLS_12; | |
| } | |
| #ifdef TLS1_3_VERSION | |
| if (ssl_version == TLS1_3_VERSION) { | |
| return SSL_PROTOCOL_TLS_13; | |
| } | |
| #endif | |
| } | |
| return SSL_PROTOCOL_NOT_GIVEN; | |
| } | |
| bool OpenSSLStreamAdapter::GetSslVersionBytes(int* version) const { | |
| if (state_ != SSL_CONNECTED) { | |
| return false; | |
| } | |
| *version = SSL_version(ssl_); | |
| return true; | |
| } | |
| uint16_t OpenSSLStreamAdapter::GetSslGroupId() const { | |
| if (state_ != SSL_CONNECTED) { | |
| return 0; | |
| } | |
| #ifdef OPENSSL_IS_BORINGSSL | |
| return SSL_get_group_id(ssl_); | |
| #else | |
| return 0; | |
| #endif | |
| } | |
| bool OpenSSLStreamAdapter::ExportSrtpKeyingMaterial( | |
| ZeroOnFreeBuffer<uint8_t>& keying_material) { | |
| // Arguments are: | |
| // keying material/len -- a buffer to hold the keying material. | |
| // label -- the exporter label. | |
| // part of the RFC defining each exporter | |
| // usage. We only use RFC 5764 for DTLS-SRTP. | |
| // context/context_len -- a context to bind to for this connection; | |
| // use_context optional, can be null, 0 (IN). Not used by WebRTC. | |
| if (SSL_export_keying_material( | |
| ssl_, keying_material.data(), keying_material.size(), | |
| kDtlsSrtpExporterLabel.data(), kDtlsSrtpExporterLabel.size(), nullptr, | |
| 0, false) != 1) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool OpenSSLStreamAdapter::AppendSrtpKeyingMaterial( | |
| ZeroOnFreeBuffer<uint8_t>& key_buffer) { | |
| // Compute size of keying material. | |
| int selected_crypto_suite; | |
| if (!GetDtlsSrtpCryptoSuite(&selected_crypto_suite)) { | |
| RTC_LOG(LS_ERROR) << "No crypto suite"; | |
| return false; | |
| } | |
| int key_len, salt_len; | |
| if (!GetSrtpKeyAndSaltLengths(selected_crypto_suite, &key_len, &salt_len)) { | |
| RTC_LOG(LS_ERROR) << "Unable to get key and salt len from crypto suite"; | |
| return false; | |
| } | |
| int key_material_size = key_len * 2 + salt_len * 2; | |
| // Arguments are: | |
| // keying material/len -- a buffer to hold the keying material. | |
| // label -- the exporter label. | |
| // part of the RFC defining each exporter | |
| // usage. We only use RFC 5764 for DTLS-SRTP. | |
| // context/context_len -- a context to bind to for this connection; | |
| // use_context optional, can be null, 0 (IN). Not used by WebRTC. | |
| bool success = false; | |
| key_buffer.AppendData( | |
| key_material_size, [&](std::span<uint8_t> keying_material) -> size_t { | |
| int result = SSL_export_keying_material( | |
| ssl_, keying_material.data(), keying_material.size(), | |
| kDtlsSrtpExporterLabel.data(), kDtlsSrtpExporterLabel.size(), | |
| nullptr, 0, false); | |
| if (result != 0) { | |
| success = true; | |
| return keying_material.size(); | |
| } else { | |
| return 0; // Consider no data appended | |
| } | |
| }); | |
| return success; | |
| } | |
| uint16_t OpenSSLStreamAdapter::GetPeerSignatureAlgorithm() const { | |
| if (state_ != SSL_CONNECTED) { | |
| return 0; | |
| } | |
| #ifdef OPENSSL_IS_BORINGSSL | |
| return SSL_get_peer_signature_algorithm(ssl_); | |
| #else | |
| return kSslSignatureAlgorithmUnknown; | |
| #endif | |
| } | |
| bool OpenSSLStreamAdapter::SetDtlsSrtpCryptoSuites( | |
| const std::vector<int>& ciphers) { | |
| if (state_ != SSL_NONE) { | |
| return false; | |
| } | |
| std::string internal_ciphers; | |
| for (const int cipher : ciphers) { | |
| bool found = false; | |
| for (const auto& entry : kSrtpCipherMap) { | |
| if (cipher == entry.id) { | |
| found = true; | |
| if (!internal_ciphers.empty()) { | |
| internal_ciphers += ":"; | |
| } | |
| internal_ciphers += entry.internal_name; | |
| break; | |
| } | |
| } | |
| if (!found) { | |
| RTC_LOG(LS_ERROR) << "Could not find cipher: " << cipher; | |
| return false; | |
| } | |
| } | |
| if (internal_ciphers.empty()) { | |
| return false; | |
| } | |
| srtp_ciphers_ = internal_ciphers; | |
| return true; | |
| } | |
| bool OpenSSLStreamAdapter::GetDtlsSrtpCryptoSuite(int* crypto_suite) const { | |
| RTC_DCHECK(state_ == SSL_CONNECTED); | |
| if (state_ != SSL_CONNECTED) { | |
| return false; | |
| } | |
| const SRTP_PROTECTION_PROFILE* srtp_profile = | |
| SSL_get_selected_srtp_profile(ssl_); | |
| if (!srtp_profile) { | |
| return false; | |
| } | |
| *crypto_suite = srtp_profile->id; | |
| RTC_DCHECK(!SrtpCryptoSuiteToName(*crypto_suite).empty()); | |
| return true; | |
| } | |
| bool OpenSSLStreamAdapter::IsTlsConnected() { | |
| return state_ == SSL_CONNECTED; | |
| } | |
| int OpenSSLStreamAdapter::StartSSL() { | |
| // Don't allow StartSSL to be called twice. | |
| if (state_ != SSL_NONE) { | |
| return -1; | |
| } | |
| if (stream_->GetState() != SS_OPEN) { | |
| state_ = SSL_WAIT; | |
| return 0; | |
| } | |
| state_ = SSL_CONNECTING; | |
| if (int err = BeginSSL()) { | |
| Error("BeginSSL", err, 0, false); | |
| return err; | |
| } | |
| return 0; | |
| } | |
| void OpenSSLStreamAdapter::SetMode(SSLMode mode) { | |
| RTC_DCHECK(state_ == SSL_NONE); | |
| ssl_mode_ = mode; | |
| } | |
| void OpenSSLStreamAdapter::SetMaxProtocolVersion(SSLProtocolVersion version) { | |
| RTC_DCHECK(ssl_ctx_ == nullptr); | |
| ssl_max_version_ = version; | |
| } | |
| void OpenSSLStreamAdapter::SetInitialRetransmissionTimeout(int timeout_ms) { | |
| dtls_handshake_timeout_ms_ = timeout_ms; | |
| #ifdef OPENSSL_IS_BORINGSSL | |
| if (ssl_ctx_ != nullptr && ssl_mode_ == SSL_MODE_DTLS) { | |
| DTLSv1_set_initial_timeout_duration(ssl_, dtls_handshake_timeout_ms_); | |
| } | |
| #endif | |
| } | |
| void OpenSSLStreamAdapter::UpdateRetransmissionTimeout(int timeout_ms) { | |
| dtls_handshake_timeout_ms_ = timeout_ms; | |
| #ifdef OPENSSL_IS_BORINGSSL | |
| if (ssl_ctx_ != nullptr && ssl_mode_ == SSL_MODE_DTLS) { | |
| DTLSv1_set_initial_timeout_duration(ssl_, dtls_handshake_timeout_ms_); | |
| // Clear the DTLS timer | |
| timeout_task_.Stop(); | |
| MaybeSetTimeout(); | |
| } | |
| #endif | |
| } | |
| void OpenSSLStreamAdapter::SetMTU(int mtu) { | |
| dtls_mtu_ = mtu; | |
| if (ssl_) { | |
| RTC_CHECK(SSL_set_mtu(ssl_, dtls_mtu_)) << "Call to SSL_set_mtu failed."; | |
| } | |
| } | |
| // | |
| // StreamInterface Implementation | |
| // | |
| StreamResult OpenSSLStreamAdapter::Write(std::span<const uint8_t> data, | |
| size_t& written, | |
| int& error) { | |
| RTC_DLOG(LS_VERBOSE) << "OpenSSLStreamAdapter::Write(" << data.size() << ")"; | |
| switch (state_) { | |
| case SSL_NONE: | |
| // pass-through in clear text | |
| return stream_->Write(data, written, error); | |
| case SSL_WAIT: | |
| case SSL_CONNECTING: | |
| return SR_BLOCK; | |
| case SSL_CONNECTED: | |
| if (WaitingToVerifyPeerCertificate()) { | |
| return SR_BLOCK; | |
| } | |
| break; | |
| case SSL_ERROR: | |
| case SSL_CLOSED: | |
| default: | |
| error = ssl_error_code_; | |
| return SR_ERROR; | |
| } | |
| // OpenSSL will return an error if we try to write zero bytes | |
| if (data.empty()) { | |
| written = 0; | |
| return SR_SUCCESS; | |
| } | |
| ssl_write_needs_read_ = false; | |
| int code = SSL_write(ssl_, data.data(), checked_cast<int>(data.size())); | |
| int ssl_error = SSL_get_error(ssl_, code); | |
| MaybeSetTimeout(); | |
| switch (ssl_error) { | |
| case SSL_ERROR_NONE: | |
| RTC_DLOG(LS_VERBOSE) << " -- success"; | |
| RTC_DCHECK_GT(code, 0); | |
| RTC_DCHECK_LE(code, data.size()); | |
| written = code; | |
| return SR_SUCCESS; | |
| case SSL_ERROR_WANT_READ: | |
| RTC_DLOG(LS_VERBOSE) << " -- error want read"; | |
| ssl_write_needs_read_ = true; | |
| return SR_BLOCK; | |
| case SSL_ERROR_WANT_WRITE: | |
| RTC_DLOG(LS_VERBOSE) << " -- error want write"; | |
| return SR_BLOCK; | |
| case SSL_ERROR_ZERO_RETURN: | |
| default: | |
| Error("SSL_write", (ssl_error ? ssl_error : -1), 0, false); | |
| error = ssl_error_code_; | |
| return SR_ERROR; | |
| } | |
| // not reached | |
| } | |
| StreamResult OpenSSLStreamAdapter::Read(std::span<uint8_t> data, | |
| size_t& read, | |
| int& error) { | |
| RTC_DLOG(LS_VERBOSE) << "OpenSSLStreamAdapter::Read(" << data.size() << ")"; | |
| switch (state_) { | |
| case SSL_NONE: | |
| // pass-through in clear text | |
| return stream_->Read(data, read, error); | |
| case SSL_WAIT: | |
| case SSL_CONNECTING: | |
| return SR_BLOCK; | |
| case SSL_CONNECTED: | |
| if (WaitingToVerifyPeerCertificate()) { | |
| return SR_BLOCK; | |
| } | |
| break; | |
| case SSL_CLOSED: | |
| return SR_EOS; | |
| case SSL_ERROR: | |
| default: | |
| error = ssl_error_code_; | |
| return SR_ERROR; | |
| } | |
| // Don't trust OpenSSL with zero byte reads | |
| if (data.empty()) { | |
| read = 0; | |
| return SR_SUCCESS; | |
| } | |
| ssl_read_needs_write_ = false; | |
| const int code = SSL_read(ssl_, data.data(), checked_cast<int>(data.size())); | |
| const int ssl_error = SSL_get_error(ssl_, code); | |
| MaybeSetTimeout(); | |
| switch (ssl_error) { | |
| case SSL_ERROR_NONE: | |
| RTC_DLOG(LS_VERBOSE) << " -- success"; | |
| RTC_DCHECK_GT(code, 0); | |
| RTC_DCHECK_LE(code, data.size()); | |
| read = code; | |
| if (ssl_mode_ == SSL_MODE_DTLS) { | |
| // Enforce atomic reads -- this is a short read | |
| unsigned int pending = SSL_pending(ssl_); | |
| if (pending) { | |
| RTC_DLOG(LS_INFO) << " -- short DTLS read. flushing"; | |
| FlushInput(pending); | |
| error = SSE_MSG_TRUNC; | |
| return SR_ERROR; | |
| } | |
| } | |
| return SR_SUCCESS; | |
| case SSL_ERROR_WANT_READ: | |
| RTC_DLOG(LS_VERBOSE) << " -- error want read"; | |
| return SR_BLOCK; | |
| case SSL_ERROR_WANT_WRITE: | |
| RTC_DLOG(LS_VERBOSE) << " -- error want write"; | |
| ssl_read_needs_write_ = true; | |
| return SR_BLOCK; | |
| case SSL_ERROR_ZERO_RETURN: | |
| RTC_DLOG(LS_VERBOSE) << " -- remote side closed"; | |
| Close(); | |
| return SR_EOS; | |
| default: | |
| Error("SSL_read", (ssl_error ? ssl_error : -1), 0, false); | |
| error = ssl_error_code_; | |
| return SR_ERROR; | |
| } | |
| // not reached | |
| } | |
| void OpenSSLStreamAdapter::FlushInput(unsigned int left) { | |
| unsigned char buf[2048]; | |
| while (left) { | |
| // This should always succeed | |
| const int toread = (sizeof(buf) < left) ? sizeof(buf) : left; | |
| const int code = SSL_read(ssl_, buf, toread); | |
| const int ssl_error = SSL_get_error(ssl_, code); | |
| RTC_DCHECK(ssl_error == SSL_ERROR_NONE); | |
| MaybeSetTimeout(); | |
| if (ssl_error != SSL_ERROR_NONE) { | |
| RTC_DLOG(LS_VERBOSE) << " -- error " << code; | |
| Error("SSL_read", (ssl_error ? ssl_error : -1), 0, false); | |
| return; | |
| } | |
| RTC_DLOG(LS_VERBOSE) << " -- flushed " << code << " bytes"; | |
| left -= code; | |
| } | |
| } | |
| void OpenSSLStreamAdapter::Close() { | |
| 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. | |
| stream_->Close(); | |
| } | |
| StreamState OpenSSLStreamAdapter::GetState() const { | |
| switch (state_) { | |
| case SSL_WAIT: | |
| case SSL_CONNECTING: | |
| return SS_OPENING; | |
| case SSL_CONNECTED: | |
| if (WaitingToVerifyPeerCertificate()) { | |
| return SS_OPENING; | |
| } | |
| return SS_OPEN; | |
| default: | |
| return SS_CLOSED; | |
| } | |
| // not reached | |
| } | |
| void OpenSSLStreamAdapter::OnEvent(int events, int err) { | |
| RTC_DCHECK_RUN_ON(&callback_sequence_); | |
| int events_to_signal = 0; | |
| int signal_error = 0; | |
| if ((events & SE_OPEN)) { | |
| RTC_DLOG(LS_VERBOSE) << "OpenSSLStreamAdapter::OnEvent SE_OPEN"; | |
| if (state_ != SSL_WAIT) { | |
| RTC_DCHECK(state_ == SSL_NONE); | |
| events_to_signal |= SE_OPEN; | |
| } else { | |
| state_ = SSL_CONNECTING; | |
| if (int error = BeginSSL()) { | |
| Error("BeginSSL", error, 0, true); | |
| return; | |
| } | |
| } | |
| } | |
| if ((events & (SE_READ | SE_WRITE))) { | |
| RTC_DLOG(LS_VERBOSE) << "OpenSSLStreamAdapter::OnEvent" | |
| << ((events & SE_READ) ? " SE_READ" : "") | |
| << ((events & SE_WRITE) ? " SE_WRITE" : ""); | |
| if (state_ == SSL_NONE) { | |
| events_to_signal |= events & (SE_READ | SE_WRITE); | |
| } else if (state_ == SSL_CONNECTING) { | |
| if (int error = ContinueSSL()) { | |
| Error("ContinueSSL", error, 0, true); | |
| return; | |
| } | |
| } else if (state_ == SSL_CONNECTED) { | |
| if (((events & SE_READ) && ssl_write_needs_read_) || | |
| (events & SE_WRITE)) { | |
| RTC_DLOG(LS_VERBOSE) << " -- onStreamWriteable"; | |
| events_to_signal |= SE_WRITE; | |
| } | |
| if (((events & SE_WRITE) && ssl_read_needs_write_) || | |
| (events & SE_READ)) { | |
| RTC_DLOG(LS_VERBOSE) << " -- onStreamReadable"; | |
| events_to_signal |= SE_READ; | |
| } | |
| } | |
| } | |
| if ((events & SE_CLOSE)) { | |
| RTC_DLOG(LS_VERBOSE) << "OpenSSLStreamAdapter::OnEvent(SE_CLOSE, " << err | |
| << ")"; | |
| Cleanup(0); | |
| events_to_signal |= SE_CLOSE; | |
| // SE_CLOSE is the only event that uses the final parameter to OnEvent(). | |
| RTC_DCHECK(signal_error == 0); | |
| signal_error = err; | |
| } | |
| if (events_to_signal) { | |
| // Note that the adapter presents itself as the origin of the stream events, | |
| // since users of the adapter may not recognize the adapted object. | |
| FireEvent(events_to_signal, signal_error); | |
| } | |
| } | |
| void OpenSSLStreamAdapter::PostEvent(int events, int err) { | |
| owner_->PostTask(SafeTask(task_safety_.flag(), [this, events, err]() { | |
| RTC_DCHECK_RUN_ON(&callback_sequence_); | |
| FireEvent(events, err); | |
| })); | |
| } | |
| void OpenSSLStreamAdapter::SetTimeout(int delay_ms) { | |
| // We need to accept 0 delay here as well as >0 delay, because | |
| // DTLSv1_get_timeout seems to frequently return 0 ms. | |
| RTC_DCHECK_GE(delay_ms, 0); | |
| RTC_DCHECK(!timeout_task_.Running()); | |
| timeout_task_ = RepeatingTaskHandle::DelayedStart( | |
| owner_, TimeDelta::Millis(delay_ms), | |
| [flag = task_safety_.flag(), this]() { | |
| if (flag->alive()) { | |
| RTC_DLOG(LS_INFO) << "DTLS timeout expired"; | |
| timeout_task_.Stop(); | |
| int res = DTLSv1_handle_timeout(ssl_); | |
| if (res > 0) { | |
| retransmission_count_++; | |
| RTC_LOG(LS_INFO) << "DTLS retransmission"; | |
| } else if (res < 0) { | |
| RTC_LOG(LS_INFO) << "DTLSv1_handle_timeout() return -1"; | |
| Error("DTLSv1_handle_timeout", res, -1, true); | |
| return TimeDelta::PlusInfinity(); | |
| } | |
| // We check the timer even after SSL_CONNECTED, | |
| // but ContinueSSL() is only needed when SSL_CONNECTING | |
| if (state_ == SSL_CONNECTING) { | |
| // Note: timeout is set inside ContinueSSL() | |
| ContinueSSL(); | |
| } else if (state_ == SSL_CONNECTED) { | |
| MaybeSetTimeout(); | |
| } else { | |
| RTC_DCHECK_NOTREACHED() << "state_: " << state_; | |
| } | |
| } else { | |
| RTC_DCHECK_NOTREACHED() << "flag->alive() == false"; | |
| } | |
| // This callback will never run again (stopped above). | |
| return TimeDelta::PlusInfinity(); | |
| }); | |
| } | |
| int OpenSSLStreamAdapter::BeginSSL() { | |
| RTC_DCHECK(state_ == SSL_CONNECTING); | |
| // The underlying stream has opened. | |
| RTC_DLOG(LS_INFO) << "BeginSSL with peer."; | |
| BIO* bio = nullptr; | |
| // First set up the context. | |
| RTC_DCHECK(ssl_ctx_ == nullptr); | |
| ssl_ctx_ = SetupSSLContext(); | |
| if (!ssl_ctx_) { | |
| return -1; | |
| } | |
| bio = BIO_new_stream(stream_.get()); | |
| if (!bio) { | |
| return -1; | |
| } | |
| ssl_ = SSL_new(ssl_ctx_); | |
| if (!ssl_) { | |
| BIO_free(bio); | |
| return -1; | |
| } | |
| SSL_set_app_data(ssl_, this); | |
| SSL_set_bio(ssl_, bio, bio); // the SSL object owns the bio now. | |
| // Use SSL_set_mtu to configure MTU insted of | |
| // BIO_CTRL_DGRAM_QUERY_MTU | |
| SSL_set_options(ssl_, SSL_OP_NO_QUERY_MTU); | |
| SSL_set_mtu(ssl_, dtls_mtu_); | |
| #ifdef OPENSSL_IS_BORINGSSL | |
| if (ssl_mode_ == SSL_MODE_DTLS) { | |
| DTLSv1_set_initial_timeout_duration(ssl_, dtls_handshake_timeout_ms_); | |
| } | |
| if (!disable_ssl_group_ids_) { | |
| if (!SSL_set1_group_ids(ssl_, ssl_cipher_groups_.data(), | |
| ssl_cipher_groups_.size())) { | |
| RTC_LOG(LS_WARNING) << "Failed to call SSL_set1_group_ids."; | |
| return -1; | |
| } | |
| } | |
| #endif | |
| SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE | | |
| SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); | |
| // Do the connect | |
| return ContinueSSL(); | |
| } | |
| int OpenSSLStreamAdapter::ContinueSSL() { | |
| RTC_DCHECK_RUN_ON(&callback_sequence_); | |
| RTC_DLOG(LS_VERBOSE) << "ContinueSSL"; | |
| RTC_DCHECK_EQ(state_, SSL_CONNECTING); | |
| // Clear the DTLS timer | |
| timeout_task_.Stop(); | |
| const int code = (role_ == SSL_CLIENT) ? SSL_connect(ssl_) : SSL_accept(ssl_); | |
| const int ssl_error = SSL_get_error(ssl_, code); | |
| switch (ssl_error) { | |
| case SSL_ERROR_NONE: | |
| RTC_DLOG(LS_INFO) << " -- success"; | |
| // By this point, OpenSSL should have given us a certificate, or errored | |
| // out if one was missing. | |
| RTC_DCHECK(peer_cert_chain_ || !GetClientAuthEnabled()); | |
| state_ = SSL_CONNECTED; | |
| if (!WaitingToVerifyPeerCertificate()) { | |
| // 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. | |
| // | |
| // TODO(deadbeef): Post this 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); | |
| FireEvent(SE_OPEN | SE_READ | SE_WRITE, 0); | |
| } | |
| break; | |
| case SSL_ERROR_WANT_READ: | |
| RTC_DLOG(LS_INFO) << " -- error when we want to read"; | |
| break; | |
| case SSL_ERROR_WANT_WRITE: | |
| RTC_DLOG(LS_INFO) << " -- error when we want to write"; | |
| break; | |
| case SSL_ERROR_ZERO_RETURN: | |
| default: { | |
| SSLHandshakeError ssl_handshake_err = SSLHandshakeError::UNKNOWN; | |
| int err_code = ERR_peek_last_error(); | |
| if (err_code != 0 && ERR_GET_REASON(err_code) == SSL_R_NO_SHARED_CIPHER) { | |
| ssl_handshake_err = SSLHandshakeError::INCOMPATIBLE_CIPHERSUITE; | |
| } | |
| RTC_DLOG(LS_VERBOSE) << " -- error " << code << ", " << err_code << ", " | |
| << ERR_GET_REASON(err_code); | |
| if (handshake_error_) { | |
| handshake_error_(ssl_handshake_err); | |
| } | |
| return (ssl_error != 0) ? ssl_error : -1; | |
| } | |
| } | |
| MaybeSetTimeout(); | |
| return 0; | |
| } | |
| void OpenSSLStreamAdapter::MaybeSetTimeout() { | |
| if (ssl_ != nullptr && !timeout_task_.Running()) { | |
| struct timeval timeout; | |
| if (DTLSv1_get_timeout(ssl_, &timeout)) { | |
| int delay = timeout.tv_sec * 1000 + timeout.tv_usec / 1000; | |
| SetTimeout(delay); | |
| } | |
| } | |
| } | |
| void OpenSSLStreamAdapter::Error(absl::string_view context, | |
| int err, | |
| uint8_t alert, | |
| bool signal) { | |
| RTC_DCHECK_RUN_ON(&callback_sequence_); | |
| RTC_LOG(LS_WARNING) << "OpenSSLStreamAdapter::Error(" << context << ", " | |
| << err << ", " << static_cast<int>(alert) << ")"; | |
| #ifdef OPENSSL_IS_BORINGSSL | |
| if (err == SSL_ERROR_SSL) { | |
| RTC_LOG(LS_WARNING) << "GetOpenSslError: " << GetOpenSslError(); | |
| } | |
| #endif | |
| state_ = SSL_ERROR; | |
| ssl_error_code_ = err; | |
| Cleanup(alert); | |
| if (signal) { | |
| FireEvent(SE_CLOSE, err); | |
| } | |
| } | |
| void OpenSSLStreamAdapter::Cleanup(uint8_t alert) { | |
| RTC_DLOG(LS_INFO) << "Cleanup"; | |
| if (state_ != SSL_ERROR) { | |
| state_ = SSL_CLOSED; | |
| ssl_error_code_ = 0; | |
| } | |
| if (ssl_) { | |
| 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) { | |
| RTC_LOG(LS_WARNING) << "SSL_send_fatal_alert failed, error = " | |
| << SSL_get_error(ssl_, ret); | |
| } | |
| } else { | |
| #endif | |
| ret = SSL_shutdown(ssl_); | |
| if (ret < 0) { | |
| RTC_LOG(LS_WARNING) | |
| << "SSL_shutdown failed, error = " << SSL_get_error(ssl_, ret); | |
| } | |
| #ifdef OPENSSL_IS_BORINGSSL | |
| } | |
| #endif | |
| SSL_free(ssl_); | |
| ssl_ = nullptr; | |
| } | |
| if (ssl_ctx_) { | |
| SSL_CTX_free(ssl_ctx_); | |
| ssl_ctx_ = nullptr; | |
| } | |
| identity_.reset(); | |
| peer_cert_chain_.reset(); | |
| // Clear the DTLS timer | |
| timeout_task_.Stop(); | |
| } | |
| 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; | |
| } | |
| auto min_version = | |
| ssl_mode_ == SSL_MODE_DTLS ? DTLS1_2_VERSION : TLS1_2_VERSION; | |
| auto max_version = GetMaxVersion(ssl_mode_, ssl_max_version_); | |
| #ifdef DTLS1_3_VERSION | |
| if (force_dtls_13_ == kForceDtls13Enabled) { | |
| max_version = DTLS1_3_VERSION; | |
| } else if (force_dtls_13_ == kForceDtls13Only) { | |
| min_version = DTLS1_3_VERSION; | |
| max_version = DTLS1_3_VERSION; | |
| } | |
| #endif | |
| SSL_CTX_set_min_proto_version(ctx, min_version); | |
| SSL_CTX_set_max_proto_version(ctx, max_version); | |
| #ifdef OPENSSL_IS_BORINGSSL | |
| // SSL_CTX_set_current_time_cb is only supported in BoringSSL. | |
| 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)) { | |
| SSL_CTX_free(ctx); | |
| return nullptr; | |
| } | |
| // TODO(bugs.webrtc.org/339300437): Remove dependency. | |
| SSL_CTX_set_info_callback(ctx, OpenSSLAdapter::SSLInfoCallback); | |
| int mode = SSL_VERIFY_PEER; | |
| if (GetClientAuthEnabled()) { | |
| // Require a certificate from the client. | |
| // Note: Normally this is always true in production, but it may be disabled | |
| // for testing purposes (e.g. SSLAdapter unit tests). | |
| mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; | |
| } | |
| // Configure a custom certificate verification callback to check the peer | |
| // 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 | |
| // with SHA256 or SHA384 as the handshake hash. | |
| // This matches the list of SSLClientSocketImpl in Chromium. | |
| SSL_CTX_set_cipher_list( | |
| ctx, | |
| "DEFAULT:!NULL:!aNULL:!SHA256:!SHA384:!aECDH:!AESGCM+AES256:!aPSK:!3DES"); | |
| if (!srtp_ciphers_.empty()) { | |
| if (SSL_CTX_set_tlsext_use_srtp(ctx, srtp_ciphers_.c_str())) { | |
| SSL_CTX_free(ctx); | |
| return nullptr; | |
| } | |
| } | |
| #ifdef OPENSSL_IS_BORINGSSL | |
| SSL_CTX_set_permute_extensions(ctx, true); | |
| #endif | |
| #if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER >= 0x30000000L) | |
| SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET); | |
| #endif | |
| return ctx; | |
| } | |
| bool OpenSSLStreamAdapter::VerifyPeerCertificate() { | |
| if (!HasPeerCertificateDigest() || !peer_cert_chain_ || | |
| !peer_cert_chain_->GetSize()) { | |
| RTC_LOG(LS_WARNING) << "Missing digest or peer certificate."; | |
| return false; | |
| } | |
| Buffer computed_digest = Buffer::CreateWithCapacity(EVP_MAX_MD_SIZE); | |
| if (!peer_cert_chain_->Get(0).ComputeDigest( | |
| peer_certificate_digest_algorithm_, computed_digest)) { | |
| RTC_LOG(LS_WARNING) << "Failed to compute peer cert digest."; | |
| return false; | |
| } | |
| if (computed_digest != peer_certificate_digest_value_) { | |
| RTC_LOG(LS_WARNING) | |
| << "Rejected peer certificate due to mismatched digest using " | |
| << peer_certificate_digest_algorithm_ << ". Expected " | |
| << hex_encode_with_delimiter(peer_certificate_digest_value_, ':') | |
| << " got " << hex_encode_with_delimiter(computed_digest, ':'); | |
| return false; | |
| } | |
| // Ignore any verification error if the digest matches, since there is no | |
| // value in checking the validity of a self-signed cert issued by untrusted | |
| // sources. | |
| RTC_DLOG(LS_INFO) << "Accepted peer certificate."; | |
| peer_certificate_verified_ = true; | |
| return true; | |
| } | |
| std::unique_ptr<SSLCertChain> OpenSSLStreamAdapter::GetPeerSSLCertChain() | |
| const { | |
| 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*>( | |
| 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)); | |
| // 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))); | |
| // 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_DLOG(LS_INFO) << "Waiting to verify certificate until digest is known."; | |
| return 1; | |
| } | |
| if (!stream->VerifyPeerCertificate()) { | |
| X509_STORE_CTX_set_error(store, X509_V_ERR_CERT_REJECTED); | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| #endif // !OPENSSL_IS_BORINGSSL | |
| bool OpenSSLStreamAdapter::IsBoringSsl() { | |
| #ifdef OPENSSL_IS_BORINGSSL | |
| return true; | |
| #else | |
| return false; | |
| #endif | |
| } | |
| #define CDEF(X) \ | |
| { static_cast<uint16_t>(TLS1_CK_##X & 0xffff), "TLS_" #X } | |
| struct cipher_list { | |
| uint16_t cipher; | |
| const char* cipher_str; | |
| }; | |
| // TODO(torbjorng): Perhaps add more cipher suites to these lists. | |
| static const cipher_list OK_RSA_ciphers[] = { | |
| CDEF(ECDHE_RSA_WITH_AES_128_CBC_SHA), | |
| CDEF(ECDHE_RSA_WITH_AES_256_CBC_SHA), | |
| CDEF(ECDHE_RSA_WITH_AES_128_GCM_SHA256), | |
| #ifdef TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA256 | |
| CDEF(ECDHE_RSA_WITH_AES_256_GCM_SHA256), | |
| #endif | |
| #ifdef TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 // BoringSSL. | |
| CDEF(ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256), | |
| #elif defined(TLS1_RFC_ECDHE_ECDSA_WITH_CHACHA20_POLY1305) // OpenSSL. | |
| CDEF(ECDHE_RSA_WITH_CHACHA20_POLY1305), | |
| #endif | |
| }; | |
| static const cipher_list OK_ECDSA_ciphers[] = { | |
| CDEF(ECDHE_ECDSA_WITH_AES_128_CBC_SHA), | |
| CDEF(ECDHE_ECDSA_WITH_AES_256_CBC_SHA), | |
| CDEF(ECDHE_ECDSA_WITH_AES_128_GCM_SHA256), | |
| #ifdef TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA256 | |
| CDEF(ECDHE_ECDSA_WITH_AES_256_GCM_SHA256), | |
| #endif | |
| #ifdef TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 // BoringSSL. | |
| CDEF(ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256), | |
| #elif defined(TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305) // OpenSSL. | |
| CDEF(ECDHE_ECDSA_WITH_CHACHA20_POLY1305), | |
| #endif | |
| }; | |
| #undef CDEF | |
| static const cipher_list OK_DTLS13_ciphers[] = { | |
| #ifdef TLS1_3_CK_AES_128_GCM_SHA256 // BoringSSL TLS 1.3 | |
| {.cipher = static_cast<uint16_t>(TLS1_3_CK_AES_128_GCM_SHA256 & 0xffff), | |
| .cipher_str = "TLS_AES_128_GCM_SHA256"}, | |
| #endif | |
| #ifdef TLS1_3_CK_AES_256_GCM_SHA256 // BoringSSL TLS 1.3 | |
| {static_cast<uint16_t>(TLS1_3_CK_AES_256_GCM_SHA256 & 0xffff), | |
| "TLS_AES_256_GCM_SHA256"}, | |
| #endif | |
| #ifdef TLS1_3_CK_CHACHA20_POLY1305_SHA256 // BoringSSL TLS 1.3 | |
| {.cipher = | |
| static_cast<uint16_t>(TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff), | |
| .cipher_str = "TLS_CHACHA20_POLY1305_SHA256"}, | |
| #endif | |
| }; | |
| bool OpenSSLStreamAdapter::IsAcceptableCipher(int cipher, KeyType key_type) { | |
| if (key_type == KT_RSA) { | |
| for (const cipher_list& c : OK_RSA_ciphers) { | |
| if (cipher == c.cipher) { | |
| return true; | |
| } | |
| } | |
| } | |
| if (key_type == KT_ECDSA) { | |
| for (const cipher_list& c : OK_ECDSA_ciphers) { | |
| if (cipher == c.cipher) { | |
| return true; | |
| } | |
| } | |
| } | |
| for (const cipher_list& c : OK_DTLS13_ciphers) { | |
| if (cipher == c.cipher) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| bool OpenSSLStreamAdapter::IsAcceptableCipher(absl::string_view cipher, | |
| KeyType key_type) { | |
| if (key_type == KT_RSA) { | |
| for (const cipher_list& c : OK_RSA_ciphers) { | |
| if (cipher == c.cipher_str) { | |
| return true; | |
| } | |
| } | |
| } | |
| if (key_type == KT_ECDSA) { | |
| for (const cipher_list& c : OK_ECDSA_ciphers) { | |
| if (cipher == c.cipher_str) { | |
| return true; | |
| } | |
| } | |
| } | |
| for (const cipher_list& c : OK_DTLS13_ciphers) { | |
| if (cipher == c.cipher_str) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| void OpenSSLStreamAdapter::EnableTimeCallbackForTesting() { | |
| #ifdef OPENSSL_IS_BORINGSSL | |
| g_use_time_callback_for_testing = true; | |
| #endif | |
| } | |
| SSLProtocolVersion OpenSSLStreamAdapter::GetMaxSupportedDTLSProtocolVersion() { | |
| #if defined(OPENSSL_IS_BORINGSSL) && defined(DTLS1_3_VERSION) | |
| return SSL_PROTOCOL_DTLS_13; | |
| #else | |
| return SSL_PROTOCOL_DTLS_12; | |
| #endif | |
| } | |
| bool OpenSSLStreamAdapter::SetSslGroupIds(const std::vector<uint16_t>& groups) { | |
| if (state_ != SSL_NONE) { | |
| return false; | |
| } | |
| ssl_cipher_groups_ = groups; | |
| return true; | |
| } | |
| } // namespace webrtc |