diff --git a/CMakeLists.txt b/CMakeLists.txt index 883fbbc82..265f645d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,6 +100,7 @@ set( fc_sources src/crypto/private_key.cpp src/crypto/signature.cpp src/network/ip.cpp + src/network/platform_root_ca.cpp src/network/resolve.cpp src/network/udp_socket.cpp src/network/url.cpp @@ -152,7 +153,12 @@ target_include_directories(fc IF(NOT WIN32) set(LINK_USR_LOCAL_LIB -L/usr/local/lib) ENDIF() -target_link_libraries( fc PUBLIC ${LINK_USR_LOCAL_LIB} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${PLATFORM_SPECIFIC_LIBS} ${RPCRT4} ${CMAKE_DL_LIBS} ${rt_library} ${readline_libraries} ${ECC_LIB} ) + +IF(APPLE) + find_library(security_framework Security) + find_library(corefoundation_framework CoreFoundation) +ENDIF() +target_link_libraries( fc PUBLIC ${LINK_USR_LOCAL_LIB} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${PLATFORM_SPECIFIC_LIBS} ${RPCRT4} ${CMAKE_DL_LIBS} ${rt_library} ${readline_libraries} ${ECC_LIB} ${security_framework} ${corefoundation_framework} ) SET(OPENSSL_CONF_TARGET ) diff --git a/include/fc/network/platform_root_ca.hpp b/include/fc/network/platform_root_ca.hpp new file mode 100644 index 000000000..3679102d7 --- /dev/null +++ b/include/fc/network/platform_root_ca.hpp @@ -0,0 +1,13 @@ +/** + * @file + * @copyright defined in LICENSE.txt + */ +#pragma once + +#include + +namespace fc { + +//Add the platform's trusted root CAs to the ssl context +void add_platform_root_cas_to_context(boost::asio::ssl::context& ctx); +} \ No newline at end of file diff --git a/src/network/LICENSE.go b/src/network/LICENSE.go new file mode 100644 index 000000000..6a66aea5e --- /dev/null +++ b/src/network/LICENSE.go @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/network/platform_root_ca.cpp b/src/network/platform_root_ca.cpp new file mode 100644 index 000000000..f307fb537 --- /dev/null +++ b/src/network/platform_root_ca.cpp @@ -0,0 +1,123 @@ +/** + * @file + * @copyright defined in LICENSE.txt; Parts of this file Copyright (c) 2009 The Go Authors + */ +#include + +#include + +#include + +#if defined(__APPLE__) +#include +#include +#endif + +namespace fc { + +#if defined(__APPLE__) +static void add_macos_root_cas(boost::asio::ssl::context& ctx) { + boost::container::flat_set trusted_certs; + boost::container::flat_set untrusted_certs; + + SecTrustSettingsDomain domains[] = { kSecTrustSettingsDomainSystem, + kSecTrustSettingsDomainAdmin, + kSecTrustSettingsDomainUser }; + + unsigned int number_domains = sizeof(domains)/sizeof(domains[0]); + + for (unsigned int i = 0; i < number_domains; i++) { + CFArrayRef certs; + OSStatus err = SecTrustSettingsCopyCertificates(domains[i], &certs); + if(err != noErr) + continue; + + for(CFIndex j = 0; j < CFArrayGetCount(certs); ++j) { + CFArrayRef trustSettings = nullptr; + SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j); + if(cert == nullptr) + continue; + + bool untrusted{false}, trust_as_root{i == 0}, trust_root{false}; + if(i != 0) { + for (unsigned int k = i; k < number_domains; k++) { + CFArrayRef domainTrustSettings = nullptr; + err = SecTrustSettingsCopyTrustSettings(cert, domains[k], &domainTrustSettings); + if (err == errSecSuccess && domainTrustSettings != nullptr) { + if(trustSettings) + CFRelease(trustSettings); + trustSettings = domainTrustSettings; + } + } + if(trustSettings == nullptr) + continue; + for(CFIndex k = 0; k < CFArrayGetCount(trustSettings); k++) { + CFNumberRef cfNum; + CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, k); + if(CFDictionaryGetValueIfPresent(tSetting, kSecTrustSettingsResult, (const void**)&cfNum)){ + SInt32 result = 0; + CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result); + if(result == kSecTrustSettingsResultDeny) + untrusted = true; + else if (result == kSecTrustSettingsResultTrustAsRoot) + trust_as_root = true; + else if (result == kSecTrustSettingsResultTrustRoot) + trust_root = true; + } + } + CFRelease(trustSettings); + } + + //double check that these manually trusted ones are actually CAs + if(trust_root) { + CFErrorRef errRef = nullptr; + CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, &errRef); + if(errRef != nullptr) { + CFRelease(errRef); + continue; + } + CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, &errRef); + if(errRef != nullptr) { + CFRelease(subjectName); + CFRelease(errRef); + continue; + } + Boolean equal = CFEqual(subjectName, issuerName); + CFRelease(subjectName); + CFRelease(issuerName); + if(!equal) + continue; + } + + CFDataRef certAsPEM; + err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, nullptr, &certAsPEM); + if(err != noErr) + continue; + if(certAsPEM) { + if(!trust_root && !trust_as_root) + untrusted_certs.emplace((const char*)CFDataGetBytePtr(certAsPEM), CFDataGetLength(certAsPEM)); + else + trusted_certs.emplace((const char*)CFDataGetBytePtr(certAsPEM), CFDataGetLength(certAsPEM)); + CFRelease(certAsPEM); + } + } + CFRelease(certs); + } + for(const auto& untrusted : untrusted_certs) + trusted_certs.erase(untrusted); + boost::system::error_code dummy; + for(const auto& trusted : trusted_certs) + ctx.add_certificate_authority(boost::asio::const_buffer(trusted.data(), trusted.size()), dummy); +} +#endif + +void add_platform_root_cas_to_context(boost::asio::ssl::context& ctx) { +#if defined( __APPLE__ ) + add_macos_root_cas(ctx); +#elif defined( _WIN32 ) + FC_THROW("HTTPS on Windows not supported"); +#else + ctx.set_default_verify_paths(); +#endif +} +} \ No newline at end of file