Chromium Code Reviews| Index: net/base/x509_certificate_mac.cc |
| diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc |
| index 7c5cc643c650d0320b8ca227646c159f79d93235..c565cfa55196216fd8861db3515dab2714321d46 100644 |
| --- a/net/base/x509_certificate_mac.cc |
| +++ b/net/base/x509_certificate_mac.cc |
| @@ -8,8 +8,13 @@ |
| #include <Security/Security.h> |
| #include <time.h> |
| +#include <map> |
| + |
| +#include "base/crypto/cssm_init.h" |
| +#include "base/crypto/rsa_private_key.h" |
| #include "base/lazy_instance.h" |
| #include "base/logging.h" |
| +#include "base/nss_util.h" |
| #include "base/pickle.h" |
| #include "base/singleton.h" |
| #include "base/mac/scoped_cftyperef.h" |
| @@ -18,6 +23,7 @@ |
| #include "net/base/cert_verify_result.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/test_root_certs.h" |
| +#include "third_party/nss/mozilla/security/nss/lib/certdb/cert.h" |
| using base::mac::ScopedCFTypeRef; |
| using base::Time; |
| @@ -369,6 +375,77 @@ void AddCertificatesFromBytes(const char* data, size_t length, |
| } |
| } |
| +struct CSSMOIDCompare { |
| + bool operator()(const CSSM_OID* a, const CSSM_OID* b) const { |
| + return a < b; |
| + } |
| +}; |
| + |
| +typedef std::map<const CSSM_OID*, std::string, CSSMOIDCompare> CSSMOIDMap; |
| + |
| +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.
|
| + struct OIDCSSMMap { |
| + SECOidTag sec_OID_; |
| + const CSSM_OID* cssm_OID_; |
| + }; |
| + |
| + const OIDCSSMMap kOIDs[] = { |
| + { SEC_OID_AVA_COMMON_NAME, &CSSMOID_CommonName }, |
| + { SEC_OID_AVA_COUNTRY_NAME, &CSSMOID_CountryName }, |
| + { SEC_OID_AVA_LOCALITY, &CSSMOID_LocalityName }, |
| + { SEC_OID_AVA_STATE_OR_PROVINCE, &CSSMOID_StateProvinceName }, |
| + { SEC_OID_AVA_STREET_ADDRESS, &CSSMOID_StreetAddress }, |
| + { SEC_OID_AVA_ORGANIZATION_NAME, &CSSMOID_OrganizationName }, |
| + { SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME, &CSSMOID_OrganizationalUnitName }, |
| + { SEC_OID_AVA_DN_QUALIFIER, &CSSMOID_DNQualifier }, |
| + { SEC_OID_RFC1274_UID, &CSSMOID_UniqueIdentifier }, |
| + { SEC_OID_PKCS9_EMAIL_ADDRESS, &CSSMOID_EmailAddress }, |
| + }; |
| + |
| + CERTRDN** rdns = name->rdns; |
| + for (size_t rdn = 0; rdns[rdn]; ++rdn) { |
| + CERTAVA** avas = rdns[rdn]->avas; |
| + for (size_t pair = 0; avas[pair] != 0; ++pair) { |
| + SECOidTag tag = CERT_GetAVATag(avas[pair]); |
| + if (tag == SEC_OID_UNKNOWN) { |
| + return false; |
| + } |
| + bool found_oid = false; |
| + for (size_t oid = 0; oid < ARRAYSIZE_UNSAFE(kOIDs); ++oid) { |
| + if (kOIDs[oid].sec_OID_ == tag) { |
| + SECItem* decode_item = CERT_DecodeAVAValue(&avas[pair]->value); |
| + if (!decode_item) { |
| + return false; |
| + } |
| + // TODO(wtc): Pass decode_item to CERT_RFC1485_EscapeAndQuote. |
| + std::string value(reinterpret_cast<char*>(decode_item->data), |
| + decode_item->len); |
| + (*out_values)[kOIDs[oid].cssm_OID_] = value; |
| + SECITEM_FreeItem(decode_item, PR_TRUE); |
| + found_oid = true; |
| + break; |
| + } |
| + } |
| + if (!found_oid) { |
| + DLOG(ERROR) << "Unrecognized OID: " << tag; |
| + } |
| + } |
| + } |
| + return true; |
| +} |
| + |
| +class ScopedCertName { |
| + public: |
| + explicit ScopedCertName(CERTName* name) : name_(name) { } |
| + ~ScopedCertName() { |
| + if (name_) CERT_DestroyName(name_); |
| + } |
| + operator CERTName*() { return name_; } |
| + |
| + private: |
| + CERTName* name_; |
| +}; |
| + |
| } // namespace |
| void X509Certificate::Initialize() { |
| @@ -406,8 +483,124 @@ X509Certificate* X509Certificate::CreateSelfSigned( |
| const std::string& subject, |
| uint32 serial_number, |
| base::TimeDelta valid_duration) { |
| - // TODO(port): Implement. |
| - return NULL; |
| + DCHECK(key); |
| + DCHECK(subject.length() > 0); |
|
Ryan Sleevi
2011/02/05 00:23:37
nit: DCHECK(!subject.empty()) or DCHECK_GT(subject
|
| + |
| + // There is a comment in |
| + // http://www.opensource.apple.com/source/security_certtool/security_certtool-31828/src/CertTool.cpp |
| + // that serial_numbers being passed into CSSM_TP_SubmitCredRequest can't have |
| + // their high bit set. We will continue though and mask it out below. |
| + if (serial_number & 0x80000000) { |
| + LOG(ERROR) << "serial_number has high bit set " << serial_number; |
| + } |
| + |
| + // NSS is used to parse the subject string into a set of |
| + // CSSM_OID/string pairs. There doesn't appear to be a system routine for |
| + // parsing Distinguished Name strings. |
| + base::EnsureNSSInit(); |
| + |
| + CSSMOIDMap subject_name_oids; |
| + ScopedCertName subject_name( |
| + CERT_AsciiToName(const_cast<char*>(subject.c_str()))); |
| + if (!CERTNameToCSSMOIDMap(subject_name, &subject_name_oids)) { |
| + DLOG(ERROR) << "Unable to generate CSSMOIDMap from " << subject; |
| + return NULL; |
| + } |
| + |
| + // Convert the map of oid/string pairs into an array of |
| + // CSSM_APPLE_TP_NAME_OIDs. |
| + std::vector<CSSM_APPLE_TP_NAME_OID> cssm_subject_names; |
| + for(CSSMOIDMap::iterator iter = subject_name_oids.begin(); |
| + iter != subject_name_oids.end(); ++iter) { |
| + CSSM_APPLE_TP_NAME_OID cssm_subject_name; |
| + cssm_subject_name.oid = iter->first; |
| + cssm_subject_name.string = iter->second.c_str(); |
| + cssm_subject_names.push_back(cssm_subject_name); |
| + } |
| + |
| + if (cssm_subject_names.size() == 0) { |
| + DLOG(ERROR) << "cssm_subject_names.size() == 0. Input: " << subject; |
| + return NULL; |
| + } |
| + |
| + // Set up a certificate request. |
| + CSSM_APPLE_TP_CERT_REQUEST certReq; |
| + memset(&certReq, 0, sizeof(certReq)); |
| + certReq.challengeString = NULL; |
| + certReq.cspHand = base::GetSharedCSPHandle(); |
| + certReq.clHand = base::GetSharedCLHandle(); |
| + certReq.numSubjectNames = cssm_subject_names.size(); |
| + certReq.subjectNames = &cssm_subject_names[0]; |
| + // See comment about serial numbers above. |
| + certReq.serialNumber = serial_number & 0x7f000000; |
| + certReq.numIssuerNames = 0; // Root. |
| + certReq.issuerNames = NULL; |
| + certReq.issuerNameX509 = NULL; |
| + certReq.certPublicKey = key->public_key(); |
| + certReq.issuerPrivateKey = key->key(); |
| + // 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.
|
| + certReq.signatureAlg = CSSM_ALGID_SHA1WithRSA; |
| + certReq.signatureOid = CSSMOID_SHA1WithRSA; |
| + certReq.notBefore = 0; |
| + 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.
|
| + certReq.numExtensions = 0; |
| + certReq.extensions = NULL; |
| + |
| + CSSM_TP_REQUEST_SET reqSet; |
| + reqSet.NumberOfRequests = 1; |
| + reqSet.Requests = &certReq; |
| + |
| + CSSM_FIELD policyId; |
| + memset(&policyId, 0, sizeof(policyId)); |
| + policyId.FieldOid = CSSMOID_APPLE_TP_LOCAL_CERT_GEN; |
| + |
| + CSSM_TP_CALLERAUTH_CONTEXT callerAuthContext; |
| + memset(&callerAuthContext, 0, sizeof(callerAuthContext)); |
| + callerAuthContext.Policy.NumberOfPolicyIds = 1; |
| + callerAuthContext.Policy.PolicyIds = &policyId; |
| + |
| + CSSM_TP_HANDLE tp_handle = base::GetSharedTPHandle(); |
| + base::ScopedCSSMData refId; |
| + sint32 estTime; |
| + CSSM_RETURN crtn = CSSM_TP_SubmitCredRequest( |
| + tp_handle, NULL, CSSM_TP_AUTHORITY_REQUEST_CERTISSUE, &reqSet, |
| + &callerAuthContext, &estTime, refId); |
| + if(crtn) { |
| + DLOG(ERROR) << "CSSM_TP_SubmitCredRequest failed " << crtn; |
| + return NULL; |
| + } |
| + |
| + CSSM_BOOL confirmRequired; |
| + base::ScopedCSSMTPtr<CSSM_TP_RESULT_SET> resultSet; |
| + crtn = CSSM_TP_RetrieveCredResult(tp_handle, refId, NULL, &estTime, |
| + &confirmRequired, &resultSet.receive()); |
| + if (crtn) { |
| + DLOG(ERROR) << "CSSM_TP_RetrieveCredResult failed " << crtn; |
| + return NULL; |
| + } |
| + |
| + 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.
|
| + DLOG(ERROR) << "CSSM_TP_RetrieveCredResult required confirmation"; |
| + return NULL; |
| + } |
| + |
|
Ryan Sleevi
2011/02/05 00:23:37
BUG: Check that resultSet->NumberOfResults == 1 be
dmac
2011/02/08 01:23:45
Done.
|
| + CSSM_ENCODED_CERT* encCert = |
| + reinterpret_cast<CSSM_ENCODED_CERT*>(resultSet->Results); |
| + base::mac::ScopedCFTypeRef<SecCertificateRef> scoped_cert; |
| + { |
| + 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.
|
| + SecCertificateRef certificate_ref = NULL; |
| + OSStatus os_status = SecCertificateCreateFromData(&encCert->CertBlob, |
| + encCert->CertType, encCert->CertEncoding, &certificate_ref); |
| + if (os_status != 0) { |
| + DLOG(ERROR) << "SecCertificateCreateFromData failed: " << os_status; |
| + return NULL; |
| + } |
| + scoped_cert.reset(certificate_ref); |
| + } |
|
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.
|
| + return CreateFromHandle( |
| + scoped_cert, X509Certificate::SOURCE_LONE_CERT_IMPORT, |
| + X509Certificate::OSCertHandles()); |
| } |
| void X509Certificate::Persist(Pickle* pickle) { |
| @@ -934,6 +1127,7 @@ CFArrayRef X509Certificate::CreateClientCertificateChain() const { |
| CFArrayRef cert_chain = NULL; |
| result = CopyCertChain(cert_handle_, &cert_chain); |
| + ScopedCFTypeRef<CFArrayRef> scoped_cert_chain(cert_chain); |
| if (result) { |
| LOG(ERROR) << "CreateIdentityCertificateChain error " << result; |
| return chain.release(); |
| @@ -947,7 +1141,6 @@ CFArrayRef X509Certificate::CreateClientCertificateChain() const { |
| cert_chain, |
| CFRangeMake(1, chain_count - 1)); |
| } |
| - CFRelease(cert_chain); |
| } |
| return chain.release(); |