diff options
Diffstat (limited to 'third_party/libwebrtc/rtc_base/openssl_utility.cc')
-rw-r--r-- | third_party/libwebrtc/rtc_base/openssl_utility.cc | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/third_party/libwebrtc/rtc_base/openssl_utility.cc b/third_party/libwebrtc/rtc_base/openssl_utility.cc new file mode 100644 index 0000000000..eba3788a94 --- /dev/null +++ b/third_party/libwebrtc/rtc_base/openssl_utility.cc @@ -0,0 +1,274 @@ +/* + * Copyright 2018 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_utility.h" + +#include "absl/strings/string_view.h" +#if defined(WEBRTC_WIN) +// Must be included first before openssl headers. +#include "rtc_base/win32.h" // NOLINT +#endif // WEBRTC_WIN + +#ifdef OPENSSL_IS_BORINGSSL +#include <openssl/pool.h> +#endif +#include <openssl/err.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <stddef.h> + +#include "rtc_base/arraysize.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/openssl.h" +#include "rtc_base/ssl_identity.h" +#ifndef WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS +#include "rtc_base/ssl_roots.h" +#endif // WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS + +namespace rtc { +namespace openssl { + +// Holds various helper methods. +namespace { + +// TODO(crbug.com/webrtc/11710): When OS certificate verification is available, +// and we don't need VerifyPeerCertMatchesHost, don't compile this in order to +// avoid a dependency on OpenSSL X509 objects (see crbug.com/webrtc/11410). +void LogCertificates(SSL* ssl, X509* certificate) { +// Logging certificates is extremely verbose. So it is disabled by default. +#ifdef LOG_CERTIFICATES + BIO* mem = BIO_new(BIO_s_mem()); + if (mem == nullptr) { + RTC_DLOG(LS_ERROR) << "BIO_new() failed to allocate memory."; + return; + } + + RTC_DLOG(LS_INFO) << "Certificate from server:"; + X509_print_ex(mem, certificate, XN_FLAG_SEP_CPLUS_SPC, X509_FLAG_NO_HEADER); + BIO_write(mem, "\0", 1); + + char* buffer = nullptr; + BIO_get_mem_data(mem, &buffer); + if (buffer != nullptr) { + RTC_DLOG(LS_INFO) << buffer; + } else { + RTC_DLOG(LS_ERROR) << "BIO_get_mem_data() failed to get buffer."; + } + BIO_free(mem); + + const char* cipher_name = SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)); + if (cipher_name != nullptr) { + RTC_DLOG(LS_INFO) << "Cipher: " << cipher_name; + } else { + RTC_DLOG(LS_ERROR) << "SSL_CIPHER_DESCRIPTION() failed to get cipher_name."; + } +#endif +} +} // namespace + +#ifdef OPENSSL_IS_BORINGSSL +bool ParseCertificate(CRYPTO_BUFFER* cert_buffer, + CBS* signature_algorithm_oid, + int64_t* expiration_time) { + CBS cbs; + CRYPTO_BUFFER_init_CBS(cert_buffer, &cbs); + + // Certificate ::= SEQUENCE { + CBS certificate; + if (!CBS_get_asn1(&cbs, &certificate, CBS_ASN1_SEQUENCE)) { + return false; + } + // tbsCertificate TBSCertificate, + CBS tbs_certificate; + if (!CBS_get_asn1(&certificate, &tbs_certificate, CBS_ASN1_SEQUENCE)) { + return false; + } + // signatureAlgorithm AlgorithmIdentifier, + CBS signature_algorithm; + if (!CBS_get_asn1(&certificate, &signature_algorithm, CBS_ASN1_SEQUENCE)) { + return false; + } + if (!CBS_get_asn1(&signature_algorithm, signature_algorithm_oid, + CBS_ASN1_OBJECT)) { + return false; + } + // signatureValue BIT STRING } + if (!CBS_get_asn1(&certificate, nullptr, CBS_ASN1_BITSTRING)) { + return false; + } + if (CBS_len(&certificate)) { + return false; + } + + // Now parse the inner TBSCertificate. + // version [0] EXPLICIT Version DEFAULT v1, + if (!CBS_get_optional_asn1( + &tbs_certificate, nullptr, nullptr, + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC)) { + return false; + } + // serialNumber CertificateSerialNumber, + if (!CBS_get_asn1(&tbs_certificate, nullptr, CBS_ASN1_INTEGER)) { + return false; + } + // signature AlgorithmIdentifier + if (!CBS_get_asn1(&tbs_certificate, nullptr, CBS_ASN1_SEQUENCE)) { + return false; + } + // issuer Name, + if (!CBS_get_asn1(&tbs_certificate, nullptr, CBS_ASN1_SEQUENCE)) { + return false; + } + // validity Validity, + CBS validity; + if (!CBS_get_asn1(&tbs_certificate, &validity, CBS_ASN1_SEQUENCE)) { + return false; + } + // Skip over notBefore. + if (!CBS_get_any_asn1_element(&validity, nullptr, nullptr, nullptr)) { + return false; + } + // Parse notAfter. + CBS not_after; + unsigned not_after_tag; + if (!CBS_get_any_asn1(&validity, ¬_after, ¬_after_tag)) { + return false; + } + bool long_format; + if (not_after_tag == CBS_ASN1_UTCTIME) { + long_format = false; + } else if (not_after_tag == CBS_ASN1_GENERALIZEDTIME) { + long_format = true; + } else { + return false; + } + if (expiration_time) { + *expiration_time = + ASN1TimeToSec(CBS_data(¬_after), CBS_len(¬_after), long_format); + } + // subject Name, + if (!CBS_get_asn1_element(&tbs_certificate, nullptr, CBS_ASN1_SEQUENCE)) { + return false; + } + // subjectPublicKeyInfo SubjectPublicKeyInfo, + if (!CBS_get_asn1(&tbs_certificate, nullptr, CBS_ASN1_SEQUENCE)) { + return false; + } + // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL + if (!CBS_get_optional_asn1(&tbs_certificate, nullptr, nullptr, + 0x01 | CBS_ASN1_CONTEXT_SPECIFIC)) { + return false; + } + // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL + if (!CBS_get_optional_asn1(&tbs_certificate, nullptr, nullptr, + 0x02 | CBS_ASN1_CONTEXT_SPECIFIC)) { + return false; + } + // extensions [3] EXPLICIT Extensions OPTIONAL + if (!CBS_get_optional_asn1( + &tbs_certificate, nullptr, nullptr, + 0x03 | CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC)) { + return false; + } + if (CBS_len(&tbs_certificate)) { + return false; + } + + return true; +} +#endif // OPENSSL_IS_BORINGSSL + +bool VerifyPeerCertMatchesHost(SSL* ssl, absl::string_view host) { + if (host.empty()) { + RTC_DLOG(LS_ERROR) << "Hostname is empty. Cannot verify peer certificate."; + return false; + } + + if (ssl == nullptr) { + RTC_DLOG(LS_ERROR) << "SSL is nullptr. Cannot verify peer certificate."; + return false; + } + +#ifdef OPENSSL_IS_BORINGSSL + // We can't grab a X509 object directly, as the SSL context may have been + // initialized with TLS_with_buffers_method. + const STACK_OF(CRYPTO_BUFFER)* chain = SSL_get0_peer_certificates(ssl); + if (chain == nullptr || sk_CRYPTO_BUFFER_num(chain) == 0) { + RTC_LOG(LS_ERROR) + << "SSL_get0_peer_certificates failed. This should never happen."; + return false; + } + CRYPTO_BUFFER* leaf = sk_CRYPTO_BUFFER_value(chain, 0); + bssl::UniquePtr<X509> x509(X509_parse_from_buffer(leaf)); + if (!x509) { + RTC_LOG(LS_ERROR) << "Failed to parse certificate to X509 object."; + return false; + } + LogCertificates(ssl, x509.get()); + return X509_check_host(x509.get(), host.data(), host.size(), 0, nullptr) == 1; +#else // OPENSSL_IS_BORINGSSL + X509* certificate = SSL_get_peer_certificate(ssl); + if (certificate == nullptr) { + RTC_LOG(LS_ERROR) + << "SSL_get_peer_certificate failed. This should never happen."; + return false; + } + + LogCertificates(ssl, certificate); + + bool is_valid_cert_name = + X509_check_host(certificate, host.data(), host.size(), 0, nullptr) == 1; + X509_free(certificate); + return is_valid_cert_name; +#endif // !defined(OPENSSL_IS_BORINGSSL) +} + +void LogSSLErrors(absl::string_view prefix) { + char error_buf[200]; + unsigned long err; // NOLINT + + while ((err = ERR_get_error()) != 0) { + ERR_error_string_n(err, error_buf, sizeof(error_buf)); + RTC_LOG(LS_ERROR) << prefix << ": " << error_buf << "\n"; + } +} + +#ifndef WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS +bool LoadBuiltinSSLRootCertificates(SSL_CTX* ctx) { + int count_of_added_certs = 0; + for (size_t i = 0; i < arraysize(kSSLCertCertificateList); i++) { + const unsigned char* cert_buffer = kSSLCertCertificateList[i]; + size_t cert_buffer_len = kSSLCertCertificateSizeList[i]; + X509* cert = d2i_X509(nullptr, &cert_buffer, + checked_cast<long>(cert_buffer_len)); // NOLINT + if (cert) { + int return_value = X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx), cert); + if (return_value == 0) { + RTC_LOG(LS_WARNING) << "Unable to add certificate."; + } else { + count_of_added_certs++; + } + X509_free(cert); + } + } + return count_of_added_certs > 0; +} +#endif // WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS + +#ifdef OPENSSL_IS_BORINGSSL +CRYPTO_BUFFER_POOL* GetBufferPool() { + static CRYPTO_BUFFER_POOL* instance = CRYPTO_BUFFER_POOL_new(); + return instance; +} +#endif + +} // namespace openssl +} // namespace rtc |