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