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

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

Powered by Google App Engine
This is Rietveld 408576698