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_utils.h" 6 7 #include "base/base64.h" 8 #include "base/json/json_reader.h" 9 #include "base/logging.h" 10 #include "base/metrics/histogram.h" 11 #include "base/strings/string_util.h" 12 #include "base/values.h" 13 #include "chromeos/network/network_event_log.h" 14 #include "chromeos/network/onc/onc_mapper.h" 15 #include "chromeos/network/onc/onc_signature.h" 16 #include "chromeos/network/onc/onc_utils.h" 17 #include "chromeos/network/onc/onc_validator.h" 18 #include "crypto/encryptor.h" 19 #include "crypto/hmac.h" 20 #include "crypto/symmetric_key.h" 21 #include "net/cert/pem_tokenizer.h" 22 #include "net/cert/x509_certificate.h" 23 24 #define ONC_LOG_WARNING(message) NET_LOG_WARNING("ONC", message) 25 #define ONC_LOG_ERROR(message) NET_LOG_ERROR("ONC", message) 26 27 namespace chromeos { 28 namespace onc { 29 30 namespace { 31 32 const char kUnableToDecrypt[] = "Unable to decrypt encrypted ONC"; 33 const char kUnableToDecode[] = "Unable to decode encrypted ONC"; 34 35 } // namespace 36 37 const char kEmptyUnencryptedConfiguration[] = 38 "{\"Type\":\"UnencryptedConfiguration\",\"NetworkConfigurations\":[]," 39 "\"Certificates\":[]}"; 40 41 scoped_ptr<base::DictionaryValue> ReadDictionaryFromJson( 42 const std::string& json) { 43 std::string error; 44 base::Value* root = base::JSONReader::ReadAndReturnError( 45 json, base::JSON_ALLOW_TRAILING_COMMAS, NULL, &error); 46 47 base::DictionaryValue* dict_ptr = NULL; 48 if (!root || !root->GetAsDictionary(&dict_ptr)) { 49 ONC_LOG_ERROR("Invalid JSON Dictionary: " + error); 50 delete root; 51 } 52 53 return make_scoped_ptr(dict_ptr); 54 } 55 56 scoped_ptr<base::DictionaryValue> Decrypt(const std::string& passphrase, 57 const base::DictionaryValue& root) { 58 const int kKeySizeInBits = 256; 59 const int kMaxIterationCount = 500000; 60 std::string onc_type; 61 std::string initial_vector; 62 std::string salt; 63 std::string cipher; 64 std::string stretch_method; 65 std::string hmac_method; 66 std::string hmac; 67 int iterations; 68 std::string ciphertext; 69 70 if (!root.GetString(encrypted::kCiphertext, &ciphertext) || 71 !root.GetString(encrypted::kCipher, &cipher) || 72 !root.GetString(encrypted::kHMAC, &hmac) || 73 !root.GetString(encrypted::kHMACMethod, &hmac_method) || 74 !root.GetString(encrypted::kIV, &initial_vector) || 75 !root.GetInteger(encrypted::kIterations, &iterations) || 76 !root.GetString(encrypted::kSalt, &salt) || 77 !root.GetString(encrypted::kStretch, &stretch_method) || 78 !root.GetString(toplevel_config::kType, &onc_type) || 79 onc_type != toplevel_config::kEncryptedConfiguration) { 80 81 ONC_LOG_ERROR("Encrypted ONC malformed."); 82 return scoped_ptr<base::DictionaryValue>(); 83 } 84 85 if (hmac_method != encrypted::kSHA1 || 86 cipher != encrypted::kAES256 || 87 stretch_method != encrypted::kPBKDF2) { 88 ONC_LOG_ERROR("Encrypted ONC unsupported encryption scheme."); 89 return scoped_ptr<base::DictionaryValue>(); 90 } 91 92 // Make sure iterations != 0, since that's not valid. 93 if (iterations == 0) { 94 ONC_LOG_ERROR(kUnableToDecrypt); 95 return scoped_ptr<base::DictionaryValue>(); 96 } 97 98 // Simply a sanity check to make sure we can't lock up the machine 99 // for too long with a huge number (or a negative number). 100 if (iterations < 0 || iterations > kMaxIterationCount) { 101 ONC_LOG_ERROR("Too many iterations in encrypted ONC"); 102 return scoped_ptr<base::DictionaryValue>(); 103 } 104 105 if (!base::Base64Decode(salt, &salt)) { 106 ONC_LOG_ERROR(kUnableToDecode); 107 return scoped_ptr<base::DictionaryValue>(); 108 } 109 110 scoped_ptr<crypto::SymmetricKey> key( 111 crypto::SymmetricKey::DeriveKeyFromPassword(crypto::SymmetricKey::AES, 112 passphrase, 113 salt, 114 iterations, 115 kKeySizeInBits)); 116 117 if (!base::Base64Decode(initial_vector, &initial_vector)) { 118 ONC_LOG_ERROR(kUnableToDecode); 119 return scoped_ptr<base::DictionaryValue>(); 120 } 121 if (!base::Base64Decode(ciphertext, &ciphertext)) { 122 ONC_LOG_ERROR(kUnableToDecode); 123 return scoped_ptr<base::DictionaryValue>(); 124 } 125 if (!base::Base64Decode(hmac, &hmac)) { 126 ONC_LOG_ERROR(kUnableToDecode); 127 return scoped_ptr<base::DictionaryValue>(); 128 } 129 130 crypto::HMAC hmac_verifier(crypto::HMAC::SHA1); 131 if (!hmac_verifier.Init(key.get()) || 132 !hmac_verifier.Verify(ciphertext, hmac)) { 133 ONC_LOG_ERROR(kUnableToDecrypt); 134 return scoped_ptr<base::DictionaryValue>(); 135 } 136 137 crypto::Encryptor decryptor; 138 if (!decryptor.Init(key.get(), crypto::Encryptor::CBC, initial_vector)) { 139 ONC_LOG_ERROR(kUnableToDecrypt); 140 return scoped_ptr<base::DictionaryValue>(); 141 } 142 143 std::string plaintext; 144 if (!decryptor.Decrypt(ciphertext, &plaintext)) { 145 ONC_LOG_ERROR(kUnableToDecrypt); 146 return scoped_ptr<base::DictionaryValue>(); 147 } 148 149 scoped_ptr<base::DictionaryValue> new_root = 150 ReadDictionaryFromJson(plaintext); 151 if (new_root.get() == NULL) { 152 ONC_LOG_ERROR("Property dictionary malformed."); 153 return scoped_ptr<base::DictionaryValue>(); 154 } 155 156 return new_root.Pass(); 157 } 158 159 std::string GetSourceAsString(ONCSource source) { 160 switch (source) { 161 case ONC_SOURCE_DEVICE_POLICY: 162 return "device policy"; 163 case ONC_SOURCE_USER_POLICY: 164 return "user policy"; 165 case ONC_SOURCE_NONE: 166 return "none"; 167 case ONC_SOURCE_USER_IMPORT: 168 return "user import"; 169 } 170 NOTREACHED() << "unknown ONC source " << source; 171 return "unknown"; 172 } 173 174 void ExpandField(const std::string fieldname, 175 const StringSubstitution& substitution, 176 base::DictionaryValue* onc_object) { 177 std::string user_string; 178 if (!onc_object->GetStringWithoutPathExpansion(fieldname, &user_string)) 179 return; 180 181 std::string login_id; 182 if (substitution.GetSubstitute(substitutes::kLoginIDField, &login_id)) { 183 ReplaceSubstringsAfterOffset(&user_string, 0, 184 substitutes::kLoginIDField, 185 login_id); 186 } 187 188 std::string email; 189 if (substitution.GetSubstitute(substitutes::kEmailField, &email)) { 190 ReplaceSubstringsAfterOffset(&user_string, 0, 191 substitutes::kEmailField, 192 email); 193 } 194 195 onc_object->SetStringWithoutPathExpansion(fieldname, user_string); 196 } 197 198 void ExpandStringsInOncObject( 199 const OncValueSignature& signature, 200 const StringSubstitution& substitution, 201 base::DictionaryValue* onc_object) { 202 if (&signature == &kEAPSignature) { 203 ExpandField(eap::kAnonymousIdentity, substitution, onc_object); 204 ExpandField(eap::kIdentity, substitution, onc_object); 205 } else if (&signature == &kL2TPSignature || 206 &signature == &kOpenVPNSignature) { 207 ExpandField(vpn::kUsername, substitution, onc_object); 208 } 209 210 // Recurse into nested objects. 211 for (base::DictionaryValue::Iterator it(*onc_object); !it.IsAtEnd(); 212 it.Advance()) { 213 base::DictionaryValue* inner_object = NULL; 214 if (!onc_object->GetDictionaryWithoutPathExpansion(it.key(), &inner_object)) 215 continue; 216 217 const OncFieldSignature* field_signature = 218 GetFieldSignature(signature, it.key()); 219 if (!field_signature) 220 continue; 221 222 ExpandStringsInOncObject(*field_signature->value_signature, 223 substitution, inner_object); 224 } 225 } 226 227 void ExpandStringsInNetworks(const StringSubstitution& substitution, 228 base::ListValue* network_configs) { 229 for (base::ListValue::iterator it = network_configs->begin(); 230 it != network_configs->end(); ++it) { 231 base::DictionaryValue* network = NULL; 232 (*it)->GetAsDictionary(&network); 233 DCHECK(network); 234 ExpandStringsInOncObject( 235 kNetworkConfigurationSignature, substitution, network); 236 } 237 } 238 239 namespace { 240 241 class OncMaskValues : public Mapper { 242 public: 243 static scoped_ptr<base::DictionaryValue> Mask( 244 const OncValueSignature& signature, 245 const base::DictionaryValue& onc_object, 246 const std::string& mask) { 247 OncMaskValues masker(mask); 248 bool unused_error; 249 return masker.MapObject(signature, onc_object, &unused_error); 250 } 251 252 protected: 253 explicit OncMaskValues(const std::string& mask) 254 : mask_(mask) { 255 } 256 257 virtual scoped_ptr<base::Value> MapField( 258 const std::string& field_name, 259 const OncValueSignature& object_signature, 260 const base::Value& onc_value, 261 bool* found_unknown_field, 262 bool* error) OVERRIDE { 263 if (FieldIsCredential(object_signature, field_name)) { 264 return scoped_ptr<base::Value>(new base::StringValue(mask_)); 265 } else { 266 return Mapper::MapField(field_name, object_signature, onc_value, 267 found_unknown_field, error); 268 } 269 } 270 271 // Mask to insert in place of the sensitive values. 272 std::string mask_; 273 }; 274 275 } // namespace 276 277 scoped_ptr<base::DictionaryValue> MaskCredentialsInOncObject( 278 const OncValueSignature& signature, 279 const base::DictionaryValue& onc_object, 280 const std::string& mask) { 281 return OncMaskValues::Mask(signature, onc_object, mask); 282 } 283 284 namespace { 285 286 std::string DecodePEM(const std::string& pem_encoded) { 287 // The PEM block header used for DER certificates 288 const char kCertificateHeader[] = "CERTIFICATE"; 289 290 // This is an older PEM marker for DER certificates. 291 const char kX509CertificateHeader[] = "X509 CERTIFICATE"; 292 293 std::vector<std::string> pem_headers; 294 pem_headers.push_back(kCertificateHeader); 295 pem_headers.push_back(kX509CertificateHeader); 296 297 net::PEMTokenizer pem_tokenizer(pem_encoded, pem_headers); 298 std::string decoded; 299 if (pem_tokenizer.GetNext()) { 300 decoded = pem_tokenizer.data(); 301 } else { 302 // If we failed to read the data as a PEM file, then try plain base64 decode 303 // in case the PEM marker strings are missing. For this to work, there has 304 // to be no white space, and it has to only contain the base64-encoded data. 305 if (!base::Base64Decode(pem_encoded, &decoded)) { 306 LOG(ERROR) << "Unable to base64 decode X509 data: " << pem_encoded; 307 return std::string(); 308 } 309 } 310 return decoded; 311 } 312 313 CertPEMsByGUIDMap GetServerAndCACertsByGUID( 314 const base::ListValue& certificates) { 315 CertPEMsByGUIDMap certs_by_guid; 316 for (base::ListValue::const_iterator it = certificates.begin(); 317 it != certificates.end(); ++it) { 318 base::DictionaryValue* cert = NULL; 319 (*it)->GetAsDictionary(&cert); 320 321 std::string guid; 322 cert->GetStringWithoutPathExpansion(certificate::kGUID, &guid); 323 std::string cert_type; 324 cert->GetStringWithoutPathExpansion(certificate::kType, &cert_type); 325 if (cert_type != certificate::kServer && 326 cert_type != certificate::kAuthority) { 327 continue; 328 } 329 std::string x509_data; 330 cert->GetStringWithoutPathExpansion(certificate::kX509, &x509_data); 331 332 std::string der = DecodePEM(x509_data); 333 std::string pem; 334 if (der.empty() || !net::X509Certificate::GetPEMEncodedFromDER(der, &pem)) { 335 LOG(ERROR) << "Certificate with GUID " << guid 336 << " is not in PEM encoding."; 337 continue; 338 } 339 certs_by_guid[guid] = pem; 340 } 341 342 return certs_by_guid; 343 } 344 345 } // namespace 346 347 bool ParseAndValidateOncForImport(const std::string& onc_blob, 348 ONCSource onc_source, 349 const std::string& passphrase, 350 base::ListValue* network_configs, 351 base::ListValue* certificates) { 352 certificates->Clear(); 353 network_configs->Clear(); 354 if (onc_blob.empty()) 355 return true; 356 357 scoped_ptr<base::DictionaryValue> toplevel_onc = 358 ReadDictionaryFromJson(onc_blob); 359 if (toplevel_onc.get() == NULL) { 360 LOG(ERROR) << "ONC loaded from " << GetSourceAsString(onc_source) 361 << " is not a valid JSON dictionary."; 362 return false; 363 } 364 365 // Check and see if this is an encrypted ONC file. If so, decrypt it. 366 std::string onc_type; 367 toplevel_onc->GetStringWithoutPathExpansion(toplevel_config::kType, 368 &onc_type); 369 if (onc_type == toplevel_config::kEncryptedConfiguration) { 370 toplevel_onc = Decrypt(passphrase, *toplevel_onc); 371 if (toplevel_onc.get() == NULL) { 372 LOG(ERROR) << "Couldn't decrypt the ONC from " 373 << GetSourceAsString(onc_source); 374 return false; 375 } 376 } 377 378 bool from_policy = (onc_source == ONC_SOURCE_USER_POLICY || 379 onc_source == ONC_SOURCE_DEVICE_POLICY); 380 381 // Validate the ONC dictionary. We are liberal and ignore unknown field 382 // names and ignore invalid field names in kRecommended arrays. 383 Validator validator(false, // Ignore unknown fields. 384 false, // Ignore invalid recommended field names. 385 true, // Fail on missing fields. 386 from_policy); 387 validator.SetOncSource(onc_source); 388 389 Validator::Result validation_result; 390 toplevel_onc = validator.ValidateAndRepairObject( 391 &kToplevelConfigurationSignature, 392 *toplevel_onc, 393 &validation_result); 394 395 if (from_policy) { 396 UMA_HISTOGRAM_BOOLEAN("Enterprise.ONC.PolicyValidation", 397 validation_result == Validator::VALID); 398 } 399 400 bool success = true; 401 if (validation_result == Validator::VALID_WITH_WARNINGS) { 402 LOG(WARNING) << "ONC from " << GetSourceAsString(onc_source) 403 << " produced warnings."; 404 success = false; 405 } else if (validation_result == Validator::INVALID || toplevel_onc == NULL) { 406 LOG(ERROR) << "ONC from " << GetSourceAsString(onc_source) 407 << " is invalid and couldn't be repaired."; 408 return false; 409 } 410 411 base::ListValue* validated_certs = NULL; 412 if (toplevel_onc->GetListWithoutPathExpansion(toplevel_config::kCertificates, 413 &validated_certs)) { 414 certificates->Swap(validated_certs); 415 } 416 417 base::ListValue* validated_networks = NULL; 418 if (toplevel_onc->GetListWithoutPathExpansion( 419 toplevel_config::kNetworkConfigurations, &validated_networks)) { 420 CertPEMsByGUIDMap server_and_ca_certs = 421 GetServerAndCACertsByGUID(*certificates); 422 423 if (!ResolveServerCertRefsInNetworks(server_and_ca_certs, 424 validated_networks)) { 425 LOG(ERROR) << "Some certificate references in the ONC policy for source " 426 << GetSourceAsString(onc_source) << " could not be resolved."; 427 success = false; 428 } 429 430 ResolveServerCertRefsInNetworks(server_and_ca_certs, validated_networks); 431 network_configs->Swap(validated_networks); 432 } 433 434 return success; 435 } 436 437 scoped_refptr<net::X509Certificate> DecodePEMCertificate( 438 const std::string& pem_encoded) { 439 std::string decoded = DecodePEM(pem_encoded); 440 scoped_refptr<net::X509Certificate> cert = 441 net::X509Certificate::CreateFromBytes(decoded.data(), decoded.size()); 442 LOG_IF(ERROR, !cert.get()) << "Couldn't create certificate from X509 data: " 443 << decoded; 444 return cert; 445 } 446 447 namespace { 448 449 bool GUIDRefToPEMEncoding(const CertPEMsByGUIDMap& certs_by_guid, 450 const std::string& guid_ref, 451 std::string* pem_encoded) { 452 CertPEMsByGUIDMap::const_iterator it = certs_by_guid.find(guid_ref); 453 if (it == certs_by_guid.end()) { 454 LOG(ERROR) << "Couldn't resolve certificate reference " << guid_ref; 455 return false; 456 } 457 *pem_encoded = it->second; 458 if (pem_encoded->empty()) { 459 LOG(ERROR) << "Couldn't PEM-encode certificate with GUID " << guid_ref; 460 return false; 461 } 462 return true; 463 } 464 465 bool ResolveSingleCertRef(const CertPEMsByGUIDMap& certs_by_guid, 466 const std::string& key_guid_ref, 467 const std::string& key_pem, 468 base::DictionaryValue* onc_object) { 469 std::string guid_ref; 470 if (!onc_object->GetStringWithoutPathExpansion(key_guid_ref, &guid_ref)) 471 return true; 472 473 std::string pem_encoded; 474 if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded)) 475 return false; 476 477 onc_object->RemoveWithoutPathExpansion(key_guid_ref, NULL); 478 onc_object->SetStringWithoutPathExpansion(key_pem, pem_encoded); 479 return true; 480 } 481 482 bool ResolveCertRefList(const CertPEMsByGUIDMap& certs_by_guid, 483 const std::string& key_guid_ref_list, 484 const std::string& key_pem_list, 485 base::DictionaryValue* onc_object) { 486 const base::ListValue* guid_ref_list = NULL; 487 if (!onc_object->GetListWithoutPathExpansion(key_guid_ref_list, 488 &guid_ref_list)) { 489 return true; 490 } 491 492 scoped_ptr<base::ListValue> pem_list(new base::ListValue); 493 for (base::ListValue::const_iterator it = guid_ref_list->begin(); 494 it != guid_ref_list->end(); ++it) { 495 std::string guid_ref; 496 (*it)->GetAsString(&guid_ref); 497 498 std::string pem_encoded; 499 if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded)) 500 return false; 501 502 pem_list->AppendString(pem_encoded); 503 } 504 505 onc_object->RemoveWithoutPathExpansion(key_guid_ref_list, NULL); 506 onc_object->SetWithoutPathExpansion(key_pem_list, pem_list.release()); 507 return true; 508 } 509 510 bool ResolveSingleCertRefToList(const CertPEMsByGUIDMap& certs_by_guid, 511 const std::string& key_guid_ref, 512 const std::string& key_pem_list, 513 base::DictionaryValue* onc_object) { 514 std::string guid_ref; 515 if (!onc_object->GetStringWithoutPathExpansion(key_guid_ref, &guid_ref)) 516 return true; 517 518 std::string pem_encoded; 519 if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded)) 520 return false; 521 522 scoped_ptr<base::ListValue> pem_list(new base::ListValue); 523 pem_list->AppendString(pem_encoded); 524 onc_object->RemoveWithoutPathExpansion(key_guid_ref, NULL); 525 onc_object->SetWithoutPathExpansion(key_pem_list, pem_list.release()); 526 return true; 527 } 528 529 bool ResolveServerCertRefsInObject(const CertPEMsByGUIDMap& certs_by_guid, 530 const OncValueSignature& signature, 531 base::DictionaryValue* onc_object) { 532 if (&signature == &kCertificatePatternSignature) { 533 if (!ResolveCertRefList(certs_by_guid, certificate::kIssuerCARef, 534 certificate::kIssuerCAPEMs, onc_object)) { 535 return false; 536 } 537 } else if (&signature == &kEAPSignature) { 538 if (!ResolveSingleCertRefToList(certs_by_guid, eap::kServerCARef, 539 eap::kServerCAPEMs, onc_object)) { 540 return false; 541 } 542 } else if (&signature == &kIPsecSignature) { 543 if (!ResolveSingleCertRefToList(certs_by_guid, ipsec::kServerCARef, 544 ipsec::kServerCAPEMs, onc_object)) { 545 return false; 546 } 547 } else if (&signature == &kIPsecSignature || 548 &signature == &kOpenVPNSignature) { 549 if (!ResolveSingleCertRef(certs_by_guid, openvpn::kServerCertRef, 550 openvpn::kServerCertPEM, onc_object) || 551 !ResolveSingleCertRefToList(certs_by_guid, openvpn::kServerCARef, 552 openvpn::kServerCAPEMs, onc_object)) { 553 return false; 554 } 555 } 556 557 // Recurse into nested objects. 558 for (base::DictionaryValue::Iterator it(*onc_object); !it.IsAtEnd(); 559 it.Advance()) { 560 base::DictionaryValue* inner_object = NULL; 561 if (!onc_object->GetDictionaryWithoutPathExpansion(it.key(), &inner_object)) 562 continue; 563 564 const OncFieldSignature* field_signature = 565 GetFieldSignature(signature, it.key()); 566 if (!field_signature) 567 continue; 568 569 if (!ResolveServerCertRefsInObject(certs_by_guid, 570 *field_signature->value_signature, 571 inner_object)) { 572 return false; 573 } 574 } 575 return true; 576 } 577 578 } // namespace 579 580 bool ResolveServerCertRefsInNetworks(const CertPEMsByGUIDMap& certs_by_guid, 581 base::ListValue* network_configs) { 582 bool success = true; 583 for (base::ListValue::iterator it = network_configs->begin(); 584 it != network_configs->end(); ) { 585 base::DictionaryValue* network = NULL; 586 (*it)->GetAsDictionary(&network); 587 if (!ResolveServerCertRefsInNetwork(certs_by_guid, network)) { 588 std::string guid; 589 network->GetStringWithoutPathExpansion(network_config::kGUID, &guid); 590 // This might happen even with correct validation, if the referenced 591 // certificate couldn't be imported. 592 LOG(ERROR) << "Couldn't resolve some certificate reference of network " 593 << guid; 594 it = network_configs->Erase(it, NULL); 595 success = false; 596 continue; 597 } 598 ++it; 599 } 600 return success; 601 } 602 603 bool ResolveServerCertRefsInNetwork(const CertPEMsByGUIDMap& certs_by_guid, 604 base::DictionaryValue* network_config) { 605 return ResolveServerCertRefsInObject(certs_by_guid, 606 kNetworkConfigurationSignature, 607 network_config); 608 } 609 610 } // namespace onc 611 } // namespace chromeos 612