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 <map> | |
| 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" | |
| 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 CSSMOIDCompare { | |
| 379 bool operator()(const CSSM_OID* a, const CSSM_OID* b) const { | |
| 380 return a < b; | |
| 381 } | |
| 382 }; | |
| 383 | |
| 384 typedef std::map<const CSSM_OID*, std::string, CSSMOIDCompare> CSSMOIDMap; | |
| 385 | |
| 386 bool CERTNameToCSSMOIDMap(CERTName* name, CSSMOIDMap* out_values) { | |
|
Ryan Sleevi
2011/02/05 00:23:37
BUG: I could be reading wrong, but I don't think t
dmac
2011/02/08 01:23:45
Done.
| |
| 387 struct OIDCSSMMap { | |
| 388 SECOidTag sec_OID_; | |
| 389 const CSSM_OID* cssm_OID_; | |
| 390 }; | |
| 391 | |
| 392 const OIDCSSMMap kOIDs[] = { | |
| 393 { SEC_OID_AVA_COMMON_NAME, &CSSMOID_CommonName }, | |
| 394 { SEC_OID_AVA_COUNTRY_NAME, &CSSMOID_CountryName }, | |
| 395 { SEC_OID_AVA_LOCALITY, &CSSMOID_LocalityName }, | |
| 396 { SEC_OID_AVA_STATE_OR_PROVINCE, &CSSMOID_StateProvinceName }, | |
| 397 { SEC_OID_AVA_STREET_ADDRESS, &CSSMOID_StreetAddress }, | |
| 398 { SEC_OID_AVA_ORGANIZATION_NAME, &CSSMOID_OrganizationName }, | |
| 399 { SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME, &CSSMOID_OrganizationalUnitName }, | |
| 400 { SEC_OID_AVA_DN_QUALIFIER, &CSSMOID_DNQualifier }, | |
| 401 { SEC_OID_RFC1274_UID, &CSSMOID_UniqueIdentifier }, | |
| 402 { SEC_OID_PKCS9_EMAIL_ADDRESS, &CSSMOID_EmailAddress }, | |
| 403 }; | |
| 404 | |
| 405 CERTRDN** rdns = name->rdns; | |
| 406 for (size_t rdn = 0; rdns[rdn]; ++rdn) { | |
| 407 CERTAVA** avas = rdns[rdn]->avas; | |
| 408 for (size_t pair = 0; avas[pair] != 0; ++pair) { | |
| 409 SECOidTag tag = CERT_GetAVATag(avas[pair]); | |
| 410 if (tag == SEC_OID_UNKNOWN) { | |
| 411 return false; | |
| 412 } | |
| 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 } | |
| 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 (*out_values)[kOIDs[oid].cssm_OID_] = value; | |
| 424 SECITEM_FreeItem(decode_item, PR_TRUE); | |
| 425 found_oid = true; | |
| 426 break; | |
| 427 } | |
| 428 } | |
| 429 if (!found_oid) { | |
| 430 DLOG(ERROR) << "Unrecognized OID: " << tag; | |
| 431 } | |
| 432 } | |
| 433 } | |
| 434 return true; | |
| 435 } | |
| 436 | |
| 437 class ScopedCertName { | |
| 438 public: | |
| 439 explicit ScopedCertName(CERTName* name) : name_(name) { } | |
| 440 ~ScopedCertName() { | |
| 441 if (name_) CERT_DestroyName(name_); | |
| 442 } | |
| 443 operator CERTName*() { return name_; } | |
| 444 | |
| 445 private: | |
| 446 CERTName* name_; | |
| 447 }; | |
| 448 | |
| 372 } // namespace | 449 } // namespace |
| 373 | 450 |
| 374 void X509Certificate::Initialize() { | 451 void X509Certificate::Initialize() { |
| 375 const CSSM_X509_NAME* name; | 452 const CSSM_X509_NAME* name; |
| 376 OSStatus status = SecCertificateGetSubject(cert_handle_, &name); | 453 OSStatus status = SecCertificateGetSubject(cert_handle_, &name); |
| 377 if (!status) | 454 if (!status) |
| 378 subject_.Parse(name); | 455 subject_.Parse(name); |
| 379 | 456 |
| 380 status = SecCertificateGetIssuer(cert_handle_, &name); | 457 status = SecCertificateGetIssuer(cert_handle_, &name); |
| 381 if (!status) | 458 if (!status) |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 399 | 476 |
| 400 return CreateFromBytes(data, length); | 477 return CreateFromBytes(data, length); |
| 401 } | 478 } |
| 402 | 479 |
| 403 // static | 480 // static |
| 404 X509Certificate* X509Certificate::CreateSelfSigned( | 481 X509Certificate* X509Certificate::CreateSelfSigned( |
| 405 base::RSAPrivateKey* key, | 482 base::RSAPrivateKey* key, |
| 406 const std::string& subject, | 483 const std::string& subject, |
| 407 uint32 serial_number, | 484 uint32 serial_number, |
| 408 base::TimeDelta valid_duration) { | 485 base::TimeDelta valid_duration) { |
| 409 // TODO(port): Implement. | 486 DCHECK(key); |
| 410 return NULL; | 487 DCHECK(subject.length() > 0); |
|
Ryan Sleevi
2011/02/05 00:23:37
nit: DCHECK(!subject.empty()) or DCHECK_GT(subject
| |
| 488 | |
| 489 // There is a comment in | |
| 490 // http://www.opensource.apple.com/source/security_certtool/security_certtool- 31828/src/CertTool.cpp | |
| 491 // that serial_numbers being passed into CSSM_TP_SubmitCredRequest can't have | |
| 492 // their high bit set. We will continue though and mask it out below. | |
| 493 if (serial_number & 0x80000000) { | |
| 494 LOG(ERROR) << "serial_number has high bit set " << serial_number; | |
| 495 } | |
| 496 | |
| 497 // NSS is used to parse the subject string into a set of | |
| 498 // CSSM_OID/string pairs. There doesn't appear to be a system routine for | |
| 499 // parsing Distinguished Name strings. | |
| 500 base::EnsureNSSInit(); | |
| 501 | |
| 502 CSSMOIDMap subject_name_oids; | |
| 503 ScopedCertName subject_name( | |
| 504 CERT_AsciiToName(const_cast<char*>(subject.c_str()))); | |
| 505 if (!CERTNameToCSSMOIDMap(subject_name, &subject_name_oids)) { | |
| 506 DLOG(ERROR) << "Unable to generate CSSMOIDMap from " << subject; | |
| 507 return NULL; | |
| 508 } | |
| 509 | |
| 510 // Convert the map of oid/string pairs into an array of | |
| 511 // CSSM_APPLE_TP_NAME_OIDs. | |
| 512 std::vector<CSSM_APPLE_TP_NAME_OID> cssm_subject_names; | |
| 513 for(CSSMOIDMap::iterator iter = subject_name_oids.begin(); | |
| 514 iter != subject_name_oids.end(); ++iter) { | |
| 515 CSSM_APPLE_TP_NAME_OID cssm_subject_name; | |
| 516 cssm_subject_name.oid = iter->first; | |
| 517 cssm_subject_name.string = iter->second.c_str(); | |
| 518 cssm_subject_names.push_back(cssm_subject_name); | |
| 519 } | |
| 520 | |
| 521 if (cssm_subject_names.size() == 0) { | |
| 522 DLOG(ERROR) << "cssm_subject_names.size() == 0. Input: " << subject; | |
| 523 return NULL; | |
| 524 } | |
| 525 | |
| 526 // Set up a certificate request. | |
| 527 CSSM_APPLE_TP_CERT_REQUEST certReq; | |
| 528 memset(&certReq, 0, sizeof(certReq)); | |
| 529 certReq.challengeString = NULL; | |
| 530 certReq.cspHand = base::GetSharedCSPHandle(); | |
| 531 certReq.clHand = base::GetSharedCLHandle(); | |
| 532 certReq.numSubjectNames = cssm_subject_names.size(); | |
| 533 certReq.subjectNames = &cssm_subject_names[0]; | |
| 534 // See comment about serial numbers above. | |
| 535 certReq.serialNumber = serial_number & 0x7f000000; | |
| 536 certReq.numIssuerNames = 0; // Root. | |
| 537 certReq.issuerNames = NULL; | |
| 538 certReq.issuerNameX509 = NULL; | |
| 539 certReq.certPublicKey = key->public_key(); | |
| 540 certReq.issuerPrivateKey = key->key(); | |
| 541 // This is the Apple defaults. | |
|
Ryan Sleevi
2011/02/05 00:23:37
world's smallest nit: defaults -> default or this
dmac
2011/02/08 01:23:45
Done.
| |
| 542 certReq.signatureAlg = CSSM_ALGID_SHA1WithRSA; | |
| 543 certReq.signatureOid = CSSMOID_SHA1WithRSA; | |
| 544 certReq.notBefore = 0; | |
| 545 certReq.notAfter = valid_duration.InSeconds(); | |
|
Ryan Sleevi
2011/02/05 00:23:37
nit: InSeconds() returns an Int64, but notAfter ex
dmac
2011/02/08 01:23:45
Done.
| |
| 546 certReq.numExtensions = 0; | |
| 547 certReq.extensions = NULL; | |
| 548 | |
| 549 CSSM_TP_REQUEST_SET reqSet; | |
| 550 reqSet.NumberOfRequests = 1; | |
| 551 reqSet.Requests = &certReq; | |
| 552 | |
| 553 CSSM_FIELD policyId; | |
| 554 memset(&policyId, 0, sizeof(policyId)); | |
| 555 policyId.FieldOid = CSSMOID_APPLE_TP_LOCAL_CERT_GEN; | |
| 556 | |
| 557 CSSM_TP_CALLERAUTH_CONTEXT callerAuthContext; | |
| 558 memset(&callerAuthContext, 0, sizeof(callerAuthContext)); | |
| 559 callerAuthContext.Policy.NumberOfPolicyIds = 1; | |
| 560 callerAuthContext.Policy.PolicyIds = &policyId; | |
| 561 | |
| 562 CSSM_TP_HANDLE tp_handle = base::GetSharedTPHandle(); | |
| 563 base::ScopedCSSMData refId; | |
| 564 sint32 estTime; | |
| 565 CSSM_RETURN crtn = CSSM_TP_SubmitCredRequest( | |
| 566 tp_handle, NULL, CSSM_TP_AUTHORITY_REQUEST_CERTISSUE, &reqSet, | |
| 567 &callerAuthContext, &estTime, refId); | |
| 568 if(crtn) { | |
| 569 DLOG(ERROR) << "CSSM_TP_SubmitCredRequest failed " << crtn; | |
| 570 return NULL; | |
| 571 } | |
| 572 | |
| 573 CSSM_BOOL confirmRequired; | |
| 574 base::ScopedCSSMTPtr<CSSM_TP_RESULT_SET> resultSet; | |
| 575 crtn = CSSM_TP_RetrieveCredResult(tp_handle, refId, NULL, &estTime, | |
| 576 &confirmRequired, &resultSet.receive()); | |
| 577 if (crtn) { | |
| 578 DLOG(ERROR) << "CSSM_TP_RetrieveCredResult failed " << crtn; | |
| 579 return NULL; | |
| 580 } | |
| 581 | |
| 582 if (confirmRequired) { | |
|
Ryan Sleevi
2011/02/05 00:23:37
BUG/nit? If confirmRequired is true, it's still po
dmac
2011/02/08 01:23:45
Done.
| |
| 583 DLOG(ERROR) << "CSSM_TP_RetrieveCredResult required confirmation"; | |
| 584 return NULL; | |
| 585 } | |
| 586 | |
|
Ryan Sleevi
2011/02/05 00:23:37
BUG: Check that resultSet->NumberOfResults == 1 be
dmac
2011/02/08 01:23:45
Done.
| |
| 587 CSSM_ENCODED_CERT* encCert = | |
| 588 reinterpret_cast<CSSM_ENCODED_CERT*>(resultSet->Results); | |
| 589 base::mac::ScopedCFTypeRef<SecCertificateRef> scoped_cert; | |
| 590 { | |
| 591 base::AutoLock sec_lock(base::GetMacSecurityServicesLock()); | |
|
Ryan Sleevi
2011/02/05 00:23:37
You shouldn't need to lock here. It's only needed
dmac
2011/02/08 01:23:45
Done.
| |
| 592 SecCertificateRef certificate_ref = NULL; | |
| 593 OSStatus os_status = SecCertificateCreateFromData(&encCert->CertBlob, | |
| 594 encCert->CertType, encCert->CertEncoding, &certificate_ref); | |
| 595 if (os_status != 0) { | |
| 596 DLOG(ERROR) << "SecCertificateCreateFromData failed: " << os_status; | |
| 597 return NULL; | |
| 598 } | |
| 599 scoped_cert.reset(certificate_ref); | |
| 600 } | |
|
Ryan Sleevi
2011/02/05 00:23:37
BUG: You also need to free
resultSet->Results
and
dmac
2011/02/08 01:23:45
Done.
| |
| 601 return CreateFromHandle( | |
| 602 scoped_cert, X509Certificate::SOURCE_LONE_CERT_IMPORT, | |
| 603 X509Certificate::OSCertHandles()); | |
| 411 } | 604 } |
| 412 | 605 |
| 413 void X509Certificate::Persist(Pickle* pickle) { | 606 void X509Certificate::Persist(Pickle* pickle) { |
| 414 CSSM_DATA cert_data; | 607 CSSM_DATA cert_data; |
| 415 OSStatus status = SecCertificateGetData(cert_handle_, &cert_data); | 608 OSStatus status = SecCertificateGetData(cert_handle_, &cert_data); |
| 416 if (status) { | 609 if (status) { |
| 417 NOTREACHED(); | 610 NOTREACHED(); |
| 418 return; | 611 return; |
| 419 } | 612 } |
| 420 | 613 |
| (...skipping 506 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 927 if (result) { | 1120 if (result) { |
| 928 LOG(ERROR) << "SecIdentityCreateWithCertificate error " << result; | 1121 LOG(ERROR) << "SecIdentityCreateWithCertificate error " << result; |
| 929 return NULL; | 1122 return NULL; |
| 930 } | 1123 } |
| 931 ScopedCFTypeRef<CFMutableArrayRef> chain( | 1124 ScopedCFTypeRef<CFMutableArrayRef> chain( |
| 932 CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)); | 1125 CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)); |
| 933 CFArrayAppendValue(chain, identity); | 1126 CFArrayAppendValue(chain, identity); |
| 934 | 1127 |
| 935 CFArrayRef cert_chain = NULL; | 1128 CFArrayRef cert_chain = NULL; |
| 936 result = CopyCertChain(cert_handle_, &cert_chain); | 1129 result = CopyCertChain(cert_handle_, &cert_chain); |
| 1130 ScopedCFTypeRef<CFArrayRef> scoped_cert_chain(cert_chain); | |
| 937 if (result) { | 1131 if (result) { |
| 938 LOG(ERROR) << "CreateIdentityCertificateChain error " << result; | 1132 LOG(ERROR) << "CreateIdentityCertificateChain error " << result; |
| 939 return chain.release(); | 1133 return chain.release(); |
| 940 } | 1134 } |
| 941 | 1135 |
| 942 // Append the intermediate certs from SecTrust to the result array: | 1136 // Append the intermediate certs from SecTrust to the result array: |
| 943 if (cert_chain) { | 1137 if (cert_chain) { |
| 944 int chain_count = CFArrayGetCount(cert_chain); | 1138 int chain_count = CFArrayGetCount(cert_chain); |
| 945 if (chain_count > 1) { | 1139 if (chain_count > 1) { |
| 946 CFArrayAppendArray(chain, | 1140 CFArrayAppendArray(chain, |
| 947 cert_chain, | 1141 cert_chain, |
| 948 CFRangeMake(1, chain_count - 1)); | 1142 CFRangeMake(1, chain_count - 1)); |
| 949 } | 1143 } |
| 950 CFRelease(cert_chain); | |
| 951 } | 1144 } |
| 952 | 1145 |
| 953 return chain.release(); | 1146 return chain.release(); |
| 954 } | 1147 } |
| 955 | 1148 |
| 956 } // namespace net | 1149 } // namespace net |
| OLD | NEW |