OLD | NEW |
---|---|
(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 | |
OLD | NEW |