| Index: net/base/x509_certificate_mac.cc
|
| ===================================================================
|
| --- net/base/x509_certificate_mac.cc (revision 49024)
|
| +++ net/base/x509_certificate_mac.cc (working copy)
|
| @@ -8,6 +8,8 @@
|
| #include <Security/Security.h>
|
| #include <time.h>
|
|
|
| +#include "base/file_path.h"
|
| +#include "base/file_util.h"
|
| #include "base/scoped_cftyperef.h"
|
| #include "base/logging.h"
|
| #include "base/pickle.h"
|
| @@ -358,6 +360,80 @@
|
| return SecTrustGetResult(trust, &status, out_cert_chain, &status_chain);
|
| }
|
|
|
| +void AddCertificatesFromData(const char* data, size_t length,
|
| + SecExternalFormat format,
|
| + X509Certificate::OSCertHandles* output) {
|
| + // In order to use SecKeychainItemImport successfully, a destination
|
| + // keychain must be specified to receive the created objects. Further,
|
| + // the created objects remain valid only for as long as the keychain does.
|
| + // In order to match the handling on Windows/NSS, which has the handles
|
| + // valid for an arbitrary length of time, a temporary keychain is created,
|
| + // the certificates are imported into it, and then free-standing
|
| + // certificates are created from the DER representation of the original
|
| + // keychain item.
|
| + FilePath temp_file;
|
| + if (!file_util::CreateTemporaryFile(&temp_file))
|
| + return;
|
| +
|
| + SecExternalFormat input_format = format;
|
| + scoped_cftyperef<CFDataRef> local_data(CFDataCreateWithBytesNoCopy(
|
| + kCFAllocatorDefault, reinterpret_cast<const UInt8*>(data),
|
| + length, kCFAllocatorDefault);
|
| +
|
| + scoped_cftyperef<SecKeychainRef> temp_keychain;
|
| + SecKeychainRef keychain = NULL;
|
| + OSStatus rv = SecKeychainCreate(temp_file.value().c_str(), 0, NULL, FALSE,
|
| + NULL, &keychain);
|
| + if (rv != noErr) {
|
| + DLOG(WARNING) << rv << " Unable to create temporary keychain at "
|
| + << temp_file.value();
|
| + return;
|
| + }
|
| +
|
| + temp_keychain.reset(keychain);
|
| + keychain = NULL;
|
| +
|
| + CFArrayRef items = NULL;
|
| + rv = SecKeychainItemImport(local_data, NULL, &input_format, NULL,
|
| + 0, NULL, NULL, temp_keychain, &items);
|
| + if (rv != noErr) {
|
| + scoped_cftyperef<CFArrayRef> scoped_items(items);
|
| + CFTypeID certTypeID = SecCertificateGetTypeID();
|
| +
|
| + for (CFIndex i = 0; i < CFArrayGetCount(items); ++i) {
|
| + SecKeychainItemRef item = CFArrayGetValueAtIndex(items, i);
|
| +
|
| + // While inputFormat implies only certificates will be imported, if/when
|
| + // other formats (eg: PKCS#12) are supported, this may also include
|
| + // private keys or other items types, so filter appropriately.
|
| + if (CFGetTypeID(item) == certTypeID) {
|
| + // Create a freestanding SecCertificateRef from the keychain item.
|
| + // Unfortunately, this invokes a double-parse of the DER data, which
|
| + // is less than optimally efficient.
|
| + scoped_cftyperef<CFDataRef> scoped_cert_data =
|
| + SecCertificateCopyData(reinterpret_cast<SecCertificateRef>(item));
|
| + if (scoped_cert_data == NULL)
|
| + continue;
|
| +
|
| + SecCertificateRef cert = SecCertificateCreateWithData(NULL,
|
| + scoped_cert_data);
|
| + if (cert == NULL)
|
| + continue;
|
| +
|
| + output->push_back(cert);
|
| + }
|
| + } else {
|
| + DLOG(WARNING) << rv << " Unable to import items from data of length "
|
| + << length;
|
| + }
|
| +
|
| + rv = SecKeychainDelete(temp_keychain);
|
| + if (rv != noErr) {
|
| + DLOG(WARNING) << rv << " Unable to remove temporary keychain at "
|
| + << temp_file.value();
|
| + }
|
| +}
|
| +
|
| } // namespace
|
|
|
| void X509Certificate::Initialize() {
|
| @@ -387,7 +463,7 @@
|
| if (!pickle.ReadData(pickle_iter, &data, &length))
|
| return NULL;
|
|
|
| - return CreateFromBytes(data, length);
|
| + return CreateFromBytes(data, length, X509Certificate::FORMAT_DER);
|
| }
|
|
|
| void X509Certificate::Persist(Pickle* pickle) {
|
| @@ -647,7 +723,7 @@
|
|
|
| // static
|
| X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
|
| - const char* data, int length) {
|
| + const char* data, size_t length) {
|
| CSSM_DATA cert_data;
|
| cert_data.Data = const_cast<uint8*>(reinterpret_cast<const uint8*>(data));
|
| cert_data.Length = length;
|
| @@ -664,6 +740,34 @@
|
| }
|
|
|
| // static
|
| +X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes(
|
| + const char* data, size_t length, CertificateFormat format) {
|
| + OSCertHandles results;
|
| +
|
| + switch (format) {
|
| + case FORMAT_DER:
|
| + {
|
| + OSCertHandle handle = CreateOSCertHandleFromBytes(data, length);
|
| + if (handle)
|
| + results.push_back(handle);
|
| + }
|
| + break;
|
| + case FORMAT_PKCS7:
|
| + AddCertificatesFromData(data, length, kSecFormatPKCS7, &results);
|
| + break;
|
| + case FORMAT_LEGACY_NETSCAPE:
|
| + AddCertificatesFromData(data, length, kSecFormatNetscapeCertSequence,
|
| + &results);
|
| + break;
|
| + default:
|
| + NOTREACHED() << "Certificate format " << format << " unimplemented";
|
| + break;
|
| + }
|
| +
|
| + return results;
|
| +}
|
| +
|
| +// static
|
| X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
|
| OSCertHandle handle) {
|
| if (!handle)
|
| @@ -763,7 +867,7 @@
|
| }
|
|
|
| // static
|
| -bool X509Certificate::GetSSLClientCertificates (
|
| +bool X509Certificate::GetSSLClientCertificates(
|
| const std::string& server_domain,
|
| const std::vector<Principal>& valid_issuers,
|
| std::vector<scoped_refptr<X509Certificate> >* certs) {
|
| @@ -775,7 +879,7 @@
|
| SecIdentityRef identity = NULL;
|
| if (SecIdentityCopyPreference(domain_str,
|
| 0,
|
| - NULL, // validIssuers argument is ignored :(
|
| + NULL, // validIssuers argument is ignored :(
|
| &identity) == noErr)
|
| preferred_identity.reset(identity);
|
| }
|
| @@ -867,7 +971,7 @@
|
| }
|
| CFRelease(cert_chain);
|
| }
|
| -exit:
|
| + exit:
|
| if (result)
|
| LOG(ERROR) << "CreateIdentityCertificateChain error " << result;
|
| return chain.release();
|
|
|