1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "components/nacl/loader/nacl_validation_query.h"
11
12 #include <stdint.h>
13 #include <string.h>
14
15 #include <string_view>
16
17 #include "base/check.h"
18 #include "components/nacl/loader/nacl_validation_db.h"
19 #include "crypto/nss_util.h"
20 #include "native_client/src/public/validation_cache.h"
21
NaClValidationQueryContext(NaClValidationDB * db,const std::string & profile_key,const std::string & nacl_version)22 NaClValidationQueryContext::NaClValidationQueryContext(
23 NaClValidationDB* db,
24 const std::string& profile_key,
25 const std::string& nacl_version)
26 : db_(db),
27 profile_key_(profile_key),
28 nacl_version_(nacl_version) {
29
30 // Sanity checks.
31 CHECK(profile_key.length() >= 8);
32 CHECK(nacl_version.length() >= 4);
33 }
34
CreateQuery()35 NaClValidationQuery* NaClValidationQueryContext::CreateQuery() {
36 NaClValidationQuery* query = new NaClValidationQuery(db_, profile_key_);
37 // Changing the version effectively invalidates existing hashes.
38 query->AddData(nacl_version_);
39 return query;
40 }
41
NaClValidationQuery(NaClValidationDB * db,const std::string & profile_key)42 NaClValidationQuery::NaClValidationQuery(NaClValidationDB* db,
43 const std::string& profile_key)
44 : state_(READY),
45 hasher_(crypto::HMAC::SHA256),
46 db_(db),
47 buffer_length_(0) {
48 CHECK(hasher_.Init(profile_key));
49 }
50
AddData(const char * data,size_t length)51 void NaClValidationQuery::AddData(const char* data, size_t length) {
52 CHECK(state_ == READY);
53 CHECK(buffer_length_ <= sizeof(buffer_));
54 // Chrome's HMAC class doesn't support incremental signing. Work around
55 // this by using a (small) temporary buffer to accumulate data.
56 // Check if there is space in the buffer.
57 if (buffer_length_ + kDigestLength > sizeof(buffer_)) {
58 // Hash the buffer to make space.
59 CompressBuffer();
60 }
61 // Hash the input data into the buffer. Assumes that sizeof(buffer_) >=
62 // kDigestLength * 2 (the buffer can store at least two digests.)
63 CHECK(hasher_.Sign(std::string_view(data, length),
64 reinterpret_cast<unsigned char*>(buffer_ + buffer_length_),
65 kDigestLength));
66 buffer_length_ += kDigestLength;
67 }
68
AddData(const unsigned char * data,size_t length)69 void NaClValidationQuery::AddData(const unsigned char* data, size_t length) {
70 AddData(reinterpret_cast<const char*>(data), length);
71 }
72
AddData(std::string_view data)73 void NaClValidationQuery::AddData(std::string_view data) {
74 AddData(data.data(), data.length());
75 }
76
QueryKnownToValidate()77 int NaClValidationQuery::QueryKnownToValidate() {
78 CHECK(state_ == READY);
79 // It is suspicious if we have less than a digest's worth of data.
80 CHECK(buffer_length_ >= kDigestLength);
81 CHECK(buffer_length_ <= sizeof(buffer_));
82 state_ = GET_CALLED;
83 // Ensure the buffer contains only one digest worth of data.
84 CompressBuffer();
85 return db_->QueryKnownToValidate(std::string(buffer_, buffer_length_));
86 }
87
SetKnownToValidate()88 void NaClValidationQuery::SetKnownToValidate() {
89 CHECK(state_ == GET_CALLED);
90 CHECK(buffer_length_ == kDigestLength);
91 state_ = SET_CALLED;
92 db_->SetKnownToValidate(std::string(buffer_, buffer_length_));
93 }
94
95 // Reduce the size of the data in the buffer by hashing it and writing it back
96 // to the buffer.
CompressBuffer()97 void NaClValidationQuery::CompressBuffer() {
98 // Calculate the digest into a temp buffer. It is likely safe to calculate it
99 // directly back into the buffer, but this is an "accidental" semantic we're
100 // avoiding depending on.
101 unsigned char temp[kDigestLength];
102 CHECK(hasher_.Sign(std::string_view(buffer_, buffer_length_), temp,
103 kDigestLength));
104 memcpy(buffer_, temp, kDigestLength);
105 buffer_length_ = kDigestLength;
106 }
107
108 // OO wrappers
109
CreateQuery(void * handle)110 static void* CreateQuery(void* handle) {
111 return static_cast<NaClValidationQueryContext*>(handle)->CreateQuery();
112 }
113
AddData(void * query,const uint8_t * data,size_t length)114 static void AddData(void* query, const uint8_t* data, size_t length) {
115 static_cast<NaClValidationQuery*>(query)->AddData(data, length);
116 }
117
QueryKnownToValidate(void * query)118 static int QueryKnownToValidate(void* query) {
119 return static_cast<NaClValidationQuery*>(query)->QueryKnownToValidate();
120 }
121
SetKnownToValidate(void * query)122 static void SetKnownToValidate(void* query) {
123 static_cast<NaClValidationQuery*>(query)->SetKnownToValidate();
124 }
125
DestroyQuery(void * query)126 static void DestroyQuery(void* query) {
127 delete static_cast<NaClValidationQuery*>(query);
128 }
129
CreateValidationCache(NaClValidationDB * db,const std::string & profile_key,const std::string & nacl_version)130 struct NaClValidationCache* CreateValidationCache(
131 NaClValidationDB* db, const std::string& profile_key,
132 const std::string& nacl_version) {
133 NaClValidationCache* cache =
134 static_cast<NaClValidationCache*>(malloc(sizeof(NaClValidationCache)));
135 // Make sure any fields introduced in a cross-repo change are zeroed.
136 memset(cache, 0, sizeof(*cache));
137 cache->handle = new NaClValidationQueryContext(db, profile_key, nacl_version);
138 cache->CreateQuery = CreateQuery;
139 cache->AddData = AddData;
140 cache->QueryKnownToValidate = QueryKnownToValidate;
141 cache->SetKnownToValidate = SetKnownToValidate;
142 cache->DestroyQuery = DestroyQuery;
143 return cache;
144 }
145