// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/cert/x509_certificate.h" #include #include #include #include #include "base/lazy_instance.h" #include "base/logging.h" #include "base/mac/mac_logging.h" #include "base/mac/scoped_cftyperef.h" #include "base/memory/singleton.h" #include "base/pickle.h" #include "base/strings/string_piece.h" #include "base/strings/sys_string_conversions.h" #include "base/synchronization/lock.h" #include "crypto/cssm_init.h" #include "crypto/mac_security_services_lock.h" #include "net/cert/x509_util_mac.h" using base::ScopedCFTypeRef; using base::Time; namespace net { // CSSM functions are deprecated as of OSX 10.7, but have no replacement. // https://bugs.chromium.org/p/chromium/issues/detail?id=590914#c1 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" namespace { bool GetCertDistinguishedName( const x509_util::CSSMCachedCertificate& cached_cert, const CSSM_OID* oid, CertPrincipal* result) { x509_util::CSSMFieldValue distinguished_name; OSStatus status = cached_cert.GetField(oid, &distinguished_name); if (status || !distinguished_name.field()) return false; result->ParseDistinguishedName(distinguished_name.field()->Data, distinguished_name.field()->Length); return true; } bool IsCertIssuerInEncodedList(X509Certificate::OSCertHandle cert_handle, const std::vector& issuers) { x509_util::CSSMCachedCertificate cached_cert; if (cached_cert.Init(cert_handle) != CSSM_OK) return false; x509_util::CSSMFieldValue distinguished_name; OSStatus status = cached_cert.GetField(&CSSMOID_X509V1IssuerNameStd, &distinguished_name); if (status || !distinguished_name.field()) return false; base::StringPiece name_piece( reinterpret_cast(distinguished_name.field()->Data), static_cast(distinguished_name.field()->Length)); for (std::vector::const_iterator it = issuers.begin(); it != issuers.end(); ++it) { base::StringPiece issuer_piece(*it); if (name_piece == issuer_piece) return true; } return false; } bool GetCertDateForOID(const x509_util::CSSMCachedCertificate& cached_cert, const CSSM_OID* oid, Time* result) { *result = Time(); x509_util::CSSMFieldValue field; OSStatus status = cached_cert.GetField(oid, &field); if (status) return false; const CSSM_X509_TIME* x509_time = field.GetAs(); if (x509_time->timeType != BER_TAG_UTC_TIME && x509_time->timeType != BER_TAG_GENERALIZED_TIME) { LOG(ERROR) << "Unsupported date/time format " << x509_time->timeType; return false; } base::StringPiece time_string( reinterpret_cast(x509_time->time.Data), x509_time->time.Length); CertDateFormat format = x509_time->timeType == BER_TAG_UTC_TIME ? CERT_DATE_FORMAT_UTC_TIME : CERT_DATE_FORMAT_GENERALIZED_TIME; if (!ParseCertificateDate(time_string, format, result)) { LOG(ERROR) << "Invalid certificate date/time " << time_string; return false; } return true; } std::string GetCertSerialNumber( const x509_util::CSSMCachedCertificate& cached_cert) { x509_util::CSSMFieldValue serial_number; OSStatus status = cached_cert.GetField(&CSSMOID_X509V1SerialNumber, &serial_number); if (status || !serial_number.field()) return std::string(); return std::string( reinterpret_cast(serial_number.field()->Data), serial_number.field()->Length); } // Parses |data| of length |length|, attempting to decode it as the specified // |format|. If |data| is in the specified format, any certificates contained // within are stored into |output|. void AddCertificatesFromBytes(const char* data, size_t length, SecExternalFormat format, X509Certificate::OSCertHandles* output) { SecExternalFormat input_format = format; ScopedCFTypeRef local_data(CFDataCreateWithBytesNoCopy( kCFAllocatorDefault, reinterpret_cast(data), base::checked_cast(length), kCFAllocatorNull)); CFArrayRef items = NULL; OSStatus status; { base::AutoLock lock(crypto::GetMacSecurityServicesLock()); status = SecKeychainItemImport(local_data, NULL, &input_format, NULL, 0, NULL, NULL, &items); } if (status) { OSSTATUS_DLOG(WARNING, status) << "Unable to import items from data of length " << length; return; } ScopedCFTypeRef scoped_items(items); CFTypeID cert_type_id = SecCertificateGetTypeID(); for (CFIndex i = 0; i < CFArrayGetCount(items); ++i) { SecKeychainItemRef item = reinterpret_cast( const_cast(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) == cert_type_id) { SecCertificateRef cert = reinterpret_cast(item); // OS X ignores |input_format| if it detects that |local_data| is PEM // encoded, attempting to decode data based on internal rules for PEM // block headers. If a PKCS#7 blob is encoded with a PEM block of // CERTIFICATE, OS X 10.5 will return a single, invalid certificate // based on the decoded data. If this happens, the certificate should // not be included in |output|. Because |output| is empty, // CreateCertificateListfromBytes will use PEMTokenizer to decode the // data. When called again with the decoded data, OS X will honor // |input_format|, causing decode to succeed. On OS X 10.6, the data // is properly decoded as a PKCS#7, whether PEM or not, which avoids // the need to fallback to internal decoding. if (x509_util::IsValidSecCertificate(cert)) { CFRetain(cert); output->push_back(cert); } } } } } // namespace bool X509Certificate::Initialize() { x509_util::CSSMCachedCertificate cached_cert; if (cached_cert.Init(cert_handle_) != CSSM_OK) return false; serial_number_ = GetCertSerialNumber(cached_cert); return (!serial_number_.empty() && GetCertDistinguishedName(cached_cert, &CSSMOID_X509V1SubjectNameStd, &subject_) && GetCertDistinguishedName(cached_cert, &CSSMOID_X509V1IssuerNameStd, &issuer_) && GetCertDateForOID(cached_cert, &CSSMOID_X509V1ValidityNotBefore, &valid_start_) && GetCertDateForOID(cached_cert, &CSSMOID_X509V1ValidityNotAfter, &valid_expiry_)); } bool X509Certificate::IsIssuedByEncoded( const std::vector& valid_issuers) { if (IsCertIssuerInEncodedList(cert_handle_, valid_issuers)) return true; for (OSCertHandles::iterator it = intermediate_ca_certs_.begin(); it != intermediate_ca_certs_.end(); ++it) { if (IsCertIssuerInEncodedList(*it, valid_issuers)) return true; } return false; } bool X509Certificate::GetSubjectAltName( std::vector* dns_names, std::vector* ip_addrs) const { if (dns_names) dns_names->clear(); if (ip_addrs) ip_addrs->clear(); x509_util::CSSMCachedCertificate cached_cert; OSStatus status = cached_cert.Init(cert_handle_); if (status) return false; x509_util::CSSMFieldValue subject_alt_name; status = cached_cert.GetField(&CSSMOID_SubjectAltName, &subject_alt_name); if (status || !subject_alt_name.field()) return false; const CSSM_X509_EXTENSION* cssm_ext = subject_alt_name.GetAs(); if (!cssm_ext || !cssm_ext->value.parsedValue) return false; const CE_GeneralNames* alt_name = reinterpret_cast(cssm_ext->value.parsedValue); bool has_san = false; for (size_t name = 0; name < alt_name->numNames; ++name) { const CE_GeneralName& name_struct = alt_name->generalName[name]; const CSSM_DATA& name_data = name_struct.name; // DNSName and IPAddress are encoded as IA5String and OCTET STRINGs // respectively, both of which can be byte copied from // CSSM_DATA::data into the appropriate output vector. if (name_struct.nameType == GNT_DNSName) { has_san = true; if (dns_names) { dns_names->push_back(std::string( reinterpret_cast(name_data.Data), name_data.Length)); } } else if (name_struct.nameType == GNT_IPAddress) { has_san = true; if (ip_addrs) { ip_addrs->push_back(std::string( reinterpret_cast(name_data.Data), name_data.Length)); } } // Fast path: Found at least one subjectAltName and the caller doesn't // need the actual values. if (has_san && !ip_addrs && !dns_names) return true; } return has_san; } // static bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle, std::string* encoded) { CSSM_DATA der_data; if (!cert_handle || SecCertificateGetData(cert_handle, &der_data) != noErr) return false; encoded->assign(reinterpret_cast(der_data.Data), der_data.Length); return true; } // static bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a, X509Certificate::OSCertHandle b) { DCHECK(a && b); return CFEqual(a, b); } // static X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( const char* data, size_t length) { return x509_util::CreateSecCertificateFromBytes( reinterpret_cast(data), length) .release(); } // static X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes( const char* data, size_t length, Format format) { OSCertHandles results; switch (format) { case FORMAT_SINGLE_CERTIFICATE: { OSCertHandle handle = CreateOSCertHandleFromBytes(data, length); if (handle) results.push_back(handle); break; } case FORMAT_PKCS7: AddCertificatesFromBytes(data, length, kSecFormatPKCS7, &results); break; default: NOTREACHED() << "Certificate format " << format << " unimplemented"; break; } return results; } // static X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle( OSCertHandle handle) { if (!handle) return NULL; return reinterpret_cast(const_cast(CFRetain(handle))); } // static void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) { if (cert_handle) CFRelease(cert_handle); } // static SHA256HashValue X509Certificate::CalculateFingerprint256(OSCertHandle cert) { return x509_util::CalculateFingerprint256(cert); } // static SHA256HashValue X509Certificate::CalculateCAFingerprint256( const OSCertHandles& intermediates) { SHA256HashValue sha256; memset(sha256.data, 0, sizeof(sha256.data)); // The CC_SHA(3cc) man page says all CC_SHA256_xxx routines return 1, so // we don't check their return values. CC_SHA256_CTX sha256_ctx; CC_SHA256_Init(&sha256_ctx); CSSM_DATA cert_data; for (size_t i = 0; i < intermediates.size(); ++i) { OSStatus status = SecCertificateGetData(intermediates[i], &cert_data); if (status) return sha256; CC_SHA256_Update(&sha256_ctx, cert_data.Data, cert_data.Length); } CC_SHA256_Final(sha256.data, &sha256_ctx); return sha256; } // static X509Certificate::OSCertHandle X509Certificate::ReadOSCertHandleFromPickle( base::PickleIterator* pickle_iter) { const char* data; int length; if (!pickle_iter->ReadData(&data, &length)) return NULL; return CreateOSCertHandleFromBytes(data, length); } // static bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle, base::Pickle* pickle) { CSSM_DATA cert_data; OSStatus status = SecCertificateGetData(cert_handle, &cert_data); if (status) return false; return pickle->WriteData(reinterpret_cast(cert_data.Data), cert_data.Length); } // static void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle, size_t* size_bits, PublicKeyType* type) { // Since we might fail, set the output parameters to default values first. *type = kPublicKeyTypeUnknown; *size_bits = 0; SecKeyRef key; OSStatus status = SecCertificateCopyPublicKey(cert_handle, &key); if (status) { // SecCertificateCopyPublicKey may fail if the certificate has an invalid // key. See https://crbug.com/472291. LOG(WARNING) << "SecCertificateCopyPublicKey failed: " << status; return; } ScopedCFTypeRef scoped_key(key); const CSSM_KEY* cssm_key; status = SecKeyGetCSSMKey(key, &cssm_key); if (status) { NOTREACHED() << "SecKeyGetCSSMKey failed: " << status; return; } *size_bits = cssm_key->KeyHeader.LogicalKeySizeInBits; switch (cssm_key->KeyHeader.AlgorithmId) { case CSSM_ALGID_RSA: *type = kPublicKeyTypeRSA; break; case CSSM_ALGID_DSA: *type = kPublicKeyTypeDSA; break; case CSSM_ALGID_ECDSA: *type = kPublicKeyTypeECDSA; break; case CSSM_ALGID_DH: *type = kPublicKeyTypeDH; break; default: *type = kPublicKeyTypeUnknown; *size_bits = 0; break; } } // static bool X509Certificate::IsSelfSigned(OSCertHandle cert_handle) { return x509_util::IsSelfSigned(cert_handle); } #pragma clang diagnostic pop // "-Wdeprecated-declarations" } // namespace net