summaryrefslogtreecommitdiffstats
path: root/chrome/nacl/nacl_validation_query.cc
blob: 0ff831c5f0025d45cb7f8a15cff9a18cf062c5b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// 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.

#include "chrome/nacl/nacl_validation_query.h"

#include "base/logging.h"
#include "crypto/nss_util.h"
#include "chrome/nacl/nacl_validation_db.h"
#include "native_client/src/include/portability.h"
#include "native_client/src/trusted/validator/nacl_file_info.h"
#include "native_client/src/trusted/validator/validation_cache.h"

NaClValidationQueryContext::NaClValidationQueryContext(
    NaClValidationDB* db,
    const std::string& profile_key,
    const std::string& nacl_version)
    : db_(db),
      profile_key_(profile_key),
      nacl_version_(nacl_version) {

  // Sanity checks.
  CHECK(profile_key.length() >= 8);
  CHECK(nacl_version.length() >= 4);
}

NaClValidationQuery* NaClValidationQueryContext::CreateQuery() {
  NaClValidationQuery* query = new NaClValidationQuery(db_, profile_key_);
  // Changing the version effectively invalidates existing hashes.
  query->AddData(nacl_version_);
  return query;
}

bool NaClValidationQueryContext::ResolveFileToken(
    struct NaClFileToken* file_token,
    int32* fd,
    std::string* path) {
  return db_->ResolveFileToken(file_token, fd, path);
}

NaClValidationQuery::NaClValidationQuery(NaClValidationDB* db,
                                         const std::string& profile_key)
    : state_(READY),
      hasher_(crypto::HMAC::SHA256),
      db_(db),
      buffer_length_(0) {
  // Without this line on Linux, HMAC::Init will instantiate a singleton that
  // in turn attempts to open a file.  Disabling this behavior avoids a ~70 ms
  // stall the first time HMAC is used.
  // This function is also called in nacl_helper_linux.cc, but nacl_helper may
  // not be used in all cases.
  // TODO(ncbray) remove when nacl_helper becomes the only code path.
  // http://code.google.com/p/chromium/issues/detail?id=118263
#if defined(USE_NSS)
  crypto::ForceNSSNoDBInit();
#endif
  CHECK(hasher_.Init(profile_key));
}

void NaClValidationQuery::AddData(const char* data, size_t length) {
  CHECK(state_ == READY);
  CHECK(buffer_length_ <= sizeof(buffer_));
  // Chrome's HMAC class doesn't support incremental signing.  Work around
  // this by using a (small) temporary buffer to accumulate data.
  // Check if there is space in the buffer.
  if (buffer_length_ + kDigestLength > sizeof(buffer_)) {
    // Hash the buffer to make space.
    CompressBuffer();
  }
  // Hash the input data into the buffer.  Assumes that sizeof(buffer_) >=
  // kDigestLength * 2 (the buffer can store at least two digests.)
  CHECK(hasher_.Sign(base::StringPiece(data, length),
                     reinterpret_cast<unsigned char*>(buffer_ + buffer_length_),
                     kDigestLength));
  buffer_length_ += kDigestLength;
}

void NaClValidationQuery::AddData(const unsigned char* data, size_t length) {
  AddData(reinterpret_cast<const char*>(data), length);
}

void NaClValidationQuery::AddData(const base::StringPiece& data) {
  AddData(data.data(), data.length());
}

int NaClValidationQuery::QueryKnownToValidate() {
  CHECK(state_ == READY);
  // It is suspicious if we have less than a digest's worth of data.
  CHECK(buffer_length_ >= kDigestLength);
  CHECK(buffer_length_ <= sizeof(buffer_));
  state_ = GET_CALLED;
  // Ensure the buffer contains only one digest worth of data.
  CompressBuffer();
  return db_->QueryKnownToValidate(std::string(buffer_, buffer_length_));
}

void NaClValidationQuery::SetKnownToValidate() {
  CHECK(state_ == GET_CALLED);
  CHECK(buffer_length_ == kDigestLength);
  state_ = SET_CALLED;
  db_->SetKnownToValidate(std::string(buffer_, buffer_length_));
}

// Reduce the size of the data in the buffer by hashing it and writing it back
// to the buffer.
void NaClValidationQuery::CompressBuffer() {
  // Calculate the digest into a temp buffer.  It is likely safe to calculate it
  // directly back into the buffer, but this is an "accidental" semantic we're
  // avoiding depending on.
  unsigned char temp[kDigestLength];
  CHECK(hasher_.Sign(base::StringPiece(buffer_, buffer_length_), temp,
                     kDigestLength));
  memcpy(buffer_, temp, kDigestLength);
  buffer_length_ = kDigestLength;
}

// OO wrappers

static void* CreateQuery(void* handle) {
  return static_cast<NaClValidationQueryContext*>(handle)->CreateQuery();
}

static void AddData(void* query, const uint8* data, size_t length) {
  static_cast<NaClValidationQuery*>(query)->AddData(data, length);
}

static int QueryKnownToValidate(void* query) {
  return static_cast<NaClValidationQuery*>(query)->QueryKnownToValidate();
}

static void SetKnownToValidate(void* query) {
  static_cast<NaClValidationQuery*>(query)->SetKnownToValidate();
}

static void DestroyQuery(void* query) {
  delete static_cast<NaClValidationQuery*>(query);
}

static int ResolveFileToken(void* handle, struct NaClFileToken* file_token,
                            int32* fd, char** file_path,
                            uint32* file_path_length) {
  std::string path;
  *file_path = NULL;
  *file_path_length = 0;
  bool ok = static_cast<NaClValidationQueryContext*>(handle)->
      ResolveFileToken(file_token, fd, &path);
  if (ok) {
    *file_path = static_cast<char*>(malloc(path.length() + 1));
    CHECK(*file_path);
    memcpy(*file_path, path.data(), path.length());
    (*file_path)[path.length()] = 0;
    *file_path_length = static_cast<uint32>(path.length());
  }
  return ok;
}

struct NaClValidationCache* CreateValidationCache(
    NaClValidationDB* db, const std::string& profile_key,
    const std::string& nacl_version) {
  NaClValidationCache* cache =
      static_cast<NaClValidationCache*>(malloc(sizeof(NaClValidationCache)));
  // Make sure any fields introduced in a cross-repo change are zeroed.
  memset(cache, 0, sizeof(*cache));
  cache->handle = new NaClValidationQueryContext(db, profile_key, nacl_version);
  cache->CreateQuery = CreateQuery;
  cache->AddData = AddData;
  cache->QueryKnownToValidate = QueryKnownToValidate;
  cache->SetKnownToValidate = SetKnownToValidate;
  cache->DestroyQuery = DestroyQuery;
  cache->ResolveFileToken = ResolveFileToken;
  return cache;
}