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

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

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

Powered by Google App Engine
This is Rietveld 408576698