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

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

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

Powered by Google App Engine
This is Rietveld 408576698