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

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: Created 9 years, 6 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/policy/cros_user_policy_identity_strategy.h"
15 #include "chrome/browser/policy/proto/cloud_policy.pb.h"
16 #include "chrome/common/extensions/extension_constants.h"
17 #include "content/browser/browser_thread.h"
18 #include "crypto/signature_verifier.h"
19
20 using extension_misc::kSignatureAlgorithm;
21
22 static const FilePath::CharType kUserPolicyKeyFile[] =
23 "/home/chronos/user/.enterprise/key";
24
25 namespace policy {
26
27 // Decodes a CloudPolicySettings object into two maps with mandatory and
28 // recommended settings, respectively. The implementation is generated code
29 // in policy/cloud_policy_generated.cc.
30 void DecodePolicy(const em::CloudPolicySettings& policy,
31 PolicyMap* mandatory, PolicyMap* recommended);
32
33 // Manages the policy key and implements signature checking on the key.
34 class CrosUserPolicyCache::PolicyKey
35 : public base::RefCountedThreadSafe<PolicyKey> {
36 public:
37 typedef base::Callback<void(bool)> StatusCallback;
38
39 explicit PolicyKey(const FilePath& key_file);
40
41 // Loads the key asynchronously on the file thread.
42 void LoadKey();
43
44 // Checks whether |signature| is a valid over |data|. The status will be
45 // reported through |callback| once the check is complete. If the |reload_key|
46 // flag is set, the key is first reloaded from the file.
47 void CheckSignature(const std::string& data,
48 const std::string& signature,
49 bool reload_key,
50 const StatusCallback& callback);
51
52 private:
53 // Reports the result of a signature check. Must be called on the UI thread.
54 void ReportResult(const StatusCallback& callback, bool result);
55
56 // Read the public key from |key_file_|. Must be called on the file thread.
57 void ReadKeyFromFile();
58
59 // Runs the actual signature checking. Must be called on the file thread.
60 void CheckSignatureOnFileThread(const std::string& data,
61 const std::string& signature,
62 bool reload_key,
63 const StatusCallback& callback);
64
65 // The data members should only be accessed from the file thread.
66 const FilePath key_file_;
67 std::vector<uint8> public_key_;
68
69 DISALLOW_COPY_AND_ASSIGN(PolicyKey);
70 };
71
72 CrosUserPolicyCache::PolicyKey::PolicyKey(const FilePath& key_file)
73 : key_file_(key_file) {}
74
75 void CrosUserPolicyCache::PolicyKey::LoadKey() {
76 BrowserThread::PostTask(BrowserThread::FILE,
77 FROM_HERE,
78 NewRunnableMethod(this, &PolicyKey::ReadKeyFromFile));
79 }
80
81 void CrosUserPolicyCache::PolicyKey::CheckSignature(
82 const std::string& data,
83 const std::string& signature,
84 bool reload_key,
85 const StatusCallback& callback) {
86 BrowserThread::PostTask(
87 BrowserThread::FILE,
88 FROM_HERE,
89 NewRunnableMethod(this,
90 &PolicyKey::CheckSignatureOnFileThread,
91 data,
92 signature,
93 reload_key,
94 callback));
gfeher 2011/06/22 12:41:31 How about collapsing these arguments into fewer li
Mattias Nissler (ping if slow) 2011/06/22 17:17:35 Done.
95 }
96
97 void CrosUserPolicyCache::PolicyKey::CheckSignatureOnFileThread(
98 const std::string& data,
99 const std::string& signature,
100 bool reload_key,
101 const StatusCallback& callback) {
102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
103 bool status = false;
104
105 // Re-read the key if necessary.
106 if (public_key_.empty() || reload_key)
107 ReadKeyFromFile();
108
109 if (!public_key_.empty()) {
110 crypto::SignatureVerifier verifier;
111 if (verifier.VerifyInit(kSignatureAlgorithm, sizeof(kSignatureAlgorithm),
112 reinterpret_cast<const uint8*>(&signature[0]),
gfeher 2011/06/22 12:41:31 Nit: indent.
Mattias Nissler (ping if slow) 2011/06/22 17:17:35 Done.
113 signature.size(),
114 &public_key_[0], public_key_.size())) {
115 verifier.VerifyUpdate(reinterpret_cast<const uint8*>(data.c_str()),
116 data.length());
117 status = verifier.VerifyFinal();
118 }
119 }
120
121 BrowserThread::PostTask(BrowserThread::UI,
122 FROM_HERE,
123 NewRunnableMethod(this,
124 &PolicyKey::ReportResult,
125 callback,
126 status));
127 }
128
129 void CrosUserPolicyCache::PolicyKey::ReportResult(
130 const StatusCallback& callback,
131 bool result) {
132 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
133 callback.Run(result);
134 }
135
136 void CrosUserPolicyCache::PolicyKey::ReadKeyFromFile() {
137 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
138
139 public_key_.clear();
140 if (!file_util::PathExists(key_file_))
141 return;
142
143 int64 size = -1;
144 if (!file_util::GetFileSize(key_file_, &size)) {
145 LOG(ERROR) << "Failed to get file size for " << key_file_.value();
146 return;
147 }
148
149 const int64 kMaxSize = 1 * 1024 * 1024; // 1M should be sufficient.
150 if (size < 0 || size > kMaxSize) {
151 LOG(ERROR) << "Bogus policy key file size " << size;
152 return;
153 }
154
155 std::vector<uint8> key_data(size);
156 if (!file_util::ReadFile(key_file_,
157 reinterpret_cast<char*>(&key_data[0]),
158 key_data.size())) {
159 LOG(ERROR) << "Failed to read " << key_file_.value();
160 return;
161 }
162
163 public_key_.swap(key_data);
164 }
165
166 // Takes care of sending a new policy blob to session manager and reports back
167 // the status through a callback.
168 class CrosUserPolicyCache::StorePolicyOperation {
169 public:
170 typedef base::Callback<void(bool)> StatusCallback;
171
172 StorePolicyOperation(const em::PolicyFetchResponse& policy,
173 chromeos::LoginLibrary* login_library,
174 const StatusCallback& callback);
175
176 // Executes the operation.
177 void Run();
178
179 // Cancels the operation, making sure that any pending callbacks get killed.
180 void Cancel();
181
182 const em::PolicyFetchResponse& policy() { return policy_; }
183
184 private:
185 // A callback function suitable for passing to login_library.
186 static void StorePolicyCallback(void* delegate, bool result);
187
188 em::PolicyFetchResponse policy_;
189 chromeos::LoginLibrary* login_library_;
190 StatusCallback callback_;
191
192 DISALLOW_COPY_AND_ASSIGN(StorePolicyOperation);
193 };
194
195 CrosUserPolicyCache::StorePolicyOperation::StorePolicyOperation(
196 const em::PolicyFetchResponse& policy,
197 chromeos::LoginLibrary* login_library,
198 const StatusCallback& callback)
199 : policy_(policy),
200 login_library_(login_library),
201 callback_(callback) {}
202
203 void CrosUserPolicyCache::StorePolicyOperation::Run() {
204 std::string serialized;
205 if (!policy_.SerializeToString(&serialized)) {
206 LOG(ERROR) << "Failed to serialize policy protobuf!";
207 callback_.Run(false);
208 delete this;
gfeher 2011/06/22 12:41:31 Here and all the similar cases: please make sure t
Mattias Nissler (ping if slow) 2011/06/22 17:17:35 Done.
209 return;
210 }
211 login_library_->RequestStoreUserPolicy(serialized, StorePolicyCallback, this);
212 }
213
214 void CrosUserPolicyCache::StorePolicyOperation::Cancel() {
gfeher 2011/06/22 12:41:31 delete this?
Mattias Nissler (ping if slow) 2011/06/22 17:17:35 Actually no, since the operation may be in flight.
215 callback_.Reset();
216 }
217
218 // static
219 void CrosUserPolicyCache::StorePolicyOperation::StorePolicyCallback(
220 void* delegate,
221 bool result) {
222 scoped_ptr<StorePolicyOperation> op(
223 static_cast<StorePolicyOperation*>(delegate));
224 if (!op->callback_.is_null())
225 op->callback_.Run(result);
226 }
227
228 class CrosUserPolicyCache::RetrievePolicyOperation {
229 public:
230 typedef base::Callback<void(bool, const em::PolicyFetchResponse&)>
231 ResultCallback;
232
233 RetrievePolicyOperation(PolicyKey* key,
234 chromeos::LoginLibrary* login_library,
235 bool reload_key,
236 const ResultCallback& callback);
237
238 // Executes the operation.
239 void Run();
240
241 // Cancels the operation, disengaging all pending callbacks.
242 void Cancel();
243
244 private:
245 // Decodes the policy data and triggers a signature check.
246 void OnPolicyRetrieved(const char* data, unsigned int size);
247
248 // Handles the signature verification callback and reports the result.
249 void OnSignatureChecked(bool result);
250
251 // Finishes the operation, makes the |callback_|, and deletes |this|.
252 void Finish(bool status);
253
254 // A callback function suitable for passing to login_library.
255 static void RetrievePolicyCallback(void* delegate,
256 const char* data,
257 unsigned int size);
258 scoped_refptr<PolicyKey> key_;
259 chromeos::LoginLibrary* login_library_;
260 bool reload_key_;
261 ResultCallback callback_;
262 em::PolicyFetchResponse policy_;
263
264 DISALLOW_COPY_AND_ASSIGN(RetrievePolicyOperation);
265 };
266
267 CrosUserPolicyCache::RetrievePolicyOperation::RetrievePolicyOperation(
268 PolicyKey* key,
269 chromeos::LoginLibrary* login_library,
270 bool reload_key,
271 const ResultCallback& callback)
272 : key_(key),
273 login_library_(login_library),
274 reload_key_(reload_key),
275 callback_(callback) {}
276
277 void CrosUserPolicyCache::RetrievePolicyOperation::Run() {
278 login_library_->RequestRetrieveUserPolicy(RetrievePolicyCallback, this);
279 }
280
281 void CrosUserPolicyCache::RetrievePolicyOperation::Cancel() {
gfeher 2011/06/22 12:41:31 delete this?
Mattias Nissler (ping if slow) 2011/06/22 17:17:35 See above.
282 callback_.Reset();
283 }
284
285 void CrosUserPolicyCache::RetrievePolicyOperation::OnPolicyRetrieved(
286 const char* data,
287 unsigned int size) {
288 if (!policy_.ParseFromArray(data, size) ||
289 !policy_.has_policy_data() ||
290 !policy_.has_policy_data_signature()) {
291 LOG(ERROR) << "Failed to decode policy";
292 Finish(false);
293 return;
294 }
295 key_->CheckSignature(policy_.policy_data(),
296 policy_.policy_data_signature(),
297 reload_key_,
298 base::Bind(&RetrievePolicyOperation::OnSignatureChecked,
299 base::Unretained(this)));
300 }
301
302 void CrosUserPolicyCache::RetrievePolicyOperation::OnSignatureChecked(
303 bool result) {
304 if (!result)
305 LOG(ERROR) << "User policy signature check failed.";
306 Finish(result);
307 }
308
309 void CrosUserPolicyCache::RetrievePolicyOperation::Finish(bool status) {
310 scoped_ptr<RetrievePolicyOperation> scoped_killer(this);
gfeher 2011/06/22 12:41:31 Please also try and implement this in a way that t
Mattias Nissler (ping if slow) 2011/06/22 17:17:35 Done.
311 if (!callback_.is_null())
312 callback_.Run(status, policy_);
313 }
314
315 // static
316 void CrosUserPolicyCache::RetrievePolicyOperation::RetrievePolicyCallback(
317 void *delegate,
318 const char* data,
319 unsigned int size) {
320 static_cast<RetrievePolicyOperation*>(delegate)->OnPolicyRetrieved(data,
321 size);
322 }
323
324 CrosUserPolicyCache::CrosUserPolicyCache(
325 chromeos::LoginLibrary* login_library,
326 CrosUserPolicyIdentityStrategy* identity_strategy,
327 const FilePath& legacy_cache_file)
328 : key_(new PolicyKey(FilePath(kUserPolicyKeyFile))),
329 login_library_(login_library),
330 identity_strategy_(identity_strategy),
331 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
332 legacy_cache_file_(legacy_cache_file),
333 store_operation_(NULL),
334 retrieve_operation_(NULL) {
335 legacy_token_cache_ = new UserPolicyTokenCache(weak_ptr_factory_.GetWeakPtr(),
336 legacy_cache_file);
337 }
338
339 CrosUserPolicyCache::~CrosUserPolicyCache() {
340 CancelStore();
341 CancelRetrieve();
342 }
343
344 void CrosUserPolicyCache::Load() {
345 key_->LoadKey();
346 retrieve_operation_ =
347 new RetrievePolicyOperation(
348 key_,
349 login_library_,
350 false,
351 base::Bind(&CrosUserPolicyCache::OnPolicyLoadDone,
352 base::Unretained(this)));
353 retrieve_operation_->Run();
354 }
355
356 void CrosUserPolicyCache::SetPolicy(const em::PolicyFetchResponse& policy) {
357 CancelStore();
358 set_last_policy_refresh_time(base::Time::NowFromSystemTime());
359 store_operation_ =
360 new StorePolicyOperation(policy,
361 login_library_,
362 base::Bind(&CrosUserPolicyCache::OnPolicyStored,
363 base::Unretained(this)));
364 store_operation_->Run();
365 }
366
367 void CrosUserPolicyCache::SetUnmanaged() {
368 base::Time now(base::Time::NowFromSystemTime());
369 SetUnmanagedInternal(now);
370
371 // Construct a policy blob with unmanaged state.
372 em::PolicyData policy_data;
373 policy_data.set_policy_type(identity_strategy_->GetPolicyType());
374 policy_data.set_timestamp((now - base::Time::UnixEpoch()).InMilliseconds());
375 policy_data.set_state(em::PolicyData::UNMANAGED);
376
377 em::PolicyFetchResponse policy;
378 if (!policy_data.SerializeToString(policy.mutable_policy_data())) {
379 LOG(ERROR) << "Failed to serialize policy_data";
380 return;
381 }
382
383 SetPolicy(policy);
384 }
385
386 bool CrosUserPolicyCache::DecodePolicyData(const em::PolicyData& policy_data,
387 PolicyMap* mandatory,
388 PolicyMap* recommended) {
389 em::CloudPolicySettings policy;
390 if (!policy.ParseFromString(policy_data.policy_value())) {
391 LOG(WARNING) << "Failed to parse CloudPolicySettings protobuf.";
392 return false;
393 }
394 DecodePolicy(policy, mandatory, recommended);
395 return true;
396 }
397
398 void CrosUserPolicyCache::OnTokenCacheLoaded(const std::string& token,
399 const std::string& device_id) {
400 if (!token.empty() && !device_id.empty()) {
401 identity_strategy_->SetDeviceCredentials(device_id, token);
402 BrowserThread::PostTask(BrowserThread::FILE,
403 FROM_HERE,
404 NewRunnableFunction(&RemoveLegacyCacheDir,
405 legacy_cache_file_.DirName()));
406 } else {
407 identity_strategy_->EnableRegistration();
408 }
409 }
410
411 void CrosUserPolicyCache::OnPolicyStored(bool result) {
412 DCHECK(store_operation_);
413 if (result) {
gfeher 2011/06/22 12:41:31 So after storing a policy you immediately retrieve
Mattias Nissler (ping if slow) 2011/06/22 17:17:35 Oh, the idea is to only have one read path and use
414 CancelRetrieve();
415 retrieve_operation_ =
416 new RetrievePolicyOperation(
417 key_,
418 login_library_,
419 store_operation_->policy().has_new_public_key(),
420 base::Bind(&CrosUserPolicyCache::OnPolicyReloadDone,
421 base::Unretained(this)));
422 retrieve_operation_->Run();
423 } else {
424 InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
425 CloudPolicySubsystem::POLICY_LOCAL_ERROR);
426 }
427 CancelStore();
428 }
429
430 void CrosUserPolicyCache::OnPolicyLoadDone(
431 bool result,
432 const em::PolicyFetchResponse& policy) {
433 DCHECK(retrieve_operation_);
434 CancelRetrieve();
435 if (!result) {
436 // No policy present. Try to load the legacy token cache.
437 legacy_token_cache_->Load();
gfeher 2011/06/22 12:41:31 Do we get a SetPolicy callback if this succeeds?
Mattias Nissler (ping if slow) 2011/06/22 17:17:35 No, we just pull down policy again. I'm not trying
gfeher 2011/06/22 22:18:17 I was formulating my arguments against wasting tim
438 return;
439 }
440
441 em::PolicyData policy_data;
442 if (!policy_data.ParseFromString(policy.policy_data())) {
443 LOG(WARNING) << "Failed to parse PolicyData protobuf.";
444 InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
445 CloudPolicySubsystem::POLICY_LOCAL_ERROR);
446 identity_strategy_->EnableRegistration();
447 return;
448 }
449 if (policy_data.request_token().empty() ||
450 policy_data.username().empty() ||
451 policy_data.device_id().empty()) {
452 LOG(WARNING) << "Policy protobuf is missing credentials";
453 InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
454 CloudPolicySubsystem::POLICY_LOCAL_ERROR);
455 identity_strategy_->EnableRegistration();
456 return;
457 }
458 identity_strategy_->SetDeviceCredentials(policy_data.device_id(),
459 policy_data.request_token());
460 SetPolicyInternal(policy, NULL, true);
461 }
462
463 void CrosUserPolicyCache::OnPolicyReloadDone(
464 bool result,
465 const em::PolicyFetchResponse& policy) {
466 DCHECK(retrieve_operation_);
467 CancelRetrieve();
468 if (result) {
469 SetPolicyInternal(policy, NULL, false);
470 } else {
471 InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
472 CloudPolicySubsystem::POLICY_LOCAL_ERROR);
473 }
474 }
475
476 void CrosUserPolicyCache::CancelStore() {
477 if (store_operation_) {
478 store_operation_->Cancel();
479 store_operation_ = NULL;
480 }
481 }
482
483 void CrosUserPolicyCache::CancelRetrieve() {
484 if (retrieve_operation_) {
485 retrieve_operation_->Cancel();
486 retrieve_operation_ = NULL;
487 }
488 }
489
490 // static
491 void CrosUserPolicyCache::RemoveLegacyCacheDir(const FilePath& dir) {
492 if (!file_util::Delete(dir, true))
493 PLOG(ERROR) << "Failed to remove " << dir.value();
494 }
495
496 } // namespace policy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698