| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chromeos/network/onc/onc_utils.h" | 5 #include "chromeos/network/onc/onc_utils.h" |
| 6 | 6 |
| 7 #include "base/base64.h" | 7 #include "base/base64.h" |
| 8 #include "base/json/json_reader.h" | 8 #include "base/json/json_reader.h" |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
| 11 #include "base/strings/string_util.h" | 11 #include "base/strings/string_util.h" |
| 12 #include "base/values.h" | 12 #include "base/values.h" |
| 13 #include "chromeos/network/network_event_log.h" | 13 #include "chromeos/network/network_event_log.h" |
| 14 #include "chromeos/network/onc/onc_mapper.h" | 14 #include "chromeos/network/onc/onc_mapper.h" |
| 15 #include "chromeos/network/onc/onc_signature.h" | 15 #include "chromeos/network/onc/onc_signature.h" |
| 16 #include "chromeos/network/onc/onc_utils.h" | 16 #include "chromeos/network/onc/onc_utils.h" |
| 17 #include "chromeos/network/onc/onc_validator.h" | 17 #include "chromeos/network/onc/onc_validator.h" |
| 18 #include "crypto/encryptor.h" | 18 #include "crypto/encryptor.h" |
| 19 #include "crypto/hmac.h" | 19 #include "crypto/hmac.h" |
| 20 #include "crypto/symmetric_key.h" | 20 #include "crypto/symmetric_key.h" |
| 21 #include "net/cert/pem_tokenizer.h" | 21 #include "net/cert/pem_tokenizer.h" |
| 22 #include "net/cert/x509_certificate.h" |
| 22 | 23 |
| 23 #define ONC_LOG_WARNING(message) NET_LOG_WARNING("ONC", message) | 24 #define ONC_LOG_WARNING(message) NET_LOG_WARNING("ONC", message) |
| 24 #define ONC_LOG_ERROR(message) NET_LOG_ERROR("ONC", message) | 25 #define ONC_LOG_ERROR(message) NET_LOG_ERROR("ONC", message) |
| 25 | 26 |
| 26 namespace chromeos { | 27 namespace chromeos { |
| 27 namespace onc { | 28 namespace onc { |
| 28 | 29 |
| 29 namespace { | 30 namespace { |
| 30 | 31 |
| 31 const char kUnableToDecrypt[] = "Unable to decrypt encrypted ONC"; | 32 const char kUnableToDecrypt[] = "Unable to decrypt encrypted ONC"; |
| (...skipping 308 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 340 | 341 |
| 341 base::ListValue* validated_networks = NULL; | 342 base::ListValue* validated_networks = NULL; |
| 342 if (toplevel_onc->GetListWithoutPathExpansion( | 343 if (toplevel_onc->GetListWithoutPathExpansion( |
| 343 toplevel_config::kNetworkConfigurations, &validated_networks)) { | 344 toplevel_config::kNetworkConfigurations, &validated_networks)) { |
| 344 network_configs->Swap(validated_networks); | 345 network_configs->Swap(validated_networks); |
| 345 } | 346 } |
| 346 | 347 |
| 347 return success; | 348 return success; |
| 348 } | 349 } |
| 349 | 350 |
| 350 scoped_refptr<net::X509Certificate> DecodePEMCertificate( | 351 namespace { |
| 351 const std::string& pem_encoded, | 352 |
| 352 const std::string& nickname) { | 353 std::string DecodePEM(const std::string& pem_encoded) { |
| 353 // The PEM block header used for DER certificates | 354 // The PEM block header used for DER certificates |
| 354 static const char kCertificateHeader[] = "CERTIFICATE"; | 355 static const char kCertificateHeader[] = "CERTIFICATE"; |
| 355 // This is an older PEM marker for DER certificates. | 356 // This is an older PEM marker for DER certificates. |
| 356 static const char kX509CertificateHeader[] = "X509 CERTIFICATE"; | 357 static const char kX509CertificateHeader[] = "X509 CERTIFICATE"; |
| 357 | 358 |
| 358 std::vector<std::string> pem_headers; | 359 std::vector<std::string> pem_headers; |
| 359 pem_headers.push_back(kCertificateHeader); | 360 pem_headers.push_back(kCertificateHeader); |
| 360 pem_headers.push_back(kX509CertificateHeader); | 361 pem_headers.push_back(kX509CertificateHeader); |
| 361 | 362 |
| 362 net::PEMTokenizer pem_tokenizer(pem_encoded, pem_headers); | 363 net::PEMTokenizer pem_tokenizer(pem_encoded, pem_headers); |
| 363 std::string decoded; | 364 std::string decoded; |
| 364 if (pem_tokenizer.GetNext()) { | 365 if (pem_tokenizer.GetNext()) { |
| 365 decoded = pem_tokenizer.data(); | 366 decoded = pem_tokenizer.data(); |
| 366 } else { | 367 } else { |
| 367 // If we failed to read the data as a PEM file, then try plain base64 decode | 368 // If we failed to read the data as a PEM file, then try plain base64 decode |
| 368 // in case the PEM marker strings are missing. For this to work, there has | 369 // in case the PEM marker strings are missing. For this to work, there has |
| 369 // to be no white space, and it has to only contain the base64-encoded data. | 370 // to be no white space, and it has to only contain the base64-encoded data. |
| 370 if (!base::Base64Decode(pem_encoded, &decoded)) { | 371 if (!base::Base64Decode(pem_encoded, &decoded)) { |
| 371 LOG(ERROR) << "Unable to base64 decode X509 data: " << pem_encoded; | 372 LOG(ERROR) << "Unable to base64 decode X509 data: " << pem_encoded; |
| 372 return scoped_refptr<net::X509Certificate>(); | 373 return std::string(); |
| 373 } | 374 } |
| 374 } | 375 } |
| 376 return decoded; |
| 377 } |
| 375 | 378 |
| 379 } // namespace |
| 380 |
| 381 scoped_refptr<net::X509Certificate> DecodePEMCertificate( |
| 382 const std::string& pem_encoded) { |
| 383 std::string decoded = DecodePEM(pem_encoded); |
| 376 scoped_refptr<net::X509Certificate> cert = | 384 scoped_refptr<net::X509Certificate> cert = |
| 377 net::X509Certificate::CreateFromBytesWithNickname(decoded.data(), | 385 net::X509Certificate::CreateFromBytes(decoded.data(), decoded.size()); |
| 378 decoded.size(), | |
| 379 nickname.c_str()); | |
| 380 LOG_IF(ERROR, !cert) << "Couldn't create certificate from X509 data: " | 386 LOG_IF(ERROR, !cert) << "Couldn't create certificate from X509 data: " |
| 381 << decoded; | 387 << decoded; |
| 382 return cert; | 388 return cert; |
| 383 } | 389 } |
| 384 | 390 |
| 391 namespace { |
| 392 |
| 393 bool GUIDRefToPEMEncoding(const CertsByGUIDMap& certs_by_guid, |
| 394 const std::string& guid_ref, |
| 395 std::string* pem_encoded) { |
| 396 CertsByGUIDMap::const_iterator it = certs_by_guid.find(guid_ref); |
| 397 if (it == certs_by_guid.end()) { |
| 398 LOG(ERROR) << "Couldn't resolve certificate reference " << guid_ref; |
| 399 return false; |
| 400 } |
| 401 if (!net::X509Certificate::GetPEMEncoded(it->second->os_cert_handle(), |
| 402 pem_encoded)) { |
| 403 LOG(ERROR) << "Couldn't PEM-encode certificate with GUID " << guid_ref; |
| 404 return false; |
| 405 } |
| 406 return true; |
| 407 } |
| 408 |
| 409 bool ResolveSingleCertRef(const CertsByGUIDMap& certs_by_guid, |
| 410 const std::string& key_guid_ref, |
| 411 const std::string& key_pem, |
| 412 base::DictionaryValue* onc_object) { |
| 413 std::string guid_ref; |
| 414 if (!onc_object->GetStringWithoutPathExpansion(key_guid_ref, &guid_ref)) |
| 415 return true; |
| 416 |
| 417 std::string pem_encoded; |
| 418 if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded)) |
| 419 return false; |
| 420 |
| 421 onc_object->RemoveWithoutPathExpansion(key_guid_ref, NULL); |
| 422 onc_object->SetStringWithoutPathExpansion(key_pem, pem_encoded); |
| 423 return true; |
| 424 } |
| 425 |
| 426 bool ResolveCertRefList(const CertsByGUIDMap& certs_by_guid, |
| 427 const std::string& key_guid_ref_list, |
| 428 const std::string& key_pem_list, |
| 429 base::DictionaryValue* onc_object) { |
| 430 const base::ListValue* guid_ref_list = NULL; |
| 431 if (!onc_object->GetListWithoutPathExpansion(key_guid_ref_list, |
| 432 &guid_ref_list)) { |
| 433 return true; |
| 434 } |
| 435 |
| 436 scoped_ptr<base::ListValue> pem_list(new base::ListValue); |
| 437 for (base::ListValue::const_iterator it = guid_ref_list->begin(); |
| 438 it != guid_ref_list->end(); ++it) { |
| 439 std::string guid_ref; |
| 440 (*it)->GetAsString(&guid_ref); |
| 441 |
| 442 std::string pem_encoded; |
| 443 if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded)) |
| 444 return false; |
| 445 |
| 446 pem_list->AppendString(pem_encoded); |
| 447 } |
| 448 |
| 449 onc_object->RemoveWithoutPathExpansion(key_guid_ref_list, NULL); |
| 450 onc_object->SetWithoutPathExpansion(key_pem_list, pem_list.release()); |
| 451 return true; |
| 452 } |
| 453 |
| 454 bool ResolveSingleCertRefToList(const CertsByGUIDMap& certs_by_guid, |
| 455 const std::string& key_guid_ref, |
| 456 const std::string& key_pem_list, |
| 457 base::DictionaryValue* onc_object) { |
| 458 std::string guid_ref; |
| 459 if (!onc_object->GetStringWithoutPathExpansion(key_guid_ref, &guid_ref)) |
| 460 return true; |
| 461 |
| 462 std::string pem_encoded; |
| 463 if (!GUIDRefToPEMEncoding(certs_by_guid, guid_ref, &pem_encoded)) |
| 464 return false; |
| 465 |
| 466 scoped_ptr<base::ListValue> pem_list(new base::ListValue); |
| 467 pem_list->AppendString(pem_encoded); |
| 468 onc_object->RemoveWithoutPathExpansion(key_guid_ref, NULL); |
| 469 onc_object->SetWithoutPathExpansion(key_pem_list, pem_list.release()); |
| 470 return true; |
| 471 } |
| 472 |
| 473 bool ResolveServerCertRefsInObject(const CertsByGUIDMap& certs_by_guid, |
| 474 const OncValueSignature& signature, |
| 475 base::DictionaryValue* onc_object) { |
| 476 if (&signature == &kCertificatePatternSignature) { |
| 477 if (!ResolveCertRefList(certs_by_guid, certificate::kIssuerCARef, |
| 478 certificate::kIssuerCAPEMs, onc_object)) { |
| 479 return false; |
| 480 } |
| 481 } else if (&signature == &kEAPSignature) { |
| 482 if (!ResolveSingleCertRefToList(certs_by_guid, eap::kServerCARef, |
| 483 eap::kServerCAPEMs, onc_object)) { |
| 484 return false; |
| 485 } |
| 486 } else if (&signature == &kIPsecSignature) { |
| 487 if (!ResolveSingleCertRefToList(certs_by_guid, ipsec::kServerCARef, |
| 488 ipsec::kServerCAPEMs, onc_object)) { |
| 489 return false; |
| 490 } |
| 491 } else if (&signature == &kIPsecSignature || |
| 492 &signature == &kOpenVPNSignature) { |
| 493 if (!ResolveSingleCertRef(certs_by_guid, openvpn::kServerCertRef, |
| 494 openvpn::kServerCertPEM, onc_object) || |
| 495 !ResolveSingleCertRefToList(certs_by_guid, openvpn::kServerCARef, |
| 496 openvpn::kServerCAPEMs, onc_object)) { |
| 497 return false; |
| 498 } |
| 499 } |
| 500 |
| 501 // Recurse into nested objects. |
| 502 for (base::DictionaryValue::Iterator it(*onc_object); !it.IsAtEnd(); |
| 503 it.Advance()) { |
| 504 base::DictionaryValue* inner_object = NULL; |
| 505 if (!onc_object->GetDictionaryWithoutPathExpansion(it.key(), &inner_object)) |
| 506 continue; |
| 507 |
| 508 const OncFieldSignature* field_signature = |
| 509 GetFieldSignature(signature, it.key()); |
| 510 if (!field_signature) |
| 511 continue; |
| 512 |
| 513 if (!ResolveServerCertRefsInObject(certs_by_guid, |
| 514 *field_signature->value_signature, |
| 515 inner_object)) { |
| 516 return false; |
| 517 } |
| 518 } |
| 519 return true; |
| 520 } |
| 521 |
| 522 } // namespace |
| 523 |
| 524 bool ResolveServerCertRefsInNetworks(const CertsByGUIDMap& certs_by_guid, |
| 525 base::ListValue* network_configs) { |
| 526 bool success = true; |
| 527 for (base::ListValue::iterator it = network_configs->begin(); |
| 528 it != network_configs->end(); ) { |
| 529 base::DictionaryValue* network = NULL; |
| 530 (*it)->GetAsDictionary(&network); |
| 531 if (!ResolveServerCertRefsInNetwork(certs_by_guid, network)) { |
| 532 std::string guid; |
| 533 network->GetStringWithoutPathExpansion(network_config::kGUID, &guid); |
| 534 // This might happen even with correct validation, if the referenced |
| 535 // certificate couldn't be imported. |
| 536 LOG(ERROR) << "Couldn't resolve some certificate reference of network " |
| 537 << guid; |
| 538 it = network_configs->Erase(it, NULL); |
| 539 success = false; |
| 540 continue; |
| 541 } |
| 542 ++it; |
| 543 } |
| 544 return success; |
| 545 } |
| 546 |
| 547 bool ResolveServerCertRefsInNetwork(const CertsByGUIDMap& certs_by_guid, |
| 548 base::DictionaryValue* network_config) { |
| 549 return ResolveServerCertRefsInObject(certs_by_guid, |
| 550 kNetworkConfigurationSignature, |
| 551 network_config); |
| 552 } |
| 553 |
| 385 } // namespace onc | 554 } // namespace onc |
| 386 } // namespace chromeos | 555 } // namespace chromeos |
| OLD | NEW |