OLD | NEW |
| (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/user_cloud_policy_store_chromeos.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/bind_helpers.h" | |
9 #include "base/callback.h" | |
10 #include "base/file_util.h" | |
11 #include "base/logging.h" | |
12 #include "base/memory/ref_counted.h" | |
13 #include "base/metrics/histogram.h" | |
14 #include "base/stl_util.h" | |
15 #include "base/stringprintf.h" | |
16 #include "chrome/browser/policy/proto/cloud_policy.pb.h" | |
17 #include "chrome/browser/policy/proto/device_management_local.pb.h" | |
18 #include "chrome/browser/policy/user_policy_disk_cache.h" | |
19 #include "chrome/browser/policy/user_policy_token_loader.h" | |
20 #include "chromeos/dbus/cryptohome_client.h" | |
21 #include "chromeos/dbus/session_manager_client.h" | |
22 #include "content/public/browser/browser_thread.h" | |
23 #include "google_apis/gaia/gaia_auth_util.h" | |
24 | |
25 namespace em = enterprise_management; | |
26 | |
27 namespace policy { | |
28 | |
29 namespace { | |
30 | |
31 // Path within |user_policy_key_dir_| that contains the policy key. | |
32 // "%s" must be substituted with the sanitized username. | |
33 const base::FilePath::CharType kPolicyKeyFile[] = | |
34 FILE_PATH_LITERAL("%s/policy.pub"); | |
35 | |
36 // Maximum key size that will be loaded, in bytes. | |
37 const int kKeySizeLimit = 16 * 1024; | |
38 | |
39 enum ValidationFailure { | |
40 VALIDATION_FAILURE_DBUS, | |
41 VALIDATION_FAILURE_LOAD_KEY, | |
42 VALIDATION_FAILURE_SIZE, | |
43 }; | |
44 | |
45 void SampleValidationFailure(ValidationFailure sample) { | |
46 UMA_HISTOGRAM_ENUMERATION("Enterprise.UserPolicyValidationFailure", | |
47 sample, | |
48 VALIDATION_FAILURE_SIZE); | |
49 } | |
50 | |
51 } // namespace | |
52 | |
53 // Helper class for loading legacy policy caches. | |
54 class LegacyPolicyCacheLoader : public UserPolicyTokenLoader::Delegate, | |
55 public UserPolicyDiskCache::Delegate { | |
56 public: | |
57 typedef base::Callback<void(const std::string&, | |
58 const std::string&, | |
59 CloudPolicyStore::Status, | |
60 scoped_ptr<em::PolicyFetchResponse>)> Callback; | |
61 | |
62 LegacyPolicyCacheLoader(const base::FilePath& token_cache_file, | |
63 const base::FilePath& policy_cache_file); | |
64 virtual ~LegacyPolicyCacheLoader(); | |
65 | |
66 // Starts loading, and reports the result to |callback| when done. | |
67 void Load(const Callback& callback); | |
68 | |
69 // UserPolicyTokenLoader::Delegate: | |
70 virtual void OnTokenLoaded(const std::string& token, | |
71 const std::string& device_id) OVERRIDE; | |
72 | |
73 // UserPolicyDiskCache::Delegate: | |
74 virtual void OnDiskCacheLoaded( | |
75 UserPolicyDiskCache::LoadResult result, | |
76 const em::CachedCloudPolicyResponse& policy) OVERRIDE; | |
77 | |
78 private: | |
79 // Checks whether the load operations from the legacy caches completed. If so, | |
80 // fires the appropriate notification. | |
81 void CheckLoadFinished(); | |
82 | |
83 // Maps a disk cache LoadResult to a CloudPolicyStore::Status. | |
84 static CloudPolicyStore::Status TranslateLoadResult( | |
85 UserPolicyDiskCache::LoadResult result); | |
86 | |
87 base::WeakPtrFactory<LegacyPolicyCacheLoader> weak_factory_; | |
88 | |
89 scoped_refptr<UserPolicyTokenLoader> token_loader_; | |
90 scoped_refptr<UserPolicyDiskCache> policy_cache_; | |
91 | |
92 std::string dm_token_; | |
93 std::string device_id_; | |
94 bool has_policy_; | |
95 scoped_ptr<em::PolicyFetchResponse> policy_; | |
96 CloudPolicyStore::Status status_; | |
97 | |
98 Callback callback_; | |
99 | |
100 DISALLOW_COPY_AND_ASSIGN(LegacyPolicyCacheLoader); | |
101 }; | |
102 | |
103 LegacyPolicyCacheLoader::LegacyPolicyCacheLoader( | |
104 const base::FilePath& token_cache_file, | |
105 const base::FilePath& policy_cache_file) | |
106 : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), | |
107 has_policy_(false), | |
108 status_(CloudPolicyStore::STATUS_OK) { | |
109 token_loader_ = new UserPolicyTokenLoader(weak_factory_.GetWeakPtr(), | |
110 token_cache_file); | |
111 policy_cache_ = new UserPolicyDiskCache(weak_factory_.GetWeakPtr(), | |
112 policy_cache_file); | |
113 } | |
114 | |
115 LegacyPolicyCacheLoader::~LegacyPolicyCacheLoader() {} | |
116 | |
117 void LegacyPolicyCacheLoader::Load(const Callback& callback) { | |
118 callback_ = callback; | |
119 token_loader_->Load(); | |
120 policy_cache_->Load(); | |
121 } | |
122 | |
123 void LegacyPolicyCacheLoader::OnTokenLoaded(const std::string& token, | |
124 const std::string& device_id) { | |
125 dm_token_ = token; | |
126 device_id_ = device_id; | |
127 token_loader_ = NULL; | |
128 CheckLoadFinished(); | |
129 } | |
130 | |
131 void LegacyPolicyCacheLoader::OnDiskCacheLoaded( | |
132 UserPolicyDiskCache::LoadResult result, | |
133 const em::CachedCloudPolicyResponse& policy) { | |
134 status_ = TranslateLoadResult(result); | |
135 if (result == UserPolicyDiskCache::LOAD_RESULT_SUCCESS) { | |
136 if (policy.has_cloud_policy()) | |
137 policy_.reset(new em::PolicyFetchResponse(policy.cloud_policy())); | |
138 } else { | |
139 LOG(WARNING) << "Failed to load legacy policy cache: " << result; | |
140 } | |
141 policy_cache_ = NULL; | |
142 CheckLoadFinished(); | |
143 } | |
144 | |
145 void LegacyPolicyCacheLoader::CheckLoadFinished() { | |
146 if (!token_loader_.get() && !policy_cache_.get()) | |
147 callback_.Run(dm_token_, device_id_, status_, policy_.Pass()); | |
148 } | |
149 | |
150 // static | |
151 CloudPolicyStore::Status LegacyPolicyCacheLoader::TranslateLoadResult( | |
152 UserPolicyDiskCache::LoadResult result) { | |
153 switch (result) { | |
154 case UserPolicyDiskCache::LOAD_RESULT_SUCCESS: | |
155 case UserPolicyDiskCache::LOAD_RESULT_NOT_FOUND: | |
156 return CloudPolicyStore::STATUS_OK; | |
157 case UserPolicyDiskCache::LOAD_RESULT_PARSE_ERROR: | |
158 case UserPolicyDiskCache::LOAD_RESULT_READ_ERROR: | |
159 return CloudPolicyStore::STATUS_LOAD_ERROR; | |
160 } | |
161 NOTREACHED(); | |
162 return CloudPolicyStore::STATUS_OK; | |
163 } | |
164 | |
165 UserCloudPolicyStoreChromeOS::UserCloudPolicyStoreChromeOS( | |
166 chromeos::CryptohomeClient* cryptohome_client, | |
167 chromeos::SessionManagerClient* session_manager_client, | |
168 const std::string& username, | |
169 const base::FilePath& user_policy_key_dir, | |
170 const base::FilePath& legacy_token_cache_file, | |
171 const base::FilePath& legacy_policy_cache_file) | |
172 : cryptohome_client_(cryptohome_client), | |
173 session_manager_client_(session_manager_client), | |
174 username_(username), | |
175 user_policy_key_dir_(user_policy_key_dir), | |
176 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), | |
177 legacy_cache_dir_(legacy_token_cache_file.DirName()), | |
178 legacy_loader_(new LegacyPolicyCacheLoader(legacy_token_cache_file, | |
179 legacy_policy_cache_file)), | |
180 legacy_caches_loaded_(false), | |
181 policy_key_loaded_(false) {} | |
182 | |
183 UserCloudPolicyStoreChromeOS::~UserCloudPolicyStoreChromeOS() {} | |
184 | |
185 void UserCloudPolicyStoreChromeOS::Store( | |
186 const em::PolicyFetchResponse& policy) { | |
187 // Cancel all pending requests. | |
188 weak_factory_.InvalidateWeakPtrs(); | |
189 scoped_ptr<em::PolicyFetchResponse> response( | |
190 new em::PolicyFetchResponse(policy)); | |
191 EnsurePolicyKeyLoaded( | |
192 base::Bind(&UserCloudPolicyStoreChromeOS::ValidatePolicyForStore, | |
193 weak_factory_.GetWeakPtr(), | |
194 base::Passed(&response))); | |
195 } | |
196 | |
197 void UserCloudPolicyStoreChromeOS::Load() { | |
198 // Cancel all pending requests. | |
199 weak_factory_.InvalidateWeakPtrs(); | |
200 session_manager_client_->RetrieveUserPolicy( | |
201 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyRetrieved, | |
202 weak_factory_.GetWeakPtr())); | |
203 } | |
204 | |
205 void UserCloudPolicyStoreChromeOS::ValidatePolicyForStore( | |
206 scoped_ptr<em::PolicyFetchResponse> policy) { | |
207 // Create and configure a validator. | |
208 scoped_ptr<UserCloudPolicyValidator> validator = | |
209 CreateValidator(policy.Pass()); | |
210 validator->ValidateUsername(username_); | |
211 if (policy_key_.empty()) { | |
212 validator->ValidateInitialKey(); | |
213 } else { | |
214 const bool allow_rotation = true; | |
215 validator->ValidateSignature(policy_key_, allow_rotation); | |
216 } | |
217 | |
218 // Start validation. The Validator will delete itself once validation is | |
219 // complete. | |
220 validator.release()->StartValidation( | |
221 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated, | |
222 weak_factory_.GetWeakPtr())); | |
223 } | |
224 | |
225 void UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated( | |
226 UserCloudPolicyValidator* validator) { | |
227 validation_status_ = validator->status(); | |
228 | |
229 UMA_HISTOGRAM_ENUMERATION( | |
230 "Enterprise.UserPolicyValidationStoreStatus", | |
231 validation_status_, | |
232 UserCloudPolicyValidator::VALIDATION_POLICY_PARSE_ERROR + 1); | |
233 | |
234 if (!validator->success()) { | |
235 status_ = STATUS_VALIDATION_ERROR; | |
236 NotifyStoreError(); | |
237 return; | |
238 } | |
239 | |
240 std::string policy_blob; | |
241 if (!validator->policy()->SerializeToString(&policy_blob)) { | |
242 status_ = STATUS_SERIALIZE_ERROR; | |
243 NotifyStoreError(); | |
244 return; | |
245 } | |
246 | |
247 session_manager_client_->StoreUserPolicy( | |
248 policy_blob, | |
249 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyStored, | |
250 weak_factory_.GetWeakPtr())); | |
251 } | |
252 | |
253 void UserCloudPolicyStoreChromeOS::OnPolicyStored(bool success) { | |
254 if (!success) { | |
255 status_ = STATUS_STORE_ERROR; | |
256 NotifyStoreError(); | |
257 } else { | |
258 // Load the policy right after storing it, to make sure it was accepted by | |
259 // the session manager. An additional validation is performed after the | |
260 // load; reload the key for that validation too, in case it was rotated. | |
261 ReloadPolicyKey(base::Bind(&UserCloudPolicyStoreChromeOS::Load, | |
262 weak_factory_.GetWeakPtr())); | |
263 } | |
264 } | |
265 | |
266 void UserCloudPolicyStoreChromeOS::OnPolicyRetrieved( | |
267 const std::string& policy_blob) { | |
268 if (policy_blob.empty()) { | |
269 // Policy fetch failed. Try legacy caches if we haven't done that already. | |
270 if (!legacy_caches_loaded_ && legacy_loader_.get()) { | |
271 legacy_caches_loaded_ = true; | |
272 legacy_loader_->Load( | |
273 base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished, | |
274 weak_factory_.GetWeakPtr())); | |
275 } else { | |
276 // session_manager doesn't have policy. Adjust internal state and notify | |
277 // the world about the policy update. | |
278 policy_.reset(); | |
279 NotifyStoreLoaded(); | |
280 } | |
281 return; | |
282 } | |
283 | |
284 // Policy is supplied by session_manager. Disregard legacy data from now on. | |
285 legacy_loader_.reset(); | |
286 | |
287 scoped_ptr<em::PolicyFetchResponse> policy(new em::PolicyFetchResponse()); | |
288 if (!policy->ParseFromString(policy_blob)) { | |
289 status_ = STATUS_PARSE_ERROR; | |
290 NotifyStoreError(); | |
291 return; | |
292 } | |
293 | |
294 // Load |policy_key_| to verify the loaded policy. | |
295 EnsurePolicyKeyLoaded( | |
296 base::Bind(&UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy, | |
297 weak_factory_.GetWeakPtr(), | |
298 base::Passed(&policy))); | |
299 } | |
300 | |
301 void UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy( | |
302 scoped_ptr<em::PolicyFetchResponse> policy) { | |
303 // Create and configure a validator for the loaded policy. | |
304 scoped_ptr<UserCloudPolicyValidator> validator = | |
305 CreateValidator(policy.Pass()); | |
306 validator->ValidateUsername(username_); | |
307 const bool allow_rotation = false; | |
308 validator->ValidateSignature(policy_key_, allow_rotation); | |
309 // Start validation. The Validator will delete itself once validation is | |
310 // complete. | |
311 validator.release()->StartValidation( | |
312 base::Bind(&UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated, | |
313 weak_factory_.GetWeakPtr())); | |
314 } | |
315 | |
316 void UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated( | |
317 UserCloudPolicyValidator* validator) { | |
318 validation_status_ = validator->status(); | |
319 | |
320 UMA_HISTOGRAM_ENUMERATION( | |
321 "Enterprise.UserPolicyValidationLoadStatus", | |
322 validation_status_, | |
323 UserCloudPolicyValidator::VALIDATION_POLICY_PARSE_ERROR + 1); | |
324 | |
325 if (!validator->success()) { | |
326 status_ = STATUS_VALIDATION_ERROR; | |
327 NotifyStoreError(); | |
328 return; | |
329 } | |
330 | |
331 InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass()); | |
332 status_ = STATUS_OK; | |
333 | |
334 // Policy has been loaded successfully. This indicates that new-style policy | |
335 // is working, so the legacy cache directory can be removed. | |
336 if (!legacy_cache_dir_.empty()) { | |
337 content::BrowserThread::PostBlockingPoolTask( | |
338 FROM_HERE, | |
339 base::Bind(&UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir, | |
340 legacy_cache_dir_)); | |
341 legacy_cache_dir_.clear(); | |
342 } | |
343 NotifyStoreLoaded(); | |
344 } | |
345 | |
346 void UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished( | |
347 const std::string& dm_token, | |
348 const std::string& device_id, | |
349 Status status, | |
350 scoped_ptr<em::PolicyFetchResponse> policy) { | |
351 status_ = status; | |
352 if (policy.get()) { | |
353 // Create and configure a validator for the loaded legacy policy. Note that | |
354 // the signature on this policy is not verified. | |
355 scoped_ptr<UserCloudPolicyValidator> validator = | |
356 CreateValidator(policy.Pass()); | |
357 validator->ValidateUsername(username_); | |
358 validator.release()->StartValidation( | |
359 base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated, | |
360 weak_factory_.GetWeakPtr(), | |
361 dm_token, | |
362 device_id)); | |
363 } else { | |
364 InstallLegacyTokens(dm_token, device_id); | |
365 } | |
366 } | |
367 | |
368 void UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated( | |
369 const std::string& dm_token, | |
370 const std::string& device_id, | |
371 UserCloudPolicyValidator* validator) { | |
372 validation_status_ = validator->status(); | |
373 if (validator->success()) { | |
374 status_ = STATUS_OK; | |
375 InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass()); | |
376 | |
377 // Clear the public key version. The public key version field would | |
378 // otherwise indicate that we have key installed in the store when in fact | |
379 // we haven't. This may result in policy updates failing signature | |
380 // verification. | |
381 policy_->clear_public_key_version(); | |
382 } else { | |
383 status_ = STATUS_VALIDATION_ERROR; | |
384 } | |
385 | |
386 InstallLegacyTokens(dm_token, device_id); | |
387 } | |
388 | |
389 void UserCloudPolicyStoreChromeOS::InstallLegacyTokens( | |
390 const std::string& dm_token, | |
391 const std::string& device_id) { | |
392 // Write token and device ID to |policy_|, giving them precedence over the | |
393 // policy blob. This is to match the legacy behavior, which used token and | |
394 // device id exclusively from the token cache file. | |
395 if (!dm_token.empty() && !device_id.empty()) { | |
396 if (!policy_.get()) | |
397 policy_.reset(new em::PolicyData()); | |
398 policy_->set_request_token(dm_token); | |
399 policy_->set_device_id(device_id); | |
400 } | |
401 | |
402 // Tell the rest of the world that the policy load completed. | |
403 NotifyStoreLoaded(); | |
404 } | |
405 | |
406 // static | |
407 void UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir( | |
408 const base::FilePath& dir) { | |
409 if (file_util::PathExists(dir) && !file_util::Delete(dir, true)) | |
410 LOG(ERROR) << "Failed to remove cache dir " << dir.value(); | |
411 } | |
412 | |
413 void UserCloudPolicyStoreChromeOS::ReloadPolicyKey( | |
414 const base::Closure& callback) { | |
415 std::vector<uint8>* key = new std::vector<uint8>(); | |
416 content::BrowserThread::PostBlockingPoolTaskAndReply( | |
417 FROM_HERE, | |
418 base::Bind(&UserCloudPolicyStoreChromeOS::LoadPolicyKey, | |
419 policy_key_path_, | |
420 key), | |
421 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded, | |
422 weak_factory_.GetWeakPtr(), | |
423 base::Owned(key), | |
424 callback)); | |
425 } | |
426 | |
427 // static | |
428 void UserCloudPolicyStoreChromeOS::LoadPolicyKey(const base::FilePath& path, | |
429 std::vector<uint8>* key) { | |
430 if (!file_util::PathExists(path)) { | |
431 // There is no policy key the first time that a user fetches policy. If | |
432 // |path| does not exist then that is the most likely scenario, so there's | |
433 // no need to sample a failure. | |
434 VLOG(1) << "No key at " << path.value(); | |
435 return; | |
436 } | |
437 | |
438 int64 size; | |
439 if (!file_util::GetFileSize(path, &size)) { | |
440 LOG(ERROR) << "Could not get size of " << path.value(); | |
441 } else if (size == 0 || size > kKeySizeLimit) { | |
442 LOG(ERROR) << "Key at " << path.value() << " has bad size " << size; | |
443 } else { | |
444 key->resize(size); | |
445 int read_size = file_util::ReadFile( | |
446 path, reinterpret_cast<char*>(vector_as_array(key)), size); | |
447 if (read_size != size) { | |
448 LOG(ERROR) << "Failed to read key at " << path.value(); | |
449 key->clear(); | |
450 } | |
451 } | |
452 | |
453 if (key->empty()) | |
454 SampleValidationFailure(VALIDATION_FAILURE_LOAD_KEY); | |
455 } | |
456 | |
457 void UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded( | |
458 std::vector<uint8>* key, | |
459 const base::Closure& callback) { | |
460 policy_key_.swap(*key); | |
461 policy_key_loaded_ = true; | |
462 callback.Run(); | |
463 } | |
464 | |
465 void UserCloudPolicyStoreChromeOS::EnsurePolicyKeyLoaded( | |
466 const base::Closure& callback) { | |
467 if (policy_key_loaded_) { | |
468 callback.Run(); | |
469 } else { | |
470 // Get the hashed username that's part of the key's path, to determine | |
471 // |policy_key_path_|. | |
472 cryptohome_client_->GetSanitizedUsername(username_, | |
473 base::Bind(&UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername, | |
474 weak_factory_.GetWeakPtr(), | |
475 callback)); | |
476 } | |
477 } | |
478 | |
479 void UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername( | |
480 const base::Closure& callback, | |
481 chromeos::DBusMethodCallStatus call_status, | |
482 const std::string& sanitized_username) { | |
483 // The default empty path will always yield an empty key. | |
484 if (call_status == chromeos::DBUS_METHOD_CALL_SUCCESS && | |
485 !sanitized_username.empty()) { | |
486 policy_key_path_ = user_policy_key_dir_.Append( | |
487 base::StringPrintf(kPolicyKeyFile, sanitized_username.c_str())); | |
488 } else { | |
489 SampleValidationFailure(VALIDATION_FAILURE_DBUS); | |
490 } | |
491 ReloadPolicyKey(callback); | |
492 } | |
493 | |
494 } // namespace policy | |
OLD | NEW |