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