1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "components/nacl/loader/nacl_validation_query.h" 6 7 #include "base/logging.h" 8 #include "components/nacl/loader/nacl_validation_db.h" 9 #include "crypto/nss_util.h" 10 #include "native_client/src/include/portability.h" 11 #include "native_client/src/trusted/validator/nacl_file_info.h" 12 #include "native_client/src/trusted/validator/validation_cache.h" 13 14 NaClValidationQueryContext::NaClValidationQueryContext( 15 NaClValidationDB* db, 16 const std::string& profile_key, 17 const std::string& nacl_version) 18 : db_(db), 19 profile_key_(profile_key), 20 nacl_version_(nacl_version) { 21 22 // Sanity checks. 23 CHECK(profile_key.length() >= 8); 24 CHECK(nacl_version.length() >= 4); 25 } 26 27 NaClValidationQuery* NaClValidationQueryContext::CreateQuery() { 28 NaClValidationQuery* query = new NaClValidationQuery(db_, profile_key_); 29 // Changing the version effectively invalidates existing hashes. 30 query->AddData(nacl_version_); 31 return query; 32 } 33 34 bool NaClValidationQueryContext::ResolveFileToken( 35 struct NaClFileToken* file_token, 36 int32* fd, 37 std::string* path) { 38 return db_->ResolveFileToken(file_token, fd, path); 39 } 40 41 NaClValidationQuery::NaClValidationQuery(NaClValidationDB* db, 42 const std::string& profile_key) 43 : state_(READY), 44 hasher_(crypto::HMAC::SHA256), 45 db_(db), 46 buffer_length_(0) { 47 // Without this line on Linux, HMAC::Init will instantiate a singleton that 48 // in turn attempts to open a file. Disabling this behavior avoids a ~70 ms 49 // stall the first time HMAC is used. 50 // This function is also called in nacl_helper_linux.cc, but nacl_helper may 51 // not be used in all cases. 52 // TODO(ncbray) remove when nacl_helper becomes the only code path. 53 // http://code.google.com/p/chromium/issues/detail?id=118263 54 #if defined(USE_NSS) 55 crypto::ForceNSSNoDBInit(); 56 #endif 57 CHECK(hasher_.Init(profile_key)); 58 } 59 60 void NaClValidationQuery::AddData(const char* data, size_t length) { 61 CHECK(state_ == READY); 62 CHECK(buffer_length_ <= sizeof(buffer_)); 63 // Chrome's HMAC class doesn't support incremental signing. Work around 64 // this by using a (small) temporary buffer to accumulate data. 65 // Check if there is space in the buffer. 66 if (buffer_length_ + kDigestLength > sizeof(buffer_)) { 67 // Hash the buffer to make space. 68 CompressBuffer(); 69 } 70 // Hash the input data into the buffer. Assumes that sizeof(buffer_) >= 71 // kDigestLength * 2 (the buffer can store at least two digests.) 72 CHECK(hasher_.Sign(base::StringPiece(data, length), 73 reinterpret_cast<unsigned char*>(buffer_ + buffer_length_), 74 kDigestLength)); 75 buffer_length_ += kDigestLength; 76 } 77 78 void NaClValidationQuery::AddData(const unsigned char* data, size_t length) { 79 AddData(reinterpret_cast<const char*>(data), length); 80 } 81 82 void NaClValidationQuery::AddData(const base::StringPiece& data) { 83 AddData(data.data(), data.length()); 84 } 85 86 int NaClValidationQuery::QueryKnownToValidate() { 87 CHECK(state_ == READY); 88 // It is suspicious if we have less than a digest's worth of data. 89 CHECK(buffer_length_ >= kDigestLength); 90 CHECK(buffer_length_ <= sizeof(buffer_)); 91 state_ = GET_CALLED; 92 // Ensure the buffer contains only one digest worth of data. 93 CompressBuffer(); 94 return db_->QueryKnownToValidate(std::string(buffer_, buffer_length_)); 95 } 96 97 void NaClValidationQuery::SetKnownToValidate() { 98 CHECK(state_ == GET_CALLED); 99 CHECK(buffer_length_ == kDigestLength); 100 state_ = SET_CALLED; 101 db_->SetKnownToValidate(std::string(buffer_, buffer_length_)); 102 } 103 104 // Reduce the size of the data in the buffer by hashing it and writing it back 105 // to the buffer. 106 void NaClValidationQuery::CompressBuffer() { 107 // Calculate the digest into a temp buffer. It is likely safe to calculate it 108 // directly back into the buffer, but this is an "accidental" semantic we're 109 // avoiding depending on. 110 unsigned char temp[kDigestLength]; 111 CHECK(hasher_.Sign(base::StringPiece(buffer_, buffer_length_), temp, 112 kDigestLength)); 113 memcpy(buffer_, temp, kDigestLength); 114 buffer_length_ = kDigestLength; 115 } 116 117 // OO wrappers 118 119 static void* CreateQuery(void* handle) { 120 return static_cast<NaClValidationQueryContext*>(handle)->CreateQuery(); 121 } 122 123 static void AddData(void* query, const uint8* data, size_t length) { 124 static_cast<NaClValidationQuery*>(query)->AddData(data, length); 125 } 126 127 static int QueryKnownToValidate(void* query) { 128 return static_cast<NaClValidationQuery*>(query)->QueryKnownToValidate(); 129 } 130 131 static void SetKnownToValidate(void* query) { 132 static_cast<NaClValidationQuery*>(query)->SetKnownToValidate(); 133 } 134 135 static void DestroyQuery(void* query) { 136 delete static_cast<NaClValidationQuery*>(query); 137 } 138 139 static int ResolveFileToken(void* handle, struct NaClFileToken* file_token, 140 int32* fd, char** file_path, 141 uint32* file_path_length) { 142 std::string path; 143 *file_path = NULL; 144 *file_path_length = 0; 145 bool ok = static_cast<NaClValidationQueryContext*>(handle)-> 146 ResolveFileToken(file_token, fd, &path); 147 if (ok) { 148 *file_path = static_cast<char*>(malloc(path.length() + 1)); 149 CHECK(*file_path); 150 memcpy(*file_path, path.data(), path.length()); 151 (*file_path)[path.length()] = 0; 152 *file_path_length = static_cast<uint32>(path.length()); 153 } 154 return ok; 155 } 156 157 struct NaClValidationCache* CreateValidationCache( 158 NaClValidationDB* db, const std::string& profile_key, 159 const std::string& nacl_version) { 160 NaClValidationCache* cache = 161 static_cast<NaClValidationCache*>(malloc(sizeof(NaClValidationCache))); 162 // Make sure any fields introduced in a cross-repo change are zeroed. 163 memset(cache, 0, sizeof(*cache)); 164 cache->handle = new NaClValidationQueryContext(db, profile_key, nacl_version); 165 cache->CreateQuery = CreateQuery; 166 cache->AddData = AddData; 167 cache->QueryKnownToValidate = QueryKnownToValidate; 168 cache->SetKnownToValidate = SetKnownToValidate; 169 cache->DestroyQuery = DestroyQuery; 170 cache->ResolveFileToken = ResolveFileToken; 171 return cache; 172 } 173