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

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

Issue 6705031: Send policy blobs to session_manager (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 9 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 "content/browser/browser_thread.h"
14 #include "chrome/browser/policy/configuration_policy_pref_store.h"
15 #include "chrome/browser/policy/proto/cloud_policy.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 // A thin ConfigurationPolicyProvider implementation sitting on top of
37 // CloudPolicyCache for hooking up with ConfigurationPolicyPrefStore.
38 class CloudPolicyCache::CloudPolicyProvider
39 : public ConfigurationPolicyProvider {
40 public:
41 CloudPolicyProvider(const PolicyDefinitionList* policy_list,
42 CloudPolicyCache* cache,
43 CloudPolicyCache::PolicyLevel level)
44 : ConfigurationPolicyProvider(policy_list),
45 cache_(cache),
46 level_(level) {}
47 virtual ~CloudPolicyProvider() {}
48
49 virtual bool Provide(ConfigurationPolicyStoreInterface* store) {
50 if (!cache_->has_device_policy()) {
51 if (level_ == POLICY_LEVEL_MANDATORY)
52 ApplyPolicyMap(&cache_->mandatory_policy_, store);
53 else if (level_ == POLICY_LEVEL_RECOMMENDED)
54 ApplyPolicyMap(&cache_->recommended_policy_, store);
55 } else {
56 ApplyPolicyValueTree(cache_->device_policy_.get(), store);
57 }
58 return true;
59 }
60
61 virtual bool IsInitializationComplete() const {
62 return cache_->initialization_complete_;
63 }
64
65 virtual void AddObserver(ConfigurationPolicyProvider::Observer* observer) {
66 cache_->observer_list_.AddObserver(observer);
67 }
68 virtual void RemoveObserver(ConfigurationPolicyProvider::Observer* observer) {
69 cache_->observer_list_.RemoveObserver(observer);
70 }
71
72 private:
73 // The underlying policy cache.
74 CloudPolicyCache* cache_;
75 // Policy level this provider will handle.
76 CloudPolicyCache::PolicyLevel level_;
77
78 DISALLOW_COPY_AND_ASSIGN(CloudPolicyProvider);
79 };
80
81 // Saves policy information to a file.
82 class PersistPolicyTask : public Task {
83 public:
84 PersistPolicyTask(const FilePath& path,
85 const em::PolicyFetchResponse* cloud_policy_response,
86 const em::DevicePolicyResponse* device_policy_response,
87 const bool is_unmanaged)
88 : path_(path),
89 cloud_policy_response_(cloud_policy_response),
90 device_policy_response_(device_policy_response),
91 is_unmanaged_(is_unmanaged) {}
92
93 private:
94 // Task override.
95 virtual void Run();
96
97 const FilePath path_;
98 scoped_ptr<const em::PolicyFetchResponse> cloud_policy_response_;
99 scoped_ptr<const em::DevicePolicyResponse> device_policy_response_;
100 const bool is_unmanaged_;
101 };
102
103 void PersistPolicyTask::Run() {
104 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
105 std::string data;
106 em::CachedCloudPolicyResponse cached_policy;
107 if (cloud_policy_response_.get()) {
108 cached_policy.mutable_cloud_policy()->CopyFrom(*cloud_policy_response_);
109 } else if (device_policy_response_.get()) {
110 cached_policy.mutable_device_policy()->CopyFrom(*device_policy_response_);
111 cached_policy.set_timestamp(base::Time::NowFromSystemTime().ToTimeT());
112 }
113 if (is_unmanaged_) {
114 cached_policy.set_unmanaged(true);
115 cached_policy.set_timestamp(base::Time::NowFromSystemTime().ToTimeT());
116 }
117 if (!cached_policy.SerializeToString(&data)) {
118 LOG(WARNING) << "Failed to serialize policy data";
119 return;
120 }
121
122 int size = data.size();
123 if (file_util::WriteFile(path_, data.c_str(), size) != size) {
124 LOG(WARNING) << "Failed to write " << path_.value();
125 return;
126 }
127 }
128
129 CloudPolicyCache::CloudPolicyCache(
130 const FilePath& backing_file_path)
131 : backing_file_path_(backing_file_path),
132 device_policy_(new DictionaryValue),
133 initialization_complete_(false),
134 is_unmanaged_(false),
135 has_device_policy_(false) {
136 managed_policy_provider_.reset(
137 new CloudPolicyProvider(
138 ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(),
139 this,
140 POLICY_LEVEL_MANDATORY));
141 recommended_policy_provider_.reset(
142 new CloudPolicyProvider(
143 ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(),
144 this,
145 POLICY_LEVEL_RECOMMENDED));
146 }
147
148 CloudPolicyCache::~CloudPolicyCache() {
149 FOR_EACH_OBSERVER(ConfigurationPolicyProvider::Observer,
150 observer_list_, OnProviderGoingAway());
151 }
152
153 void CloudPolicyCache::LoadFromFile() {
154 // TODO(jkummerow): This method is doing file IO during browser startup. In
155 // the long run it would be better to delay this until the FILE thread exists.
156 if (!file_util::PathExists(backing_file_path_) || initialization_complete_) {
157 return;
158 }
159
160 // Read the protobuf from the file.
161 std::string data;
162 if (!file_util::ReadFileToString(backing_file_path_, &data)) {
163 LOG(WARNING) << "Failed to read policy data from "
164 << backing_file_path_.value();
165 return;
166 }
167
168 em::CachedCloudPolicyResponse cached_response;
169 if (!cached_response.ParseFromArray(data.c_str(), data.size())) {
170 LOG(WARNING) << "Failed to parse policy data read from "
171 << backing_file_path_.value();
172 return;
173 }
174 base::Time timestamp;
175 PolicyMap mandatory_policy;
176 PolicyMap recommended_policy;
177 is_unmanaged_ = cached_response.unmanaged();
178 if (is_unmanaged_ || cached_response.has_device_policy())
179 timestamp = base::Time::FromTimeT(cached_response.timestamp());
180 if (cached_response.has_cloud_policy()) {
181 DCHECK(!is_unmanaged_);
182 bool ok = DecodePolicyResponse(cached_response.cloud_policy(),
183 &mandatory_policy,
184 &recommended_policy,
185 &timestamp);
186 if (!ok) {
187 LOG(WARNING) << "Decoding policy data failed.";
188 return;
189 }
190 }
191 if (timestamp > base::Time::NowFromSystemTime()) {
192 LOG(WARNING) << "Rejected policy data from " << backing_file_path_.value()
193 << ", file is from the future.";
194 return;
195 }
196 // Swap in the new policy information.
197 if (cached_response.has_cloud_policy()) {
198 mandatory_policy_.Swap(&mandatory_policy);
199 recommended_policy_.Swap(&recommended_policy);
200 has_device_policy_ = false;
201 } else if (cached_response.has_device_policy()) {
202 scoped_ptr<DictionaryValue> value(
203 DecodeDevicePolicy(cached_response.device_policy()));
204 device_policy_.reset(value.release());
205 has_device_policy_ = true;
206 }
207 last_policy_refresh_time_ = timestamp;
208 initialization_complete_ = true;
209
210 FOR_EACH_OBSERVER(ConfigurationPolicyProvider::Observer,
211 observer_list_, OnUpdatePolicy());
212 }
213
214 void CloudPolicyCache::SetPolicy(const em::PolicyFetchResponse& policy) {
215 DCHECK(CalledOnValidThread());
216 bool initialization_was_not_complete = !initialization_complete_;
217 is_unmanaged_ = false;
218 base::Time timestamp;
219 PolicyMap mandatory_policy;
220 PolicyMap recommended_policy;
221 bool ok = DecodePolicyResponse(policy, &mandatory_policy, &recommended_policy,
222 &timestamp);
223 if (!ok)
224 return;
225
226 const bool new_policy_differs =
227 !mandatory_policy_.Equals(mandatory_policy) ||
228 !recommended_policy_.Equals(recommended_policy);
229 mandatory_policy_.Swap(&mandatory_policy);
230 recommended_policy_.Swap(&recommended_policy);
231 initialization_complete_ = true;
232 last_policy_refresh_time_ = timestamp;
233 has_device_policy_ = false;
234
235 if (new_policy_differs || initialization_was_not_complete) {
236 FOR_EACH_OBSERVER(ConfigurationPolicyProvider::Observer,
237 observer_list_, OnUpdatePolicy());
238 }
239
240 if (timestamp > base::Time::NowFromSystemTime() +
241 base::TimeDelta::FromMinutes(1)) {
242 LOG(WARNING) << "Server returned policy with timestamp from the future, "
243 "not persisting to disk.";
244 } else {
245 em::PolicyFetchResponse* policy_copy = new em::PolicyFetchResponse;
246 policy_copy->CopyFrom(policy);
247 BrowserThread::PostTask(
248 BrowserThread::FILE,
249 FROM_HERE,
250 new PersistPolicyTask(backing_file_path_, policy_copy, NULL, false));
251 }
252 }
253
254 void CloudPolicyCache::SetDevicePolicy(const em::DevicePolicyResponse& policy) {
255 DCHECK(CalledOnValidThread());
256 bool initialization_was_not_complete = !initialization_complete_;
257 is_unmanaged_ = false;
258 DictionaryValue* value = DecodeDevicePolicy(policy);
259 const bool new_policy_differs = !(value->Equals(device_policy_.get()));
260 base::Time now(base::Time::NowFromSystemTime());
261 device_policy_.reset(value);
262 initialization_complete_ = true;
263 last_policy_refresh_time_ = now;
264 has_device_policy_ = true;
265
266 if (new_policy_differs || initialization_was_not_complete) {
267 FOR_EACH_OBSERVER(ConfigurationPolicyProvider::Observer,
268 observer_list_, OnUpdatePolicy());
269 }
270
271 em::DevicePolicyResponse* policy_copy = new em::DevicePolicyResponse;
272 policy_copy->CopyFrom(policy);
273 BrowserThread::PostTask(
274 BrowserThread::FILE,
275 FROM_HERE,
276 new PersistPolicyTask(backing_file_path_, NULL, policy_copy, false));
277 }
278
279 ConfigurationPolicyProvider* CloudPolicyCache::GetManagedPolicyProvider() {
280 DCHECK(CalledOnValidThread());
281 return managed_policy_provider_.get();
282 }
283
284 ConfigurationPolicyProvider* CloudPolicyCache::GetRecommendedPolicyProvider() {
285 DCHECK(CalledOnValidThread());
286 return recommended_policy_provider_.get();
287 }
288
289 void CloudPolicyCache::SetUnmanaged() {
290 DCHECK(CalledOnValidThread());
291 is_unmanaged_ = true;
292 mandatory_policy_.Clear();
293 recommended_policy_.Clear();
294 device_policy_.reset(new DictionaryValue);
295 last_policy_refresh_time_ = base::Time::NowFromSystemTime();
296
297 FOR_EACH_OBSERVER(ConfigurationPolicyProvider::Observer,
298 observer_list_, OnUpdatePolicy());
299
300 BrowserThread::PostTask(
301 BrowserThread::FILE,
302 FROM_HERE,
303 new PersistPolicyTask(backing_file_path_, NULL, NULL, true));
304 }
305
306 // static
307 bool CloudPolicyCache::DecodePolicyResponse(
308 const em::PolicyFetchResponse& policy_response,
309 PolicyMap* mandatory,
310 PolicyMap* recommended,
311 base::Time* timestamp) {
312 std::string data = policy_response.policy_data();
313
314 // Will do proper cleanup in my next CL. I promise. --jkummerow
315 //if (!VerifySignature(policy_response.policy_data_signature(), data,
316 // policy_response.certificate_chain())) {
317 // LOG(WARNING) << "Failed to verify signature.";
318 // return false;
319 //}
320
321 em::PolicyData policy_data;
322 if (!policy_data.ParseFromString(data)) {
323 LOG(WARNING) << "Failed to parse PolicyData protobuf.";
324 return false;
325 }
326
327 // TODO(jkummerow): Verify policy_data.device_token(). Needs final
328 // specification which token we're actually sending / expecting to get back.
329
330 // TODO(jkummerow): Store policy_data.device_name(), if we decide to transfer
331 // it from the server to the client.
332
333 *timestamp = base::Time::UnixEpoch() +
334 base::TimeDelta::FromMilliseconds(policy_data.timestamp());
335 em::CloudPolicySettings policy;
336 if (!policy.ParseFromString(policy_data.policy_value())) {
337 LOG(WARNING) << "Failed to parse CloudPolicySettings protobuf.";
338 return false;
339 }
340 DecodePolicy(policy, mandatory, recommended);
341 return true;
342 }
343
344 // static
345 bool CloudPolicyCache::VerifySignature(
346 const std::string& signature,
347 const std::string& data,
348 const RepeatedPtrField<std::string>& certificate_chain) {
349 // TODO(jkummerow): Implement this. Non-trivial because we want to do it
350 // for all platforms -> it's enough work to deserve its own CL.
351 // Don't forget to also verify the hostname of the server against the cert.
352 return true;
353 }
354
355 // static
356 Value* CloudPolicyCache::DecodeIntegerValue(google::protobuf::int64 value) {
357 if (value < std::numeric_limits<int>::min() ||
358 value > std::numeric_limits<int>::max()) {
359 LOG(WARNING) << "Integer value " << value
360 << " out of numeric limits, ignoring.";
361 return NULL;
362 }
363
364 return Value::CreateIntegerValue(static_cast<int>(value));
365 }
366
367 // static
368 Value* CloudPolicyCache::DecodeValue(const em::GenericValue& value) {
369 if (!value.has_value_type())
370 return NULL;
371
372 switch (value.value_type()) {
373 case em::GenericValue::VALUE_TYPE_BOOL:
374 if (value.has_bool_value())
375 return Value::CreateBooleanValue(value.bool_value());
376 return NULL;
377 case em::GenericValue::VALUE_TYPE_INT64:
378 if (value.has_int64_value())
379 return DecodeIntegerValue(value.int64_value());
380 return NULL;
381 case em::GenericValue::VALUE_TYPE_STRING:
382 if (value.has_string_value())
383 return Value::CreateStringValue(value.string_value());
384 return NULL;
385 case em::GenericValue::VALUE_TYPE_DOUBLE:
386 if (value.has_double_value())
387 return Value::CreateDoubleValue(value.double_value());
388 return NULL;
389 case em::GenericValue::VALUE_TYPE_BYTES:
390 if (value.has_bytes_value()) {
391 std::string bytes = value.bytes_value();
392 return BinaryValue::CreateWithCopiedBuffer(bytes.c_str(), bytes.size());
393 }
394 return NULL;
395 case em::GenericValue::VALUE_TYPE_BOOL_ARRAY: {
396 ListValue* list = new ListValue;
397 RepeatedField<bool>::const_iterator i;
398 for (i = value.bool_array().begin(); i != value.bool_array().end(); ++i)
399 list->Append(Value::CreateBooleanValue(*i));
400 return list;
401 }
402 case em::GenericValue::VALUE_TYPE_INT64_ARRAY: {
403 ListValue* list = new ListValue;
404 RepeatedField<google::protobuf::int64>::const_iterator i;
405 for (i = value.int64_array().begin();
406 i != value.int64_array().end(); ++i) {
407 Value* int_value = DecodeIntegerValue(*i);
408 if (int_value)
409 list->Append(int_value);
410 }
411 return list;
412 }
413 case em::GenericValue::VALUE_TYPE_STRING_ARRAY: {
414 ListValue* list = new ListValue;
415 RepeatedPtrField<std::string>::const_iterator i;
416 for (i = value.string_array().begin();
417 i != value.string_array().end(); ++i)
418 list->Append(Value::CreateStringValue(*i));
419 return list;
420 }
421 case em::GenericValue::VALUE_TYPE_DOUBLE_ARRAY: {
422 ListValue* list = new ListValue;
423 RepeatedField<double>::const_iterator i;
424 for (i = value.double_array().begin();
425 i != value.double_array().end(); ++i)
426 list->Append(Value::CreateDoubleValue(*i));
427 return list;
428 }
429 default:
430 NOTREACHED() << "Unhandled value type";
431 }
432
433 return NULL;
434 }
435
436 // static
437 DictionaryValue* CloudPolicyCache::DecodeDevicePolicy(
438 const em::DevicePolicyResponse& policy) {
439 DictionaryValue* result = new DictionaryValue;
440 RepeatedPtrField<em::DevicePolicySetting>::const_iterator setting;
441 for (setting = policy.setting().begin();
442 setting != policy.setting().end();
443 ++setting) {
444 // Wrong policy key? Skip.
445 if (setting->policy_key().compare(kChromeDevicePolicySettingKey) != 0)
446 continue;
447
448 // No policy value? Skip.
449 if (!setting->has_policy_value())
450 continue;
451
452 // Iterate through all the name-value pairs wrapped in |setting|.
453 const em::GenericSetting& policy_value(setting->policy_value());
454 RepeatedPtrField<em::GenericNamedValue>::const_iterator named_value;
455 for (named_value = policy_value.named_value().begin();
456 named_value != policy_value.named_value().end();
457 ++named_value) {
458 if (named_value->has_value()) {
459 Value* decoded_value =
460 CloudPolicyCache::DecodeValue(named_value->value());
461 if (decoded_value)
462 result->Set(named_value->name(), decoded_value);
463 }
464 }
465 }
466 return result;
467 }
468
469 } // namespace policy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698