Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(76)

Side by Side Diff: chrome/browser/policy/cloud_policy_validator.cc

Issue 12189011: Split up chrome/browser/policy subdirectory (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase. Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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 "chrome/browser/policy/cloud_policy_validator.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/message_loop.h"
10 #include "base/stl_util.h"
11 #include "chrome/browser/policy/cloud_policy_constants.h"
12 #include "chrome/browser/policy/proto/chrome_device_policy.pb.h"
13 #include "chrome/browser/policy/proto/cloud_policy.pb.h"
14 #include "chrome/browser/policy/proto/device_management_backend.pb.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "crypto/signature_verifier.h"
17 #include "google_apis/gaia/gaia_auth_util.h"
18
19 namespace em = enterprise_management;
20
21 namespace policy {
22
23 namespace {
24
25 // Grace interval for policy timestamp checks, in seconds.
26 const int kTimestampGraceIntervalSeconds = 60;
27
28 // DER-encoded ASN.1 object identifier for the SHA1-RSA signature algorithm.
29 const uint8 kSignatureAlgorithm[] = {
30 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
31 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00
32 };
33
34 } // namespace
35
36 CloudPolicyValidatorBase::~CloudPolicyValidatorBase() {}
37
38 void CloudPolicyValidatorBase::ValidateTimestamp(base::Time not_before,
39 base::Time now,
40 bool allow_missing_timestamp) {
41 // Timestamp should be from the past. We allow for a 1-minute grace interval
42 // to cover clock drift.
43 validation_flags_ |= VALIDATE_TIMESTAMP;
44 timestamp_not_before_ =
45 (not_before - base::Time::UnixEpoch()).InMilliseconds();
46 timestamp_not_after_ =
47 ((now + base::TimeDelta::FromSeconds(kTimestampGraceIntervalSeconds)) -
48 base::Time::UnixEpoch()).InMillisecondsRoundedUp();
49 allow_missing_timestamp_ = allow_missing_timestamp;
50 }
51
52 void CloudPolicyValidatorBase::ValidateUsername(
53 const std::string& expected_user) {
54 validation_flags_ |= VALIDATE_USERNAME;
55 user_ = gaia::CanonicalizeEmail(expected_user);
56 }
57
58 void CloudPolicyValidatorBase::ValidateDomain(
59 const std::string& expected_domain) {
60 validation_flags_ |= VALIDATE_DOMAIN;
61 domain_ = gaia::CanonicalizeDomain(expected_domain);
62 }
63
64 void CloudPolicyValidatorBase::ValidateDMToken(const std::string& token) {
65 validation_flags_ |= VALIDATE_TOKEN;
66 token_ = token;
67 }
68
69 void CloudPolicyValidatorBase::ValidatePolicyType(
70 const std::string& policy_type) {
71 validation_flags_ |= VALIDATE_POLICY_TYPE;
72 policy_type_ = policy_type;
73 }
74
75 void CloudPolicyValidatorBase::ValidatePayload() {
76 validation_flags_ |= VALIDATE_PAYLOAD;
77 }
78
79 void CloudPolicyValidatorBase::ValidateSignature(const std::vector<uint8>& key,
80 bool allow_key_rotation) {
81 validation_flags_ |= VALIDATE_SIGNATURE;
82 key_ = std::string(reinterpret_cast<const char*>(vector_as_array(&key)),
83 key.size());
84 allow_key_rotation_ = allow_key_rotation;
85 }
86
87 void CloudPolicyValidatorBase::ValidateInitialKey() {
88 validation_flags_ |= VALIDATE_INITIAL_KEY;
89 }
90
91 void CloudPolicyValidatorBase::ValidateAgainstCurrentPolicy(
92 const em::PolicyData* policy_data,
93 bool allow_missing_timestamp) {
94 base::Time last_policy_timestamp;
95 std::string expected_dm_token;
96 if (policy_data) {
97 last_policy_timestamp =
98 base::Time::UnixEpoch() +
99 base::TimeDelta::FromMilliseconds(policy_data->timestamp());
100 expected_dm_token = policy_data->request_token();
101 }
102 ValidateTimestamp(last_policy_timestamp, base::Time::NowFromSystemTime(),
103 allow_missing_timestamp);
104 if (!expected_dm_token.empty())
105 ValidateDMToken(expected_dm_token);
106 }
107
108 CloudPolicyValidatorBase::CloudPolicyValidatorBase(
109 scoped_ptr<em::PolicyFetchResponse> policy_response,
110 google::protobuf::MessageLite* payload)
111 : status_(VALIDATION_OK),
112 policy_(policy_response.Pass()),
113 payload_(payload),
114 validation_flags_(0),
115 timestamp_not_before_(0),
116 timestamp_not_after_(0),
117 allow_missing_timestamp_(false),
118 allow_key_rotation_(false) {}
119
120 // static
121 void CloudPolicyValidatorBase::PerformValidation(
122 scoped_ptr<CloudPolicyValidatorBase> self,
123 scoped_refptr<base::MessageLoopProxy> message_loop,
124 const base::Closure& completion_callback) {
125 // Run the validation activities on this thread.
126 self->RunValidation();
127
128 // Report completion on |message_loop|.
129 message_loop->PostTask(
130 FROM_HERE,
131 base::Bind(&CloudPolicyValidatorBase::ReportCompletion,
132 base::Passed(&self),
133 completion_callback));
134 }
135
136 // static
137 void CloudPolicyValidatorBase::ReportCompletion(
138 scoped_ptr<CloudPolicyValidatorBase> self,
139 const base::Closure& completion_callback) {
140 completion_callback.Run();
141 }
142
143 void CloudPolicyValidatorBase::RunValidation() {
144 policy_data_.reset(new em::PolicyData());
145 RunChecks();
146 }
147
148 void CloudPolicyValidatorBase::RunChecks() {
149 status_ = VALIDATION_OK;
150 if ((policy_->has_error_code() && policy_->error_code() != 200) ||
151 (policy_->has_error_message() && !policy_->error_message().empty())) {
152 LOG(ERROR) << "Error in policy blob."
153 << " code: " << policy_->error_code()
154 << " message: " << policy_->error_message();
155 status_ = VALIDATION_ERROR_CODE_PRESENT;
156 return;
157 }
158
159 // Parse policy data.
160 if (!policy_data_->ParseFromString(policy_->policy_data()) ||
161 !policy_data_->IsInitialized()) {
162 LOG(ERROR) << "Failed to parse policy response";
163 status_ = VALIDATION_PAYLOAD_PARSE_ERROR;
164 return;
165 }
166
167 // Table of checks we run. These are sorted by descending severity of the
168 // error, s.t. the most severe check will determine the validation status.
169 static const struct {
170 int flag;
171 Status (CloudPolicyValidatorBase::* checkFunction)();
172 } kCheckFunctions[] = {
173 { VALIDATE_SIGNATURE, &CloudPolicyValidatorBase::CheckSignature },
174 { VALIDATE_INITIAL_KEY, &CloudPolicyValidatorBase::CheckInitialKey },
175 { VALIDATE_POLICY_TYPE, &CloudPolicyValidatorBase::CheckPolicyType },
176 { VALIDATE_TOKEN, &CloudPolicyValidatorBase::CheckToken },
177 { VALIDATE_USERNAME, &CloudPolicyValidatorBase::CheckUsername },
178 { VALIDATE_DOMAIN, &CloudPolicyValidatorBase::CheckDomain },
179 { VALIDATE_TIMESTAMP, &CloudPolicyValidatorBase::CheckTimestamp },
180 { VALIDATE_PAYLOAD, &CloudPolicyValidatorBase::CheckPayload },
181 };
182
183 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kCheckFunctions); ++i) {
184 if (validation_flags_ & kCheckFunctions[i].flag) {
185 status_ = (this->*(kCheckFunctions[i].checkFunction))();
186 if (status_ != VALIDATION_OK)
187 break;
188 }
189 }
190 }
191
192 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckSignature() {
193 const std::string* signature_key = &key_;
194 if (policy_->has_new_public_key() && allow_key_rotation_) {
195 signature_key = &policy_->new_public_key();
196 if (!policy_->has_new_public_key_signature() ||
197 !VerifySignature(policy_->new_public_key(), key_,
198 policy_->new_public_key_signature())) {
199 LOG(ERROR) << "New public key signature verification failed";
200 return VALIDATION_BAD_SIGNATURE;
201 }
202 }
203
204 if (!policy_->has_policy_data_signature() ||
205 !VerifySignature(policy_->policy_data(), *signature_key,
206 policy_->policy_data_signature())) {
207 LOG(ERROR) << "Policy signature validation failed";
208 return VALIDATION_BAD_SIGNATURE;
209 }
210
211 return VALIDATION_OK;
212 }
213
214 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckInitialKey() {
215 if (!policy_->has_new_public_key() ||
216 !policy_->has_policy_data_signature() ||
217 !VerifySignature(policy_->policy_data(), policy_->new_public_key(),
218 policy_->policy_data_signature())) {
219 LOG(ERROR) << "Initial policy signature validation failed";
220 return VALIDATION_BAD_SIGNATURE;
221 }
222
223 return VALIDATION_OK;
224 }
225
226 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckPolicyType() {
227 if (!policy_data_->has_policy_type() ||
228 policy_data_->policy_type() != policy_type_) {
229 LOG(ERROR) << "Wrong policy type " << policy_data_->policy_type();
230 return VALIDATION_WRONG_POLICY_TYPE;
231 }
232
233 return VALIDATION_OK;
234 }
235
236 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckTimestamp() {
237 if (!policy_data_->has_timestamp()) {
238 if (allow_missing_timestamp_) {
239 return VALIDATION_OK;
240 } else {
241 LOG(ERROR) << "Policy timestamp missing";
242 return VALIDATION_BAD_TIMESTAMP;
243 }
244 }
245
246 if (policy_data_->timestamp() < timestamp_not_before_) {
247 LOG(ERROR) << "Policy too old: " << policy_data_->timestamp();
248 return VALIDATION_BAD_TIMESTAMP;
249 }
250 if (policy_data_->timestamp() > timestamp_not_after_) {
251 LOG(ERROR) << "Policy from the future: " << policy_data_->timestamp();
252 return VALIDATION_BAD_TIMESTAMP;
253 }
254
255 return VALIDATION_OK;
256 }
257
258 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckToken() {
259 if (!policy_data_->has_request_token() ||
260 policy_data_->request_token() != token_) {
261 LOG(ERROR) << "Invalid DM token " << policy_data_->request_token();
262 return VALIDATION_WRONG_TOKEN;
263 }
264
265 return VALIDATION_OK;
266 }
267
268 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckUsername() {
269 if (!policy_data_->has_username()) {
270 LOG(ERROR) << "Policy is missing user name";
271 return VALIDATION_BAD_USERNAME;
272 }
273
274 std::string policy_username =
275 gaia::CanonicalizeEmail(gaia::SanitizeEmail(policy_data_->username()));
276
277 if (user_ != policy_username) {
278 LOG(ERROR) << "Invalid user name " << policy_data_->username();
279 return VALIDATION_BAD_USERNAME;
280 }
281
282 return VALIDATION_OK;
283 }
284
285
286 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckDomain() {
287 if (!policy_data_->has_username()) {
288 LOG(ERROR) << "Policy is missing user name";
289 return VALIDATION_BAD_USERNAME;
290 }
291
292 std::string policy_domain =
293 gaia::ExtractDomainName(
294 gaia::CanonicalizeEmail(
295 gaia::SanitizeEmail(policy_data_->username())));
296
297 if (domain_ != policy_domain) {
298 LOG(ERROR) << "Invalid user name " << policy_data_->username();
299 return VALIDATION_BAD_USERNAME;
300 }
301
302 return VALIDATION_OK;
303 }
304
305 CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckPayload() {
306 if (!policy_data_->has_policy_value() ||
307 !payload_->ParseFromString(policy_data_->policy_value()) ||
308 !payload_->IsInitialized()) {
309 LOG(ERROR) << "Failed to decode policy payload protobuf";
310 return VALIDATION_POLICY_PARSE_ERROR;
311 }
312
313 return VALIDATION_OK;
314 }
315
316 // static
317 bool CloudPolicyValidatorBase::VerifySignature(const std::string& data,
318 const std::string& key,
319 const std::string& signature) {
320 crypto::SignatureVerifier verifier;
321
322 if (!verifier.VerifyInit(kSignatureAlgorithm, sizeof(kSignatureAlgorithm),
323 reinterpret_cast<const uint8*>(signature.c_str()),
324 signature.size(),
325 reinterpret_cast<const uint8*>(key.c_str()),
326 key.size())) {
327 return false;
328 }
329 verifier.VerifyUpdate(reinterpret_cast<const uint8*>(data.c_str()),
330 data.size());
331 return verifier.VerifyFinal();
332 }
333
334 template<typename PayloadProto>
335 CloudPolicyValidator<PayloadProto>::~CloudPolicyValidator() {}
336
337 template<typename PayloadProto>
338 CloudPolicyValidator<PayloadProto>* CloudPolicyValidator<PayloadProto>::Create(
339 scoped_ptr<em::PolicyFetchResponse> policy_response) {
340 return new CloudPolicyValidator(
341 policy_response.Pass(),
342 scoped_ptr<PayloadProto>(new PayloadProto()));
343 }
344
345 template<typename PayloadProto>
346 CloudPolicyValidator<PayloadProto>::CloudPolicyValidator(
347 scoped_ptr<em::PolicyFetchResponse> policy_response,
348 scoped_ptr<PayloadProto> payload)
349 : CloudPolicyValidatorBase(policy_response.Pass(), payload.get()),
350 payload_(payload.Pass()) {}
351
352 template<typename PayloadProto>
353 void CloudPolicyValidator<PayloadProto>::StartValidation(
354 const CompletionCallback& completion_callback) {
355 content::BrowserThread::PostTask(
356 content::BrowserThread::FILE, FROM_HERE,
357 base::Bind(&CloudPolicyValidatorBase::PerformValidation,
358 base::Passed(scoped_ptr<CloudPolicyValidatorBase>(this)),
359 MessageLoop::current()->message_loop_proxy(),
360 base::Bind(completion_callback, this)));
361 }
362
363 template class CloudPolicyValidator<em::ChromeDeviceSettingsProto>;
364 template class CloudPolicyValidator<em::CloudPolicySettings>;
365
366 } // namespace policy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698