Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(132)

Side by Side Diff: chrome/browser/policy/cloud_policy_cache.cc

Issue 6532019: New policy protobuf protocol. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix one more leak Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/cloud_policy_cache.h"
6
7 #include <limits>
8
9 #include "base/file_util.h"
10 #include "base/logging.h"
11 #include "base/task.h"
12 #include "base/values.h"
13 #include "chrome/browser/browser_thread.h"
14 #include "chrome/browser/policy/proto/cloud_policy.pb.h"
15 #include "chrome/browser/policy/proto/device_management_backend.pb.h"
16 #include "chrome/browser/policy/proto/device_management_constants.h"
17 #include "chrome/browser/policy/proto/device_management_local.pb.h"
18
19 using google::protobuf::RepeatedField;
20 using google::protobuf::RepeatedPtrField;
21
22 // This CloudPolicyCache currently supports two protocols for the interaction
23 // with DMServer: the old "DevicePolicy" format, which is being used in the
24 // CrOS Pilot Program and will be deprecated afterwards, and the new
25 // "CloudPolicy" format, which will be used exclusively after the public launch
26 // of ChromeOS.
27
28 namespace policy {
29
30 // Decodes a CloudPolicySettings object into two maps with mandatory and
31 // recommended settings, respectively. The implementation is generated code
32 // in policy/cloud_policy_generated.cc.
33 void DecodePolicy(const em::CloudPolicySettings& policy,
34 PolicyMap* mandatory, PolicyMap* recommended);
35
36 // Saves policy information to a file.
37 class PersistPolicyTask : public Task {
38 public:
39 PersistPolicyTask(const FilePath& path,
40 const em::CloudPolicyResponse* cloud_policy_response,
41 const em::DevicePolicyResponse* device_policy_response,
42 const bool is_unmanaged)
43 : path_(path),
44 cloud_policy_response_(cloud_policy_response),
45 device_policy_response_(device_policy_response),
46 is_unmanaged_(is_unmanaged) {}
47
48 private:
49 // Task override.
50 virtual void Run();
51
52 const FilePath path_;
53 scoped_ptr<const em::CloudPolicyResponse> cloud_policy_response_;
54 scoped_ptr<const em::DevicePolicyResponse> device_policy_response_;
55 const bool is_unmanaged_;
56 };
57
58 void PersistPolicyTask::Run() {
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
60 std::string data;
61 em::CachedCloudPolicyResponse cached_policy;
62 if (cloud_policy_response_.get()) {
63 cached_policy.mutable_cloud_policy()->CopyFrom(*cloud_policy_response_);
64 } else if (device_policy_response_.get()) {
65 cached_policy.mutable_device_policy()->CopyFrom(*device_policy_response_);
66 cached_policy.set_timestamp(base::Time::NowFromSystemTime().ToTimeT());
67 }
68 if (is_unmanaged_) {
69 cached_policy.set_unmanaged(true);
70 cached_policy.set_timestamp(base::Time::NowFromSystemTime().ToTimeT());
71 }
72 if (!cached_policy.SerializeToString(&data)) {
73 LOG(WARNING) << "Failed to serialize policy data";
74 return;
75 }
76
77 int size = data.size();
78 if (file_util::WriteFile(path_, data.c_str(), size) != size) {
79 LOG(WARNING) << "Failed to write " << path_.value();
80 return;
81 }
82 }
83
84 CloudPolicyCache::CloudPolicyCache(
85 const FilePath& backing_file_path)
86 : backing_file_path_(backing_file_path),
87 device_policy_(new DictionaryValue),
88 fresh_policy_(false),
89 is_unmanaged_(false),
90 has_device_policy_(false) {
91 }
92
93 CloudPolicyCache::~CloudPolicyCache() {}
94
95 void CloudPolicyCache::LoadPolicyFromFile() {
96 // TODO(jkummerow): This method is doing file IO during browser startup. In
97 // the long run it would be better to delay this until the FILE thread exists.
98 if (!file_util::PathExists(backing_file_path_) || fresh_policy_) {
99 return;
100 }
101
102 // Read the protobuf from the file.
103 std::string data;
104 if (!file_util::ReadFileToString(backing_file_path_, &data)) {
105 LOG(WARNING) << "Failed to read policy data from "
106 << backing_file_path_.value();
107 return;
108 }
109
110 em::CachedCloudPolicyResponse cached_response;
111 if (!cached_response.ParseFromArray(data.c_str(), data.size())) {
112 LOG(WARNING) << "Failed to parse policy data read from "
113 << backing_file_path_.value();
114 return;
115 }
116 base::Time timestamp;
117 PolicyMap mandatory_policy;
118 PolicyMap recommended_policy;
119 is_unmanaged_ = cached_response.unmanaged();
120 if (is_unmanaged_ || cached_response.has_device_policy())
121 timestamp = base::Time::FromTimeT(cached_response.timestamp());
122 if (cached_response.has_cloud_policy()) {
123 DCHECK(!is_unmanaged_);
124 bool ok = DecodePolicyResponse(cached_response.cloud_policy(),
125 &mandatory_policy,
126 &recommended_policy,
127 &timestamp);
128 if (!ok) {
129 LOG(WARNING) << "Decoding policy data failed.";
130 return;
131 }
132 }
133 if (timestamp > base::Time::NowFromSystemTime()) {
134 LOG(WARNING) << "Rejected policy data from " << backing_file_path_.value()
135 << ", file is from the future.";
136 return;
137 }
138 // Swap in the new policy information.
139 if (is_unmanaged_) {
140 base::AutoLock lock(lock_);
141 last_policy_refresh_time_ = timestamp;
142 return;
143 } else if (cached_response.has_cloud_policy()) {
144 if (!fresh_policy_) {
145 base::AutoLock lock(lock_);
146 mandatory_policy_.Swap(&mandatory_policy);
147 recommended_policy_.Swap(&recommended_policy);
148 last_policy_refresh_time_ = timestamp;
149 has_device_policy_ = false;
150 }
151 } else if (cached_response.has_device_policy()) {
152 scoped_ptr<DictionaryValue> value(
153 DecodeDevicePolicy(cached_response.device_policy()));
154 if (!fresh_policy_) {
155 base::AutoLock lock(lock_);
156 device_policy_.reset(value.release());
157 last_policy_refresh_time_ = timestamp;
158 has_device_policy_ = true;
159 }
160 }
161 }
162
163 bool CloudPolicyCache::SetPolicy(const em::CloudPolicyResponse& policy) {
164 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
165 is_unmanaged_ = false;
166 base::Time timestamp;
167 PolicyMap mandatory_policy;
168 PolicyMap recommended_policy;
169 bool ok = DecodePolicyResponse(policy, &mandatory_policy, &recommended_policy,
170 &timestamp);
171 if (!ok) {
172 // TODO(jkummerow): Signal error to PolicyProvider.
173 return false;
174 }
175 const bool new_policy_differs =
176 !mandatory_policy.Equals(mandatory_policy_) ||
177 !recommended_policy.Equals(recommended_policy_);
178 {
179 base::AutoLock lock(lock_);
180 mandatory_policy_.Swap(&mandatory_policy);
danno 2011/02/17 11:05:44 Comment about using Swap to cleanup to old value o
181 recommended_policy_.Swap(&recommended_policy);
182 fresh_policy_ = true;
183 last_policy_refresh_time_ = timestamp;
184 has_device_policy_ = false;
185 }
186
187 if (timestamp > base::Time::NowFromSystemTime() +
188 base::TimeDelta::FromMinutes(1)) {
189 LOG(WARNING) << "Server returned policy with timestamp from the future, "
190 "not persisting to disk.";
191 } else {
192 em::CloudPolicyResponse* policy_copy = new em::CloudPolicyResponse;
193 policy_copy->CopyFrom(policy);
194 BrowserThread::PostTask(
195 BrowserThread::FILE,
196 FROM_HERE,
197 new PersistPolicyTask(backing_file_path_, policy_copy, NULL, false));
198 }
199 return new_policy_differs;
200 }
201
202 bool CloudPolicyCache::SetDevicePolicy(const em::DevicePolicyResponse& policy) {
203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
204 is_unmanaged_ = false;
205 DictionaryValue* value = DecodeDevicePolicy(policy);
206 const bool new_policy_differs = !(value->Equals(device_policy_.get()));
207 base::Time now(base::Time::NowFromSystemTime());
208 {
209 base::AutoLock lock(lock_);
210 device_policy_.reset(value);
211 fresh_policy_ = true;
212 last_policy_refresh_time_ = now;
213 has_device_policy_ = true;
214 }
215
216 em::DevicePolicyResponse* policy_copy = new em::DevicePolicyResponse;
217 policy_copy->CopyFrom(policy);
218 BrowserThread::PostTask(
219 BrowserThread::FILE,
220 FROM_HERE,
221 new PersistPolicyTask(backing_file_path_, NULL, policy_copy, false));
222 return new_policy_differs;
223 }
224
225 DictionaryValue* CloudPolicyCache::GetDevicePolicy() {
226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
227 base::AutoLock lock(lock_);
228 return device_policy_->DeepCopy();
229 }
230
231 const PolicyMap* CloudPolicyCache::GetMandatoryPolicy() const {
232 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
233 return &mandatory_policy_;
234 }
235
236 const PolicyMap* CloudPolicyCache::GetRecommendedPolicy() const {
237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
238 return &recommended_policy_;
239 }
240
241 void CloudPolicyCache::SetUnmanaged() {
242 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
243 is_unmanaged_ = true;
244 {
245 base::AutoLock lock(lock_);
246 mandatory_policy_.Clear();
247 recommended_policy_.Clear();
248 device_policy_.reset(new DictionaryValue);
249 last_policy_refresh_time_ = base::Time::NowFromSystemTime();
250 }
251 BrowserThread::PostTask(
252 BrowserThread::FILE,
253 FROM_HERE,
254 new PersistPolicyTask(backing_file_path_, NULL, NULL, true));
255 }
256
257 // static
258 bool CloudPolicyCache::DecodePolicyResponse(
259 const em::CloudPolicyResponse& policy_response,
260 PolicyMap* mandatory,
261 PolicyMap* recommended,
262 base::Time* timestamp) {
263 std::string data = policy_response.signed_response();
264
265 if (!VerifySignature(policy_response.signature(), data,
266 policy_response.certificate_chain())) {
267 LOG(WARNING) << "Failed to verify signature.";
268 return false;
269 }
270
271 em::SignedCloudPolicyResponse response;
272 if (!response.ParseFromArray(data.c_str(), data.size())) {
273 LOG(WARNING) << "Failed to parse SignedCloudPolicyResponse protobuf.";
274 return false;
275 }
276
277 // TODO(jkummerow): Verify response.device_token(). Needs final specification
278 // which token we're actually sending / expecting to get back.
279
280 // TODO(jkummerow): Store response.device_name(), if we decide to transfer
281 // it from the server to the client.
282
283 DCHECK(timestamp);
284 *timestamp = base::Time::FromTimeT(response.timestamp());
285 DecodePolicy(response.settings(), mandatory, recommended);
286 return true;
287 }
288
289 // static
290 bool CloudPolicyCache::VerifySignature(
291 const std::string& signature,
292 const std::string& data,
293 const RepeatedPtrField<std::string>& certificate_chain) {
294 // TODO(jkummerow): Implement this. Non-trivial because we want to do it
295 // for all platforms -> it's enough work to deserve its own CL.
296 // Don't forget to also verify the hostname of the server against the cert.
297 return true;
298 }
299
300 // static
301 Value* CloudPolicyCache::DecodeIntegerValue(google::protobuf::int64 value) {
302 if (value < std::numeric_limits<int>::min() ||
303 value > std::numeric_limits<int>::max()) {
304 LOG(WARNING) << "Integer value " << value
305 << " out of numeric limits, ignoring.";
306 return NULL;
307 }
308
309 return Value::CreateIntegerValue(static_cast<int>(value));
310 }
311
312 // static
313 Value* CloudPolicyCache::DecodeValue(const em::GenericValue& value) {
314 if (!value.has_value_type())
315 return NULL;
316
317 switch (value.value_type()) {
318 case em::GenericValue::VALUE_TYPE_BOOL:
319 if (value.has_bool_value())
320 return Value::CreateBooleanValue(value.bool_value());
321 return NULL;
322 case em::GenericValue::VALUE_TYPE_INT64:
323 if (value.has_int64_value())
324 return DecodeIntegerValue(value.int64_value());
325 return NULL;
326 case em::GenericValue::VALUE_TYPE_STRING:
327 if (value.has_string_value())
328 return Value::CreateStringValue(value.string_value());
329 return NULL;
330 case em::GenericValue::VALUE_TYPE_DOUBLE:
331 if (value.has_double_value())
332 return Value::CreateDoubleValue(value.double_value());
333 return NULL;
334 case em::GenericValue::VALUE_TYPE_BYTES:
335 if (value.has_bytes_value()) {
336 std::string bytes = value.bytes_value();
337 return BinaryValue::CreateWithCopiedBuffer(bytes.c_str(), bytes.size());
338 }
339 return NULL;
340 case em::GenericValue::VALUE_TYPE_BOOL_ARRAY: {
341 ListValue* list = new ListValue;
342 RepeatedField<bool>::const_iterator i;
343 for (i = value.bool_array().begin(); i != value.bool_array().end(); ++i)
344 list->Append(Value::CreateBooleanValue(*i));
345 return list;
346 }
347 case em::GenericValue::VALUE_TYPE_INT64_ARRAY: {
348 ListValue* list = new ListValue;
349 RepeatedField<google::protobuf::int64>::const_iterator i;
350 for (i = value.int64_array().begin();
351 i != value.int64_array().end(); ++i) {
352 Value* int_value = DecodeIntegerValue(*i);
353 if (int_value)
354 list->Append(int_value);
355 }
356 return list;
357 }
358 case em::GenericValue::VALUE_TYPE_STRING_ARRAY: {
359 ListValue* list = new ListValue;
360 RepeatedPtrField<std::string>::const_iterator i;
361 for (i = value.string_array().begin();
362 i != value.string_array().end(); ++i)
363 list->Append(Value::CreateStringValue(*i));
364 return list;
365 }
366 case em::GenericValue::VALUE_TYPE_DOUBLE_ARRAY: {
367 ListValue* list = new ListValue;
368 RepeatedField<double>::const_iterator i;
369 for (i = value.double_array().begin();
370 i != value.double_array().end(); ++i)
371 list->Append(Value::CreateDoubleValue(*i));
372 return list;
373 }
374 default:
375 NOTREACHED() << "Unhandled value type";
376 }
377
378 return NULL;
379 }
380
381 // static
382 DictionaryValue* CloudPolicyCache::DecodeDevicePolicy(
383 const em::DevicePolicyResponse& policy) {
384 DictionaryValue* result = new DictionaryValue;
385 RepeatedPtrField<em::DevicePolicySetting>::const_iterator setting;
386 for (setting = policy.setting().begin();
387 setting != policy.setting().end();
388 ++setting) {
389 // Wrong policy key? Skip.
390 if (setting->policy_key().compare(kChromeDevicePolicySettingKey) != 0)
391 continue;
392
393 // No policy value? Skip.
394 if (!setting->has_policy_value())
395 continue;
396
397 // Iterate through all the name-value pairs wrapped in |setting|.
398 const em::GenericSetting& policy_value(setting->policy_value());
399 RepeatedPtrField<em::GenericNamedValue>::const_iterator named_value;
400 for (named_value = policy_value.named_value().begin();
401 named_value != policy_value.named_value().end();
402 ++named_value) {
403 if (named_value->has_value()) {
404 Value* decoded_value =
405 CloudPolicyCache::DecodeValue(named_value->value());
406 if (decoded_value)
407 result->Set(named_value->name(), decoded_value);
408 }
409 }
410 }
411 return result;
412 }
413
414 } // namespace policy
OLDNEW
« no previous file with comments | « chrome/browser/policy/cloud_policy_cache.h ('k') | chrome/browser/policy/cloud_policy_cache_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698