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/cloud/user_cloud_policy_store.h" 6 7#include "base/bind.h" 8#include "base/file_util.h" 9#include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h" 10#include "chrome/browser/policy/proto/cloud/device_management_local.pb.h" 11#include "chrome/browser/profiles/profile.h" 12#include "chrome/browser/signin/signin_manager.h" 13#include "chrome/browser/signin/signin_manager_factory.h" 14#include "content/public/browser/browser_thread.h" 15#include "policy/policy_constants.h" 16#include "policy/proto/cloud_policy.pb.h" 17 18namespace em = enterprise_management; 19 20namespace policy { 21 22enum PolicyLoadStatus { 23 // Policy blob was successfully loaded and parsed. 24 LOAD_RESULT_SUCCESS, 25 26 // No previously stored policy was found. 27 LOAD_RESULT_NO_POLICY_FILE, 28 29 // Could not load the previously stored policy due to either a parse or 30 // file read error. 31 LOAD_RESULT_LOAD_ERROR, 32}; 33 34// Struct containing the result of a policy load - if |status| == 35// LOAD_RESULT_SUCCESS, |policy| is initialized from the policy file on disk. 36struct PolicyLoadResult { 37 PolicyLoadStatus status; 38 em::PolicyFetchResponse policy; 39}; 40 41namespace { 42 43// Subdirectory in the user's profile for storing user policies. 44const base::FilePath::CharType kPolicyDir[] = FILE_PATH_LITERAL("Policy"); 45// File in the above directory for storing user policy data. 46const base::FilePath::CharType kPolicyCacheFile[] = 47 FILE_PATH_LITERAL("User Policy"); 48 49// Loads policy from the backing file. Returns a PolicyLoadResult with the 50// results of the fetch. 51policy::PolicyLoadResult LoadPolicyFromDisk(const base::FilePath& path) { 52 policy::PolicyLoadResult result; 53 // If the backing file does not exist, just return. 54 if (!base::PathExists(path)) { 55 result.status = policy::LOAD_RESULT_NO_POLICY_FILE; 56 return result; 57 } 58 std::string data; 59 if (!file_util::ReadFileToString(path, &data) || 60 !result.policy.ParseFromArray(data.c_str(), data.size())) { 61 LOG(WARNING) << "Failed to read or parse policy data from " << path.value(); 62 result.status = policy::LOAD_RESULT_LOAD_ERROR; 63 return result; 64 } 65 66 result.status = policy::LOAD_RESULT_SUCCESS; 67 return result; 68} 69 70// Stores policy to the backing file (must be called via a task on 71// the FILE thread). 72void StorePolicyToDiskOnFileThread(const base::FilePath& path, 73 const em::PolicyFetchResponse& policy) { 74 DVLOG(1) << "Storing policy to " << path.value(); 75 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); 76 std::string data; 77 if (!policy.SerializeToString(&data)) { 78 DLOG(WARNING) << "Failed to serialize policy data"; 79 return; 80 } 81 82 if (!file_util::CreateDirectory(path.DirName())) { 83 DLOG(WARNING) << "Failed to create directory " << path.DirName().value(); 84 return; 85 } 86 87 int size = data.size(); 88 if (file_util::WriteFile(path, data.c_str(), size) != size) { 89 DLOG(WARNING) << "Failed to write " << path.value(); 90 } 91} 92 93} // namespace 94 95UserCloudPolicyStore::UserCloudPolicyStore(Profile* profile, 96 const base::FilePath& path) 97 : weak_factory_(this), 98 profile_(profile), 99 backing_file_path_(path) { 100} 101 102UserCloudPolicyStore::~UserCloudPolicyStore() {} 103 104// static 105scoped_ptr<UserCloudPolicyStore> UserCloudPolicyStore::Create( 106 Profile* profile) { 107 base::FilePath path = 108 profile->GetPath().Append(kPolicyDir).Append(kPolicyCacheFile); 109 return make_scoped_ptr(new UserCloudPolicyStore(profile, path)); 110} 111 112void UserCloudPolicyStore::LoadImmediately() { 113 DVLOG(1) << "Initiating immediate policy load from disk"; 114 // Cancel any pending Load/Store/Validate operations. 115 weak_factory_.InvalidateWeakPtrs(); 116 // Load the policy from disk... 117 PolicyLoadResult result = LoadPolicyFromDisk(backing_file_path_); 118 // ...and install it, reporting success/failure to any observers. 119 PolicyLoaded(false, result); 120} 121 122void UserCloudPolicyStore::Clear() { 123 content::BrowserThread::PostTask( 124 content::BrowserThread::FILE, FROM_HERE, 125 base::Bind(base::IgnoreResult(&base::DeleteFile), 126 backing_file_path_, 127 false)); 128 policy_.reset(); 129 policy_map_.Clear(); 130 NotifyStoreLoaded(); 131} 132 133void UserCloudPolicyStore::Load() { 134 DVLOG(1) << "Initiating policy load from disk"; 135 // Cancel any pending Load/Store/Validate operations. 136 weak_factory_.InvalidateWeakPtrs(); 137 138 // Start a new Load operation and have us get called back when it is 139 // complete. 140 content::BrowserThread::PostTaskAndReplyWithResult( 141 content::BrowserThread::FILE, FROM_HERE, 142 base::Bind(&LoadPolicyFromDisk, backing_file_path_), 143 base::Bind(&UserCloudPolicyStore::PolicyLoaded, 144 weak_factory_.GetWeakPtr(), true)); 145} 146 147void UserCloudPolicyStore::PolicyLoaded(bool validate_in_background, 148 PolicyLoadResult result) { 149 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 150 switch (result.status) { 151 case LOAD_RESULT_LOAD_ERROR: 152 status_ = STATUS_LOAD_ERROR; 153 NotifyStoreError(); 154 break; 155 156 case LOAD_RESULT_NO_POLICY_FILE: 157 DVLOG(1) << "No policy found on disk"; 158 NotifyStoreLoaded(); 159 break; 160 161 case LOAD_RESULT_SUCCESS: { 162 // Found policy on disk - need to validate it before it can be used. 163 scoped_ptr<em::PolicyFetchResponse> cloud_policy( 164 new em::PolicyFetchResponse(result.policy)); 165 Validate(cloud_policy.Pass(), 166 validate_in_background, 167 base::Bind( 168 &UserCloudPolicyStore::InstallLoadedPolicyAfterValidation, 169 weak_factory_.GetWeakPtr())); 170 break; 171 } 172 default: 173 NOTREACHED(); 174 } 175} 176 177void UserCloudPolicyStore::InstallLoadedPolicyAfterValidation( 178 UserCloudPolicyValidator* validator) { 179 validation_status_ = validator->status(); 180 if (!validator->success()) { 181 DVLOG(1) << "Validation failed: status=" << validation_status_; 182 status_ = STATUS_VALIDATION_ERROR; 183 NotifyStoreError(); 184 return; 185 } 186 187 DVLOG(1) << "Validation succeeded - installing policy with dm_token: " << 188 validator->policy_data()->request_token(); 189 DVLOG(1) << "Device ID: " << validator->policy_data()->device_id(); 190 191 InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass()); 192 status_ = STATUS_OK; 193 NotifyStoreLoaded(); 194} 195 196void UserCloudPolicyStore::Store(const em::PolicyFetchResponse& policy) { 197 // Stop any pending requests to store policy, then validate the new policy 198 // before storing it. 199 weak_factory_.InvalidateWeakPtrs(); 200 scoped_ptr<em::PolicyFetchResponse> policy_copy( 201 new em::PolicyFetchResponse(policy)); 202 Validate(policy_copy.Pass(), 203 true, 204 base::Bind(&UserCloudPolicyStore::StorePolicyAfterValidation, 205 weak_factory_.GetWeakPtr())); 206} 207 208void UserCloudPolicyStore::Validate( 209 scoped_ptr<em::PolicyFetchResponse> policy, 210 bool validate_in_background, 211 const UserCloudPolicyValidator::CompletionCallback& callback) { 212 // Configure the validator. 213 scoped_ptr<UserCloudPolicyValidator> validator = 214 CreateValidator(policy.Pass()); 215 SigninManager* signin = SigninManagerFactory::GetForProfileIfExists(profile_); 216 if (signin) { 217 std::string username = signin->GetAuthenticatedUsername(); 218 if (username.empty()) 219 username = signin->GetUsernameForAuthInProgress(); 220 221 // Validate the username if the user is signed in (or in the process of 222 // signing in). 223 if (!username.empty()) 224 validator->ValidateUsername(username); 225 } 226 227 if (validate_in_background) { 228 // Start validation in the background. The Validator will free itself once 229 // validation is complete. 230 validator.release()->StartValidation(callback); 231 } else { 232 // Run validation immediately and invoke the callback with the results. 233 validator->RunValidation(); 234 callback.Run(validator.get()); 235 } 236} 237 238void UserCloudPolicyStore::StorePolicyAfterValidation( 239 UserCloudPolicyValidator* validator) { 240 validation_status_ = validator->status(); 241 DVLOG(1) << "Policy validation complete: status = " << validation_status_; 242 if (!validator->success()) { 243 status_ = STATUS_VALIDATION_ERROR; 244 NotifyStoreError(); 245 return; 246 } 247 248 // Persist the validated policy (just fire a task - don't bother getting a 249 // reply because we can't do anything if it fails). 250 content::BrowserThread::PostTask( 251 content::BrowserThread::FILE, FROM_HERE, 252 base::Bind(&StorePolicyToDiskOnFileThread, 253 backing_file_path_, *validator->policy())); 254 InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass()); 255 status_ = STATUS_OK; 256 NotifyStoreLoaded(); 257} 258 259} // namespace policy 260