Home | History | Annotate | Download | only in loader
      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/public/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