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