Commit d0de1771 authored by Vlad Tsyrklevich's avatar Vlad Tsyrklevich Committed by Commit Bot

[CFI] Use ProtectedMemory in CertVerifyProcNSS

Because CertVerifyProcNSS dynamically resolves a pointer to the function
CERT_CacheOCSPResponseFromSideChannel(), Control Flow Integrity [1]
indirect call (cfi-icall) checking can not verify that it is the
intended target for that function pointer call site.

Since we can not use cfi-icall to check the function pointer, instead we
place the pointer in ProtectedMemory, a wrapper for keeping variables in
read-only memory except for when they are initialized. After setting the
pointer in protected memory we can use the UnsanitizedCfiCall wrapper to
disable cfi-icall checking when calling it since we know it can not be
tampered with.

[1] https://www.chromium.org/developers/testing/control-flow-integrity

Bug: 771365
Cq-Include-Trybots: master.tryserver.chromium.android:android_cronet_tester;master.tryserver.chromium.mac:ios-simulator-cronet
Change-Id: I5d65b3591681f3daa917b6516eec1e5e47513d12
Reviewed-on: https://chromium-review.googlesource.com/765098Reviewed-by: default avatarPeter Collingbourne <pcc@chromium.org>
Reviewed-by: default avatarEric Roman <eroman@chromium.org>
Commit-Queue: Peter Collingbourne <pcc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#517169}
parent 0fd7eeee
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/protected_memory.h"
#include "base/memory/protected_memory_cfi.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "crypto/nss_util.h" #include "crypto/nss_util.h"
#include "crypto/scoped_nss_types.h" #include "crypto/scoped_nss_types.h"
...@@ -38,6 +40,28 @@ namespace net { ...@@ -38,6 +40,28 @@ namespace net {
namespace { namespace {
using CacheOCSPResponseFunction = SECStatus (*)(CERTCertDBHandle* handle,
CERTCertificate* cert,
PRTime time,
const SECItem* encodedResponse,
void* pwArg);
static PROTECTED_MEMORY_SECTION base::ProtectedMemory<CacheOCSPResponseFunction>
g_cache_ocsp_response;
// The function pointer for CERT_CacheOCSPResponseFromSideChannel is saved to
// read-only memory after being dynamically resolved as a security mitigation to
// prevent the pointer from being tampered with. See crbug.com/771365 for
// details.
const base::ProtectedMemory<CacheOCSPResponseFunction>&
ResolveCacheOCSPResponse() {
static base::ProtectedMemory<CacheOCSPResponseFunction>::Initializer init(
&g_cache_ocsp_response,
reinterpret_cast<CacheOCSPResponseFunction>(
dlsym(RTLD_DEFAULT, "CERT_CacheOCSPResponseFromSideChannel")));
return g_cache_ocsp_response;
}
typedef std::unique_ptr< typedef std::unique_ptr<
CERTCertificatePolicies, CERTCertificatePolicies,
crypto::NSSDestroyer<CERTCertificatePolicies, crypto::NSSDestroyer<CERTCertificatePolicies,
...@@ -754,13 +778,6 @@ ScopedCERTCertList CertificateListToCERTCertListIgnoringErrors( ...@@ -754,13 +778,6 @@ ScopedCERTCertList CertificateListToCERTCertListIgnoringErrors(
} // namespace } // namespace
CertVerifyProcNSS::CertVerifyProcNSS()
: cache_ocsp_response_from_side_channel_(
reinterpret_cast<CacheOCSPResponseFromSideChannelFunction>(
dlsym(RTLD_DEFAULT, "CERT_CacheOCSPResponseFromSideChannel")))
{
}
CertVerifyProcNSS::~CertVerifyProcNSS() {} CertVerifyProcNSS::~CertVerifyProcNSS() {}
bool CertVerifyProcNSS::SupportsAdditionalTrustAnchors() const { bool CertVerifyProcNSS::SupportsAdditionalTrustAnchors() const {
...@@ -768,7 +785,7 @@ bool CertVerifyProcNSS::SupportsAdditionalTrustAnchors() const { ...@@ -768,7 +785,7 @@ bool CertVerifyProcNSS::SupportsAdditionalTrustAnchors() const {
} }
bool CertVerifyProcNSS::SupportsOCSPStapling() const { bool CertVerifyProcNSS::SupportsOCSPStapling() const {
return cache_ocsp_response_from_side_channel_; return *ResolveCacheOCSPResponse() != nullptr;
} }
int CertVerifyProcNSS::VerifyInternalImpl( int CertVerifyProcNSS::VerifyInternalImpl(
...@@ -793,7 +810,7 @@ int CertVerifyProcNSS::VerifyInternalImpl( ...@@ -793,7 +810,7 @@ int CertVerifyProcNSS::VerifyInternalImpl(
} }
CERTCertificate* cert_handle = input_chain[0].get(); CERTCertificate* cert_handle = input_chain[0].get();
if (!ocsp_response.empty() && cache_ocsp_response_from_side_channel_) { if (!ocsp_response.empty() && SupportsOCSPStapling()) {
// Note: NSS uses a thread-safe global hash table, so this call will // Note: NSS uses a thread-safe global hash table, so this call will
// affect any concurrent verification operations on |cert| or copies of // affect any concurrent verification operations on |cert| or copies of
// the same certificate. This is an unavoidable limitation of NSS's OCSP // the same certificate. This is an unavoidable limitation of NSS's OCSP
...@@ -802,9 +819,9 @@ int CertVerifyProcNSS::VerifyInternalImpl( ...@@ -802,9 +819,9 @@ int CertVerifyProcNSS::VerifyInternalImpl(
ocsp_response_item.data = reinterpret_cast<unsigned char*>( ocsp_response_item.data = reinterpret_cast<unsigned char*>(
const_cast<char*>(ocsp_response.data())); const_cast<char*>(ocsp_response.data()));
ocsp_response_item.len = ocsp_response.size(); ocsp_response_item.len = ocsp_response.size();
cache_ocsp_response_from_side_channel_(CERT_GetDefaultCertDB(), cert_handle, UnsanitizedCfiCall(ResolveCacheOCSPResponse())(
PR_Now(), &ocsp_response_item, CERT_GetDefaultCertDB(), cert_handle, PR_Now(), &ocsp_response_item,
nullptr); nullptr);
} }
// Setup a callback to call into CheckChainRevocationWithCRLSet with the // Setup a callback to call into CheckChainRevocationWithCRLSet with the
......
...@@ -15,7 +15,7 @@ namespace net { ...@@ -15,7 +15,7 @@ namespace net {
// Performs certificate path construction and validation using NSS's libpkix. // Performs certificate path construction and validation using NSS's libpkix.
class NET_EXPORT_PRIVATE CertVerifyProcNSS : public CertVerifyProc { class NET_EXPORT_PRIVATE CertVerifyProcNSS : public CertVerifyProc {
public: public:
CertVerifyProcNSS(); CertVerifyProcNSS() = default;
bool SupportsAdditionalTrustAnchors() const override; bool SupportsAdditionalTrustAnchors() const override;
bool SupportsOCSPStapling() const override; bool SupportsOCSPStapling() const override;
...@@ -43,15 +43,6 @@ class NET_EXPORT_PRIVATE CertVerifyProcNSS : public CertVerifyProc { ...@@ -43,15 +43,6 @@ class NET_EXPORT_PRIVATE CertVerifyProcNSS : public CertVerifyProc {
CRLSet* crl_set, CRLSet* crl_set,
const CertificateList& additional_trust_anchors, const CertificateList& additional_trust_anchors,
CertVerifyResult* verify_result) override; CertVerifyResult* verify_result) override;
using CacheOCSPResponseFromSideChannelFunction =
SECStatus (*)(CERTCertDBHandle* handle,
CERTCertificate* cert,
PRTime time,
const SECItem* encodedResponse,
void* pwArg);
const CacheOCSPResponseFromSideChannelFunction
cache_ocsp_response_from_side_channel_;
}; };
} // namespace net } // namespace net
......
...@@ -172,8 +172,6 @@ src:*base/native_library_unittest.cc ...@@ -172,8 +172,6 @@ src:*base/native_library_unittest.cc
fun:*GtkUi* fun:*GtkUi*
# chrome/browser/ui/views/frame/global_menu_bar_x11.cc # chrome/browser/ui/views/frame/global_menu_bar_x11.cc
fun:*GlobalMenuBarX11* fun:*GlobalMenuBarX11*
# net/cert/cert_verify_proc_nss.cc
fun:*CertVerifyProcNSS*VerifyInternalImpl*
# third_party/skia/include/gpu/gl/GrGLFunctions.h # third_party/skia/include/gpu/gl/GrGLFunctions.h
fun:*GrGLFunction* fun:*GrGLFunction*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment