• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(), &timestamp, 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