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: 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 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 72 UserPolicyCache::UserPolicyCache(const FilePath& backing_file_path) 73 : backing_file_path_(backing_file_path) { 74 } 75 76 UserPolicyCache::~UserPolicyCache() { 77 } 78 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 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 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 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 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. 168 explicit PolicyMapProxy(PolicyMap* policy_map) 169 : policy_map_(policy_map) {} 170 virtual ~PolicyMapProxy() {} 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 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 214 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 226 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