1// Copyright (c) 2011 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/device_policy_cache.h"
6
7#include "base/basictypes.h"
8#include "base/compiler_specific.h"
9#include "base/logging.h"
10#include "base/task.h"
11#include "base/values.h"
12#include "chrome/browser/chromeos/cros_settings_names.h"
13#include "chrome/browser/chromeos/login/ownership_service.h"
14#include "chrome/browser/chromeos/login/signed_settings_helper.h"
15#include "chrome/browser/chromeos/user_cros_settings_provider.h"
16#include "chrome/browser/policy/configuration_policy_pref_store.h"
17#include "chrome/browser/policy/device_policy_identity_strategy.h"
18#include "chrome/browser/policy/enterprise_install_attributes.h"
19#include "chrome/browser/policy/policy_map.h"
20#include "chrome/browser/policy/proto/device_management_backend.pb.h"
21#include "chrome/browser/policy/proto/device_management_constants.h"
22#include "chrome/browser/policy/proto/device_management_local.pb.h"
23#include "content/browser/browser_thread.h"
24#include "policy/configuration_policy_type.h"
25
26namespace {
27
28// Stores policy, updates the owner key if required and reports the status
29// through a callback.
30class StorePolicyOperation : public chromeos::SignedSettingsHelper::Callback,
31                             public chromeos::OwnerManager::KeyUpdateDelegate {
32 public:
33  typedef Callback1<chromeos::SignedSettings::ReturnCode>::Type Callback;
34
35  StorePolicyOperation(chromeos::SignedSettingsHelper* signed_settings_helper,
36                       const em::PolicyFetchResponse& policy,
37                       Callback* callback)
38      : signed_settings_helper_(signed_settings_helper),
39        policy_(policy),
40        callback_(callback) {
41    signed_settings_helper_->StartStorePolicyOp(policy, this);
42  }
43  virtual ~StorePolicyOperation() {
44    signed_settings_helper_->CancelCallback(this);
45  }
46
47  // SignedSettingsHelper implementation:
48  virtual void OnStorePolicyCompleted(
49      chromeos::SignedSettings::ReturnCode code) OVERRIDE {
50    if (code != chromeos::SignedSettings::SUCCESS) {
51      callback_->Run(code);
52      delete this;
53      return;
54    }
55
56    if (policy_.has_new_public_key()) {
57      // The session manager has successfully done a key rotation. Replace the
58      // owner key also in chrome.
59      const std::string& new_key = policy_.new_public_key();
60      const std::vector<uint8> new_key_data(new_key.c_str(),
61                                            new_key.c_str() + new_key.size());
62      chromeos::OwnershipService::GetSharedInstance()->StartUpdateOwnerKey(
63          new_key_data, this);
64      return;
65    } else {
66      UpdateUserCrosSettings();
67      callback_->Run(chromeos::SignedSettings::SUCCESS);
68      delete this;
69      return;
70    }
71  }
72
73  // OwnerManager::KeyUpdateDelegate implementation:
74  virtual void OnKeyUpdated() OVERRIDE {
75    UpdateUserCrosSettings();
76    callback_->Run(chromeos::SignedSettings::SUCCESS);
77    delete this;
78  }
79
80 private:
81  void UpdateUserCrosSettings() {
82    // TODO(mnissler): Find a better way. This is a hack that updates the
83    // UserCrosSettingsProvider's cache, since it is unable to notice we've
84    // updated policy information.
85    chromeos::UserCrosSettingsProvider().Reload();
86  }
87
88  chromeos::SignedSettingsHelper* signed_settings_helper_;
89  em::PolicyFetchResponse policy_;
90  scoped_ptr<Callback> callback_;
91
92  DISALLOW_COPY_AND_ASSIGN(StorePolicyOperation);
93};
94
95// Decodes a protobuf integer to an IntegerValue. The caller assumes ownership
96// of the return Value*. Returns NULL in case the input value is out of bounds.
97Value* DecodeIntegerValue(google::protobuf::int64 value) {
98  if (value < std::numeric_limits<int>::min() ||
99      value > std::numeric_limits<int>::max()) {
100    LOG(WARNING) << "Integer value " << value
101                 << " out of numeric limits, ignoring.";
102    return NULL;
103  }
104
105  return Value::CreateIntegerValue(static_cast<int>(value));
106}
107
108}  // namespace
109
110namespace policy {
111
112DevicePolicyCache::DevicePolicyCache(
113    DevicePolicyIdentityStrategy* identity_strategy,
114    EnterpriseInstallAttributes* install_attributes)
115    : identity_strategy_(identity_strategy),
116      install_attributes_(install_attributes),
117      signed_settings_helper_(chromeos::SignedSettingsHelper::Get()),
118      starting_up_(true),
119      ALLOW_THIS_IN_INITIALIZER_LIST(callback_factory_(this)) {
120}
121
122DevicePolicyCache::DevicePolicyCache(
123    DevicePolicyIdentityStrategy* identity_strategy,
124    EnterpriseInstallAttributes* install_attributes,
125    chromeos::SignedSettingsHelper* signed_settings_helper)
126    : identity_strategy_(identity_strategy),
127      install_attributes_(install_attributes),
128      signed_settings_helper_(signed_settings_helper),
129      starting_up_(true),
130      ALLOW_THIS_IN_INITIALIZER_LIST(callback_factory_(this)) {
131}
132
133DevicePolicyCache::~DevicePolicyCache() {
134  signed_settings_helper_->CancelCallback(this);
135}
136
137void DevicePolicyCache::Load() {
138  signed_settings_helper_->StartRetrievePolicyOp(this);
139}
140
141void DevicePolicyCache::SetPolicy(const em::PolicyFetchResponse& policy) {
142  DCHECK(!starting_up_);
143
144  // Make sure we have an enterprise device.
145  std::string registration_user(install_attributes_->GetRegistrationUser());
146  if (registration_user.empty()) {
147    LOG(WARNING) << "Refusing to accept policy on non-enterprise device.";
148    InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
149                   CloudPolicySubsystem::POLICY_LOCAL_ERROR);
150    return;
151  }
152
153  // Check the user this policy is for against the device-locked name.
154  em::PolicyData policy_data;
155  if (!policy_data.ParseFromString(policy.policy_data())) {
156    LOG(WARNING) << "Invalid policy protobuf";
157    InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
158                   CloudPolicySubsystem::POLICY_LOCAL_ERROR);
159    return;
160  }
161
162  if (registration_user != policy_data.username()) {
163    LOG(WARNING) << "Refusing policy blob for " << policy_data.username()
164                 << " which doesn't match " << registration_user;
165    InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
166                   CloudPolicySubsystem::POLICY_LOCAL_ERROR);
167    return;
168  }
169
170  set_last_policy_refresh_time(base::Time::NowFromSystemTime());
171
172  // Start a store operation.
173  new StorePolicyOperation(signed_settings_helper_,
174                           policy,
175                           callback_factory_.NewCallback(
176                               &DevicePolicyCache::PolicyStoreOpCompleted));
177}
178
179void DevicePolicyCache::SetUnmanaged() {
180  LOG(WARNING) << "Tried to set DevicePolicyCache to 'unmanaged'!";
181  // This is not supported for DevicePolicyCache.
182}
183
184void DevicePolicyCache::OnRetrievePolicyCompleted(
185    chromeos::SignedSettings::ReturnCode code,
186    const em::PolicyFetchResponse& policy) {
187  DCHECK(CalledOnValidThread());
188  if (starting_up_) {
189    starting_up_ = false;
190    if (code == chromeos::SignedSettings::NOT_FOUND ||
191        code == chromeos::SignedSettings::KEY_UNAVAILABLE ||
192        !policy.has_policy_data()) {
193      InformNotifier(CloudPolicySubsystem::UNENROLLED,
194                     CloudPolicySubsystem::NO_DETAILS);
195      return;
196    }
197    em::PolicyData policy_data;
198    if (!policy_data.ParseFromString(policy.policy_data())) {
199      LOG(WARNING) << "Failed to parse PolicyData protobuf.";
200      InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
201                     CloudPolicySubsystem::POLICY_LOCAL_ERROR);
202      return;
203    }
204    if (!policy_data.has_request_token() ||
205        policy_data.request_token().empty()) {
206      SetUnmanagedInternal(base::Time::NowFromSystemTime());
207      InformNotifier(CloudPolicySubsystem::UNMANAGED,
208                     CloudPolicySubsystem::NO_DETAILS);
209      // TODO(jkummerow): Reminder: When we want to feed device-wide settings
210      // made by a local owner into this cache, we need to call
211      // SetPolicyInternal() here.
212      return;
213    }
214    if (!policy_data.has_username() || !policy_data.has_device_id()) {
215      InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
216                     CloudPolicySubsystem::POLICY_LOCAL_ERROR);
217      return;
218    }
219    identity_strategy_->SetDeviceManagementCredentials(
220        policy_data.username(),
221        policy_data.device_id(),
222        policy_data.request_token());
223    SetPolicyInternal(policy, NULL, false);
224  } else {  // In other words, starting_up_ == false.
225    if (code != chromeos::SignedSettings::SUCCESS) {
226      if (code == chromeos::SignedSettings::BAD_SIGNATURE) {
227        InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
228                       CloudPolicySubsystem::SIGNATURE_MISMATCH);
229      } else {
230        InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
231                       CloudPolicySubsystem::POLICY_LOCAL_ERROR);
232      }
233      return;
234    }
235    SetPolicyInternal(policy, NULL, false);
236  }
237}
238
239bool DevicePolicyCache::DecodePolicyData(const em::PolicyData& policy_data,
240                                         PolicyMap* mandatory,
241                                         PolicyMap* recommended) {
242  em::ChromeDeviceSettingsProto policy;
243  if (!policy.ParseFromString(policy_data.policy_value())) {
244    LOG(WARNING) << "Failed to parse ChromeDeviceSettingsProto.";
245    return false;
246  }
247  DecodeDevicePolicy(policy, mandatory, recommended);
248  return true;
249}
250
251void DevicePolicyCache::PolicyStoreOpCompleted(
252    chromeos::SignedSettings::ReturnCode code) {
253  DCHECK(CalledOnValidThread());
254  if (code != chromeos::SignedSettings::SUCCESS) {
255    if (code == chromeos::SignedSettings::BAD_SIGNATURE) {
256      InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
257                     CloudPolicySubsystem::SIGNATURE_MISMATCH);
258    } else {
259      InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
260                     CloudPolicySubsystem::POLICY_LOCAL_ERROR);
261    }
262    return;
263  }
264  signed_settings_helper_->StartRetrievePolicyOp(this);
265}
266
267// static
268void DevicePolicyCache::DecodeDevicePolicy(
269    const em::ChromeDeviceSettingsProto& policy,
270    PolicyMap* mandatory,
271    PolicyMap* recommended) {
272  if (policy.has_policy_refresh_rate()) {
273    const em::DevicePolicyRefreshRateProto container =
274        policy.policy_refresh_rate();
275    if (container.has_policy_refresh_rate()) {
276      mandatory->Set(kPolicyPolicyRefreshRate,
277                     DecodeIntegerValue(container.policy_refresh_rate()));
278    }
279  }
280
281  if (policy.has_device_proxy_settings()) {
282    const em::DeviceProxySettingsProto container =
283        policy.device_proxy_settings();
284    if (container.has_proxy_mode()) {
285      recommended->Set(kPolicyProxyMode,
286                       Value::CreateStringValue(container.proxy_mode()));
287    }
288    if (container.has_proxy_server()) {
289      recommended->Set(kPolicyProxyServer,
290                       Value::CreateStringValue(container.proxy_server()));
291    }
292    if (container.has_proxy_pac_url()) {
293      recommended->Set(kPolicyProxyPacUrl,
294                       Value::CreateStringValue(container.proxy_pac_url()));
295    }
296    if (container.has_proxy_bypass_list()) {
297      recommended->Set(kPolicyProxyBypassList,
298                       Value::CreateStringValue(container.proxy_bypass_list()));
299    }
300  }
301}
302
303}  // namespace policy
304