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 |