1// Copyright 2013 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 "components/policy/core/common/cloud/user_cloud_policy_store.h"
6
7#include "base/bind.h"
8#include "base/files/file_util.h"
9#include "base/location.h"
10#include "base/metrics/histogram.h"
11#include "base/task_runner_util.h"
12#include "google_apis/gaia/gaia_auth_util.h"
13#include "policy/proto/cloud_policy.pb.h"
14#include "policy/proto/device_management_backend.pb.h"
15#include "policy/proto/policy_signing_key.pb.h"
16
17namespace em = enterprise_management;
18
19namespace policy {
20
21// This enum is used to define the buckets for an enumerated UMA histogram.
22// Hence,
23//   (a) existing enumerated constants should never be deleted or reordered, and
24//   (b) new constants should only be appended at the end of the enumeration.
25//
26// Keep this in sync with EnterprisePolicyLoadStatus in histograms.xml.
27enum PolicyLoadStatus {
28  // Policy blob was successfully loaded and parsed.
29  LOAD_RESULT_SUCCESS,
30
31  // No previously stored policy was found.
32  LOAD_RESULT_NO_POLICY_FILE,
33
34  // Could not load the previously stored policy due to either a parse or
35  // file read error.
36  LOAD_RESULT_LOAD_ERROR,
37
38  // LOAD_RESULT_SIZE is the number of items in this enum and is used when
39  // logging histograms to set the bucket size, so should always be the last
40  // item.
41  LOAD_RESULT_SIZE,
42};
43
44// Struct containing the result of a policy load - if |status| ==
45// LOAD_RESULT_SUCCESS, |policy| is initialized from the policy file on disk.
46struct PolicyLoadResult {
47  PolicyLoadStatus status;
48  em::PolicyFetchResponse policy;
49  em::PolicySigningKey key;
50};
51
52namespace {
53
54// Subdirectory in the user's profile for storing user policies.
55const base::FilePath::CharType kPolicyDir[] = FILE_PATH_LITERAL("Policy");
56// File in the above directory for storing user policy data.
57const base::FilePath::CharType kPolicyCacheFile[] =
58    FILE_PATH_LITERAL("User Policy");
59
60// File in the above directory for storing policy signing key data.
61const base::FilePath::CharType kKeyCacheFile[] =
62    FILE_PATH_LITERAL("Signing Key");
63
64const char kMetricPolicyHasVerifiedCachedKey[] =
65    "Enterprise.PolicyHasVerifiedCachedKey";
66
67// Maximum policy and key size that will be loaded, in bytes.
68const size_t kPolicySizeLimit = 1024 * 1024;
69const size_t kKeySizeLimit = 16 * 1024;
70
71// Loads policy from the backing file. Returns a PolicyLoadResult with the
72// results of the fetch.
73policy::PolicyLoadResult LoadPolicyFromDisk(
74    const base::FilePath& policy_path,
75    const base::FilePath& key_path) {
76  policy::PolicyLoadResult result;
77  // If the backing file does not exist, just return. We don't verify the key
78  // path here, because the key is optional (the validation code will fail if
79  // the key does not exist but the loaded policy is unsigned).
80  if (!base::PathExists(policy_path)) {
81    result.status = policy::LOAD_RESULT_NO_POLICY_FILE;
82    return result;
83  }
84  std::string data;
85
86  if (!base::ReadFileToString(policy_path, &data, kPolicySizeLimit) ||
87      !result.policy.ParseFromString(data)) {
88    LOG(WARNING) << "Failed to read or parse policy data from "
89                 << policy_path.value();
90    result.status = policy::LOAD_RESULT_LOAD_ERROR;
91    return result;
92  }
93
94  if (!base::ReadFileToString(key_path, &data, kKeySizeLimit) ||
95      !result.key.ParseFromString(data)) {
96    // Log an error on missing key data, but do not trigger a load failure
97    // for now since there are still old unsigned cached policy blobs in the
98    // wild with no associated key (see kMetricPolicyHasVerifiedCachedKey UMA
99    // stat below).
100    LOG(ERROR) << "Failed to read or parse key data from " << key_path.value();
101    result.key.clear_signing_key();
102  }
103
104  // Track the occurrence of valid cached keys - when this ratio gets high
105  // enough, we can update the code to reject unsigned policy or unverified
106  // keys.
107  UMA_HISTOGRAM_BOOLEAN(kMetricPolicyHasVerifiedCachedKey,
108                        result.key.has_signing_key());
109
110  result.status = policy::LOAD_RESULT_SUCCESS;
111  return result;
112}
113
114bool WriteStringToFile(const base::FilePath path, const std::string& data) {
115 if (!base::CreateDirectory(path.DirName())) {
116    DLOG(WARNING) << "Failed to create directory " << path.DirName().value();
117    return false;
118  }
119
120  int size = data.size();
121  if (base::WriteFile(path, data.c_str(), size) != size) {
122    DLOG(WARNING) << "Failed to write " << path.value();
123    return false;
124  }
125
126  return true;
127}
128
129// Stores policy to the backing file (must be called via a task on
130// the background thread).
131void StorePolicyToDiskOnBackgroundThread(
132    const base::FilePath& policy_path,
133    const base::FilePath& key_path,
134    const std::string& verification_key,
135    const em::PolicyFetchResponse& policy) {
136  DVLOG(1) << "Storing policy to " << policy_path.value();
137  std::string data;
138  if (!policy.SerializeToString(&data)) {
139    DLOG(WARNING) << "Failed to serialize policy data";
140    return;
141  }
142
143  if (!WriteStringToFile(policy_path, data))
144    return;
145
146  if (policy.has_new_public_key()) {
147    // Write the new public key and its verification signature/key to a file.
148    em::PolicySigningKey key_info;
149    key_info.set_signing_key(policy.new_public_key());
150    key_info.set_signing_key_signature(
151        policy.new_public_key_verification_signature());
152    key_info.set_verification_key(verification_key);
153    std::string key_data;
154    if (!key_info.SerializeToString(&key_data)) {
155      DLOG(WARNING) << "Failed to serialize policy signing key";
156      return;
157    }
158
159    WriteStringToFile(key_path, key_data);
160  }
161}
162
163}  // namespace
164
165UserCloudPolicyStore::UserCloudPolicyStore(
166    const base::FilePath& policy_path,
167    const base::FilePath& key_path,
168    const std::string& verification_key,
169    scoped_refptr<base::SequencedTaskRunner> background_task_runner)
170    : UserCloudPolicyStoreBase(background_task_runner),
171      policy_path_(policy_path),
172      key_path_(key_path),
173      verification_key_(verification_key),
174      weak_factory_(this) {}
175
176UserCloudPolicyStore::~UserCloudPolicyStore() {}
177
178// static
179scoped_ptr<UserCloudPolicyStore> UserCloudPolicyStore::Create(
180    const base::FilePath& profile_path,
181    const std::string& verification_key,
182    scoped_refptr<base::SequencedTaskRunner> background_task_runner) {
183  base::FilePath policy_path =
184      profile_path.Append(kPolicyDir).Append(kPolicyCacheFile);
185  base::FilePath key_path =
186      profile_path.Append(kPolicyDir).Append(kKeyCacheFile);
187  return make_scoped_ptr(new UserCloudPolicyStore(
188      policy_path, key_path, verification_key, background_task_runner));
189}
190
191void UserCloudPolicyStore::SetSigninUsername(const std::string& username) {
192  signin_username_ = username;
193}
194
195void UserCloudPolicyStore::LoadImmediately() {
196  DVLOG(1) << "Initiating immediate policy load from disk";
197  // Cancel any pending Load/Store/Validate operations.
198  weak_factory_.InvalidateWeakPtrs();
199  // Load the policy from disk...
200  PolicyLoadResult result = LoadPolicyFromDisk(policy_path_, key_path_);
201  // ...and install it, reporting success/failure to any observers.
202  PolicyLoaded(false, result);
203}
204
205void UserCloudPolicyStore::Clear() {
206  background_task_runner()->PostTask(
207      FROM_HERE,
208      base::Bind(base::IgnoreResult(&base::DeleteFile), policy_path_, false));
209  background_task_runner()->PostTask(
210      FROM_HERE,
211      base::Bind(base::IgnoreResult(&base::DeleteFile), key_path_, false));
212  policy_.reset();
213  policy_map_.Clear();
214  policy_key_.clear();
215  NotifyStoreLoaded();
216}
217
218void UserCloudPolicyStore::Load() {
219  DVLOG(1) << "Initiating policy load from disk";
220  // Cancel any pending Load/Store/Validate operations.
221  weak_factory_.InvalidateWeakPtrs();
222
223  // Start a new Load operation and have us get called back when it is
224  // complete.
225  base::PostTaskAndReplyWithResult(
226      background_task_runner().get(),
227      FROM_HERE,
228      base::Bind(&LoadPolicyFromDisk, policy_path_, key_path_),
229      base::Bind(&UserCloudPolicyStore::PolicyLoaded,
230                 weak_factory_.GetWeakPtr(),
231                 true));
232}
233
234void UserCloudPolicyStore::PolicyLoaded(bool validate_in_background,
235                                        PolicyLoadResult result) {
236  UMA_HISTOGRAM_ENUMERATION("Enterprise.UserCloudPolicyStore.LoadStatus",
237                            result.status,
238                            LOAD_RESULT_SIZE);
239  switch (result.status) {
240    case LOAD_RESULT_LOAD_ERROR:
241      status_ = STATUS_LOAD_ERROR;
242      NotifyStoreError();
243      break;
244
245    case LOAD_RESULT_NO_POLICY_FILE:
246      DVLOG(1) << "No policy found on disk";
247      NotifyStoreLoaded();
248      break;
249
250    case LOAD_RESULT_SUCCESS: {
251      // Found policy on disk - need to validate it before it can be used.
252      scoped_ptr<em::PolicyFetchResponse> cloud_policy(
253          new em::PolicyFetchResponse(result.policy));
254      scoped_ptr<em::PolicySigningKey> key(
255          new em::PolicySigningKey(result.key));
256
257      bool doing_key_rotation = false;
258      const std::string& verification_key = verification_key_;
259      if (!key->has_verification_key() ||
260          key->verification_key() != verification_key_) {
261        // The cached key didn't match our current key, so we're doing a key
262        // rotation - make sure we request a new key from the server on our
263        // next fetch.
264        doing_key_rotation = true;
265        DLOG(WARNING) << "Verification key rotation detected";
266        // TODO(atwilson): Add code to update |verification_key| to point to
267        // the correct key to validate the existing blob (can't do this until
268        // we've done our first key rotation).
269      }
270
271      Validate(cloud_policy.Pass(),
272               key.Pass(),
273               verification_key,
274               validate_in_background,
275               base::Bind(
276                   &UserCloudPolicyStore::InstallLoadedPolicyAfterValidation,
277                   weak_factory_.GetWeakPtr(),
278                   doing_key_rotation,
279                   result.key.has_signing_key() ?
280                       result.key.signing_key() : std::string()));
281      break;
282    }
283    default:
284      NOTREACHED();
285  }
286}
287
288void UserCloudPolicyStore::InstallLoadedPolicyAfterValidation(
289    bool doing_key_rotation,
290    const std::string& signing_key,
291    UserCloudPolicyValidator* validator) {
292  UMA_HISTOGRAM_ENUMERATION(
293      "Enterprise.UserCloudPolicyStore.LoadValidationStatus",
294      validator->status(),
295      CloudPolicyValidatorBase::VALIDATION_STATUS_SIZE);
296  validation_status_ = validator->status();
297  if (!validator->success()) {
298    DVLOG(1) << "Validation failed: status=" << validation_status_;
299    status_ = STATUS_VALIDATION_ERROR;
300    NotifyStoreError();
301    return;
302  }
303
304  DVLOG(1) << "Validation succeeded - installing policy with dm_token: " <<
305      validator->policy_data()->request_token();
306  DVLOG(1) << "Device ID: " << validator->policy_data()->device_id();
307
308  // If we're doing a key rotation, clear the public key version so a future
309  // policy fetch will force regeneration of the keys.
310  if (doing_key_rotation) {
311    validator->policy_data()->clear_public_key_version();
312    policy_key_.clear();
313  } else {
314    // Policy validation succeeded, so we know the signing key is good.
315    policy_key_ = signing_key;
316  }
317
318  InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
319  status_ = STATUS_OK;
320  NotifyStoreLoaded();
321}
322
323void UserCloudPolicyStore::Store(const em::PolicyFetchResponse& policy) {
324  // Stop any pending requests to store policy, then validate the new policy
325  // before storing it.
326  weak_factory_.InvalidateWeakPtrs();
327  scoped_ptr<em::PolicyFetchResponse> policy_copy(
328      new em::PolicyFetchResponse(policy));
329  Validate(policy_copy.Pass(),
330           scoped_ptr<em::PolicySigningKey>(),
331           verification_key_,
332           true,
333           base::Bind(&UserCloudPolicyStore::StorePolicyAfterValidation,
334                      weak_factory_.GetWeakPtr()));
335}
336
337void UserCloudPolicyStore::Validate(
338    scoped_ptr<em::PolicyFetchResponse> policy,
339    scoped_ptr<em::PolicySigningKey> cached_key,
340    const std::string& verification_key,
341    bool validate_in_background,
342    const UserCloudPolicyValidator::CompletionCallback& callback) {
343  // Configure the validator.
344  scoped_ptr<UserCloudPolicyValidator> validator = CreateValidator(
345      policy.Pass(),
346      CloudPolicyValidatorBase::TIMESTAMP_NOT_BEFORE);
347
348  // Extract the owning domain from the signed-in user (if any is set yet).
349  // If there's no owning domain, then the code just ensures that the policy
350  // is self-consistent (that the keys are signed with the same domain that the
351  // username field in the policy contains). UserPolicySigninServerBase will
352  // verify that the username matches the signed in user once profile
353  // initialization is complete (http://crbug.com/342327).
354  std::string owning_domain;
355
356  // Validate the username if the user is signed in. The signin_username_ can
357  // be empty during initial policy load because this happens before the
358  // Prefs subsystem is initialized.
359  if (!signin_username_.empty()) {
360    DVLOG(1) << "Validating username: " << signin_username_;
361    validator->ValidateUsername(signin_username_, true);
362    owning_domain = gaia::ExtractDomainName(
363        gaia::CanonicalizeEmail(gaia::SanitizeEmail(signin_username_)));
364  }
365
366  // There are 4 cases:
367  //
368  // 1) Validation after loading from cache with no cached key.
369  // Action: Just validate signature with an empty key - this will result in
370  // a failed validation and the cached policy will be rejected.
371  //
372  // 2) Validation after loading from cache with a cached key
373  // Action: Validate signature on policy blob but don't allow key rotation.
374  //
375  // 3) Validation after loading new policy from the server with no cached key
376  // Action: Validate as initial key provisioning (case where we are migrating
377  // from unsigned policy)
378  //
379  // 4) Validation after loading new policy from the server with a cached key
380  // Action: Validate as normal, and allow key rotation.
381  if (cached_key) {
382    // Case #1/#2 - loading from cache. Validate the cached key (if no key,
383    // then the validation will fail), then do normal policy data signature
384    // validation using the cached key.
385
386    // Loading from cache should not change the cached keys.
387    DCHECK(policy_key_.empty() || policy_key_ == cached_key->signing_key());
388    DLOG_IF(WARNING, !cached_key->has_signing_key()) <<
389        "Unsigned policy blob detected";
390
391    validator->ValidateCachedKey(cached_key->signing_key(),
392                                 cached_key->signing_key_signature(),
393                                 verification_key,
394                                 owning_domain);
395    // Loading from cache, so don't allow key rotation.
396    const bool no_rotation = false;
397    validator->ValidateSignature(cached_key->signing_key(),
398                                 verification_key,
399                                 owning_domain,
400                                 no_rotation);
401  } else {
402    // No passed cached_key - this is not validating the initial policy load
403    // from cache, but rather an update from the server.
404    if (policy_key_.empty()) {
405      // Case #3 - no valid existing policy key (either this is the initial
406      // policy fetch, or we're doing a key rotation), so this new policy fetch
407      // should include an initial key provision.
408      validator->ValidateInitialKey(verification_key, owning_domain);
409    } else {
410      // Case #4 - verify new policy with existing key. We always allow key
411      // rotation - the verification key will prevent invalid policy from being
412      // injected. |policy_key_| is already known to be valid, so no need to
413      // verify via ValidateCachedKey().
414      const bool allow_rotation = true;
415      validator->ValidateSignature(
416          policy_key_, verification_key, owning_domain, allow_rotation);
417    }
418  }
419
420  if (validate_in_background) {
421    // Start validation in the background. The Validator will free itself once
422    // validation is complete.
423    validator.release()->StartValidation(callback);
424  } else {
425    // Run validation immediately and invoke the callback with the results.
426    validator->RunValidation();
427    callback.Run(validator.get());
428  }
429}
430
431void UserCloudPolicyStore::StorePolicyAfterValidation(
432    UserCloudPolicyValidator* validator) {
433  UMA_HISTOGRAM_ENUMERATION(
434      "Enterprise.UserCloudPolicyStore.StoreValidationStatus",
435      validator->status(),
436      CloudPolicyValidatorBase::VALIDATION_STATUS_SIZE);
437  validation_status_ = validator->status();
438  DVLOG(1) << "Policy validation complete: status = " << validation_status_;
439  if (!validator->success()) {
440    status_ = STATUS_VALIDATION_ERROR;
441    NotifyStoreError();
442    return;
443  }
444
445  // Persist the validated policy (just fire a task - don't bother getting a
446  // reply because we can't do anything if it fails).
447  background_task_runner()->PostTask(
448      FROM_HERE,
449      base::Bind(&StorePolicyToDiskOnBackgroundThread,
450                 policy_path_, key_path_, verification_key_,
451                 *validator->policy()));
452  InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
453
454  // If the key was rotated, update our local cache of the key.
455  if (validator->policy()->has_new_public_key())
456    policy_key_ = validator->policy()->new_public_key();
457  status_ = STATUS_OK;
458  NotifyStoreLoaded();
459}
460
461}  // namespace policy
462