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_validator.h" 6 7 #include <string> 8 #include <utility> 9 10 #include "base/logging.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/values.h" 13 #include "chromeos/network/onc/onc_signature.h" 14 #include "chromeos/network/onc/onc_test_utils.h" 15 #include "chromeos/network/onc/onc_utils.h" 16 #include "components/onc/onc_constants.h" 17 #include "testing/gtest/include/gtest/gtest.h" 18 19 namespace chromeos { 20 namespace onc { 21 22 class ONCValidatorTest : public ::testing::Test { 23 public: 24 // Validate |onc_object| with the given |signature|. The object is considered 25 // to be managed if |managed_onc| is true. A strict validator is used if 26 // |strict| is true. |onc_object| and the resulting repaired object of the 27 // validation is stored, so that expectations can be checked afterwards using 28 // one of the Expect* functions below. 29 void Validate(bool strict, 30 scoped_ptr<base::DictionaryValue> onc_object, 31 const OncValueSignature* signature, 32 bool managed_onc, 33 ::onc::ONCSource onc_source) { 34 scoped_ptr<Validator> validator; 35 if (strict) { 36 // Create a strict validator that complains about every error. 37 validator.reset(new Validator(true, true, true, managed_onc)); 38 } else { 39 // Create a liberal validator that ignores or repairs non-critical errors. 40 validator.reset(new Validator(false, false, false, managed_onc)); 41 } 42 validator->SetOncSource(onc_source); 43 original_object_ = onc_object.Pass(); 44 repaired_object_ = validator->ValidateAndRepairObject(signature, 45 *original_object_, 46 &validation_result_); 47 } 48 49 void ExpectValid() { 50 EXPECT_EQ(Validator::VALID, validation_result_); 51 EXPECT_TRUE(test_utils::Equals(original_object_.get(), 52 repaired_object_.get())); 53 } 54 55 void ExpectRepairWithWarnings( 56 const base::DictionaryValue& expected_repaired) { 57 EXPECT_EQ(Validator::VALID_WITH_WARNINGS, validation_result_); 58 EXPECT_TRUE(test_utils::Equals(&expected_repaired, repaired_object_.get())); 59 } 60 61 void ExpectInvalid() { 62 EXPECT_EQ(Validator::INVALID, validation_result_); 63 EXPECT_EQ(NULL, repaired_object_.get()); 64 } 65 66 private: 67 Validator::Result validation_result_; 68 scoped_ptr<const base::DictionaryValue> original_object_; 69 scoped_ptr<const base::DictionaryValue> repaired_object_; 70 }; 71 72 namespace { 73 74 struct OncParams { 75 // |location_of_object| is a string to identify the object to be tested. It 76 // may be used as a filename or as a dictionary key. 77 OncParams(const std::string& location_of_object, 78 const OncValueSignature* onc_signature, 79 bool is_managed_onc, 80 ::onc::ONCSource onc_source = ::onc::ONC_SOURCE_NONE) 81 : location(location_of_object), 82 signature(onc_signature), 83 is_managed(is_managed_onc), 84 onc_source(onc_source) { 85 } 86 87 std::string location; 88 const OncValueSignature* signature; 89 bool is_managed; 90 ::onc::ONCSource onc_source; 91 }; 92 93 ::std::ostream& operator<<(::std::ostream& os, const OncParams& onc) { 94 return os << "(" << onc.location << ", " << onc.signature << ", " 95 << (onc.is_managed ? "managed" : "unmanaged") << ", " 96 << GetSourceAsString(onc.onc_source) << ")"; 97 } 98 99 } // namespace 100 101 // Ensure that the constant |kEmptyUnencryptedConfiguration| describes a valid 102 // ONC toplevel object. 103 TEST_F(ONCValidatorTest, EmptyUnencryptedConfiguration) { 104 Validate(true, ReadDictionaryFromJson(kEmptyUnencryptedConfiguration), 105 &kToplevelConfigurationSignature, false, ::onc::ONC_SOURCE_NONE); 106 ExpectValid(); 107 } 108 109 // This test case is about validating valid ONC objects without any errors. Both 110 // the strict and the liberal validator accept the object. 111 class ONCValidatorValidTest : public ONCValidatorTest, 112 public ::testing::WithParamInterface<OncParams> { 113 }; 114 115 TEST_P(ONCValidatorValidTest, StrictValidationValid) { 116 OncParams onc = GetParam(); 117 Validate(true, test_utils::ReadTestDictionary(onc.location), onc.signature, 118 onc.is_managed, onc.onc_source); 119 ExpectValid(); 120 } 121 122 TEST_P(ONCValidatorValidTest, LiberalValidationValid) { 123 OncParams onc = GetParam(); 124 Validate(false, test_utils::ReadTestDictionary(onc.location), onc.signature, 125 onc.is_managed, onc.onc_source); 126 ExpectValid(); 127 } 128 129 // The parameters are: 130 // OncParams(string: Filename of a ONC file that is to be validated, 131 // OncValueSignature: signature of that ONC, 132 // bool: true if the ONC is managed). 133 INSTANTIATE_TEST_CASE_P( 134 ONCValidatorValidTest, 135 ONCValidatorValidTest, 136 ::testing::Values( 137 OncParams("managed_toplevel1.onc", 138 &kToplevelConfigurationSignature, 139 true), 140 OncParams("managed_toplevel2.onc", 141 &kToplevelConfigurationSignature, 142 true), 143 OncParams("managed_toplevel_with_global_config.onc", 144 &kToplevelConfigurationSignature, 145 true), 146 // Check that at least one configuration is accepted for 147 // device policies. 148 OncParams("managed_toplevel_wifi_peap.onc", 149 &kToplevelConfigurationSignature, 150 true, 151 ::onc::ONC_SOURCE_DEVICE_POLICY), 152 OncParams("managed_toplevel_l2tpipsec.onc", 153 &kToplevelConfigurationSignature, 154 true), 155 OncParams("toplevel_wifi_wpa_psk.onc", 156 &kToplevelConfigurationSignature, 157 false), 158 OncParams("toplevel_wifi_wep_proxy.onc", 159 &kToplevelConfigurationSignature, 160 false), 161 OncParams("toplevel_wifi_leap.onc", 162 &kToplevelConfigurationSignature, 163 false), 164 OncParams("toplevel_wifi_eap_clientcert_with_cert_pems.onc", 165 &kToplevelConfigurationSignature, 166 false), 167 OncParams("toplevel_wifi_remove.onc", 168 &kToplevelConfigurationSignature, 169 false), 170 OncParams("toplevel_wifi_open.onc", 171 &kToplevelConfigurationSignature, 172 false), 173 OncParams("toplevel_openvpn_clientcert_with_cert_pems.onc", 174 &kToplevelConfigurationSignature, 175 false), 176 OncParams("toplevel_empty.onc", 177 &kToplevelConfigurationSignature, 178 false), 179 OncParams("toplevel_only_global_config.onc", 180 &kToplevelConfigurationSignature, 181 true), 182 OncParams("encrypted.onc", &kToplevelConfigurationSignature, true), 183 OncParams("managed_vpn.onc", &kNetworkConfigurationSignature, true), 184 OncParams("ethernet.onc", &kNetworkConfigurationSignature, true), 185 OncParams("ethernet_with_eap.onc", 186 &kNetworkConfigurationSignature, 187 true), 188 OncParams("translation_of_shill_ethernet_with_ipconfig.onc", 189 &kNetworkConfigurationSignature, 190 true), 191 OncParams("translation_of_shill_wifi_with_state.onc", 192 &kNetworkWithStateSignature, 193 false), 194 OncParams("valid_openvpn_with_cert_pems.onc", 195 &kNetworkConfigurationSignature, 196 false))); 197 198 namespace { 199 200 struct RepairParams { 201 // Both arguments are strings to identify the object that is expected as the 202 // validation result. They may either be used as filenames or as dictionary 203 // keys. 204 RepairParams(std::string strict_repaired, 205 std::string liberal_repaired) 206 : location_of_strict_repaired(strict_repaired), 207 location_of_liberal_repaired(liberal_repaired) { 208 } 209 210 std::string location_of_strict_repaired; 211 std::string location_of_liberal_repaired; 212 }; 213 214 ::std::ostream& operator<<(::std::ostream& os, const RepairParams& rp) { 215 return os << "(" << rp.location_of_strict_repaired << ", " 216 << rp.location_of_liberal_repaired << ")"; 217 } 218 219 } // namespace 220 221 // This test case is about validating ONC objects that contain errors which can 222 // be repaired (then the errors count as warnings). If a location of the 223 // expected repaired object is given, then it is checked that the validator 224 // (either strict or liberal) returns this repaired object and the result is 225 // VALID_WITH_WARNINGS. If the location is the empty string, then it is expected 226 // that the validator returns NULL and the result INVALID. 227 class ONCValidatorTestRepairable 228 : public ONCValidatorTest, 229 public ::testing::WithParamInterface<std::pair<OncParams, 230 RepairParams> > { 231 public: 232 // Load the common test data and return the dictionary at the field with 233 // name |name|. 234 scoped_ptr<base::DictionaryValue> GetDictionaryFromTestFile( 235 const std::string &name) { 236 scoped_ptr<const base::DictionaryValue> dict( 237 test_utils::ReadTestDictionary("invalid_settings_with_repairs.json")); 238 const base::DictionaryValue* onc_object = NULL; 239 CHECK(dict->GetDictionary(name, &onc_object)); 240 return make_scoped_ptr(onc_object->DeepCopy()); 241 } 242 }; 243 244 TEST_P(ONCValidatorTestRepairable, StrictValidation) { 245 OncParams onc = GetParam().first; 246 Validate(true, GetDictionaryFromTestFile(onc.location), onc.signature, 247 onc.is_managed, onc.onc_source); 248 std::string location_of_repaired = 249 GetParam().second.location_of_strict_repaired; 250 if (location_of_repaired.empty()) 251 ExpectInvalid(); 252 else 253 ExpectRepairWithWarnings(*GetDictionaryFromTestFile(location_of_repaired)); 254 } 255 256 TEST_P(ONCValidatorTestRepairable, LiberalValidation) { 257 OncParams onc = GetParam().first; 258 Validate(false, GetDictionaryFromTestFile(onc.location), onc.signature, 259 onc.is_managed, onc.onc_source); 260 std::string location_of_repaired = 261 GetParam().second.location_of_liberal_repaired; 262 if (location_of_repaired.empty()) 263 ExpectInvalid(); 264 else 265 ExpectRepairWithWarnings(*GetDictionaryFromTestFile(location_of_repaired)); 266 } 267 268 // The parameters for all test case instantations below are: 269 // OncParams(string: A fieldname in the dictionary from the file 270 // "invalid_settings_with_repairs.json". That nested 271 // dictionary will be tested. 272 // OncValueSignature: signature of that ONC, 273 // bool: true if the ONC is managed). 274 // RepairParams(string: A fieldname in the dictionary from the file 275 // "invalid_settings_with_repairs.json". That nested 276 // dictionary is the expected result from strict 277 // validation, 278 // string: A fieldname in the dictionary from the file 279 // "invalid_settings_with_repairs.json". That nested 280 // dictionary is the expected result from liberal 281 // validation). 282 283 // Strict validator returns INVALID. Liberal validator repairs. 284 INSTANTIATE_TEST_CASE_P( 285 StrictInvalidLiberalRepair, 286 ONCValidatorTestRepairable, 287 ::testing::Values( 288 std::make_pair(OncParams("network-unknown-fieldname", 289 &kNetworkConfigurationSignature, 290 false), 291 RepairParams("", "network-repaired")), 292 std::make_pair(OncParams("managed-network-unknown-fieldname", 293 &kNetworkConfigurationSignature, 294 true), 295 RepairParams("", "managed-network-repaired")), 296 std::make_pair(OncParams("managed-network-unknown-recommended", 297 &kNetworkConfigurationSignature, 298 true), 299 RepairParams("", "managed-network-repaired")), 300 std::make_pair(OncParams("managed-network-dict-recommended", 301 &kNetworkConfigurationSignature, 302 true), 303 RepairParams("", "managed-network-repaired")), 304 std::make_pair(OncParams("network-missing-required", 305 &kNetworkConfigurationSignature, 306 false), 307 RepairParams("", "network-missing-required")), 308 std::make_pair(OncParams("managed-network-missing-required", 309 &kNetworkConfigurationSignature, 310 true), 311 RepairParams("", "managed-network-missing-required")), 312 // Ensure that state values from Shill aren't accepted as 313 // configuration. 314 std::make_pair(OncParams("network-state-field", 315 &kNetworkConfigurationSignature, 316 false), 317 RepairParams("", "network-repaired")), 318 std::make_pair(OncParams("network-nested-state-field", 319 &kNetworkConfigurationSignature, 320 false), 321 RepairParams("", "network-nested-state-field-repaired")), 322 std::make_pair(OncParams("openvpn-missing-verify-x509-name", 323 &kNetworkConfigurationSignature, false), 324 RepairParams("", "openvpn-missing-verify-x509-name")), 325 std::make_pair(OncParams("ipsec-with-client-cert-missing-cacert", 326 &kIPsecSignature, 327 false), 328 RepairParams("", 329 "ipsec-with-client-cert-missing-cacert")), 330 std::make_pair(OncParams("toplevel-with-repairable-networks", 331 &kToplevelConfigurationSignature, 332 false, 333 ::onc::ONC_SOURCE_DEVICE_POLICY), 334 RepairParams("", "toplevel-with-repaired-networks")))); 335 336 // Strict and liberal validator repair identically. 337 INSTANTIATE_TEST_CASE_P( 338 StrictAndLiberalRepairIdentically, 339 ONCValidatorTestRepairable, 340 ::testing::Values( 341 std::make_pair(OncParams("toplevel-invalid-network", 342 &kToplevelConfigurationSignature, 343 false), 344 RepairParams("toplevel-repaired", 345 "toplevel-repaired")), 346 std::make_pair(OncParams("duplicate-network-guid", 347 &kToplevelConfigurationSignature, 348 false), 349 RepairParams("repaired-duplicate-network-guid", 350 "repaired-duplicate-network-guid")), 351 std::make_pair(OncParams("duplicate-cert-guid", 352 &kToplevelConfigurationSignature, 353 false), 354 RepairParams("repaired-duplicate-cert-guid", 355 "repaired-duplicate-cert-guid")), 356 std::make_pair(OncParams("toplevel-invalid-network", 357 &kToplevelConfigurationSignature, 358 true), 359 RepairParams("toplevel-repaired", 360 "toplevel-repaired")), 361 // Ignore recommended arrays in unmanaged ONC. 362 std::make_pair(OncParams("network-with-illegal-recommended", 363 &kNetworkConfigurationSignature, 364 false), 365 RepairParams("network-repaired", "network-repaired")), 366 std::make_pair(OncParams("toplevel-with-vpn", 367 &kToplevelConfigurationSignature, 368 false, 369 ::onc::ONC_SOURCE_DEVICE_POLICY), 370 RepairParams("toplevel-empty", "toplevel-empty")), 371 std::make_pair(OncParams("toplevel-with-server-and-ca-cert", 372 &kToplevelConfigurationSignature, 373 true, 374 ::onc::ONC_SOURCE_DEVICE_POLICY), 375 RepairParams("toplevel-server-and-ca-cert-dropped", 376 "toplevel-server-and-ca-cert-dropped")))); 377 378 // Strict and liberal validator both repair, but with different results. 379 INSTANTIATE_TEST_CASE_P( 380 StrictAndLiberalRepairDifferently, 381 ONCValidatorTestRepairable, 382 ::testing::Values( 383 std::make_pair(OncParams("toplevel-with-nested-warning", 384 &kToplevelConfigurationSignature, 385 false), 386 RepairParams("toplevel-empty", "toplevel-repaired")))); 387 388 // Strict and liberal validator return both INVALID. 389 INSTANTIATE_TEST_CASE_P( 390 StrictAndLiberalInvalid, 391 ONCValidatorTestRepairable, 392 ::testing::Values( 393 std::make_pair(OncParams("network-unknown-value", 394 &kNetworkConfigurationSignature, false), 395 RepairParams("", "")), 396 std::make_pair(OncParams("managed-network-unknown-value", 397 &kNetworkConfigurationSignature, true), 398 RepairParams("", "")), 399 std::make_pair(OncParams("network-value-out-of-range", 400 &kNetworkConfigurationSignature, false), 401 RepairParams("", "")), 402 std::make_pair(OncParams("ipsec-with-psk-and-cacert", 403 &kIPsecSignature, false), 404 RepairParams("", "")), 405 std::make_pair(OncParams("ipsec-with-empty-cacertrefs", 406 &kIPsecSignature, false), 407 RepairParams("", "")), 408 std::make_pair(OncParams("ipsec-with-servercaref-and-servercarefs", 409 &kIPsecSignature, false), 410 RepairParams("", "")), 411 std::make_pair(OncParams("openvpn-with-servercaref-and-servercarefs", 412 &kOpenVPNSignature, false), 413 RepairParams("", "")), 414 std::make_pair(OncParams("eap-with-servercaref-and-servercarefs", 415 &kEAPSignature, false), 416 RepairParams("", "")), 417 std::make_pair(OncParams("managed-network-value-out-of-range", 418 &kNetworkConfigurationSignature, true), 419 RepairParams("", "")), 420 std::make_pair(OncParams("network-wrong-type", 421 &kNetworkConfigurationSignature, false), 422 RepairParams("", "")), 423 std::make_pair(OncParams("managed-network-wrong-type", 424 &kNetworkConfigurationSignature, true), 425 RepairParams("", "")), 426 std::make_pair(OncParams("network-with-client-cert-pattern", 427 &kNetworkConfigurationSignature, true, 428 ::onc::ONC_SOURCE_DEVICE_POLICY), 429 RepairParams("", "")), 430 std::make_pair(OncParams("openvpn-invalid-verify-x509-type", 431 &kNetworkConfigurationSignature, false), 432 RepairParams("", "")) 433 )); 434 435 } // namespace onc 436 } // namespace chromeos 437