15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// found in the LICENSE file. 45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "components/policy/core/common/cloud/user_cloud_policy_store.h" 65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/bind.h" 81320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h" 95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/location.h" 105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/metrics/histogram.h" 115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/task_runner_util.h" 125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "google_apis/gaia/gaia_auth_util.h" 135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "policy/proto/cloud_policy.pb.h" 145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "policy/proto/device_management_backend.pb.h" 155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "policy/proto/policy_signing_key.pb.h" 165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace em = enterprise_management; 185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace policy { 205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// This enum is used to define the buckets for an enumerated UMA histogram. 225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Hence, 235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// (a) existing enumerated constants should never be deleted or reordered, and 245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// (b) new constants should only be appended at the end of the enumeration. 255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// 265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Keep this in sync with EnterprisePolicyLoadStatus in histograms.xml. 275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)enum PolicyLoadStatus { 285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Policy blob was successfully loaded and parsed. 295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) LOAD_RESULT_SUCCESS, 305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // No previously stored policy was found. 325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) LOAD_RESULT_NO_POLICY_FILE, 335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Could not load the previously stored policy due to either a parse or 355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // file read error. 365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) LOAD_RESULT_LOAD_ERROR, 375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // LOAD_RESULT_SIZE is the number of items in this enum and is used when 395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // logging histograms to set the bucket size, so should always be the last 405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // item. 415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) LOAD_RESULT_SIZE, 425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}; 435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Struct containing the result of a policy load - if |status| == 455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// LOAD_RESULT_SUCCESS, |policy| is initialized from the policy file on disk. 465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)struct PolicyLoadResult { 475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) PolicyLoadStatus status; 485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) em::PolicyFetchResponse policy; 495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) em::PolicySigningKey key; 505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}; 515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace { 535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Subdirectory in the user's profile for storing user policies. 555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const base::FilePath::CharType kPolicyDir[] = FILE_PATH_LITERAL("Policy"); 565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// File in the above directory for storing user policy data. 575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const base::FilePath::CharType kPolicyCacheFile[] = 585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) FILE_PATH_LITERAL("User Policy"); 595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// File in the above directory for storing policy signing key data. 615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const base::FilePath::CharType kKeyCacheFile[] = 625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) FILE_PATH_LITERAL("Signing Key"); 635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const char kMetricPolicyHasVerifiedCachedKey[] = 655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) "Enterprise.PolicyHasVerifiedCachedKey"; 665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Maximum policy and key size that will be loaded, in bytes. 68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const size_t kPolicySizeLimit = 1024 * 1024; 69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const size_t kKeySizeLimit = 16 * 1024; 70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Loads policy from the backing file. Returns a PolicyLoadResult with the 725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// results of the fetch. 735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)policy::PolicyLoadResult LoadPolicyFromDisk( 745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const base::FilePath& policy_path, 755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const base::FilePath& key_path) { 765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) policy::PolicyLoadResult result; 775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // If the backing file does not exist, just return. We don't verify the key 785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // path here, because the key is optional (the validation code will fail if 795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // the key does not exist but the loaded policy is unsigned). 805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!base::PathExists(policy_path)) { 815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) result.status = policy::LOAD_RESULT_NO_POLICY_FILE; 825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return result; 835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::string data; 85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (!base::ReadFileToString(policy_path, &data, kPolicySizeLimit) || 875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) !result.policy.ParseFromString(data)) { 885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) LOG(WARNING) << "Failed to read or parse policy data from " 895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) << policy_path.value(); 905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) result.status = policy::LOAD_RESULT_LOAD_ERROR; 915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return result; 925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (!base::ReadFileToString(key_path, &data, kKeySizeLimit) || 955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) !result.key.ParseFromString(data)) { 965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Log an error on missing key data, but do not trigger a load failure 975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // for now since there are still old unsigned cached policy blobs in the 985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // wild with no associated key (see kMetricPolicyHasVerifiedCachedKey UMA 995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // stat below). 1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) LOG(ERROR) << "Failed to read or parse key data from " << key_path.value(); 1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) result.key.clear_signing_key(); 1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Track the occurrence of valid cached keys - when this ratio gets high 1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // enough, we can update the code to reject unsigned policy or unverified 1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // keys. 1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) UMA_HISTOGRAM_BOOLEAN(kMetricPolicyHasVerifiedCachedKey, 1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) result.key.has_signing_key()); 1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) result.status = policy::LOAD_RESULT_SUCCESS; 1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return result; 1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool WriteStringToFile(const base::FilePath path, const std::string& data) { 1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!base::CreateDirectory(path.DirName())) { 1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DLOG(WARNING) << "Failed to create directory " << path.DirName().value(); 1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return false; 1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) int size = data.size(); 121a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (base::WriteFile(path, data.c_str(), size) != size) { 1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DLOG(WARNING) << "Failed to write " << path.value(); 1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return false; 1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return true; 1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Stores policy to the backing file (must be called via a task on 1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// the background thread). 1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void StorePolicyToDiskOnBackgroundThread( 1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const base::FilePath& policy_path, 1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const base::FilePath& key_path, 1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const std::string& verification_key, 1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const em::PolicyFetchResponse& policy) { 1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DVLOG(1) << "Storing policy to " << policy_path.value(); 1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::string data; 1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!policy.SerializeToString(&data)) { 1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DLOG(WARNING) << "Failed to serialize policy data"; 1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return; 1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!WriteStringToFile(policy_path, data)) 1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return; 1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (policy.has_new_public_key()) { 1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Write the new public key and its verification signature/key to a file. 1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) em::PolicySigningKey key_info; 1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) key_info.set_signing_key(policy.new_public_key()); 1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) key_info.set_signing_key_signature( 1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) policy.new_public_key_verification_signature()); 1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) key_info.set_verification_key(verification_key); 1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::string key_data; 1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!key_info.SerializeToString(&key_data)) { 1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DLOG(WARNING) << "Failed to serialize policy signing key"; 1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return; 1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) WriteStringToFile(key_path, key_data); 1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} // namespace 1645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)UserCloudPolicyStore::UserCloudPolicyStore( 1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const base::FilePath& policy_path, 1675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const base::FilePath& key_path, 1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const std::string& verification_key, 1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_refptr<base::SequencedTaskRunner> background_task_runner) 1705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) : UserCloudPolicyStoreBase(background_task_runner), 1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) policy_path_(policy_path), 1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) key_path_(key_path), 1731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci verification_key_(verification_key), 1741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci weak_factory_(this) {} 1755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)UserCloudPolicyStore::~UserCloudPolicyStore() {} 1775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// static 1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)scoped_ptr<UserCloudPolicyStore> UserCloudPolicyStore::Create( 1805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const base::FilePath& profile_path, 1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const std::string& verification_key, 1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_refptr<base::SequencedTaskRunner> background_task_runner) { 1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::FilePath policy_path = 1845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) profile_path.Append(kPolicyDir).Append(kPolicyCacheFile); 1855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::FilePath key_path = 1865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) profile_path.Append(kPolicyDir).Append(kKeyCacheFile); 1875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return make_scoped_ptr(new UserCloudPolicyStore( 1885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) policy_path, key_path, verification_key, background_task_runner)); 1895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void UserCloudPolicyStore::SetSigninUsername(const std::string& username) { 1925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) signin_username_ = username; 1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void UserCloudPolicyStore::LoadImmediately() { 1965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DVLOG(1) << "Initiating immediate policy load from disk"; 1975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Cancel any pending Load/Store/Validate operations. 1985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) weak_factory_.InvalidateWeakPtrs(); 1995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Load the policy from disk... 2005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) PolicyLoadResult result = LoadPolicyFromDisk(policy_path_, key_path_); 2015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // ...and install it, reporting success/failure to any observers. 2025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) PolicyLoaded(false, result); 2035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 2045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void UserCloudPolicyStore::Clear() { 2065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) background_task_runner()->PostTask( 2075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) FROM_HERE, 2085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::Bind(base::IgnoreResult(&base::DeleteFile), policy_path_, false)); 2095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) background_task_runner()->PostTask( 2105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) FROM_HERE, 2115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::Bind(base::IgnoreResult(&base::DeleteFile), key_path_, false)); 2125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) policy_.reset(); 2135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) policy_map_.Clear(); 2145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) policy_key_.clear(); 2155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NotifyStoreLoaded(); 2165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 2175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void UserCloudPolicyStore::Load() { 2195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DVLOG(1) << "Initiating policy load from disk"; 2205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Cancel any pending Load/Store/Validate operations. 2215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) weak_factory_.InvalidateWeakPtrs(); 2225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Start a new Load operation and have us get called back when it is 2245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // complete. 2255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::PostTaskAndReplyWithResult( 2261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci background_task_runner().get(), 2275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) FROM_HERE, 2285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::Bind(&LoadPolicyFromDisk, policy_path_, key_path_), 2295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::Bind(&UserCloudPolicyStore::PolicyLoaded, 2301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci weak_factory_.GetWeakPtr(), 2311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci true)); 2325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 2335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void UserCloudPolicyStore::PolicyLoaded(bool validate_in_background, 2355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) PolicyLoadResult result) { 2365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) UMA_HISTOGRAM_ENUMERATION("Enterprise.UserCloudPolicyStore.LoadStatus", 2375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) result.status, 2385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) LOAD_RESULT_SIZE); 2395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) switch (result.status) { 2405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) case LOAD_RESULT_LOAD_ERROR: 2415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) status_ = STATUS_LOAD_ERROR; 2425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NotifyStoreError(); 2435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) break; 2445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) case LOAD_RESULT_NO_POLICY_FILE: 2465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DVLOG(1) << "No policy found on disk"; 2475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NotifyStoreLoaded(); 2485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) break; 2495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) case LOAD_RESULT_SUCCESS: { 2515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Found policy on disk - need to validate it before it can be used. 2525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<em::PolicyFetchResponse> cloud_policy( 2535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) new em::PolicyFetchResponse(result.policy)); 2545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<em::PolicySigningKey> key( 2555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) new em::PolicySigningKey(result.key)); 2565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bool doing_key_rotation = false; 2585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const std::string& verification_key = verification_key_; 2595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!key->has_verification_key() || 2605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) key->verification_key() != verification_key_) { 2615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // The cached key didn't match our current key, so we're doing a key 2625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // rotation - make sure we request a new key from the server on our 2635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // next fetch. 2645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) doing_key_rotation = true; 2655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DLOG(WARNING) << "Verification key rotation detected"; 2665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // TODO(atwilson): Add code to update |verification_key| to point to 2675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // the correct key to validate the existing blob (can't do this until 2685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // we've done our first key rotation). 2695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) Validate(cloud_policy.Pass(), 2725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) key.Pass(), 2735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) verification_key, 2745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) validate_in_background, 2755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::Bind( 2765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) &UserCloudPolicyStore::InstallLoadedPolicyAfterValidation, 2775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) weak_factory_.GetWeakPtr(), 2785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) doing_key_rotation, 2795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) result.key.has_signing_key() ? 2805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) result.key.signing_key() : std::string())); 2815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) break; 2825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) default: 2845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NOTREACHED(); 2855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 2875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void UserCloudPolicyStore::InstallLoadedPolicyAfterValidation( 2895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bool doing_key_rotation, 2905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const std::string& signing_key, 2915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) UserCloudPolicyValidator* validator) { 2925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) UMA_HISTOGRAM_ENUMERATION( 2935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) "Enterprise.UserCloudPolicyStore.LoadValidationStatus", 2945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) validator->status(), 2955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) CloudPolicyValidatorBase::VALIDATION_STATUS_SIZE); 2965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) validation_status_ = validator->status(); 2975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!validator->success()) { 2985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DVLOG(1) << "Validation failed: status=" << validation_status_; 2995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) status_ = STATUS_VALIDATION_ERROR; 3005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NotifyStoreError(); 3015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return; 3025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 3035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DVLOG(1) << "Validation succeeded - installing policy with dm_token: " << 3055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) validator->policy_data()->request_token(); 3065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DVLOG(1) << "Device ID: " << validator->policy_data()->device_id(); 3075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // If we're doing a key rotation, clear the public key version so a future 3095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // policy fetch will force regeneration of the keys. 3105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (doing_key_rotation) { 3115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) validator->policy_data()->clear_public_key_version(); 3125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) policy_key_.clear(); 3135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } else { 3145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Policy validation succeeded, so we know the signing key is good. 3155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) policy_key_ = signing_key; 3165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 3175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass()); 3195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) status_ = STATUS_OK; 3205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NotifyStoreLoaded(); 3215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 3225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void UserCloudPolicyStore::Store(const em::PolicyFetchResponse& policy) { 3245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Stop any pending requests to store policy, then validate the new policy 3255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // before storing it. 3265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) weak_factory_.InvalidateWeakPtrs(); 3275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<em::PolicyFetchResponse> policy_copy( 3285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) new em::PolicyFetchResponse(policy)); 3295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) Validate(policy_copy.Pass(), 3305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<em::PolicySigningKey>(), 3315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) verification_key_, 3325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) true, 3335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::Bind(&UserCloudPolicyStore::StorePolicyAfterValidation, 3345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) weak_factory_.GetWeakPtr())); 3355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 3365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void UserCloudPolicyStore::Validate( 3385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<em::PolicyFetchResponse> policy, 3395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<em::PolicySigningKey> cached_key, 3405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const std::string& verification_key, 3415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bool validate_in_background, 3425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const UserCloudPolicyValidator::CompletionCallback& callback) { 3435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Configure the validator. 3445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<UserCloudPolicyValidator> validator = CreateValidator( 3455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) policy.Pass(), 3465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) CloudPolicyValidatorBase::TIMESTAMP_NOT_BEFORE); 3475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Extract the owning domain from the signed-in user (if any is set yet). 3495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // If there's no owning domain, then the code just ensures that the policy 3505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // is self-consistent (that the keys are signed with the same domain that the 3515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // username field in the policy contains). UserPolicySigninServerBase will 3525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // verify that the username matches the signed in user once profile 3535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // initialization is complete (http://crbug.com/342327). 3545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::string owning_domain; 3555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Validate the username if the user is signed in. The signin_username_ can 3575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // be empty during initial policy load because this happens before the 3585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Prefs subsystem is initialized. 3595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!signin_username_.empty()) { 3605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DVLOG(1) << "Validating username: " << signin_username_; 361a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) validator->ValidateUsername(signin_username_, true); 3625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) owning_domain = gaia::ExtractDomainName( 3635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gaia::CanonicalizeEmail(gaia::SanitizeEmail(signin_username_))); 3645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 3655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // There are 4 cases: 3675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // 3685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // 1) Validation after loading from cache with no cached key. 36903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) // Action: Just validate signature with an empty key - this will result in 37003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) // a failed validation and the cached policy will be rejected. 3715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // 3725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // 2) Validation after loading from cache with a cached key 3735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Action: Validate signature on policy blob but don't allow key rotation. 3745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // 3755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // 3) Validation after loading new policy from the server with no cached key 3765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Action: Validate as initial key provisioning (case where we are migrating 3775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // from unsigned policy) 3785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // 3795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // 4) Validation after loading new policy from the server with a cached key 3805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Action: Validate as normal, and allow key rotation. 3815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (cached_key) { 38203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) // Case #1/#2 - loading from cache. Validate the cached key (if no key, 38303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) // then the validation will fail), then do normal policy data signature 38403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) // validation using the cached key. 38503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 3865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Loading from cache should not change the cached keys. 3875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(policy_key_.empty() || policy_key_ == cached_key->signing_key()); 38803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) DLOG_IF(WARNING, !cached_key->has_signing_key()) << 38903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) "Unsigned policy blob detected"; 39003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 39103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) validator->ValidateCachedKey(cached_key->signing_key(), 39203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) cached_key->signing_key_signature(), 39303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) verification_key, 39403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) owning_domain); 39503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) // Loading from cache, so don't allow key rotation. 39603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) const bool no_rotation = false; 39703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) validator->ValidateSignature(cached_key->signing_key(), 39803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) verification_key, 39903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) owning_domain, 40003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) no_rotation); 4015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } else { 4025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // No passed cached_key - this is not validating the initial policy load 4035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // from cache, but rather an update from the server. 4045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (policy_key_.empty()) { 4055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Case #3 - no valid existing policy key (either this is the initial 4065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // policy fetch, or we're doing a key rotation), so this new policy fetch 4075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // should include an initial key provision. 4085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) validator->ValidateInitialKey(verification_key, owning_domain); 4095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } else { 4105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Case #4 - verify new policy with existing key. We always allow key 4115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // rotation - the verification key will prevent invalid policy from being 4125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // injected. |policy_key_| is already known to be valid, so no need to 4135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // verify via ValidateCachedKey(). 4145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const bool allow_rotation = true; 4155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) validator->ValidateSignature( 4165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) policy_key_, verification_key, owning_domain, allow_rotation); 4175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 4185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 4195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 4205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (validate_in_background) { 4215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Start validation in the background. The Validator will free itself once 4225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // validation is complete. 4235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) validator.release()->StartValidation(callback); 4245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } else { 4255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Run validation immediately and invoke the callback with the results. 4265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) validator->RunValidation(); 4275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) callback.Run(validator.get()); 4285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 4295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 4305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 4315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void UserCloudPolicyStore::StorePolicyAfterValidation( 4325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) UserCloudPolicyValidator* validator) { 4335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) UMA_HISTOGRAM_ENUMERATION( 4345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) "Enterprise.UserCloudPolicyStore.StoreValidationStatus", 4355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) validator->status(), 4365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) CloudPolicyValidatorBase::VALIDATION_STATUS_SIZE); 4375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) validation_status_ = validator->status(); 4385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DVLOG(1) << "Policy validation complete: status = " << validation_status_; 4395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!validator->success()) { 4405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) status_ = STATUS_VALIDATION_ERROR; 4415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NotifyStoreError(); 4425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return; 4435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 4445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 4455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Persist the validated policy (just fire a task - don't bother getting a 4465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // reply because we can't do anything if it fails). 4475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) background_task_runner()->PostTask( 4485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) FROM_HERE, 4495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::Bind(&StorePolicyToDiskOnBackgroundThread, 4505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) policy_path_, key_path_, verification_key_, 4515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) *validator->policy())); 4525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass()); 4535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 4545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // If the key was rotated, update our local cache of the key. 4555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (validator->policy()->has_new_public_key()) 4565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) policy_key_ = validator->policy()->new_public_key(); 4575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) status_ = STATUS_OK; 4585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NotifyStoreLoaded(); 4595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 4605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 4615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} // namespace policy 462