1 // Copyright (c) 2012 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 "chromeos/network/onc/onc_merger.h" 6 7 #include <set> 8 #include <string> 9 #include <vector> 10 11 #include "base/basictypes.h" 12 #include "base/logging.h" 13 #include "base/values.h" 14 #include "chromeos/network/onc/onc_signature.h" 15 #include "components/onc/onc_constants.h" 16 17 namespace chromeos { 18 namespace onc { 19 namespace { 20 21 typedef scoped_ptr<base::DictionaryValue> DictionaryPtr; 22 23 // Returns true if the field is the identifier of a configuration, i.e. the GUID 24 // of a network or a certificate. These can be special handled during merging 25 // because they are always identical for the various setting sources. 26 bool IsIdentifierField(const OncValueSignature& value_signature, 27 const std::string& field_name) { 28 if (&value_signature == &kNetworkConfigurationSignature) 29 return field_name == ::onc::network_config::kGUID; 30 if (&value_signature == &kCertificateSignature) 31 return field_name == ::onc::certificate::kGUID; 32 return false; 33 } 34 35 // Inserts |true| at every field name in |result| that is recommended in 36 // |policy|. 37 void MarkRecommendedFieldnames(const base::DictionaryValue& policy, 38 base::DictionaryValue* result) { 39 const base::ListValue* recommended_value = NULL; 40 if (!policy.GetListWithoutPathExpansion(::onc::kRecommended, 41 &recommended_value)) 42 return; 43 for (base::ListValue::const_iterator it = recommended_value->begin(); 44 it != recommended_value->end(); ++it) { 45 std::string entry; 46 if ((*it)->GetAsString(&entry)) 47 result->SetBooleanWithoutPathExpansion(entry, true); 48 } 49 } 50 51 // Returns a dictionary which contains |true| at each path that is editable by 52 // the user. No other fields are set. 53 DictionaryPtr GetEditableFlags(const base::DictionaryValue& policy) { 54 DictionaryPtr result_editable(new base::DictionaryValue); 55 MarkRecommendedFieldnames(policy, result_editable.get()); 56 57 // Recurse into nested dictionaries. 58 for (base::DictionaryValue::Iterator it(policy); !it.IsAtEnd(); 59 it.Advance()) { 60 const base::DictionaryValue* child_policy = NULL; 61 if (it.key() == ::onc::kRecommended || 62 !it.value().GetAsDictionary(&child_policy)) { 63 continue; 64 } 65 66 result_editable->SetWithoutPathExpansion( 67 it.key(), GetEditableFlags(*child_policy).release()); 68 } 69 return result_editable.Pass(); 70 } 71 72 // This is the base class for merging a list of DictionaryValues in 73 // parallel. See MergeDictionaries function. 74 class MergeListOfDictionaries { 75 public: 76 typedef std::vector<const base::DictionaryValue*> DictPtrs; 77 78 MergeListOfDictionaries() { 79 } 80 81 virtual ~MergeListOfDictionaries() { 82 } 83 84 // For each path in any of the dictionaries |dicts|, the function 85 // MergeListOfValues is called with the list of values that are located at 86 // that path in each of the dictionaries. This function returns a new 87 // dictionary containing all results of MergeListOfValues at the respective 88 // paths. The resulting dictionary doesn't contain empty dictionaries. 89 DictionaryPtr MergeDictionaries(const DictPtrs &dicts) { 90 DictionaryPtr result(new base::DictionaryValue); 91 std::set<std::string> visited; 92 for (DictPtrs::const_iterator it_outer = dicts.begin(); 93 it_outer != dicts.end(); ++it_outer) { 94 if (!*it_outer) 95 continue; 96 97 for (base::DictionaryValue::Iterator field(**it_outer); !field.IsAtEnd(); 98 field.Advance()) { 99 const std::string& key = field.key(); 100 if (key == ::onc::kRecommended || !visited.insert(key).second) 101 continue; 102 103 scoped_ptr<base::Value> merged_value; 104 if (field.value().IsType(base::Value::TYPE_DICTIONARY)) { 105 DictPtrs nested_dicts; 106 for (DictPtrs::const_iterator it_inner = dicts.begin(); 107 it_inner != dicts.end(); ++it_inner) { 108 const base::DictionaryValue* nested_dict = NULL; 109 if (*it_inner) 110 (*it_inner)->GetDictionaryWithoutPathExpansion(key, &nested_dict); 111 nested_dicts.push_back(nested_dict); 112 } 113 DictionaryPtr merged_dict(MergeNestedDictionaries(key, nested_dicts)); 114 if (!merged_dict->empty()) 115 merged_value = merged_dict.Pass(); 116 } else { 117 std::vector<const base::Value*> values; 118 for (DictPtrs::const_iterator it_inner = dicts.begin(); 119 it_inner != dicts.end(); ++it_inner) { 120 const base::Value* value = NULL; 121 if (*it_inner) 122 (*it_inner)->GetWithoutPathExpansion(key, &value); 123 values.push_back(value); 124 } 125 merged_value = MergeListOfValues(key, values); 126 } 127 128 if (merged_value) 129 result->SetWithoutPathExpansion(key, merged_value.release()); 130 } 131 } 132 return result.Pass(); 133 } 134 135 protected: 136 // This function is called by MergeDictionaries for each list of values that 137 // are located at the same path in each of the dictionaries. The order of the 138 // values is the same as of the given dictionaries |dicts|. If a dictionary 139 // doesn't contain a path then it's value is NULL. 140 virtual scoped_ptr<base::Value> MergeListOfValues( 141 const std::string& key, 142 const std::vector<const base::Value*>& values) = 0; 143 144 virtual DictionaryPtr MergeNestedDictionaries(const std::string& key, 145 const DictPtrs &dicts) { 146 return MergeDictionaries(dicts); 147 } 148 149 private: 150 DISALLOW_COPY_AND_ASSIGN(MergeListOfDictionaries); 151 }; 152 153 // This is the base class for merging policies and user settings. 154 class MergeSettingsAndPolicies : public MergeListOfDictionaries { 155 public: 156 struct ValueParams { 157 const base::Value* user_policy; 158 const base::Value* device_policy; 159 const base::Value* user_setting; 160 const base::Value* shared_setting; 161 const base::Value* active_setting; 162 bool user_editable; 163 bool device_editable; 164 }; 165 166 MergeSettingsAndPolicies() {} 167 168 // Merge the provided dictionaries. For each path in any of the dictionaries, 169 // MergeValues is called. Its results are collected in a new dictionary which 170 // is then returned. The resulting dictionary never contains empty 171 // dictionaries. 172 DictionaryPtr MergeDictionaries( 173 const base::DictionaryValue* user_policy, 174 const base::DictionaryValue* device_policy, 175 const base::DictionaryValue* user_settings, 176 const base::DictionaryValue* shared_settings, 177 const base::DictionaryValue* active_settings) { 178 hasUserPolicy_ = (user_policy != NULL); 179 hasDevicePolicy_ = (device_policy != NULL); 180 181 DictionaryPtr user_editable; 182 if (user_policy != NULL) 183 user_editable = GetEditableFlags(*user_policy); 184 185 DictionaryPtr device_editable; 186 if (device_policy != NULL) 187 device_editable = GetEditableFlags(*device_policy); 188 189 std::vector<const base::DictionaryValue*> dicts(kLastIndex, NULL); 190 dicts[kUserPolicyIndex] = user_policy; 191 dicts[kDevicePolicyIndex] = device_policy; 192 dicts[kUserSettingsIndex] = user_settings; 193 dicts[kSharedSettingsIndex] = shared_settings; 194 dicts[kActiveSettingsIndex] = active_settings; 195 dicts[kUserEditableIndex] = user_editable.get(); 196 dicts[kDeviceEditableIndex] = device_editable.get(); 197 return MergeListOfDictionaries::MergeDictionaries(dicts); 198 } 199 200 protected: 201 // This function is called by MergeDictionaries for each list of values that 202 // are located at the same path in each of the dictionaries. Implementations 203 // can use the Has*Policy functions. 204 virtual scoped_ptr<base::Value> MergeValues(const std::string& key, 205 const ValueParams& values) = 0; 206 207 // Whether a user policy was provided. 208 bool HasUserPolicy() { 209 return hasUserPolicy_; 210 } 211 212 // Whether a device policy was provided. 213 bool HasDevicePolicy() { 214 return hasDevicePolicy_; 215 } 216 217 // MergeListOfDictionaries override. 218 virtual scoped_ptr<base::Value> MergeListOfValues( 219 const std::string& key, 220 const std::vector<const base::Value*>& values) OVERRIDE { 221 bool user_editable = !HasUserPolicy(); 222 if (values[kUserEditableIndex]) 223 values[kUserEditableIndex]->GetAsBoolean(&user_editable); 224 225 bool device_editable = !HasDevicePolicy(); 226 if (values[kDeviceEditableIndex]) 227 values[kDeviceEditableIndex]->GetAsBoolean(&device_editable); 228 229 ValueParams params; 230 params.user_policy = values[kUserPolicyIndex]; 231 params.device_policy = values[kDevicePolicyIndex]; 232 params.user_setting = values[kUserSettingsIndex]; 233 params.shared_setting = values[kSharedSettingsIndex]; 234 params.active_setting = values[kActiveSettingsIndex]; 235 params.user_editable = user_editable; 236 params.device_editable = device_editable; 237 return MergeValues(key, params); 238 } 239 240 private: 241 enum { 242 kUserPolicyIndex, 243 kDevicePolicyIndex, 244 kUserSettingsIndex, 245 kSharedSettingsIndex, 246 kActiveSettingsIndex, 247 kUserEditableIndex, 248 kDeviceEditableIndex, 249 kLastIndex 250 }; 251 252 bool hasUserPolicy_, hasDevicePolicy_; 253 254 DISALLOW_COPY_AND_ASSIGN(MergeSettingsAndPolicies); 255 }; 256 257 // Call MergeDictionaries to merge policies and settings to the effective 258 // values. This ignores the active settings of Shill. See the description of 259 // MergeSettingsAndPoliciesToEffective. 260 class MergeToEffective : public MergeSettingsAndPolicies { 261 public: 262 MergeToEffective() {} 263 264 protected: 265 // Merges |values| to the effective value (Mandatory policy overwrites user 266 // settings overwrites shared settings overwrites recommended policy). |which| 267 // is set to the respective onc::kAugmentation* constant that indicates which 268 // source of settings is effective. Note that this function may return a NULL 269 // pointer and set |which| to ::onc::kAugmentationUserPolicy, which means that 270 // the 271 // user policy didn't set a value but also didn't recommend it, thus enforcing 272 // the empty value. 273 scoped_ptr<base::Value> MergeValues(const std::string& key, 274 const ValueParams& values, 275 std::string* which) { 276 const base::Value* result = NULL; 277 which->clear(); 278 if (!values.user_editable) { 279 result = values.user_policy; 280 *which = ::onc::kAugmentationUserPolicy; 281 } else if (!values.device_editable) { 282 result = values.device_policy; 283 *which = ::onc::kAugmentationDevicePolicy; 284 } else if (values.user_setting) { 285 result = values.user_setting; 286 *which = ::onc::kAugmentationUserSetting; 287 } else if (values.shared_setting) { 288 result = values.shared_setting; 289 *which = ::onc::kAugmentationSharedSetting; 290 } else if (values.user_policy) { 291 result = values.user_policy; 292 *which = ::onc::kAugmentationUserPolicy; 293 } else if (values.device_policy) { 294 result = values.device_policy; 295 *which = ::onc::kAugmentationDevicePolicy; 296 } else { 297 // Can be reached if the current field is recommended, but none of the 298 // dictionaries contained a value for it. 299 } 300 if (result) 301 return make_scoped_ptr(result->DeepCopy()); 302 return scoped_ptr<base::Value>(); 303 } 304 305 // MergeSettingsAndPolicies override. 306 virtual scoped_ptr<base::Value> MergeValues( 307 const std::string& key, 308 const ValueParams& values) OVERRIDE { 309 std::string which; 310 return MergeValues(key, values, &which); 311 } 312 313 private: 314 DISALLOW_COPY_AND_ASSIGN(MergeToEffective); 315 }; 316 317 namespace { 318 319 // Returns true if all not-null values in |values| are equal to |value|. 320 bool AllPresentValuesEqual(const MergeSettingsAndPolicies::ValueParams& values, 321 const base::Value& value) { 322 if (values.user_policy && !value.Equals(values.user_policy)) 323 return false; 324 if (values.device_policy && !value.Equals(values.device_policy)) 325 return false; 326 if (values.user_setting && !value.Equals(values.user_setting)) 327 return false; 328 if (values.shared_setting && !value.Equals(values.shared_setting)) 329 return false; 330 if (values.active_setting && !value.Equals(values.active_setting)) 331 return false; 332 return true; 333 } 334 335 } // namespace 336 337 // Call MergeDictionaries to merge policies and settings to an augmented 338 // dictionary which contains a dictionary for each value in the original 339 // dictionaries. See the description of MergeSettingsAndPoliciesToAugmented. 340 class MergeToAugmented : public MergeToEffective { 341 public: 342 MergeToAugmented() {} 343 344 DictionaryPtr MergeDictionaries( 345 const OncValueSignature& signature, 346 const base::DictionaryValue* user_policy, 347 const base::DictionaryValue* device_policy, 348 const base::DictionaryValue* user_settings, 349 const base::DictionaryValue* shared_settings, 350 const base::DictionaryValue* active_settings) { 351 signature_ = &signature; 352 return MergeToEffective::MergeDictionaries(user_policy, 353 device_policy, 354 user_settings, 355 shared_settings, 356 active_settings); 357 } 358 359 protected: 360 // MergeSettingsAndPolicies override. 361 virtual scoped_ptr<base::Value> MergeValues( 362 const std::string& key, 363 const ValueParams& values) OVERRIDE { 364 const OncFieldSignature* field = NULL; 365 if (signature_) 366 field = GetFieldSignature(*signature_, key); 367 368 if (!field) { 369 // This field is not part of the provided ONCSignature, thus it cannot be 370 // controlled by policy. Return the plain active value instead of an 371 // augmented dictionary. 372 return make_scoped_ptr(values.active_setting->DeepCopy()); 373 } 374 375 // This field is part of the provided ONCSignature, thus it can be 376 // controlled by policy. 377 std::string which_effective; 378 scoped_ptr<base::Value> effective_value = 379 MergeToEffective::MergeValues(key, values, &which_effective); 380 381 if (IsIdentifierField(*signature_, key)) { 382 // Don't augment the GUID but write the plain value. 383 if (!effective_value) { 384 LOG(ERROR) << "GUID field has no effective value"; 385 return make_scoped_ptr<base::Value>(NULL); 386 } 387 388 // DCHECK that all provided GUIDs are identical. 389 DCHECK(AllPresentValuesEqual(values, *effective_value)); 390 391 // Return the un-augmented GUID. 392 return effective_value.Pass(); 393 } 394 395 scoped_ptr<base::DictionaryValue> augmented_value( 396 new base::DictionaryValue); 397 398 if (values.active_setting) { 399 augmented_value->SetWithoutPathExpansion( 400 ::onc::kAugmentationActiveSetting, values.active_setting->DeepCopy()); 401 } 402 403 if (!which_effective.empty()) { 404 augmented_value->SetStringWithoutPathExpansion( 405 ::onc::kAugmentationEffectiveSetting, which_effective); 406 } 407 408 // Prevent credentials from being forwarded in cleartext to 409 // UI. User/shared credentials are not stored separately, so they cannot 410 // leak here. 411 bool is_credential = onc::FieldIsCredential(*signature_, key); 412 if (!is_credential) { 413 if (values.user_policy) { 414 augmented_value->SetWithoutPathExpansion( 415 ::onc::kAugmentationUserPolicy, values.user_policy->DeepCopy()); 416 } 417 if (values.device_policy) { 418 augmented_value->SetWithoutPathExpansion( 419 ::onc::kAugmentationDevicePolicy, 420 values.device_policy->DeepCopy()); 421 } 422 } 423 if (values.user_setting) { 424 augmented_value->SetWithoutPathExpansion( 425 ::onc::kAugmentationUserSetting, values.user_setting->DeepCopy()); 426 } 427 if (values.shared_setting) { 428 augmented_value->SetWithoutPathExpansion( 429 ::onc::kAugmentationSharedSetting, 430 values.shared_setting->DeepCopy()); 431 } 432 if (HasUserPolicy() && values.user_editable) { 433 augmented_value->SetBooleanWithoutPathExpansion( 434 ::onc::kAugmentationUserEditable, true); 435 } 436 if (HasDevicePolicy() && values.device_editable) { 437 augmented_value->SetBooleanWithoutPathExpansion( 438 ::onc::kAugmentationDeviceEditable, true); 439 } 440 if (augmented_value->empty()) 441 augmented_value.reset(); 442 return augmented_value.PassAs<base::Value>(); 443 } 444 445 // MergeListOfDictionaries override. 446 virtual DictionaryPtr MergeNestedDictionaries( 447 const std::string& key, 448 const DictPtrs &dicts) OVERRIDE { 449 DictionaryPtr result; 450 if (signature_) { 451 const OncValueSignature* enclosing_signature = signature_; 452 signature_ = NULL; 453 454 const OncFieldSignature* field = 455 GetFieldSignature(*enclosing_signature, key); 456 if (field) 457 signature_ = field->value_signature; 458 result = MergeToEffective::MergeNestedDictionaries(key, dicts); 459 460 signature_ = enclosing_signature; 461 } else { 462 result = MergeToEffective::MergeNestedDictionaries(key, dicts); 463 } 464 return result.Pass(); 465 } 466 467 private: 468 const OncValueSignature* signature_; 469 DISALLOW_COPY_AND_ASSIGN(MergeToAugmented); 470 }; 471 472 } // namespace 473 474 DictionaryPtr MergeSettingsAndPoliciesToEffective( 475 const base::DictionaryValue* user_policy, 476 const base::DictionaryValue* device_policy, 477 const base::DictionaryValue* user_settings, 478 const base::DictionaryValue* shared_settings) { 479 MergeToEffective merger; 480 return merger.MergeDictionaries( 481 user_policy, device_policy, user_settings, shared_settings, NULL); 482 } 483 484 DictionaryPtr MergeSettingsAndPoliciesToAugmented( 485 const OncValueSignature& signature, 486 const base::DictionaryValue* user_policy, 487 const base::DictionaryValue* device_policy, 488 const base::DictionaryValue* user_settings, 489 const base::DictionaryValue* shared_settings, 490 const base::DictionaryValue* active_settings) { 491 MergeToAugmented merger; 492 return merger.MergeDictionaries( 493 signature, user_policy, device_policy, user_settings, shared_settings, 494 active_settings); 495 } 496 497 } // namespace onc 498 } // namespace chromeos 499