diff --git a/webrtc/base/openssl.h b/webrtc/base/openssl.h index 58e480fda0..2071619d5d 100644 --- a/webrtc/base/openssl.h +++ b/webrtc/base/openssl.h @@ -13,4 +13,8 @@ #include +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) +#error OpenSSL is older than 1.0.0, which is the minimum supported version. +#endif + #endif // WEBRTC_BASE_OPENSSL_H_ diff --git a/webrtc/base/openssladapter.cc b/webrtc/base/openssladapter.cc index 818e326940..264dae9e28 100644 --- a/webrtc/base/openssladapter.cc +++ b/webrtc/base/openssladapter.cc @@ -40,6 +40,34 @@ #include "webrtc/base/stringutils.h" #include "webrtc/base/thread.h" +#ifndef OPENSSL_IS_BORINGSSL + +// TODO: Use a nicer abstraction for mutex. + +#if defined(WEBRTC_WIN) + #define MUTEX_TYPE HANDLE + #define MUTEX_SETUP(x) (x) = CreateMutex(NULL, FALSE, NULL) + #define MUTEX_CLEANUP(x) CloseHandle(x) + #define MUTEX_LOCK(x) WaitForSingleObject((x), INFINITE) + #define MUTEX_UNLOCK(x) ReleaseMutex(x) + #define THREAD_ID GetCurrentThreadId() +#elif defined(WEBRTC_POSIX) + #define MUTEX_TYPE pthread_mutex_t + #define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL) + #define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x)) + #define MUTEX_LOCK(x) pthread_mutex_lock(&(x)) + #define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x)) + #define THREAD_ID pthread_self() +#else + #error You must define mutex operations appropriate for your platform! +#endif + +struct CRYPTO_dynlock_value { + MUTEX_TYPE mutex; +}; + +#endif // #ifndef OPENSSL_IS_BORINGSSL + ////////////////////////////////////////////////////////////////////// // SocketBIO ////////////////////////////////////////////////////////////////////// @@ -149,14 +177,105 @@ static long socket_ctrl(BIO* b, int cmd, long num, void* ptr) { namespace rtc { +#ifndef OPENSSL_IS_BORINGSSL + +// This array will store all of the mutexes available to OpenSSL. +static MUTEX_TYPE* mutex_buf = NULL; + +static void locking_function(int mode, int n, const char * file, int line) { + if (mode & CRYPTO_LOCK) { + MUTEX_LOCK(mutex_buf[n]); + } else { + MUTEX_UNLOCK(mutex_buf[n]); + } +} + +static unsigned long id_function() { // NOLINT + // Use old-style C cast because THREAD_ID's type varies with the platform, + // in some cases requiring static_cast, and in others requiring + // reinterpret_cast. + return (unsigned long)THREAD_ID; // NOLINT +} + +static CRYPTO_dynlock_value* dyn_create_function(const char* file, int line) { + CRYPTO_dynlock_value* value = new CRYPTO_dynlock_value; + if (!value) + return NULL; + MUTEX_SETUP(value->mutex); + return value; +} + +static void dyn_lock_function(int mode, CRYPTO_dynlock_value* l, + const char* file, int line) { + if (mode & CRYPTO_LOCK) { + MUTEX_LOCK(l->mutex); + } else { + MUTEX_UNLOCK(l->mutex); + } +} + +static void dyn_destroy_function(CRYPTO_dynlock_value* l, + const char* file, int line) { + MUTEX_CLEANUP(l->mutex); + delete l; +} + +#endif // #ifndef OPENSSL_IS_BORINGSSL + VerificationCallback OpenSSLAdapter::custom_verify_callback_ = NULL; bool OpenSSLAdapter::InitializeSSL(VerificationCallback callback) { - CRYPTO_library_init(); + if (!InitializeSSLThread() || !SSL_library_init()) + return false; +#if !defined(ADDRESS_SANITIZER) || !defined(WEBRTC_MAC) || defined(WEBRTC_IOS) + // Loading the error strings crashes mac_asan. Omit this debugging aid there. + SSL_load_error_strings(); +#endif + ERR_load_BIO_strings(); + OpenSSL_add_all_algorithms(); + RAND_poll(); custom_verify_callback_ = callback; return true; } +bool OpenSSLAdapter::InitializeSSLThread() { + // BoringSSL is doing the locking internally, so the callbacks are not used + // in this case (and are no-ops anyways). +#ifndef OPENSSL_IS_BORINGSSL + mutex_buf = new MUTEX_TYPE[CRYPTO_num_locks()]; + if (!mutex_buf) + return false; + for (int i = 0; i < CRYPTO_num_locks(); ++i) + MUTEX_SETUP(mutex_buf[i]); + + // we need to cast our id_function to return an unsigned long -- pthread_t is + // a pointer + CRYPTO_set_id_callback(id_function); + CRYPTO_set_locking_callback(locking_function); + CRYPTO_set_dynlock_create_callback(dyn_create_function); + CRYPTO_set_dynlock_lock_callback(dyn_lock_function); + CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function); +#endif // #ifndef OPENSSL_IS_BORINGSSL + return true; +} + +bool OpenSSLAdapter::CleanupSSL() { +#ifndef OPENSSL_IS_BORINGSSL + if (!mutex_buf) + return false; + CRYPTO_set_id_callback(NULL); + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_dynlock_create_callback(NULL); + CRYPTO_set_dynlock_lock_callback(NULL); + CRYPTO_set_dynlock_destroy_callback(NULL); + for (int i = 0; i < CRYPTO_num_locks(); ++i) + MUTEX_CLEANUP(mutex_buf[i]); + delete [] mutex_buf; + mutex_buf = NULL; +#endif // #ifndef OPENSSL_IS_BORINGSSL + return true; +} + OpenSSLAdapter::OpenSSLAdapter(AsyncSocket* socket) : SSLAdapter(socket), state_(SSL_NONE), diff --git a/webrtc/base/openssladapter.h b/webrtc/base/openssladapter.h index 14d8bea4f5..cdf45e603f 100644 --- a/webrtc/base/openssladapter.h +++ b/webrtc/base/openssladapter.h @@ -27,6 +27,8 @@ namespace rtc { class OpenSSLAdapter : public SSLAdapter, public MessageHandler { public: static bool InitializeSSL(VerificationCallback callback); + static bool InitializeSSLThread(); + static bool CleanupSSL(); OpenSSLAdapter(AsyncSocket* socket); ~OpenSSLAdapter() override; diff --git a/webrtc/base/opensslidentity.cc b/webrtc/base/opensslidentity.cc index 269bfced17..3c421db07c 100644 --- a/webrtc/base/opensslidentity.cc +++ b/webrtc/base/opensslidentity.cc @@ -174,7 +174,11 @@ OpenSSLKeyPair* OpenSSLKeyPair::GetReference() { } void OpenSSLKeyPair::AddReference() { +#if defined(OPENSSL_IS_BORINGSSL) EVP_PKEY_up_ref(pkey_); +#else + CRYPTO_add(&pkey_->references, 1, CRYPTO_LOCK_EVP_PKEY); +#endif } #if !defined(NDEBUG) @@ -357,7 +361,11 @@ void OpenSSLCertificate::ToDER(Buffer* der_buffer) const { void OpenSSLCertificate::AddReference() const { ASSERT(x509_ != NULL); +#if defined(OPENSSL_IS_BORINGSSL) X509_up_ref(x509_); +#else + CRYPTO_add(&x509_->references, 1, CRYPTO_LOCK_X509); +#endif } // Documented in sslidentity.h. diff --git a/webrtc/base/opensslstreamadapter.cc b/webrtc/base/opensslstreamadapter.cc index 831136c839..44f1b718f5 100644 --- a/webrtc/base/opensslstreamadapter.cc +++ b/webrtc/base/opensslstreamadapter.cc @@ -38,6 +38,11 @@ namespace rtc { +#if (OPENSSL_VERSION_NUMBER >= 0x10001000L) +#define HAVE_DTLS_SRTP +#endif + +#ifdef HAVE_DTLS_SRTP // 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(). @@ -51,6 +56,90 @@ static SrtpCipherMapEntry SrtpCipherMap[] = { {"SRTP_AES128_CM_SHA1_80", SRTP_AES128_CM_SHA1_80}, {"SRTP_AES128_CM_SHA1_32", SRTP_AES128_CM_SHA1_32}, {nullptr, 0}}; +#endif + +#ifndef OPENSSL_IS_BORINGSSL + +// Cipher name table. Maps internal OpenSSL cipher ids to the RFC name. +struct SslCipherMapEntry { + uint32_t openssl_id; + const char* rfc_name; +}; + +#define DEFINE_CIPHER_ENTRY_SSL3(name) {SSL3_CK_##name, "TLS_"#name} +#define DEFINE_CIPHER_ENTRY_TLS1(name) {TLS1_CK_##name, "TLS_"#name} + +// There currently is no method available to get a RFC-compliant name for a +// cipher suite from BoringSSL, so we need to define the mapping manually here. +// This should go away once BoringSSL supports "SSL_CIPHER_standard_name" +// (as available in OpenSSL if compiled with tracing enabled) or a similar +// method. +static const SslCipherMapEntry kSslCipherMap[] = { + // TLS v1.0 ciphersuites from RFC2246. + DEFINE_CIPHER_ENTRY_SSL3(RSA_RC4_128_SHA), + {SSL3_CK_RSA_DES_192_CBC3_SHA, + "TLS_RSA_WITH_3DES_EDE_CBC_SHA"}, + + // AES ciphersuites from RFC3268. + {TLS1_CK_RSA_WITH_AES_128_SHA, + "TLS_RSA_WITH_AES_128_CBC_SHA"}, + {TLS1_CK_DHE_RSA_WITH_AES_128_SHA, + "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"}, + {TLS1_CK_RSA_WITH_AES_256_SHA, + "TLS_RSA_WITH_AES_256_CBC_SHA"}, + {TLS1_CK_DHE_RSA_WITH_AES_256_SHA, + "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"}, + + // ECC ciphersuites from RFC4492. + DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_RC4_128_SHA), + {TLS1_CK_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA, + "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA"}, + DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_AES_128_CBC_SHA), + DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_AES_256_CBC_SHA), + + DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_RC4_128_SHA), + {TLS1_CK_ECDHE_RSA_WITH_DES_192_CBC3_SHA, + "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"}, + DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_AES_128_CBC_SHA), + DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_AES_256_CBC_SHA), + + // TLS v1.2 ciphersuites. + {TLS1_CK_RSA_WITH_AES_128_SHA256, + "TLS_RSA_WITH_AES_128_CBC_SHA256"}, + {TLS1_CK_RSA_WITH_AES_256_SHA256, + "TLS_RSA_WITH_AES_256_CBC_SHA256"}, + {TLS1_CK_DHE_RSA_WITH_AES_128_SHA256, + "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"}, + {TLS1_CK_DHE_RSA_WITH_AES_256_SHA256, + "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"}, + + // TLS v1.2 GCM ciphersuites from RFC5288. + DEFINE_CIPHER_ENTRY_TLS1(RSA_WITH_AES_128_GCM_SHA256), + DEFINE_CIPHER_ENTRY_TLS1(RSA_WITH_AES_256_GCM_SHA384), + DEFINE_CIPHER_ENTRY_TLS1(DHE_RSA_WITH_AES_128_GCM_SHA256), + DEFINE_CIPHER_ENTRY_TLS1(DHE_RSA_WITH_AES_256_GCM_SHA384), + DEFINE_CIPHER_ENTRY_TLS1(DH_RSA_WITH_AES_128_GCM_SHA256), + DEFINE_CIPHER_ENTRY_TLS1(DH_RSA_WITH_AES_256_GCM_SHA384), + + // ECDH HMAC based ciphersuites from RFC5289. + {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_SHA256, + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"}, + {TLS1_CK_ECDHE_ECDSA_WITH_AES_256_SHA384, + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"}, + {TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256, + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"}, + {TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384, + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"}, + + // ECDH GCM based ciphersuites from RFC5289. + DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_AES_128_GCM_SHA256), + DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_AES_256_GCM_SHA384), + DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_AES_128_GCM_SHA256), + DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_AES_256_GCM_SHA384), + + {0, NULL} +}; +#endif // #ifndef OPENSSL_IS_BORINGSSL #if defined(_MSC_VER) #pragma warning(push) @@ -237,6 +326,7 @@ bool OpenSSLStreamAdapter::SetPeerCertificateDigest(const std::string } std::string OpenSSLStreamAdapter::SslCipherSuiteToName(int cipher_suite) { +#ifdef OPENSSL_IS_BORINGSSL const SSL_CIPHER* ssl_cipher = SSL_get_cipher_by_value(cipher_suite); if (!ssl_cipher) { return std::string(); @@ -245,6 +335,15 @@ std::string OpenSSLStreamAdapter::SslCipherSuiteToName(int cipher_suite) { std::string rfc_name = std::string(cipher_name); OPENSSL_free(cipher_name); return rfc_name; +#else + for (const SslCipherMapEntry* entry = kSslCipherMap; entry->rfc_name; + ++entry) { + if (cipher_suite == static_cast(entry->openssl_id)) { + return entry->rfc_name; + } + } + return std::string(); +#endif } bool OpenSSLStreamAdapter::GetSslCipherSuite(int* cipher_suite) { @@ -289,6 +388,7 @@ bool OpenSSLStreamAdapter::ExportKeyingMaterial(const std::string& label, bool use_context, uint8_t* result, size_t result_len) { +#ifdef HAVE_DTLS_SRTP int i; i = SSL_export_keying_material(ssl_, result, result_len, label.c_str(), @@ -299,10 +399,14 @@ bool OpenSSLStreamAdapter::ExportKeyingMaterial(const std::string& label, return false; return true; +#else + return false; +#endif } bool OpenSSLStreamAdapter::SetDtlsSrtpCryptoSuites( const std::vector& ciphers) { +#ifdef HAVE_DTLS_SRTP std::string internal_ciphers; if (state_ != SSL_NONE) @@ -333,9 +437,13 @@ bool OpenSSLStreamAdapter::SetDtlsSrtpCryptoSuites( srtp_ciphers_ = internal_ciphers; return true; +#else + return false; +#endif } bool OpenSSLStreamAdapter::GetDtlsSrtpCryptoSuite(int* crypto_suite) { +#ifdef HAVE_DTLS_SRTP ASSERT(state_ == SSL_CONNECTED); if (state_ != SSL_CONNECTED) return false; @@ -349,6 +457,9 @@ bool OpenSSLStreamAdapter::GetDtlsSrtpCryptoSuite(int* crypto_suite) { *crypto_suite = srtp_profile->id; ASSERT(!SrtpCryptoSuiteToName(*crypto_suite).empty()); return true; +#else + return false; +#endif } int OpenSSLStreamAdapter::StartSSLWithServer(const char* server_name) { @@ -661,6 +772,13 @@ int OpenSSLStreamAdapter::BeginSSL() { SSL_set_app_data(ssl_, this); SSL_set_bio(ssl_, bio, bio); // the SSL object owns the bio now. +#ifndef OPENSSL_IS_BORINGSSL + if (ssl_mode_ == SSL_MODE_DTLS) { + // Enable read-ahead for DTLS so whole packets are read from internal BIO + // before parsing. This is done internally by BoringSSL for DTLS. + SSL_set_read_ahead(ssl_, 1); + } +#endif SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); @@ -780,13 +898,73 @@ void OpenSSLStreamAdapter::OnMessage(Message* msg) { SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() { SSL_CTX *ctx = NULL; +#ifdef OPENSSL_IS_BORINGSSL ctx = SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ? DTLS_method() : TLS_method()); // Version limiting for BoringSSL will be done below. +#else + const SSL_METHOD* method; + switch (ssl_max_version_) { + case SSL_PROTOCOL_TLS_10: + case SSL_PROTOCOL_TLS_11: + // OpenSSL doesn't support setting min/max versions, so we always use + // (D)TLS 1.0 if a max. version below the max. available is requested. + if (ssl_mode_ == SSL_MODE_DTLS) { + if (role_ == SSL_CLIENT) { + method = DTLSv1_client_method(); + } else { + method = DTLSv1_server_method(); + } + } else { + if (role_ == SSL_CLIENT) { + method = TLSv1_client_method(); + } else { + method = TLSv1_server_method(); + } + } + break; + case SSL_PROTOCOL_TLS_12: + default: + if (ssl_mode_ == SSL_MODE_DTLS) { +#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) + // DTLS 1.2 only available starting from OpenSSL 1.0.2 + if (role_ == SSL_CLIENT) { + method = DTLS_client_method(); + } else { + method = DTLS_server_method(); + } +#else + if (role_ == SSL_CLIENT) { + method = DTLSv1_client_method(); + } else { + method = DTLSv1_server_method(); + } +#endif + } else { +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + // New API only available starting from OpenSSL 1.1.0 + if (role_ == SSL_CLIENT) { + method = TLS_client_method(); + } else { + method = TLS_server_method(); + } +#else + if (role_ == SSL_CLIENT) { + method = SSLv23_client_method(); + } else { + method = SSLv23_server_method(); + } +#endif + } + break; + } + ctx = SSL_CTX_new(method); +#endif // OPENSSL_IS_BORINGSSL if (ctx == NULL) return NULL; +#ifdef OPENSSL_IS_BORINGSSL SSL_CTX_set_min_version(ctx, ssl_mode_ == SSL_MODE_DTLS ? DTLS1_VERSION : TLS1_VERSION); switch (ssl_max_version_) { @@ -804,6 +982,7 @@ SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() { DTLS1_2_VERSION : TLS1_2_VERSION); break; } +#endif if (identity_ && !identity_->ConfigureIdentity(ctx)) { SSL_CTX_free(ctx); @@ -831,12 +1010,14 @@ SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() { SSL_CTX_set_cipher_list(ctx, "DEFAULT:!NULL:!aNULL:!SHA256:!SHA384:!aECDH:!AESGCM+AES256:!aPSK"); +#ifdef HAVE_DTLS_SRTP if (!srtp_ciphers_.empty()) { if (SSL_CTX_set_tlsext_use_srtp(ctx, srtp_ciphers_.c_str())) { SSL_CTX_free(ctx); return NULL; } } +#endif return ctx; } @@ -928,11 +1109,19 @@ bool OpenSSLStreamAdapter::HaveDtls() { } bool OpenSSLStreamAdapter::HaveDtlsSrtp() { +#ifdef HAVE_DTLS_SRTP return true; +#else + return false; +#endif } bool OpenSSLStreamAdapter::HaveExporter() { +#ifdef HAVE_DTLS_SRTP return true; +#else + return false; +#endif } #define CDEF(X) \ diff --git a/webrtc/base/ssladapter.cc b/webrtc/base/ssladapter.cc index 68c3840b5b..454a56637d 100644 --- a/webrtc/base/ssladapter.cc +++ b/webrtc/base/ssladapter.cc @@ -44,14 +44,20 @@ bool InitializeSSL(VerificationCallback callback) { return OpenSSLAdapter::InitializeSSL(callback); } +bool InitializeSSLThread() { + return OpenSSLAdapter::InitializeSSLThread(); +} + +bool CleanupSSL() { + return OpenSSLAdapter::CleanupSSL(); +} + #else // !SSL_USE_OPENSSL bool InitializeSSL(VerificationCallback callback) { return true; } -#endif // SSL_USE_OPENSSL - bool InitializeSSLThread() { return true; } @@ -60,6 +66,8 @@ bool CleanupSSL() { return true; } +#endif // SSL_USE_OPENSSL + /////////////////////////////////////////////////////////////////////////////// } // namespace rtc