// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #if !defined(DART_IO_SECURE_SOCKET_DISABLED) #include "platform/globals.h" #if defined(DART_HOST_OS_WINDOWS) #include "bin/security_context.h" #include #include #include #include #include "bin/directory.h" #include "bin/file.h" #include "bin/secure_socket_filter.h" #include "bin/secure_socket_utils.h" #include "platform/syslog.h" #ifndef DART_TARGET_OS_WINDOWS_UWP #pragma comment(lib, "crypt32.lib") #endif namespace dart { namespace bin { // The security context won't necessarily use the compiled-in root certificates, // but since there is no way to update the size of the allocation after creating // the weak persistent handle, we assume that it will. Note that when the // root certs aren't compiled in, |root_certificates_pem_length| is 0. const intptr_t SSLCertContext::kApproximateSize = sizeof(SSLCertContext) + root_certificates_pem_length; static void PrintSSLErr(const char* str) { int error = ERR_get_error(); char error_string[SecureSocketUtils::SSL_ERROR_MESSAGE_BUFFER_SIZE]; ERR_error_string_n(error, error_string, SecureSocketUtils::SSL_ERROR_MESSAGE_BUFFER_SIZE); Syslog::PrintErr("%s %s\n", str, error_string); } #ifndef DART_TARGET_OS_WINDOWS_UWP static bool AddCertificatesFromNamedSystemStore(const wchar_t* name, DWORD store_type, X509_STORE* store) { ASSERT(store_type == CERT_SYSTEM_STORE_CURRENT_USER || store_type == CERT_SYSTEM_STORE_LOCAL_MACHINE); if (SSL_LOG_STATUS) { Syslog::Print("AddCertificatesFromNamedSystemStore %ls type: %s\n", name, store_type == CERT_SYSTEM_STORE_CURRENT_USER ? "Current User" : "Local Machine"); } HCERTSTORE cert_store = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, // the encoding type is not needed NULL, // use the default HCRYPTPROV store_type | CERT_STORE_READONLY_FLAG, name); if (cert_store == nullptr) { if (SSL_LOG_STATUS) { DWORD error = GetLastError(); Syslog::PrintErr( "Failed to open Windows root store %ls type %d due to %d\n", name, store_type, error); } return false; } // Iterating through all certificates in the store. A nullptr is required to // start iteration. PCCERT_CONTEXT cert_context = nullptr; do { cert_context = CertEnumCertificatesInStore(cert_store, cert_context); if (cert_context == nullptr) { // reach the end of store. break; } BIO* root_cert_bio = BIO_new_mem_buf(const_cast(cert_context->pbCertEncoded), cert_context->cbCertEncoded); // `root_cert` has to be initialized to nullptr, otherwise, it will be // considered as an existing X509 and cause segmentation fault. X509* root_cert = nullptr; if (d2i_X509_bio(root_cert_bio, &root_cert) == nullptr) { if (SSL_LOG_STATUS) { PrintSSLErr("Fail to read certificate"); } BIO_free(root_cert_bio); continue; } BIO_free(root_cert_bio); if (SSL_LOG_STATUS) { auto s_name = X509_get_subject_name(root_cert); auto s_issuer_name = X509_get_issuer_name(root_cert); auto serial_number = X509_get_serialNumber(root_cert); BIGNUM* bn = ASN1_INTEGER_to_BN(serial_number, nullptr); char* hex = BN_bn2hex(bn); Syslog::Print("Considering root certificate serial: %s subject name: ", hex); OPENSSL_free(hex); X509_NAME_print_ex_fp(stdout, s_name, 4, 0); Syslog::Print(" issuer:"); X509_NAME_print_ex_fp(stdout, s_issuer_name, 4, 0); Syslog::Print("\n"); } if (!SecureSocketUtils::IsCurrentTimeInsideCertValidDateRange(root_cert)) { if (SSL_LOG_STATUS) { Syslog::Print("...certificate is outside of its valid date range\n"); } X509_free(root_cert); continue; } int status = X509_STORE_add_cert(store, root_cert); // Always free the certificate after use. X509_free(root_cert); if (status == 0) { int error = ERR_get_error(); if (ERR_GET_REASON(error) == X509_R_CERT_ALREADY_IN_HASH_TABLE) { if (SSL_LOG_STATUS) { Syslog::Print("...duplicate\n"); } continue; } if (SSL_LOG_STATUS) { PrintSSLErr("Failed to add certificate to x509 trust store"); } CertFreeCertificateContext(cert_context); CertCloseStore(cert_store, 0); return false; } } while (cert_context != nullptr); // It always returns non-zero. CertFreeCertificateContext(cert_context); if (!CertCloseStore(cert_store, 0)) { if (SSL_LOG_STATUS) { PrintSSLErr("Fail to close system root store"); } return false; } return true; } static bool AddCertificatesFromSystemStore(DWORD store_type, X509_STORE* store) { if (!AddCertificatesFromNamedSystemStore(L"ROOT", store_type, store)) { return false; } if (!AddCertificatesFromNamedSystemStore(L"CA", store_type, store)) { return false; } if (!AddCertificatesFromNamedSystemStore(L"TRUST", store_type, store)) { return false; } if (!AddCertificatesFromNamedSystemStore(L"MY", store_type, store)) { return false; } return true; } #endif // ifdef DART_TARGET_OS_WINDOWS_UWP // Add certificates from Windows trusted root store. static bool AddCertificatesFromRootStore(X509_STORE* store) { // The UWP platform doesn't support CertEnumCertificatesInStore hence // this function cannot work when compiled in UWP mode. #ifdef DART_TARGET_OS_WINDOWS_UWP return false; #else if (!AddCertificatesFromSystemStore(CERT_SYSTEM_STORE_CURRENT_USER, store)) { return false; } if (!AddCertificatesFromSystemStore(CERT_SYSTEM_STORE_LOCAL_MACHINE, store)) { return false; } return true; #endif // ifdef DART_TARGET_OS_WINDOWS_UWP } void SSLCertContext::TrustBuiltinRoots() { // First, try to use locations specified on the command line. if (root_certs_file() != nullptr) { LoadRootCertFile(root_certs_file()); return; } if (root_certs_cache() != nullptr) { LoadRootCertCache(root_certs_cache()); return; } if (bypass_trusting_system_roots()) { if (SSL_LOG_STATUS) { Syslog::Print("Bypass trusting Windows built-in roots\n"); } } else { if (SSL_LOG_STATUS) { Syslog::Print("Trusting Windows built-in roots\n"); } X509_STORE* store = SSL_CTX_get_cert_store(context()); if (AddCertificatesFromRootStore(store)) { return; } } // Reset store. SSL_CTX_set_cert_store will take ownership of store. A manual // free is not needed. SSL_CTX_set_cert_store(context(), X509_STORE_new()); // Fall back on the compiled-in certs if the standard locations don't exist, // or fail to load certificates from Windows root store. if (SSL_LOG_STATUS) { Syslog::Print("Trusting compiled-in roots\n"); } AddCompiledInCerts(); } void SSLCertContext::RegisterCallbacks(SSL* ssl) { // No callbacks to register for implementations using BoringSSL's built-in // verification mechanism. } TrustEvaluateHandlerFunc SSLCertContext::GetTrustEvaluateHandler() const { return nullptr; } } // namespace bin } // namespace dart #endif // defined(DART_HOST_OS_WINDOWS) #endif // !defined(DART_IO_SECURE_SOCKET_DISABLED)