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

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

Issue 7233006: Store/Retrieve CrOS user policy in session_manager. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address gfeher's comments Created 9 years, 5 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) 2011 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/cros_user_policy_cache.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/file_path.h"
13 #include "base/file_util.h"
14 #include "base/path_service.h"
15 #include "chrome/browser/chromeos/cros/login_library.h"
16 #include "chrome/browser/policy/proto/cloud_policy.pb.h"
17 #include "chrome/browser/policy/proto/device_management_backend.pb.h"
18 #include "chrome/browser/policy/proto/device_management_local.pb.h"
19 #include "chrome/common/chrome_paths.h"
20 #include "chrome/common/extensions/extension_constants.h"
21 #include "content/browser/browser_thread.h"
22 #include "crypto/signature_verifier.h"
23
24 using extension_misc::kSignatureAlgorithm;
25
26 static const FilePath::CharType kUserPolicyKeyFile[] = FILE_PATH_LITERAL("key");
27
28 namespace policy {
29
30 // Decodes a CloudPolicySettings object into two maps with mandatory and
31 // recommended settings, respectively. The implementation is generated code
32 // in policy/cloud_policy_generated.cc.
33 void DecodePolicy(const em::CloudPolicySettings& policy,
34 PolicyMap* mandatory, PolicyMap* recommended);
35
36 // Manages the policy key and implements signature checking on the key.
37 class CrosUserPolicyCache::PolicyKey
38 : public base::RefCountedThreadSafe<PolicyKey> {
39 public:
40 typedef base::Callback<void(bool)> StatusCallback;
41
42 explicit PolicyKey(const FilePath& key_file);
43
44 // Loads the key asynchronously on the file thread.
45 void LoadKey();
46
47 // Checks whether |signature| is a valid over |data|. The status will be
48 // reported through |callback| once the check is complete. If the |reload_key|
49 // flag is set, the key is first reloaded from the file.
50 void CheckSignature(const std::string& data,
51 const std::string& signature,
52 bool reload_key,
53 const StatusCallback& callback);
54
55 private:
56 // Reports the result of a signature check. Must be called on the UI thread.
57 void ReportResult(const StatusCallback& callback, bool result);
58
59 // Read the public key from |key_file_|. Must be called on the file thread.
60 void ReadKeyFromFile();
61
62 // Runs the actual signature checking. Must be called on the file thread.
63 void CheckSignatureOnFileThread(const std::string& data,
64 const std::string& signature,
65 bool reload_key,
66 const StatusCallback& callback);
67
68 // The data members should only be accessed from the file thread.
69 const FilePath key_file_;
70 std::vector<uint8> public_key_;
71
72 DISALLOW_COPY_AND_ASSIGN(PolicyKey);
73 };
74
75 CrosUserPolicyCache::PolicyKey::PolicyKey(const FilePath& key_file)
76 : key_file_(key_file) {}
77
78 void CrosUserPolicyCache::PolicyKey::LoadKey() {
79 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
80 NewRunnableMethod(this, &PolicyKey::ReadKeyFromFile));
81 }
82
83 void CrosUserPolicyCache::PolicyKey::CheckSignature(
84 const std::string& data,
85 const std::string& signature,
86 bool reload_key,
87 const StatusCallback& callback) {
88 BrowserThread::PostTask(
89 BrowserThread::FILE, FROM_HERE,
90 NewRunnableMethod(this, &PolicyKey::CheckSignatureOnFileThread,
91 data, signature, reload_key, callback));
92 }
93
94 void CrosUserPolicyCache::PolicyKey::CheckSignatureOnFileThread(
95 const std::string& data,
96 const std::string& signature,
97 bool reload_key,
98 const StatusCallback& callback) {
99 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
100 bool status = false;
101
102 // Re-read the key if necessary.
103 if (public_key_.empty() || reload_key)
104 ReadKeyFromFile();
105
106 if (!public_key_.empty()) {
107 crypto::SignatureVerifier verifier;
108 if (verifier.VerifyInit(kSignatureAlgorithm, sizeof(kSignatureAlgorithm),
109 reinterpret_cast<const uint8*>(&signature[0]),
110 signature.size(),
111 &public_key_[0], public_key_.size())) {
112 verifier.VerifyUpdate(reinterpret_cast<const uint8*>(data.c_str()),
113 data.length());
114 status = verifier.VerifyFinal();
115 }
116 }
117
118 BrowserThread::PostTask(BrowserThread::UI,
119 FROM_HERE,
120 NewRunnableMethod(this, &PolicyKey::ReportResult,
121 callback, status));
122 }
123
124 void CrosUserPolicyCache::PolicyKey::ReportResult(
125 const StatusCallback& callback,
126 bool result) {
127 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
128 callback.Run(result);
129 }
130
131 void CrosUserPolicyCache::PolicyKey::ReadKeyFromFile() {
132 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
133
134 public_key_.clear();
135 if (!file_util::PathExists(key_file_))
136 return;
137
138 int64 size = -1;
139 if (!file_util::GetFileSize(key_file_, &size)) {
140 LOG(ERROR) << "Failed to get file size for " << key_file_.value();
141 return;
142 }
143
144 const int64 kMaxSize = 1 * 1024 * 1024; // 1M should be sufficient.
145 if (size < 0 || size > kMaxSize) {
146 LOG(ERROR) << "Bogus policy key file size " << size;
147 return;
148 }
149
150 std::vector<uint8> key_data(size);
151 if (!file_util::ReadFile(key_file_,
152 reinterpret_cast<char*>(&key_data[0]),
153 key_data.size())) {
154 LOG(ERROR) << "Failed to read " << key_file_.value();
155 return;
156 }
157
158 public_key_.swap(key_data);
159 }
160
161 // Takes care of sending a new policy blob to session manager and reports back
162 // the status through a callback.
163 class CrosUserPolicyCache::StorePolicyOperation {
164 public:
165 typedef base::Callback<void(bool)> StatusCallback;
166
167 // Creates and exectues an operation.
168 static StorePolicyOperation* Run(const em::PolicyFetchResponse& policy,
169 chromeos::LoginLibrary* login_library,
170 const StatusCallback& callback);
171
172 // Cancels the operation, making sure that any pending callbacks get killed.
gfeher 2011/07/21 23:37:15 This doesn't really cancel the operation, just kil
Mattias Nissler (ping if slow) 2011/07/22 11:29:48 Done.
173 void Cancel();
174
175 const em::PolicyFetchResponse& policy() { return policy_; }
176
177 private:
178 StorePolicyOperation(const em::PolicyFetchResponse& policy,
179 const StatusCallback& callback);
180
181 // StorePolicyOperation manages its own lifetime.
182 ~StorePolicyOperation() {}
183
184 // A callback function suitable for passing to login_library.
185 static void StorePolicyCallback(void* delegate, bool result);
186
187 em::PolicyFetchResponse policy_;
188 StatusCallback callback_;
189
190 DISALLOW_COPY_AND_ASSIGN(StorePolicyOperation);
191 };
192
193 // static
194 CrosUserPolicyCache::StorePolicyOperation*
195 CrosUserPolicyCache::StorePolicyOperation::Run(
196 const em::PolicyFetchResponse& policy,
197 chromeos::LoginLibrary* login_library,
198 const StatusCallback& callback) {
199 StorePolicyOperation* op = new StorePolicyOperation(policy, callback);
200 std::string serialized;
201 if (!policy.SerializeToString(&serialized)) {
202 LOG(ERROR) << "Failed to serialize policy protobuf!";
203 callback.Run(false);
204 delete op;
205 return NULL;
206 }
207 login_library->RequestStoreUserPolicy(serialized, StorePolicyCallback, op);
208 return op;
209 }
210
211 void CrosUserPolicyCache::StorePolicyOperation::Cancel() {
212 callback_.Reset();
213 }
214
215 CrosUserPolicyCache::StorePolicyOperation::StorePolicyOperation(
216 const em::PolicyFetchResponse& policy,
217 const StatusCallback& callback)
218 : policy_(policy),
219 callback_(callback) {}
220
221 // static
222 void CrosUserPolicyCache::StorePolicyOperation::StorePolicyCallback(
223 void* delegate,
224 bool result) {
225 StorePolicyOperation* op(static_cast<StorePolicyOperation*>(delegate));
226 if (!op->callback_.is_null())
227 op->callback_.Run(result);
228 delete op;
229 }
230
231 class CrosUserPolicyCache::RetrievePolicyOperation {
232 public:
233 typedef base::Callback<void(bool, const em::PolicyFetchResponse&)>
234 ResultCallback;
235
236 // Creates and executes an operation.
237 static RetrievePolicyOperation* Run(PolicyKey* key,
238 chromeos::LoginLibrary* login_library,
239 bool reload_key,
240 const ResultCallback& callback);
241
242 // Cancels the operation, disengaging all pending callbacks.
gfeher 2011/07/21 23:37:15 Same here.
Mattias Nissler (ping if slow) 2011/07/22 11:29:48 Done.
243 void Cancel();
244
245 private:
246 RetrievePolicyOperation(PolicyKey* key,
247 bool reload_key,
248 const ResultCallback& callback);
249
250 // RetrievePolicyOperation manages its own lifetime.
251 ~RetrievePolicyOperation() {}
252
253 // Decodes the policy data and triggers a signature check.
254 void OnPolicyRetrieved(const char* data, unsigned int size);
255
256 // Handles the signature verification callback and reports the result.
257 void OnSignatureChecked(bool result);
258
259 // Finishes the operation, makes the |callback_|, and deletes |this|.
260 void Finish(bool status);
261
262 // A callback function suitable for passing to login_library.
263 static void RetrievePolicyCallback(void* delegate,
264 const char* data,
265 unsigned int size);
266 scoped_refptr<PolicyKey> key_;
267 bool reload_key_;
268 ResultCallback callback_;
269 em::PolicyFetchResponse policy_;
270
271 DISALLOW_COPY_AND_ASSIGN(RetrievePolicyOperation);
272 };
273
274 // static
275 CrosUserPolicyCache::RetrievePolicyOperation*
276 CrosUserPolicyCache::RetrievePolicyOperation::Run(
277 PolicyKey* key,
278 chromeos::LoginLibrary* login_library,
279 bool reload_key,
280 const ResultCallback& callback) {
281 RetrievePolicyOperation* op =
282 new RetrievePolicyOperation(key, reload_key, callback);
283 login_library->RequestRetrieveUserPolicy(RetrievePolicyCallback, op);
284 return op;
285 }
286
287 void CrosUserPolicyCache::RetrievePolicyOperation::Cancel() {
288 callback_.Reset();
289 }
290
291 CrosUserPolicyCache::RetrievePolicyOperation::RetrievePolicyOperation(
292 PolicyKey* key,
293 bool reload_key,
294 const ResultCallback& callback)
295 : key_(key),
296 reload_key_(reload_key),
297 callback_(callback) {}
298
299 void CrosUserPolicyCache::RetrievePolicyOperation::OnPolicyRetrieved(
300 const char* data,
301 unsigned int size) {
302 if (!policy_.ParseFromArray(data, size) ||
303 !policy_.has_policy_data() ||
304 !policy_.has_policy_data_signature()) {
305 LOG(ERROR) << "Failed to decode policy";
306 Finish(false);
307 return;
308 }
309 key_->CheckSignature(policy_.policy_data(),
310 policy_.policy_data_signature(),
311 reload_key_,
312 base::Bind(&RetrievePolicyOperation::OnSignatureChecked,
313 base::Unretained(this)));
314 }
315
316 void CrosUserPolicyCache::RetrievePolicyOperation::OnSignatureChecked(
317 bool result) {
318 if (!result)
319 LOG(ERROR) << "User policy signature check failed.";
320 Finish(result);
321 }
322
323 void CrosUserPolicyCache::RetrievePolicyOperation::Finish(bool status) {
324 if (!callback_.is_null())
325 callback_.Run(status, policy_);
326 delete this;
327 }
328
329 // static
330 void CrosUserPolicyCache::RetrievePolicyOperation::RetrievePolicyCallback(
331 void *delegate,
332 const char* data,
333 unsigned int size) {
334 static_cast<RetrievePolicyOperation*>(delegate)->OnPolicyRetrieved(data,
335 size);
336 }
337
338 CrosUserPolicyCache::CrosUserPolicyCache(
339 chromeos::LoginLibrary* login_library,
340 CloudPolicyDataStore* data_store,
341 const FilePath& legacy_token_cache_file,
342 const FilePath& legacy_policy_cache_file)
343 : login_library_(login_library),
344 data_store_(data_store),
345 policy_cache_loaded_(false),
346 store_operation_(NULL),
347 retrieve_operation_(NULL),
348 legacy_cache_dir_(legacy_token_cache_file.DirName()),
349 ALLOW_THIS_IN_INITIALIZER_LIST(
350 legacy_token_cache_delegate_factory_(this)),
351 ALLOW_THIS_IN_INITIALIZER_LIST(
352 legacy_policy_cache_delegate_factory_(this)) {
353 DCHECK_EQ(legacy_token_cache_file.DirName().value(),
354 legacy_policy_cache_file.DirName().value());
355
356 FilePath key_file;
357 if (PathService::Get(chrome::DIR_USER_POLICY, &key_file))
358 key_file.Append(kUserPolicyKeyFile);
359 else
360 NOTREACHED();
361 key_ = new PolicyKey(key_file);
362
363 legacy_token_loader_ =
364 new UserPolicyTokenLoader(
365 legacy_token_cache_delegate_factory_.GetWeakPtr(),
366 legacy_token_cache_file);
367 legacy_policy_cache_ =
368 new UserPolicyDiskCache(
369 legacy_policy_cache_delegate_factory_.GetWeakPtr(),
370 legacy_policy_cache_file);
371 }
372
373 CrosUserPolicyCache::~CrosUserPolicyCache() {
374 CancelStore();
375 CancelRetrieve();
376 }
377
378 void CrosUserPolicyCache::Load() {
379 key_->LoadKey();
380 retrieve_operation_ =
381 RetrievePolicyOperation::Run(
382 key_,
383 login_library_,
384 false,
385 base::Bind(&CrosUserPolicyCache::OnPolicyLoadDone,
386 base::Unretained(this)));
387 }
388
389 void CrosUserPolicyCache::SetPolicy(const em::PolicyFetchResponse& policy) {
390 CancelStore();
391 set_last_policy_refresh_time(base::Time::NowFromSystemTime());
392 store_operation_ =
393 StorePolicyOperation::Run(policy,
394 login_library_,
395 base::Bind(&CrosUserPolicyCache::OnPolicyStored,
396 base::Unretained(this)));
397 }
398
399 void CrosUserPolicyCache::SetUnmanaged() {
400 base::Time now(base::Time::NowFromSystemTime());
401 SetUnmanagedInternal(now);
402
403 // Construct a policy blob with unmanaged state.
404 em::PolicyData policy_data;
405 policy_data.set_policy_type(data_store_->policy_type());
406 policy_data.set_timestamp((now - base::Time::UnixEpoch()).InMilliseconds());
gfeher 2011/07/21 23:37:15 I don't think it makes sense to stuff that into th
Mattias Nissler (ping if slow) 2011/07/22 11:29:48 Makes sense :) I'll keep it in mind when doing mor
407 policy_data.set_state(em::PolicyData::UNMANAGED);
408
409 em::PolicyFetchResponse policy;
410 if (!policy_data.SerializeToString(policy.mutable_policy_data())) {
411 LOG(ERROR) << "Failed to serialize policy_data";
412 return;
413 }
414
415 SetPolicy(policy);
416 }
417
418 bool CrosUserPolicyCache::IsReady() {
419 return policy_cache_loaded_;
420 }
421
422 bool CrosUserPolicyCache::DecodePolicyData(const em::PolicyData& policy_data,
423 PolicyMap* mandatory,
424 PolicyMap* recommended) {
425 em::CloudPolicySettings policy;
426 if (!policy.ParseFromString(policy_data.policy_value())) {
427 LOG(WARNING) << "Failed to parse CloudPolicySettings protobuf.";
428 return false;
429 }
430 DecodePolicy(policy, mandatory, recommended);
431 return true;
432 }
433
434 void CrosUserPolicyCache::OnTokenLoaded(const std::string& token,
435 const std::string& device_id) {
436 data_store_->set_device_id(device_id);
437 data_store_->SetDeviceToken(token, true);
438 }
439
440 void CrosUserPolicyCache::OnDiskCacheLoaded(
441 const em::CachedCloudPolicyResponse& policy) {
442 policy_cache_loaded_ = true;
443 if (policy.unmanaged()) {
444 SetUnmanagedInternal(base::Time::FromTimeT(policy.timestamp()));
445 } else if (policy.has_cloud_policy()) {
446 base::Time timestamp;
447 if (SetPolicyInternal(policy.cloud_policy(), &timestamp, true))
448 set_last_policy_refresh_time(timestamp);
449 }
450 }
451
452 void CrosUserPolicyCache::OnPolicyStored(bool result) {
453 DCHECK(store_operation_);
454 if (result) {
455 // Policy is stored successfully, reload from session_manager and apply.
456 // This helps us making sure we only use policy that session_manager has
457 // checked and confirmed to be good.
458 CancelRetrieve();
459 retrieve_operation_ =
460 RetrievePolicyOperation::Run(
461 key_,
462 login_library_,
463 store_operation_->policy().has_new_public_key(),
464 base::Bind(&CrosUserPolicyCache::OnPolicyReloadDone,
465 base::Unretained(this)));
466
467 // Now that we have installed the new policy blob, we can make sure that the
468 // old cache directory is removed.
469 if (!legacy_cache_dir_.empty()) {
470 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
471 NewRunnableFunction(&RemoveLegacyCacheDir,
472 legacy_cache_dir_));
473 }
474 } else {
475 InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
476 CloudPolicySubsystem::POLICY_LOCAL_ERROR);
477 }
478 CancelStore();
479 }
480
481 void CrosUserPolicyCache::OnPolicyLoadDone(
482 bool result,
483 const em::PolicyFetchResponse& policy) {
484 DCHECK(retrieve_operation_);
485 CancelRetrieve();
486 if (!result) {
487 // No policy present. Try to load the legacy token and policy cache.
488 legacy_token_loader_->Load();
489 legacy_policy_cache_->Load();
490 return;
491 }
492
493 policy_cache_loaded_ = true;
494
495 // We have new-style policy, no need to clean up.
496 legacy_cache_dir_.clear();
497
498 em::PolicyData policy_data;
499 if (!policy_data.ParseFromString(policy.policy_data())) {
500 LOG(WARNING) << "Failed to parse PolicyData protobuf.";
501 InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
502 CloudPolicySubsystem::POLICY_LOCAL_ERROR);
503 data_store_->SetDeviceToken(std::string(), true);
504 return;
505 }
506 if (policy_data.request_token().empty() ||
507 policy_data.username().empty() ||
508 policy_data.device_id().empty()) {
509 LOG(WARNING) << "Policy protobuf is missing credentials";
510 InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
511 CloudPolicySubsystem::POLICY_LOCAL_ERROR);
512 data_store_->SetDeviceToken(std::string(), true);
513 return;
514 }
515 data_store_->set_device_id(policy_data.device_id());
516 data_store_->SetDeviceToken(policy_data.request_token(), true);
517 if (SetPolicyInternal(policy, NULL, true))
518 set_last_policy_refresh_time(base::Time::NowFromSystemTime());
519 }
520
521 void CrosUserPolicyCache::OnPolicyReloadDone(
522 bool result,
523 const em::PolicyFetchResponse& policy) {
524 DCHECK(retrieve_operation_);
525 policy_cache_loaded_ = true;
526 CancelRetrieve();
527 if (result) {
528 if (SetPolicyInternal(policy, NULL, false))
529 set_last_policy_refresh_time(base::Time::NowFromSystemTime());
530 } else {
531 InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
532 CloudPolicySubsystem::POLICY_LOCAL_ERROR);
533 }
534 }
535
536 void CrosUserPolicyCache::CancelStore() {
537 if (store_operation_) {
538 store_operation_->Cancel();
539 store_operation_ = NULL;
540 }
541 }
542
543 void CrosUserPolicyCache::CancelRetrieve() {
544 if (retrieve_operation_) {
545 retrieve_operation_->Cancel();
546 retrieve_operation_ = NULL;
547 }
548 }
549
550 // static
551 void CrosUserPolicyCache::RemoveLegacyCacheDir(const FilePath& dir) {
552 if (!file_util::Delete(dir, true))
553 PLOG(ERROR) << "Failed to remove " << dir.value();
554 }
555
556 } // namespace policy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698