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/user_policy_cache.h"
6
7 #include <limits>
8 #include <string>
9
10 #include "base/file_util.h"
11 #include "base/logging.h"
12 #include "base/task.h"
13 #include "base/values.h"
14 #include "chrome/browser/policy/configuration_policy_pref_store.h"
15 #include "chrome/browser/policy/policy_map.h"
16 #include "chrome/browser/policy/proto/cloud_policy.pb.h"
17 #include "chrome/browser/policy/proto/device_management_local.pb.h"
18 #include "chrome/browser/policy/proto/old_generic_format.pb.h"
19 #include "content/browser/browser_thread.h"
20 #include "policy/configuration_policy_type.h"
21
22 namespace policy {
23
24 // Decodes a CloudPolicySettings object into two maps with mandatory and
25 // recommended settings, respectively. The implementation is generated code
26 // in policy/cloud_policy_generated.cc.
27 void DecodePolicy(const em::CloudPolicySettings& policy,
28 PolicyMap* mandatory, PolicyMap* recommended);
29
30 // Saves policy information to a file.
31 class PersistPolicyTask : public Task {
32 public:
PersistPolicyTask(const FilePath & path,const em::PolicyFetchResponse * cloud_policy_response,const bool is_unmanaged)33 PersistPolicyTask(const FilePath& path,
34 const em::PolicyFetchResponse* cloud_policy_response,
35 const bool is_unmanaged)
36 : path_(path),
37 cloud_policy_response_(cloud_policy_response),
38 is_unmanaged_(is_unmanaged) {}
39
40 private:
41 // Task override.
42 virtual void Run();
43
44 const FilePath path_;
45 scoped_ptr<const em::PolicyFetchResponse> cloud_policy_response_;
46 const bool is_unmanaged_;
47 };
48
Run()49 void PersistPolicyTask::Run() {
50 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
51 std::string data;
52 em::CachedCloudPolicyResponse cached_policy;
53 if (cloud_policy_response_.get()) {
54 cached_policy.mutable_cloud_policy()->CopyFrom(*cloud_policy_response_);
55 }
56 if (is_unmanaged_) {
57 cached_policy.set_unmanaged(true);
58 cached_policy.set_timestamp(base::Time::NowFromSystemTime().ToTimeT());
59 }
60 if (!cached_policy.SerializeToString(&data)) {
61 LOG(WARNING) << "Failed to serialize policy data";
62 return;
63 }
64
65 int size = data.size();
66 if (file_util::WriteFile(path_, data.c_str(), size) != size) {
67 LOG(WARNING) << "Failed to write " << path_.value();
68 return;
69 }
70 }
71
UserPolicyCache(const FilePath & backing_file_path)72 UserPolicyCache::UserPolicyCache(const FilePath& backing_file_path)
73 : backing_file_path_(backing_file_path) {
74 }
75
~UserPolicyCache()76 UserPolicyCache::~UserPolicyCache() {
77 }
78
Load()79 void UserPolicyCache::Load() {
80 // TODO(jkummerow): This method is doing file IO during browser startup. In
81 // the long run it would be better to delay this until the FILE thread exists.
82 if (!file_util::PathExists(backing_file_path_) || initialization_complete()) {
83 return;
84 }
85
86 // Read the protobuf from the file.
87 std::string data;
88 if (!file_util::ReadFileToString(backing_file_path_, &data)) {
89 LOG(WARNING) << "Failed to read policy data from "
90 << backing_file_path_.value();
91 return;
92 }
93
94 em::CachedCloudPolicyResponse cached_response;
95 if (!cached_response.ParseFromArray(data.c_str(), data.size())) {
96 LOG(WARNING) << "Failed to parse policy data read from "
97 << backing_file_path_.value();
98 return;
99 }
100
101 if (cached_response.unmanaged()) {
102 SetUnmanagedInternal(base::Time::FromTimeT(cached_response.timestamp()));
103 } else if (cached_response.has_cloud_policy()) {
104 base::Time timestamp;
105 if (SetPolicyInternal(cached_response.cloud_policy(), ×tamp, true))
106 set_last_policy_refresh_time(timestamp);
107 }
108 }
109
SetPolicy(const em::PolicyFetchResponse & policy)110 void UserPolicyCache::SetPolicy(const em::PolicyFetchResponse& policy) {
111 base::Time now = base::Time::NowFromSystemTime();
112 set_last_policy_refresh_time(now);
113 bool ok = SetPolicyInternal(policy, NULL, false);
114 if (ok)
115 PersistPolicy(policy, now);
116 }
117
SetUnmanaged()118 void UserPolicyCache::SetUnmanaged() {
119 DCHECK(CalledOnValidThread());
120 SetUnmanagedInternal(base::Time::NowFromSystemTime());
121 BrowserThread::PostTask(
122 BrowserThread::FILE,
123 FROM_HERE,
124 new PersistPolicyTask(backing_file_path_, NULL, true));
125 }
126
PersistPolicy(const em::PolicyFetchResponse & policy,const base::Time & timestamp)127 void UserPolicyCache::PersistPolicy(const em::PolicyFetchResponse& policy,
128 const base::Time& timestamp) {
129 if (timestamp > base::Time::NowFromSystemTime() +
130 base::TimeDelta::FromMinutes(1)) {
131 LOG(WARNING) << "Server returned policy with timestamp from the future, "
132 "not persisting to disk.";
133 } else {
134 em::PolicyFetchResponse* policy_copy = new em::PolicyFetchResponse;
135 policy_copy->CopyFrom(policy);
136 BrowserThread::PostTask(
137 BrowserThread::FILE,
138 FROM_HERE,
139 new PersistPolicyTask(backing_file_path_, policy_copy, false));
140 }
141 }
142
DecodePolicyData(const em::PolicyData & policy_data,PolicyMap * mandatory,PolicyMap * recommended)143 bool UserPolicyCache::DecodePolicyData(const em::PolicyData& policy_data,
144 PolicyMap* mandatory,
145 PolicyMap* recommended) {
146 // TODO(jkummerow): Verify policy_data.device_token(). Needs final
147 // specification which token we're actually sending / expecting to get back.
148 em::CloudPolicySettings policy;
149 if (!policy.ParseFromString(policy_data.policy_value())) {
150 LOG(WARNING) << "Failed to parse CloudPolicySettings protobuf.";
151 return false;
152 }
153 DecodePolicy(policy, mandatory, recommended);
154 MaybeDecodeOldstylePolicy(policy_data.policy_value(), mandatory, recommended);
155 return true;
156 }
157
158 // Everything below is only needed for supporting old-style GenericNamedValue
159 // based policy data and can be removed once this support is no longer needed.
160
161 using google::protobuf::RepeatedField;
162 using google::protobuf::RepeatedPtrField;
163
164 class PolicyMapProxy : public ConfigurationPolicyStoreInterface {
165 public:
166 // Does not take ownership of |policy_map|, and callers need to make sure
167 // that |policy_map| outlives this PolicyMapProxy.
PolicyMapProxy(PolicyMap * policy_map)168 explicit PolicyMapProxy(PolicyMap* policy_map)
169 : policy_map_(policy_map) {}
~PolicyMapProxy()170 virtual ~PolicyMapProxy() {}
Apply(ConfigurationPolicyType policy,Value * value)171 virtual void Apply(ConfigurationPolicyType policy, Value* value) {
172 policy_map_->Set(policy, value);
173 }
174
175 private:
176 PolicyMap* policy_map_;
177
178 DISALLOW_COPY_AND_ASSIGN(PolicyMapProxy);
179 };
180
MaybeDecodeOldstylePolicy(const std::string & policy_data,PolicyMap * mandatory,PolicyMap * recommended)181 void UserPolicyCache::MaybeDecodeOldstylePolicy(
182 const std::string& policy_data,
183 PolicyMap* mandatory,
184 PolicyMap* recommended) {
185 // Return immediately if we already have policy information in the maps.
186 if (!mandatory->empty() || !recommended->empty())
187 return;
188 em::LegacyChromeSettingsProto policy;
189 // Return if the input string doesn't match the protobuf definition.
190 if (!policy.ParseFromString(policy_data))
191 return;
192 // Return if there's no old-style policy to decode.
193 if (policy.named_value_size() == 0)
194 return;
195
196 // Inspect GenericNamedValues and decode them.
197 DictionaryValue result;
198 RepeatedPtrField<em::GenericNamedValue>::const_iterator named_value;
199 for (named_value = policy.named_value().begin();
200 named_value != policy.named_value().end();
201 ++named_value) {
202 if (named_value->has_value()) {
203 Value* decoded_value = DecodeValue(named_value->value());
204 if (decoded_value)
205 result.Set(named_value->name(), decoded_value);
206 }
207 }
208 // Hack: Let one of the providers do the transformation from DictionaryValue
209 // to PolicyMap, since they have the required code anyway.
210 PolicyMapProxy map_proxy(mandatory);
211 GetManagedPolicyProvider()->ApplyPolicyValueTree(&result, &map_proxy);
212 }
213
DecodeIntegerValue(google::protobuf::int64 value) const214 Value* UserPolicyCache::DecodeIntegerValue(
215 google::protobuf::int64 value) const {
216 if (value < std::numeric_limits<int>::min() ||
217 value > std::numeric_limits<int>::max()) {
218 LOG(WARNING) << "Integer value " << value
219 << " out of numeric limits, ignoring.";
220 return NULL;
221 }
222
223 return Value::CreateIntegerValue(static_cast<int>(value));
224 }
225
DecodeValue(const em::GenericValue & value) const226 Value* UserPolicyCache::DecodeValue(const em::GenericValue& value) const {
227 if (!value.has_value_type())
228 return NULL;
229
230 switch (value.value_type()) {
231 case em::GenericValue::VALUE_TYPE_BOOL:
232 if (value.has_bool_value())
233 return Value::CreateBooleanValue(value.bool_value());
234 return NULL;
235 case em::GenericValue::VALUE_TYPE_INT64:
236 if (value.has_int64_value())
237 return DecodeIntegerValue(value.int64_value());
238 return NULL;
239 case em::GenericValue::VALUE_TYPE_STRING:
240 if (value.has_string_value())
241 return Value::CreateStringValue(value.string_value());
242 return NULL;
243 case em::GenericValue::VALUE_TYPE_DOUBLE:
244 if (value.has_double_value())
245 return Value::CreateDoubleValue(value.double_value());
246 return NULL;
247 case em::GenericValue::VALUE_TYPE_BYTES:
248 if (value.has_bytes_value()) {
249 std::string bytes = value.bytes_value();
250 return BinaryValue::CreateWithCopiedBuffer(bytes.c_str(), bytes.size());
251 }
252 return NULL;
253 case em::GenericValue::VALUE_TYPE_BOOL_ARRAY: {
254 ListValue* list = new ListValue;
255 RepeatedField<bool>::const_iterator i;
256 for (i = value.bool_array().begin(); i != value.bool_array().end(); ++i)
257 list->Append(Value::CreateBooleanValue(*i));
258 return list;
259 }
260 case em::GenericValue::VALUE_TYPE_INT64_ARRAY: {
261 ListValue* list = new ListValue;
262 RepeatedField<google::protobuf::int64>::const_iterator i;
263 for (i = value.int64_array().begin();
264 i != value.int64_array().end(); ++i) {
265 Value* int_value = DecodeIntegerValue(*i);
266 if (int_value)
267 list->Append(int_value);
268 }
269 return list;
270 }
271 case em::GenericValue::VALUE_TYPE_STRING_ARRAY: {
272 ListValue* list = new ListValue;
273 RepeatedPtrField<std::string>::const_iterator i;
274 for (i = value.string_array().begin();
275 i != value.string_array().end(); ++i)
276 list->Append(Value::CreateStringValue(*i));
277 return list;
278 }
279 case em::GenericValue::VALUE_TYPE_DOUBLE_ARRAY: {
280 ListValue* list = new ListValue;
281 RepeatedField<double>::const_iterator i;
282 for (i = value.double_array().begin();
283 i != value.double_array().end(); ++i)
284 list->Append(Value::CreateDoubleValue(*i));
285 return list;
286 }
287 default:
288 NOTREACHED() << "Unhandled value type";
289 }
290
291 return NULL;
292 }
293
294 } // namespace policy
295