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/chromeos/policy/device_cloud_policy_store_chromeos.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/metrics/histogram.h"
10#include "base/sequenced_task_runner.h"
11#include "chrome/browser/chromeos/login/startup_utils.h"
12#include "chrome/browser/chromeos/policy/device_policy_decoder_chromeos.h"
13#include "chrome/browser/chromeos/policy/enterprise_install_attributes.h"
14#include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h"
15#include "components/ownership/owner_key_util.h"
16#include "policy/proto/device_management_backend.pb.h"
17
18namespace em = enterprise_management;
19
20namespace policy {
21
22DeviceCloudPolicyStoreChromeOS::DeviceCloudPolicyStoreChromeOS(
23    chromeos::DeviceSettingsService* device_settings_service,
24    EnterpriseInstallAttributes* install_attributes,
25    scoped_refptr<base::SequencedTaskRunner> background_task_runner)
26    : device_settings_service_(device_settings_service),
27      install_attributes_(install_attributes),
28      background_task_runner_(background_task_runner),
29      enrollment_validation_done_(false),
30      weak_factory_(this) {
31  device_settings_service_->AddObserver(this);
32}
33
34DeviceCloudPolicyStoreChromeOS::~DeviceCloudPolicyStoreChromeOS() {
35  device_settings_service_->RemoveObserver(this);
36}
37
38void DeviceCloudPolicyStoreChromeOS::Store(
39    const em::PolicyFetchResponse& policy) {
40  // Cancel all pending requests.
41  weak_factory_.InvalidateWeakPtrs();
42
43  scoped_refptr<ownership::PublicKey> public_key(
44      device_settings_service_->GetPublicKey());
45  if (!install_attributes_->IsEnterpriseDevice() ||
46      !device_settings_service_->policy_data() || !public_key.get() ||
47      !public_key->is_loaded()) {
48    status_ = STATUS_BAD_STATE;
49    NotifyStoreError();
50    return;
51  }
52
53  scoped_ptr<DeviceCloudPolicyValidator> validator(CreateValidator(policy));
54  validator->ValidateSignature(public_key->as_string(),
55                               GetPolicyVerificationKey(),
56                               install_attributes_->GetDomain(),
57                               true);
58  validator->ValidateAgainstCurrentPolicy(
59      device_settings_service_->policy_data(),
60      CloudPolicyValidatorBase::TIMESTAMP_REQUIRED,
61      CloudPolicyValidatorBase::DM_TOKEN_REQUIRED);
62  validator.release()->StartValidation(
63      base::Bind(&DeviceCloudPolicyStoreChromeOS::OnPolicyToStoreValidated,
64                 weak_factory_.GetWeakPtr()));
65}
66
67void DeviceCloudPolicyStoreChromeOS::Load() {
68  device_settings_service_->Load();
69}
70
71void DeviceCloudPolicyStoreChromeOS::InstallInitialPolicy(
72    const em::PolicyFetchResponse& policy) {
73  // Cancel all pending requests.
74  weak_factory_.InvalidateWeakPtrs();
75
76  if (!install_attributes_->IsEnterpriseDevice() &&
77      device_settings_service_->status() !=
78          chromeos::DeviceSettingsService::STORE_NO_POLICY) {
79    status_ = STATUS_BAD_STATE;
80    NotifyStoreError();
81    return;
82  }
83
84  scoped_ptr<DeviceCloudPolicyValidator> validator(CreateValidator(policy));
85  validator->ValidateInitialKey(GetPolicyVerificationKey(),
86                                install_attributes_->GetDomain());
87  validator.release()->StartValidation(
88      base::Bind(&DeviceCloudPolicyStoreChromeOS::OnPolicyToStoreValidated,
89                 weak_factory_.GetWeakPtr()));
90}
91
92void DeviceCloudPolicyStoreChromeOS::OwnershipStatusChanged() {
93  // Nothing to do.
94}
95
96void DeviceCloudPolicyStoreChromeOS::DeviceSettingsUpdated() {
97  if (!weak_factory_.HasWeakPtrs())
98    UpdateFromService();
99}
100
101scoped_ptr<DeviceCloudPolicyValidator>
102    DeviceCloudPolicyStoreChromeOS::CreateValidator(
103        const em::PolicyFetchResponse& policy) {
104  scoped_ptr<DeviceCloudPolicyValidator> validator(
105      DeviceCloudPolicyValidator::Create(
106          scoped_ptr<em::PolicyFetchResponse>(
107              new em::PolicyFetchResponse(policy)),
108          background_task_runner_));
109  validator->ValidateDomain(install_attributes_->GetDomain());
110  validator->ValidatePolicyType(dm_protocol::kChromeDevicePolicyType);
111  validator->ValidatePayload();
112  return validator.Pass();
113}
114
115void DeviceCloudPolicyStoreChromeOS::OnPolicyToStoreValidated(
116    DeviceCloudPolicyValidator* validator) {
117  if (!validator->success()) {
118    status_ = STATUS_VALIDATION_ERROR;
119    validation_status_ = validator->status();
120    NotifyStoreError();
121    return;
122  }
123
124  device_settings_service_->Store(
125      validator->policy().Pass(),
126      base::Bind(&DeviceCloudPolicyStoreChromeOS::OnPolicyStored,
127                 weak_factory_.GetWeakPtr()));
128}
129
130void DeviceCloudPolicyStoreChromeOS::OnPolicyStored() {
131  UpdateFromService();
132}
133
134void DeviceCloudPolicyStoreChromeOS::UpdateFromService() {
135  if (!install_attributes_->IsEnterpriseDevice()) {
136    status_ = STATUS_BAD_STATE;
137    NotifyStoreError();
138    return;
139  }
140
141  // Once per session, validate internal consistency of enrollment state (DM
142  // token must be present on enrolled devices) and in case of failure set flag
143  // to indicate that recovery is required.
144  const chromeos::DeviceSettingsService::Status status =
145      device_settings_service_->status();
146  switch (status) {
147    case chromeos::DeviceSettingsService::STORE_SUCCESS:
148    case chromeos::DeviceSettingsService::STORE_KEY_UNAVAILABLE:
149    case chromeos::DeviceSettingsService::STORE_NO_POLICY:
150    case chromeos::DeviceSettingsService::STORE_INVALID_POLICY:
151    case chromeos::DeviceSettingsService::STORE_VALIDATION_ERROR: {
152      if (!enrollment_validation_done_) {
153        enrollment_validation_done_ = true;
154        const bool has_dm_token =
155            status == chromeos::DeviceSettingsService::STORE_SUCCESS &&
156            device_settings_service_->policy_data() &&
157            device_settings_service_->policy_data()->has_request_token();
158
159        // At the time LoginDisplayHostImpl decides whether enrollment flow is
160        // to be started, policy hasn't been read yet.  To work around this,
161        // once the need for recovery is detected upon policy load, a flag is
162        // stored in prefs which is accessed by LoginDisplayHostImpl early
163        // during (next) boot.
164        if (!has_dm_token) {
165          LOG(ERROR) << "Device policy read on enrolled device yields "
166                     << "no DM token! Status: " << status << ".";
167          chromeos::StartupUtils::MarkEnrollmentRecoveryRequired();
168        }
169        UMA_HISTOGRAM_BOOLEAN("Enterprise.EnrolledPolicyHasDMToken",
170                              has_dm_token);
171      }
172      break;
173    }
174    case chromeos::DeviceSettingsService::STORE_POLICY_ERROR:
175    case chromeos::DeviceSettingsService::STORE_OPERATION_FAILED:
176    case chromeos::DeviceSettingsService::STORE_TEMP_VALIDATION_ERROR:
177      // Do nothing for write errors or transient read errors.
178      break;
179  }
180
181  switch (status) {
182    case chromeos::DeviceSettingsService::STORE_SUCCESS: {
183      status_ = STATUS_OK;
184      policy_.reset(new em::PolicyData());
185      if (device_settings_service_->policy_data())
186        policy_->MergeFrom(*device_settings_service_->policy_data());
187
188      PolicyMap new_policy_map;
189      if (is_managed()) {
190        DecodeDevicePolicy(*device_settings_service_->device_settings(),
191                           &new_policy_map, install_attributes_);
192      }
193      policy_map_.Swap(&new_policy_map);
194
195      NotifyStoreLoaded();
196      return;
197    }
198    case chromeos::DeviceSettingsService::STORE_KEY_UNAVAILABLE:
199      status_ = STATUS_BAD_STATE;
200      break;
201    case chromeos::DeviceSettingsService::STORE_POLICY_ERROR:
202    case chromeos::DeviceSettingsService::STORE_OPERATION_FAILED:
203      status_ = STATUS_STORE_ERROR;
204      break;
205    case chromeos::DeviceSettingsService::STORE_NO_POLICY:
206    case chromeos::DeviceSettingsService::STORE_INVALID_POLICY:
207    case chromeos::DeviceSettingsService::STORE_VALIDATION_ERROR:
208    case chromeos::DeviceSettingsService::STORE_TEMP_VALIDATION_ERROR:
209      status_ = STATUS_LOAD_ERROR;
210      break;
211  }
212
213  NotifyStoreError();
214}
215
216}  // namespace policy
217