Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 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 "net/base/x509_certificate.h" | 5 #include "net/base/x509_certificate.h" |
| 6 | 6 |
| 7 #include <CommonCrypto/CommonDigest.h> | 7 #include <CommonCrypto/CommonDigest.h> |
| 8 #include <Security/Security.h> | 8 #include <Security/Security.h> |
| 9 #include <time.h> | 9 #include <time.h> |
| 10 | 10 |
| 11 #include <vector> | |
| 12 | |
| 13 #include "base/crypto/cssm_init.h" | |
| 14 #include "base/crypto/rsa_private_key.h" | |
| 11 #include "base/lazy_instance.h" | 15 #include "base/lazy_instance.h" |
| 12 #include "base/logging.h" | 16 #include "base/logging.h" |
| 17 #include "base/nss_util.h" | |
| 13 #include "base/pickle.h" | 18 #include "base/pickle.h" |
| 14 #include "base/singleton.h" | 19 #include "base/singleton.h" |
| 15 #include "base/mac/scoped_cftyperef.h" | 20 #include "base/mac/scoped_cftyperef.h" |
| 16 #include "base/sys_string_conversions.h" | 21 #include "base/sys_string_conversions.h" |
| 17 #include "net/base/cert_status_flags.h" | 22 #include "net/base/cert_status_flags.h" |
| 18 #include "net/base/cert_verify_result.h" | 23 #include "net/base/cert_verify_result.h" |
| 19 #include "net/base/net_errors.h" | 24 #include "net/base/net_errors.h" |
| 20 #include "net/base/test_root_certs.h" | 25 #include "net/base/test_root_certs.h" |
| 26 #include "third_party/nss/mozilla/security/nss/lib/certdb/cert.h" | |
|
wtc
2011/02/09 01:03:31
The reason we went through the pain of writing sev
| |
| 21 | 27 |
| 22 using base::mac::ScopedCFTypeRef; | 28 using base::mac::ScopedCFTypeRef; |
| 23 using base::Time; | 29 using base::Time; |
| 24 | 30 |
| 25 namespace net { | 31 namespace net { |
| 26 | 32 |
| 27 namespace { | 33 namespace { |
| 28 | 34 |
| 29 typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef, | 35 typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef, |
| 30 CFDictionaryRef*); | 36 CFDictionaryRef*); |
| (...skipping 331 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 362 // is properly decoded as a PKCS#7, whether PEM or not, which avoids | 368 // is properly decoded as a PKCS#7, whether PEM or not, which avoids |
| 363 // the need to fallback to internal decoding. | 369 // the need to fallback to internal decoding. |
| 364 if (IsValidOSCertHandle(cert)) { | 370 if (IsValidOSCertHandle(cert)) { |
| 365 CFRetain(cert); | 371 CFRetain(cert); |
| 366 output->push_back(cert); | 372 output->push_back(cert); |
| 367 } | 373 } |
| 368 } | 374 } |
| 369 } | 375 } |
| 370 } | 376 } |
| 371 | 377 |
| 378 struct CSSMOIDString { | |
| 379 const CSSM_OID* oid_; | |
| 380 std::string string_; | |
| 381 }; | |
| 382 | |
| 383 typedef std::vector<CSSMOIDString> CSSMOIDStringVector; | |
| 384 | |
| 385 bool CERTNameToCSSMOIDVector(CERTName* name, CSSMOIDStringVector* out_values) { | |
| 386 struct OIDCSSMMap { | |
| 387 SECOidTag sec_OID_; | |
| 388 const CSSM_OID* cssm_OID_; | |
| 389 }; | |
| 390 | |
| 391 const OIDCSSMMap kOIDs[] = { | |
| 392 { SEC_OID_AVA_COMMON_NAME, &CSSMOID_CommonName }, | |
| 393 { SEC_OID_AVA_COUNTRY_NAME, &CSSMOID_CountryName }, | |
| 394 { SEC_OID_AVA_LOCALITY, &CSSMOID_LocalityName }, | |
| 395 { SEC_OID_AVA_STATE_OR_PROVINCE, &CSSMOID_StateProvinceName }, | |
| 396 { SEC_OID_AVA_STREET_ADDRESS, &CSSMOID_StreetAddress }, | |
| 397 { SEC_OID_AVA_ORGANIZATION_NAME, &CSSMOID_OrganizationName }, | |
| 398 { SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME, &CSSMOID_OrganizationalUnitName }, | |
| 399 { SEC_OID_AVA_DN_QUALIFIER, &CSSMOID_DNQualifier }, | |
| 400 { SEC_OID_RFC1274_UID, &CSSMOID_UniqueIdentifier }, | |
| 401 { SEC_OID_PKCS9_EMAIL_ADDRESS, &CSSMOID_EmailAddress }, | |
| 402 }; | |
| 403 | |
| 404 CERTRDN** rdns = name->rdns; | |
| 405 for (size_t rdn = 0; rdns[rdn]; ++rdn) { | |
| 406 CERTAVA** avas = rdns[rdn]->avas; | |
| 407 for (size_t pair = 0; avas[pair] != 0; ++pair) { | |
| 408 SECOidTag tag = CERT_GetAVATag(avas[pair]); | |
| 409 if (tag == SEC_OID_UNKNOWN) { | |
| 410 return false; | |
| 411 } | |
| 412 CSSMOIDString oidString; | |
| 413 bool found_oid = false; | |
| 414 for (size_t oid = 0; oid < ARRAYSIZE_UNSAFE(kOIDs); ++oid) { | |
| 415 if (kOIDs[oid].sec_OID_ == tag) { | |
| 416 SECItem* decode_item = CERT_DecodeAVAValue(&avas[pair]->value); | |
| 417 if (!decode_item) { | |
| 418 return false; | |
| 419 } | |
|
Ryan Sleevi
2011/02/08 02:27:55
nit: no need for braces around 1-line if.
| |
| 420 // TODO(wtc): Pass decode_item to CERT_RFC1485_EscapeAndQuote. | |
| 421 std::string value(reinterpret_cast<char*>(decode_item->data), | |
| 422 decode_item->len); | |
| 423 oidString.oid_ = kOIDs[oid].cssm_OID_; | |
| 424 oidString.string_ = value; | |
| 425 out_values->push_back(oidString); | |
| 426 SECITEM_FreeItem(decode_item, PR_TRUE); | |
| 427 found_oid = true; | |
| 428 break; | |
| 429 } | |
| 430 } | |
| 431 if (!found_oid) { | |
| 432 DLOG(ERROR) << "Unrecognized OID: " << tag; | |
| 433 } | |
|
Ryan Sleevi
2011/02/08 02:27:55
nit: no need for braces around 1-line if.
| |
| 434 } | |
| 435 } | |
| 436 return true; | |
| 437 } | |
| 438 | |
| 439 class ScopedCertName { | |
| 440 public: | |
| 441 explicit ScopedCertName(CERTName* name) : name_(name) { } | |
| 442 ~ScopedCertName() { | |
| 443 if (name_) CERT_DestroyName(name_); | |
| 444 } | |
| 445 operator CERTName*() { return name_; } | |
| 446 | |
| 447 private: | |
| 448 CERTName* name_; | |
| 449 }; | |
| 450 | |
| 451 class ScopedEncodedCertResults { | |
| 452 public: | |
| 453 explicit ScopedEncodedCertResults(CSSM_TP_RESULT_SET* results) | |
| 454 : results_(results) { } | |
| 455 ~ScopedEncodedCertResults() { | |
| 456 if (results_) { | |
| 457 for (uint32 i = 0; i < results_->NumberOfResults; i++) { | |
| 458 CSSM_ENCODED_CERT* encCert = | |
| 459 reinterpret_cast<CSSM_ENCODED_CERT*>(results_->Results); | |
|
Ryan Sleevi
2011/02/08 02:27:55
nit: move lines 458-459 outside of the loop?
| |
| 460 base::CSSMFree(encCert[i].CertBlob.Data); | |
| 461 } | |
| 462 } | |
| 463 base::CSSMFree(results_->Results); | |
| 464 base::CSSMFree(results_); | |
| 465 } | |
| 466 | |
| 467 private: | |
| 468 CSSM_TP_RESULT_SET* results_; | |
| 469 }; | |
| 470 | |
| 372 } // namespace | 471 } // namespace |
| 373 | 472 |
| 374 void X509Certificate::Initialize() { | 473 void X509Certificate::Initialize() { |
| 375 const CSSM_X509_NAME* name; | 474 const CSSM_X509_NAME* name; |
| 376 OSStatus status = SecCertificateGetSubject(cert_handle_, &name); | 475 OSStatus status = SecCertificateGetSubject(cert_handle_, &name); |
| 377 if (!status) | 476 if (!status) |
| 378 subject_.Parse(name); | 477 subject_.Parse(name); |
| 379 | 478 |
| 380 status = SecCertificateGetIssuer(cert_handle_, &name); | 479 status = SecCertificateGetIssuer(cert_handle_, &name); |
| 381 if (!status) | 480 if (!status) |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 399 | 498 |
| 400 return CreateFromBytes(data, length); | 499 return CreateFromBytes(data, length); |
| 401 } | 500 } |
| 402 | 501 |
| 403 // static | 502 // static |
| 404 X509Certificate* X509Certificate::CreateSelfSigned( | 503 X509Certificate* X509Certificate::CreateSelfSigned( |
| 405 base::RSAPrivateKey* key, | 504 base::RSAPrivateKey* key, |
| 406 const std::string& subject, | 505 const std::string& subject, |
| 407 uint32 serial_number, | 506 uint32 serial_number, |
| 408 base::TimeDelta valid_duration) { | 507 base::TimeDelta valid_duration) { |
| 409 // TODO(port): Implement. | 508 DCHECK(key); |
| 410 return NULL; | 509 DCHECK(!subject.empty()); |
| 510 | |
| 511 if (valid_duration.InSeconds() > UINT32_MAX) { | |
| 512 LOG(ERROR) << "valid_duration too big" << valid_duration.InSeconds(); | |
| 513 valid_duration = base::TimeDelta::FromSeconds(UINT32_MAX); | |
| 514 } | |
| 515 | |
| 516 // There is a comment in | |
| 517 // http://www.opensource.apple.com/source/security_certtool/security_certtool- 31828/src/CertTool.cpp | |
| 518 // that serial_numbers being passed into CSSM_TP_SubmitCredRequest can't have | |
| 519 // their high bit set. We will continue though and mask it out below. | |
| 520 if (serial_number & 0x80000000) { | |
| 521 LOG(ERROR) << "serial_number has high bit set " << serial_number; | |
| 522 } | |
|
Ryan Sleevi
2011/02/08 02:27:55
nit: no braces for one-line if's
| |
| 523 | |
| 524 // NSS is used to parse the subject string into a set of | |
| 525 // CSSM_OID/string pairs. There doesn't appear to be a system routine for | |
| 526 // parsing Distinguished Name strings. | |
| 527 base::EnsureNSSInit(); | |
| 528 | |
| 529 CSSMOIDStringVector subject_name_oids; | |
| 530 ScopedCertName subject_name( | |
| 531 CERT_AsciiToName(const_cast<char*>(subject.c_str()))); | |
| 532 if (!CERTNameToCSSMOIDVector(subject_name, &subject_name_oids)) { | |
| 533 DLOG(ERROR) << "Unable to generate CSSMOIDMap from " << subject; | |
| 534 return NULL; | |
| 535 } | |
| 536 | |
| 537 // Convert the map of oid/string pairs into an array of | |
| 538 // CSSM_APPLE_TP_NAME_OIDs. | |
| 539 std::vector<CSSM_APPLE_TP_NAME_OID> cssm_subject_names; | |
| 540 for(CSSMOIDStringVector::iterator iter = subject_name_oids.begin(); | |
| 541 iter != subject_name_oids.end(); ++iter) { | |
| 542 CSSM_APPLE_TP_NAME_OID cssm_subject_name; | |
| 543 cssm_subject_name.oid = iter->oid_; | |
| 544 cssm_subject_name.string = iter->string_.c_str(); | |
| 545 cssm_subject_names.push_back(cssm_subject_name); | |
| 546 } | |
| 547 | |
| 548 if (cssm_subject_names.size() == 0) { | |
| 549 DLOG(ERROR) << "cssm_subject_names.size() == 0. Input: " << subject; | |
| 550 return NULL; | |
| 551 } | |
| 552 | |
| 553 // Set up a certificate request. | |
| 554 CSSM_APPLE_TP_CERT_REQUEST certReq; | |
| 555 memset(&certReq, 0, sizeof(certReq)); | |
| 556 certReq.cspHand = base::GetSharedCSPHandle(); | |
| 557 certReq.clHand = base::GetSharedCLHandle(); | |
| 558 // See comment about serial numbers above. | |
|
Ryan Sleevi
2011/02/08 02:27:55
nit: remove extra spaces
| |
| 559 certReq.serialNumber = serial_number & 0x7fffffff; | |
| 560 certReq.numSubjectNames = cssm_subject_names.size(); | |
| 561 certReq.subjectNames = &cssm_subject_names[0]; | |
| 562 certReq.numIssuerNames = 0; // Root. | |
| 563 certReq.issuerNames = NULL; | |
| 564 certReq.issuerNameX509 = NULL; | |
| 565 certReq.certPublicKey = key->public_key(); | |
| 566 certReq.issuerPrivateKey = key->key(); | |
| 567 // These are the Apple defaults. | |
| 568 certReq.signatureAlg = CSSM_ALGID_SHA1WithRSA; | |
| 569 certReq.signatureOid = CSSMOID_SHA1WithRSA; | |
| 570 certReq.notBefore = 0; | |
| 571 certReq.notAfter = static_cast<uint32>(valid_duration.InSeconds()); | |
| 572 certReq.numExtensions = 0; | |
| 573 certReq.extensions = NULL; | |
| 574 certReq.challengeString = NULL; | |
| 575 | |
| 576 CSSM_TP_REQUEST_SET reqSet; | |
| 577 reqSet.NumberOfRequests = 1; | |
| 578 reqSet.Requests = &certReq; | |
| 579 | |
| 580 CSSM_FIELD policyId; | |
| 581 memset(&policyId, 0, sizeof(policyId)); | |
| 582 policyId.FieldOid = CSSMOID_APPLE_TP_LOCAL_CERT_GEN; | |
| 583 | |
| 584 CSSM_TP_CALLERAUTH_CONTEXT callerAuthContext; | |
| 585 memset(&callerAuthContext, 0, sizeof(callerAuthContext)); | |
| 586 callerAuthContext.Policy.NumberOfPolicyIds = 1; | |
| 587 callerAuthContext.Policy.PolicyIds = &policyId; | |
| 588 | |
| 589 CSSM_TP_HANDLE tp_handle = base::GetSharedTPHandle(); | |
| 590 CSSM_DATA refId; | |
| 591 memset(&refId, 0, sizeof(refId)); | |
| 592 sint32 estTime; | |
| 593 CSSM_RETURN crtn = CSSM_TP_SubmitCredRequest(tp_handle, NULL, | |
| 594 CSSM_TP_AUTHORITY_REQUEST_CERTISSUE, &reqSet, &callerAuthContext, | |
| 595 &estTime, &refId); | |
|
Ryan Sleevi
2011/02/08 02:27:55
nit: extra space
| |
| 596 if(crtn) { | |
| 597 DLOG(ERROR) << "CSSM_TP_SubmitCredRequest failed " << crtn; | |
| 598 return NULL; | |
| 599 } | |
| 600 | |
| 601 CSSM_BOOL confirmRequired; | |
| 602 CSSM_TP_RESULT_SET *resultSet = NULL; | |
| 603 crtn = CSSM_TP_RetrieveCredResult(tp_handle, &refId, NULL, &estTime, | |
| 604 &confirmRequired, &resultSet); | |
| 605 ScopedEncodedCertResults scopedResults(resultSet); | |
| 606 base::CSSMFree(refId.Data); | |
| 607 if (crtn) { | |
| 608 DLOG(ERROR) << "CSSM_TP_RetrieveCredResult failed " << crtn; | |
| 609 return NULL; | |
| 610 } | |
| 611 | |
| 612 if (confirmRequired) { | |
| 613 // Potential leak here of resultSet. |confirmRequired| should never be | |
| 614 // true, | |
|
Ryan Sleevi
2011/02/08 02:27:55
world's smallest nit pt 2: , -> .
| |
| 615 DLOG(ERROR) << "CSSM_TP_RetrieveCredResult required confirmation"; | |
| 616 return NULL; | |
| 617 } | |
| 618 | |
| 619 if (resultSet->NumberOfResults != 1) { | |
| 620 DLOG(ERROR) << "Unexpected number of results: " | |
| 621 << resultSet->NumberOfResults; | |
|
Ryan Sleevi
2011/02/08 02:27:55
nit: missing space
| |
| 622 return NULL; | |
| 623 } | |
| 624 | |
| 625 CSSM_ENCODED_CERT* encCert = | |
| 626 reinterpret_cast<CSSM_ENCODED_CERT*>(resultSet->Results); | |
| 627 base::mac::ScopedCFTypeRef<SecCertificateRef> scoped_cert; | |
| 628 SecCertificateRef certificate_ref = NULL; | |
| 629 OSStatus os_status = SecCertificateCreateFromData(&encCert->CertBlob, | |
| 630 encCert->CertType, encCert->CertEncoding, &certificate_ref); | |
|
Ryan Sleevi
2011/02/08 02:27:55
Is this style OK? I thought it was either align th
| |
| 631 if (os_status != 0) { | |
| 632 DLOG(ERROR) << "SecCertificateCreateFromData failed: " << os_status; | |
| 633 return NULL; | |
| 634 } | |
| 635 scoped_cert.reset(certificate_ref); | |
| 636 | |
| 637 return CreateFromHandle( | |
| 638 scoped_cert, X509Certificate::SOURCE_LONE_CERT_IMPORT, | |
| 639 X509Certificate::OSCertHandles()); | |
| 411 } | 640 } |
| 412 | 641 |
| 413 void X509Certificate::Persist(Pickle* pickle) { | 642 void X509Certificate::Persist(Pickle* pickle) { |
| 414 CSSM_DATA cert_data; | 643 CSSM_DATA cert_data; |
| 415 OSStatus status = SecCertificateGetData(cert_handle_, &cert_data); | 644 OSStatus status = SecCertificateGetData(cert_handle_, &cert_data); |
| 416 if (status) { | 645 if (status) { |
| 417 NOTREACHED(); | 646 NOTREACHED(); |
| 418 return; | 647 return; |
| 419 } | 648 } |
| 420 | 649 |
| (...skipping 506 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 927 if (result) { | 1156 if (result) { |
| 928 LOG(ERROR) << "SecIdentityCreateWithCertificate error " << result; | 1157 LOG(ERROR) << "SecIdentityCreateWithCertificate error " << result; |
| 929 return NULL; | 1158 return NULL; |
| 930 } | 1159 } |
| 931 ScopedCFTypeRef<CFMutableArrayRef> chain( | 1160 ScopedCFTypeRef<CFMutableArrayRef> chain( |
| 932 CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)); | 1161 CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)); |
| 933 CFArrayAppendValue(chain, identity); | 1162 CFArrayAppendValue(chain, identity); |
| 934 | 1163 |
| 935 CFArrayRef cert_chain = NULL; | 1164 CFArrayRef cert_chain = NULL; |
| 936 result = CopyCertChain(cert_handle_, &cert_chain); | 1165 result = CopyCertChain(cert_handle_, &cert_chain); |
| 1166 ScopedCFTypeRef<CFArrayRef> scoped_cert_chain(cert_chain); | |
| 937 if (result) { | 1167 if (result) { |
| 938 LOG(ERROR) << "CreateIdentityCertificateChain error " << result; | 1168 LOG(ERROR) << "CreateIdentityCertificateChain error " << result; |
| 939 return chain.release(); | 1169 return chain.release(); |
| 940 } | 1170 } |
| 941 | 1171 |
| 942 // Append the intermediate certs from SecTrust to the result array: | 1172 // Append the intermediate certs from SecTrust to the result array: |
| 943 if (cert_chain) { | 1173 if (cert_chain) { |
| 944 int chain_count = CFArrayGetCount(cert_chain); | 1174 int chain_count = CFArrayGetCount(cert_chain); |
| 945 if (chain_count > 1) { | 1175 if (chain_count > 1) { |
| 946 CFArrayAppendArray(chain, | 1176 CFArrayAppendArray(chain, |
| 947 cert_chain, | 1177 cert_chain, |
| 948 CFRangeMake(1, chain_count - 1)); | 1178 CFRangeMake(1, chain_count - 1)); |
| 949 } | 1179 } |
| 950 CFRelease(cert_chain); | |
| 951 } | 1180 } |
| 952 | 1181 |
| 953 return chain.release(); | 1182 return chain.release(); |
| 954 } | 1183 } |
| 955 | 1184 |
| 956 } // namespace net | 1185 } // namespace net |
| OLD | NEW |