From 43866cd42bd433961d3b631ebbd9afe2ae85699f Mon Sep 17 00:00:00 2001 From: "ncbray@chromium.org" Date: Tue, 27 Mar 2012 00:16:47 +0000 Subject: Create a database for NaCl validation caching that is shared between processes. This change primarily entails creating a SyncChannel between sel_ldr and the browser. Queries to the database could be made from any thread inside sel_ldr, so the query mechanism needs to be thread safe. This feature is currently disabled by default, and requires an environment variable to enable. A few changes need to be made before this features is safe and can be enabled, such as making sure each installation has a unique, crypographically secure key. BUG= http://code.google.com/p/nativeclient/issues/detail?id=2515 TEST= Run NaCl w/ NACL_VALIDATION_CACHE=1 Review URL: http://codereview.chromium.org/9796006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@129061 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/browser/nacl_host/nacl_process_host.cc | 53 +++++++++++++++- chrome/browser/nacl_host/nacl_process_host.h | 7 ++- chrome/chrome_exe.gypi | 1 + chrome/common/nacl_messages.h | 13 +++- chrome/nacl/nacl_listener.cc | 91 +++++++++++++++++++++++++-- chrome/nacl/nacl_listener.h | 22 +++++++ crypto/crypto.gyp | 53 +++++++++++++--- crypto/hmac_win.cc | 25 ++++++-- crypto/symmetric_key_win.cc | 13 ++-- 9 files changed, 246 insertions(+), 32 deletions(-) diff --git a/chrome/browser/nacl_host/nacl_process_host.cc b/chrome/browser/nacl_host/nacl_process_host.cc index e493752..c4c602e 100644 --- a/chrome/browser/nacl_host/nacl_process_host.cc +++ b/chrome/browser/nacl_host/nacl_process_host.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/command_line.h" +#include "base/memory/mru_cache.h" #include "base/memory/singleton.h" #include "base/path_service.h" #include "base/string_util.h" @@ -195,16 +196,27 @@ class NaClBrowser { // Path to IRT. Available even before IRT is loaded. const FilePath& GetIrtFilePath(); + // Is the validation signature in the database? + bool QueryKnownToValidate(const std::string& signature); + + // Put the validation signature in the database. + void SetKnownToValidate(const std::string& signature); + private: base::PlatformFile irt_platform_file_; FilePath irt_filepath_; + typedef base::HashingMRUCache ValidationCacheType; + ValidationCacheType validation_cache_; + friend struct DefaultSingletonTraits; NaClBrowser() : irt_platform_file_(base::kInvalidPlatformFileValue), - irt_filepath_() { + irt_filepath_(), + // For the moment, choose an arbitrary cache size. + validation_cache_(200) { InitIrtFilePath(); } @@ -843,7 +855,42 @@ void NaClProcessHost::SendStart(base::PlatformFile irt_file) { internal_->sockets_for_sel_ldr.clear(); } +bool NaClBrowser::QueryKnownToValidate(const std::string& signature) { + ValidationCacheType::iterator iter = validation_cache_.Get(signature); + if (iter == validation_cache_.end()) { + // Not found. + return false; + } else { + return iter->second; + } +} + +void NaClBrowser::SetKnownToValidate(const std::string& signature) { + validation_cache_.Put(signature, true); +} + +void NaClProcessHost::OnQueryKnownToValidate(const std::string& signature, + bool* result) { + *result = NaClBrowser::GetInstance()->QueryKnownToValidate(signature); +} + +void NaClProcessHost::OnSetKnownToValidate(const std::string& signature) { + NaClBrowser::GetInstance()->SetKnownToValidate(signature); +} + +// Needed to handle sync messages in OnMessageRecieved. +bool NaClProcessHost::Send(IPC::Message* msg) { + return process_->Send(msg); +} + bool NaClProcessHost::OnMessageReceived(const IPC::Message& msg) { - NOTREACHED() << "Invalid message with type = " << msg.type(); - return false; + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(NaClProcessHost, msg) + IPC_MESSAGE_HANDLER(NaClProcessMsg_QueryKnownToValidate, + OnQueryKnownToValidate) + IPC_MESSAGE_HANDLER(NaClProcessMsg_SetKnownToValidate, + OnSetKnownToValidate) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; } diff --git a/chrome/browser/nacl_host/nacl_process_host.h b/chrome/browser/nacl_host/nacl_process_host.h index f5e51ec..d4fe80b 100644 --- a/chrome/browser/nacl_host/nacl_process_host.h +++ b/chrome/browser/nacl_host/nacl_process_host.h @@ -52,6 +52,8 @@ class NaClProcessHost : public content::BrowserChildProcessHostDelegate { void OnDebugExceptionHandlerLaunchedByBroker(); #endif + bool Send(IPC::Message* msg); + private: // Internal class that holds the nacl::Handle objecs so that // nacl_process_host.h doesn't include NaCl headers. Needed since it's @@ -73,7 +75,10 @@ class NaClProcessHost : public content::BrowserChildProcessHostDelegate { void IrtReady(); void SendStart(base::PlatformFile irt_file); - private: + // Message handlers for validation caching. + void OnQueryKnownToValidate(const std::string& signature, bool* result); + void OnSetKnownToValidate(const std::string& signature); + #if defined(OS_WIN) class DebugContext; diff --git a/chrome/chrome_exe.gypi b/chrome/chrome_exe.gypi index 1271a2d..c1f029b 100644 --- a/chrome/chrome_exe.gypi +++ b/chrome/chrome_exe.gypi @@ -533,6 +533,7 @@ '../base/base.gyp:base_nacl_win64', '../base/base.gyp:base_static_win64', '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations_win64', + '../crypto/crypto.gyp:crypto_nacl_win64', '../ipc/ipc.gyp:ipc_win64', '../sandbox/sandbox.gyp:sandbox_win64', ], diff --git a/chrome/common/nacl_messages.h b/chrome/common/nacl_messages.h index 19ead85..9c7a6f9 100644 --- a/chrome/common/nacl_messages.h +++ b/chrome/common/nacl_messages.h @@ -18,7 +18,7 @@ //----------------------------------------------------------------------------- // NaClProcess messages -// These are messages sent from the browser to the NaCl process. +// These are messages sent between the browser and the NaCl process. // Tells the NaCl process to start. IPC_MESSAGE_CONTROL2(NaClProcessMsg_Start, std::vector /* sockets */, @@ -47,3 +47,14 @@ IPC_MESSAGE_CONTROL1(NaClProcessMsg_DebugExceptionHandlerLaunched, // Notify the broker that all loader processes have been terminated and it // should shutdown. IPC_MESSAGE_CONTROL0(NaClProcessMsg_StopBroker) + +// Used by the NaCl process to query a database in the browser. The database +// contains the signatures of previously validated code chunks. +IPC_SYNC_MESSAGE_CONTROL1_1(NaClProcessMsg_QueryKnownToValidate, + std::string, /* A validation signature */ + bool /* Can validation be skipped? */) + +// Used by the NaCl process to add a validation signature to the validation +// database in the browser. +IPC_MESSAGE_CONTROL1(NaClProcessMsg_SetKnownToValidate, + std::string /* A validation signature */) diff --git a/chrome/nacl/nacl_listener.cc b/chrome/nacl/nacl_listener.cc index d4a4717..0500a85 100644 --- a/chrome/nacl/nacl_listener.cc +++ b/chrome/nacl/nacl_listener.cc @@ -5,13 +5,17 @@ #include "chrome/nacl/nacl_listener.h" #include +#include #include "base/command_line.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop.h" #include "chrome/common/nacl_messages.h" -#include "ipc/ipc_channel.h" +#include "chrome/nacl/nacl_validation_db.h" +#include "chrome/nacl/nacl_validation_query.h" +#include "ipc/ipc_sync_channel.h" +#include "ipc/ipc_sync_message_filter.h" #include "ipc/ipc_switches.h" #include "native_client/src/trusted/service_runtime/sel_main_chrome.h" @@ -64,19 +68,85 @@ int CreateMemoryObject(size_t size, int executable) { } #endif + +// Use an env var because command line args are eaten by nacl_helper. +bool CheckEnvVar(const char* name, bool default_value) { + bool result = default_value; + const char* var = getenv(name); + if (var && strlen(var) > 0) { + result = var[0] != '0'; + } + return result; +} + } // namespace -NaClListener::NaClListener() : debug_enabled_(false) {} +class BrowserValidationDBProxy : public NaClValidationDB { + public: + explicit BrowserValidationDBProxy(NaClListener* listener) + : listener_(listener) { + } + + bool QueryKnownToValidate(const std::string& signature) { + // Initialize to false so that if the Send fails to write to the return + // value we're safe. For example if the message is (for some reason) + // dispatched as an async message the return parameter will not be written. + bool result = false; + if (!listener_->Send(new NaClProcessMsg_QueryKnownToValidate(signature, + &result))) { + LOG(ERROR) << "Failed to query NaCl validation cache."; + result = false; + } + return result; + } + + void SetKnownToValidate(const std::string& signature) { + // Caching is optional: NaCl will still work correctly if the IPC fails. + if (!listener_->Send(new NaClProcessMsg_SetKnownToValidate(signature))) { + LOG(ERROR) << "Failed to update NaCl validation cache."; + } + } + + private: + // The listener never dies, otherwise this might be a dangling reference. + NaClListener* listener_; +}; + + +NaClListener::NaClListener() : shutdown_event_(true, false), + io_thread_("NaCl_IOThread"), + main_loop_(NULL), + debug_enabled_(false) { + io_thread_.StartWithOptions(base::Thread::Options(MessageLoop::TYPE_IO, 0)); +} -NaClListener::~NaClListener() {} +NaClListener::~NaClListener() { + NOTREACHED(); + shutdown_event_.Signal(); +} + +bool NaClListener::Send(IPC::Message* msg) { + DCHECK(main_loop_ != NULL); + if (MessageLoop::current() == main_loop_) { + // This thread owns the channel. + return channel_->Send(msg); + } else { + // This thread does not own the channel. + return filter_->Send(msg); + } +} void NaClListener::Listen() { std::string channel_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kProcessChannelID); - IPC::Channel channel(channel_name, IPC::Channel::MODE_CLIENT, this); - CHECK(channel.Connect()); - MessageLoop::current()->Run(); + channel_.reset(new IPC::SyncChannel(this, io_thread_.message_loop_proxy(), + &shutdown_event_)); + filter_.reset(new IPC::SyncMessageFilter(&shutdown_event_)); + channel_->AddFilter(filter_.get()); + channel_->Init(channel_name, IPC::Channel::MODE_CLIENT, true); + main_loop_ = MessageLoop::current(); + main_loop_->Run(); } bool NaClListener::OnMessageReceived(const IPC::Message& msg) { @@ -120,6 +190,15 @@ void NaClListener::OnStartSelLdr(std::vector handles, args->irt_fd = irt_handle; #endif + if (CheckEnvVar("NACL_VALIDATION_CACHE", false)) { + LOG(INFO) << "NaCl validation cache enabled."; + // The cache structure is not freed and exists until the NaCl process exits. + args->validation_cache = CreateValidationCache( + new BrowserValidationDBProxy(this), + // TODO(ncbray) plumb through real keys and versions. + "bogus key for HMAC....", "bogus version"); + } + CHECK(handles.size() == 1); args->imc_bootstrap_handle = nacl::ToNativeHandle(handles[0]); args->enable_exception_handling = enable_exception_handling; diff --git a/chrome/nacl/nacl_listener.h b/chrome/nacl/nacl_listener.h index 2738d7b..2b8bb21 100644 --- a/chrome/nacl/nacl_listener.h +++ b/chrome/nacl/nacl_listener.h @@ -8,9 +8,17 @@ #include +#include "base/memory/scoped_ptr.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" #include "chrome/common/nacl_types.h" #include "ipc/ipc_channel.h" +namespace IPC { +class SyncChannel; +class SyncMessageFilter; +} + // The NaClListener is an IPC channel listener that waits for a // request to start a NaCl module. class NaClListener : public IPC::Channel::Listener { @@ -21,11 +29,25 @@ class NaClListener : public IPC::Channel::Listener { void Listen(); void set_debug_enabled(bool value) {debug_enabled_ = value;} + bool Send(IPC::Message* msg); + private: void OnStartSelLdr(std::vector handles, bool enable_exception_handling); virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE; + // A channel back to the browser. + scoped_ptr channel_; + + // A filter that allows other threads to use the channel. + scoped_ptr filter_; + + base::WaitableEvent shutdown_event_; + base::Thread io_thread_; + + // Used to identify what thread we're on. + MessageLoop* main_loop_; + bool debug_enabled_; DISALLOW_COPY_AND_ASSIGN(NaClListener); diff --git a/crypto/crypto.gyp b/crypto/crypto.gyp index 405ad0b..f095719 100644 --- a/crypto/crypto.gyp +++ b/crypto/crypto.gyp @@ -5,6 +5,19 @@ { 'variables': { 'chromium_code': 1, + # Put all transitive dependencies for Windows HMAC here. + # This is required so that we can build them for nacl win64. + 'hmac_win64_related_sources': [ + 'hmac.cc', + 'hmac.h', + 'hmac_win.cc', + 'secure_util.cc', + 'secure_util.h', + 'symmetric_key.h', + 'symmetric_key_win.cc', + 'third_party/nss/chromium-sha256.h', + 'third_party/nss/sha512.cc', + ], }, 'targets': [ { @@ -131,6 +144,9 @@ },], ], 'sources': [ + # NOTE: all transitive dependencies of HMAC on windows need + # to be placed in the source list above. + '<@(hmac_win64_related_sources)', 'capi_util.cc', 'capi_util.h', 'crypto_export.h', @@ -151,12 +167,9 @@ 'encryptor_nss.cc', 'encryptor_openssl.cc', 'encryptor_win.cc', - 'hmac.cc', - 'hmac.h', 'hmac_mac.cc', 'hmac_nss.cc', 'hmac_openssl.cc', - 'hmac_win.cc', 'keychain_mac.cc', 'keychain_mac.h', 'mac_security_services_lock.cc', @@ -185,8 +198,6 @@ 'secure_hash.h', 'secure_hash_default.cc', 'secure_hash_openssl.cc', - 'secure_util.cc', - 'secure_util.h', 'sha2.cc', 'sha2.h', 'signature_creator.h', @@ -197,18 +208,14 @@ 'signature_verifier.h', 'signature_verifier_nss.cc', 'signature_verifier_openssl.cc', - 'symmetric_key.h', 'symmetric_key_mac.cc', 'symmetric_key_nss.cc', 'symmetric_key_openssl.cc', - 'symmetric_key_win.cc', 'third_party/nss/chromium-blapi.h', 'third_party/nss/chromium-blapit.h', 'third_party/nss/chromium-nss.h', - 'third_party/nss/chromium-sha256.h', 'third_party/nss/pk11akey.cc', 'third_party/nss/secsign.cc', - 'third_party/nss/sha512.cc', ], }, { @@ -281,4 +288,32 @@ ], }, ], + 'conditions': [ + [ 'OS == "win"', { + 'targets': [ + { + 'target_name': 'crypto_nacl_win64', + 'type': '<(component)', + 'dependencies': [ + '../base/base.gyp:base_nacl_win64', + '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations_win64', + ], + 'sources': [ + '<@(hmac_win64_related_sources)', + ], + 'defines': [ + 'CRYPTO_IMPLEMENTATION', + ], + 'msvs_disabled_warnings': [ + 4018, + ], + 'configurations': { + 'Common_Base': { + 'msvs_target_platform': 'x64', + }, + }, + }, + ], + }], + ], } diff --git a/crypto/hmac_win.cc b/crypto/hmac_win.cc index ffd08ce8..ef3e261 100644 --- a/crypto/hmac_win.cc +++ b/crypto/hmac_win.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -28,6 +28,19 @@ enum { SHA256_BLOCK_SIZE = 64 // Block size (in bytes) of the input to SHA-256. }; +// NSS doesn't accept size_t for text size, divide the data into smaller +// chunks as needed. +void Wrapped_SHA256_Update(SHA256Context* ctx, const unsigned char* text, + size_t text_len) { + const unsigned int kChunkSize = 1 << 30; + while (text_len > kChunkSize) { + SHA256_Update(ctx, text, kChunkSize); + text += kChunkSize; + text_len -= kChunkSize; + } + SHA256_Update(ctx, text, (unsigned int)text_len); +} + // See FIPS 198: The Keyed-Hash Message Authentication Code (HMAC). void ComputeHMACSHA256(const unsigned char* key, size_t key_len, const unsigned char* text, size_t text_len, @@ -38,7 +51,7 @@ void ComputeHMACSHA256(const unsigned char* key, size_t key_len, unsigned char key0[SHA256_BLOCK_SIZE]; if (key_len > SHA256_BLOCK_SIZE) { SHA256_Begin(&ctx); - SHA256_Update(&ctx, key, key_len); + Wrapped_SHA256_Update(&ctx, key, key_len); SHA256_End(&ctx, key0, NULL, SHA256_LENGTH); memset(key0 + SHA256_LENGTH, 0, SHA256_BLOCK_SIZE - SHA256_LENGTH); } else { @@ -57,7 +70,7 @@ void ComputeHMACSHA256(const unsigned char* key, size_t key_len, // Compute the inner hash. SHA256_Begin(&ctx); SHA256_Update(&ctx, padded_key, SHA256_BLOCK_SIZE); - SHA256_Update(&ctx, text, text_len); + Wrapped_SHA256_Update(&ctx, text, text_len); SHA256_End(&ctx, inner_hash, NULL, SHA256_LENGTH); // XOR key0 with opad. @@ -68,7 +81,7 @@ void ComputeHMACSHA256(const unsigned char* key, size_t key_len, SHA256_Begin(&ctx); SHA256_Update(&ctx, padded_key, SHA256_BLOCK_SIZE); SHA256_Update(&ctx, inner_hash, SHA256_LENGTH); - SHA256_End(&ctx, output, NULL, output_len); + SHA256_End(&ctx, output, NULL, (unsigned int) output_len); } } // namespace @@ -138,8 +151,8 @@ bool HMAC::Init(const unsigned char* key, int key_length) { memcpy(key_blob->key_data, key, key_length); if (!CryptImportKey(plat_->provider_, &key_blob_storage[0], - key_blob_storage.size(), 0, CRYPT_IPSEC_HMAC_KEY, - plat_->key_.receive())) { + (DWORD)key_blob_storage.size(), 0, + CRYPT_IPSEC_HMAC_KEY, plat_->key_.receive())) { NOTREACHED(); return false; } diff --git a/crypto/symmetric_key_win.cc b/crypto/symmetric_key_win.cc index d2034e0..87e0bc3 100644 --- a/crypto/symmetric_key_win.cc +++ b/crypto/symmetric_key_win.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -50,11 +50,12 @@ ALG_ID GetAESAlgIDForKeySize(size_t key_size_in_bits) { // TODO(wtc): use this function in hmac_win.cc. bool ImportRawKey(HCRYPTPROV provider, ALG_ID alg, - const void* key_data, DWORD key_size, + const void* key_data, size_t key_size, ScopedHCRYPTKEY* key) { DCHECK_GT(key_size, 0); - DWORD actual_size = sizeof(PlaintextBlobHeader) + key_size; + DWORD actual_size = + static_cast(sizeof(PlaintextBlobHeader) + key_size); std::vector tmp_data(actual_size); BYTE* actual_key = &tmp_data[0]; memcpy(actual_key + sizeof(PlaintextBlobHeader), key_data, key_size); @@ -66,7 +67,7 @@ bool ImportRawKey(HCRYPTPROV provider, key_header->hdr.bVersion = CUR_BLOB_VERSION; key_header->hdr.aiKeyAlg = alg; - key_header->cbKeySize = key_size; + key_header->cbKeySize = static_cast(key_size); HCRYPTKEY unsafe_key = NULL; DWORD flags = CRYPT_EXPORTABLE; @@ -184,7 +185,7 @@ bool GenerateHMACKey(size_t key_size_in_bits, if (!ok) return false; - DWORD key_size_in_bytes = key_size_in_bits / 8; + DWORD key_size_in_bytes = static_cast(key_size_in_bits / 8); scoped_array random(new BYTE[key_size_in_bytes]); ok = CryptGenRandom(safe_provider, key_size_in_bytes, random.get()); if (!ok) @@ -258,7 +259,7 @@ bool ComputePBKDF2Block(HCRYPTHASH hash, // Iteration U_1: Compute PRF for S. ok = CryptHashData(safe_hash, reinterpret_cast(salt.data()), - salt.size(), 0); + static_cast(salt.size()), 0); if (!ok) return false; -- cgit v1.1