| Index: net/base/x509_certificate_mac.cc
|
| diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc
|
| index 6b0c1052ee2331588baba4604b16d6a40a917912..8cc33a6bb8d9b1a6d376e11369eab8bec73c0a66 100644
|
| --- a/net/base/x509_certificate_mac.cc
|
| +++ b/net/base/x509_certificate_mac.cc
|
| @@ -129,99 +129,181 @@ CertStatus CertStatusFromOSStatus(OSStatus status) {
|
| }
|
| }
|
|
|
| -struct CSSMFields {
|
| - CSSMFields() : cl_handle(NULL), num_of_fields(0), fields(NULL) {}
|
| - ~CSSMFields() {
|
| - if (cl_handle)
|
| - CSSM_CL_FreeFields(cl_handle, num_of_fields, &fields);
|
| +// Wrapper for a CSSM_DATA_PTR that was obtained via one of the CSSM field
|
| +// accessors (such as CSSM_CL_CertGet[First/Next]Value or
|
| +// CSSM_CL_CertGet[First/Next]CachedValue).
|
| +class CSSMFieldValue {
|
| + public:
|
| + CSSMFieldValue() : cl_handle_(NULL), oid_(NULL), field_(NULL) {}
|
| + CSSMFieldValue(CSSM_CL_HANDLE cl_handle,
|
| + const CSSM_OID* oid,
|
| + CSSM_DATA_PTR field)
|
| + : cl_handle_(cl_handle),
|
| + oid_(const_cast<CSSM_OID_PTR>(oid)),
|
| + field_(field) {
|
| + }
|
| +
|
| + ~CSSMFieldValue() {
|
| + Reset(NULL, NULL, NULL);
|
| + }
|
| +
|
| + CSSM_OID_PTR oid() const { return oid_; }
|
| + CSSM_DATA_PTR field() const { return field_; }
|
| +
|
| + // Returns the field as if it was an arbitrary type - most commonly, by
|
| + // interpreting the field as a specific CSSM/CDSA parsed type, such as
|
| + // CSSM_X509_SUBJECT_PUBLIC_KEY_INFO or CSSM_X509_ALGORITHM_IDENTIFIER.
|
| + // An added check is applied to ensure that the current field is large
|
| + // enough to actually contain the requested type.
|
| + template <typename T> const T* GetAs() const {
|
| + if (!field_ || field_->Length < sizeof(T))
|
| + return NULL;
|
| + return reinterpret_cast<const T*>(field_->Data);
|
| }
|
|
|
| - CSSM_CL_HANDLE cl_handle;
|
| - uint32 num_of_fields;
|
| - CSSM_FIELD_PTR fields;
|
| + void Reset(CSSM_CL_HANDLE cl_handle,
|
| + CSSM_OID_PTR oid,
|
| + CSSM_DATA_PTR field) {
|
| + if (cl_handle_ && oid_ && field_)
|
| + CSSM_CL_FreeFieldValue(cl_handle_, oid_, field_);
|
| + cl_handle_ = cl_handle;
|
| + oid_ = oid;
|
| + field_ = field;
|
| + }
|
| +
|
| + private:
|
| + CSSM_CL_HANDLE cl_handle_;
|
| + CSSM_OID_PTR oid_;
|
| + CSSM_DATA_PTR field_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(CSSMFieldValue);
|
| };
|
|
|
| -OSStatus GetCertFields(X509Certificate::OSCertHandle cert_handle,
|
| - CSSMFields* fields) {
|
| - DCHECK(cert_handle);
|
| - DCHECK(fields);
|
| +// CSSMCachedCertificate is a container class that is used to wrap the
|
| +// CSSM_CL_CertCache APIs and provide safe and efficient access to
|
| +// certificate fields in their CSSM form.
|
| +//
|
| +// To provide efficient access to certificate/CRL fields, CSSM provides an
|
| +// API/SPI to "cache" a certificate/CRL. The exact meaning of a cached
|
| +// certificate is not defined by CSSM, but is documented to generally be some
|
| +// intermediate or parsed form of the certificate. In the case of Apple's
|
| +// CSSM CL implementation, the intermediate form is the parsed certificate
|
| +// stored in an internal format (which happens to be NSS). By caching the
|
| +// certificate, callers that wish to access multiple fields (such as subject,
|
| +// issuer, and validity dates) do not need to repeatedly parse the entire
|
| +// certificate, nor are they forced to convert all fields from their NSS types
|
| +// to their CSSM equivalents. This latter point is especially helpful when
|
| +// running on OS X 10.5, as it will fail to convert some fields that reference
|
| +// unsupported algorithms, such as ECC.
|
| +class CSSMCachedCertificate {
|
| + public:
|
| + CSSMCachedCertificate() : cl_handle_(NULL), cached_cert_handle_(NULL) {}
|
| + ~CSSMCachedCertificate() {
|
| + if (cl_handle_ && cached_cert_handle_)
|
| + CSSM_CL_CertAbortCache(cl_handle_, cached_cert_handle_);
|
| + }
|
|
|
| - CSSM_DATA cert_data;
|
| - OSStatus status = SecCertificateGetData(cert_handle, &cert_data);
|
| - if (status)
|
| - return status;
|
| + // Initializes the CSSMCachedCertificate by caching the specified
|
| + // |os_cert_handle|. On success, returns noErr.
|
| + // Note: Once initialized, the cached certificate should only be accessed
|
| + // from a single thread.
|
| + OSStatus Init(SecCertificateRef os_cert_handle) {
|
| + DCHECK(!cl_handle_ && !cached_cert_handle_);
|
| + DCHECK(os_cert_handle);
|
| + CSSM_DATA cert_data;
|
| + OSStatus status = SecCertificateGetData(os_cert_handle, &cert_data);
|
| + if (status)
|
| + return status;
|
| + status = SecCertificateGetCLHandle(os_cert_handle, &cl_handle_);
|
| + if (status) {
|
| + DCHECK(!cl_handle_);
|
| + return status;
|
| + }
|
|
|
| - status = SecCertificateGetCLHandle(cert_handle, &fields->cl_handle);
|
| - if (status) {
|
| - DCHECK(!fields->cl_handle);
|
| + status = CSSM_CL_CertCache(cl_handle_, &cert_data, &cached_cert_handle_);
|
| + if (status)
|
| + DCHECK(!cached_cert_handle_);
|
| return status;
|
| }
|
|
|
| - status = CSSM_CL_CertGetAllFields(fields->cl_handle, &cert_data,
|
| - &fields->num_of_fields, &fields->fields);
|
| - return status;
|
| -}
|
| + // Fetches the first value for the field associated with |field_oid|.
|
| + // If |field_oid| is a valid OID and is present in the current certificate,
|
| + // returns CSSM_OK and stores the first value in |field|. If additional
|
| + // values are associated with |field_oid|, they are ignored.
|
| + OSStatus GetField(const CSSM_OID* field_oid,
|
| + CSSMFieldValue* field) const {
|
| + DCHECK(cl_handle_);
|
| + DCHECK(cached_cert_handle_);
|
| +
|
| + CSSM_OID_PTR oid = const_cast<CSSM_OID_PTR>(field_oid);
|
| + CSSM_DATA_PTR field_ptr = NULL;
|
| + CSSM_HANDLE results_handle = NULL;
|
| + uint32 field_value_count = 0;
|
| + CSSM_RETURN status = CSSM_CL_CertGetFirstCachedFieldValue(
|
| + cl_handle_, cached_cert_handle_, oid, &results_handle,
|
| + &field_value_count, &field_ptr);
|
| + if (status)
|
| + return status;
|
| +
|
| + // Note: |field_value_count| may be > 1, indicating that more than one
|
| + // value is present. This may happen with extensions, but for current
|
| + // usages, only the first value is returned.
|
| + CSSM_CL_CertAbortQuery(cl_handle_, results_handle);
|
| + field->Reset(cl_handle_, oid, field_ptr);
|
| + return CSSM_OK;
|
| + }
|
| +
|
| + private:
|
| + CSSM_CL_HANDLE cl_handle_;
|
| + CSSM_HANDLE cached_cert_handle_;
|
| +};
|
|
|
| -void GetCertDateForOID(X509Certificate::OSCertHandle cert_handle,
|
| - CSSM_OID oid, Time* result) {
|
| +void GetCertDateForOID(const CSSMCachedCertificate& cached_cert,
|
| + const CSSM_OID* oid,
|
| + Time* result) {
|
| *result = Time::Time();
|
|
|
| - CSSMFields fields;
|
| - OSStatus status = GetCertFields(cert_handle, &fields);
|
| + CSSMFieldValue field;
|
| + OSStatus status = cached_cert.GetField(oid, &field);
|
| if (status)
|
| return;
|
|
|
| - for (size_t field = 0; field < fields.num_of_fields; ++field) {
|
| - if (CSSMOIDEqual(&fields.fields[field].FieldOid, &oid)) {
|
| - CSSM_X509_TIME* x509_time = reinterpret_cast<CSSM_X509_TIME*>(
|
| - fields.fields[field].FieldValue.Data);
|
| - 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;
|
| - }
|
| -
|
| - base::StringPiece time_string(
|
| - reinterpret_cast<const char*>(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;
|
| - }
|
| + const CSSM_X509_TIME* x509_time = field.GetAs<CSSM_X509_TIME>();
|
| + 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;
|
| }
|
| -}
|
|
|
| -std::string GetCertSerialNumber(X509Certificate::OSCertHandle cert_handle) {
|
| - CSSMFields fields;
|
| - OSStatus status = GetCertFields(cert_handle, &fields);
|
| - if (status)
|
| - return "";
|
| + base::StringPiece time_string(
|
| + reinterpret_cast<const char*>(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;
|
| +}
|
|
|
| - std::string ret;
|
| - for (size_t field = 0; field < fields.num_of_fields; ++field) {
|
| - if (!CSSMOIDEqual(&fields.fields[field].FieldOid,
|
| - &CSSMOID_X509V1SerialNumber)) {
|
| - continue;
|
| - }
|
| - ret.assign(
|
| - reinterpret_cast<char*>(fields.fields[field].FieldValue.Data),
|
| - fields.fields[field].FieldValue.Length);
|
| - break;
|
| - }
|
| +std::string GetCertSerialNumber(const CSSMCachedCertificate& cached_cert) {
|
| + CSSMFieldValue serial_number;
|
| + OSStatus status = cached_cert.GetField(&CSSMOID_X509V1SerialNumber,
|
| + &serial_number);
|
| + if (status || !serial_number.field())
|
| + return std::string();
|
|
|
| - return ret;
|
| + return std::string(
|
| + reinterpret_cast<const char*>(serial_number.field()->Data),
|
| + serial_number.field()->Length);
|
| }
|
|
|
| // Creates a SecPolicyRef for the given OID, with optional value.
|
| -OSStatus CreatePolicy(const CSSM_OID* policy_OID,
|
| +OSStatus CreatePolicy(const CSSM_OID* policy_oid,
|
| void* option_data,
|
| size_t option_length,
|
| SecPolicyRef* policy) {
|
| SecPolicySearchRef search;
|
| - OSStatus err = SecPolicySearchCreate(CSSM_CERT_X_509v3, policy_OID, NULL,
|
| + OSStatus err = SecPolicySearchCreate(CSSM_CERT_X_509v3, policy_oid, NULL,
|
| &search);
|
| if (err)
|
| return err;
|
| @@ -322,41 +404,35 @@ void GetCertChainInfo(CFArrayRef cert_chain,
|
| continue;
|
| }
|
|
|
| - CSSMFields cssm_fields;
|
| - OSStatus status = GetCertFields(chain_cert, &cssm_fields);
|
| + CSSMCachedCertificate cached_cert;
|
| + OSStatus status = cached_cert.Init(chain_cert);
|
| if (status)
|
| continue;
|
| - CSSM_FIELD_PTR fields = cssm_fields.fields;
|
| - for (size_t field = 0; field < cssm_fields.num_of_fields; ++field) {
|
| - if (!CSSMOIDEqual(&fields[field].FieldOid,
|
| - &CSSMOID_X509V1SignatureAlgorithm)) {
|
| - continue;
|
| - }
|
| + CSSMFieldValue signature_field;
|
| + status = cached_cert.GetField(&CSSMOID_X509V1SignatureAlgorithm,
|
| + &signature_field);
|
| + if (status || !signature_field.field())
|
| + continue;
|
| + // Match the behaviour of OS X system tools and defensively check that
|
| + // sizes are appropriate. This would indicate a critical failure of the
|
| + // OS X certificate library, but based on history, it is best to play it
|
| + // safe.
|
| + const CSSM_X509_ALGORITHM_IDENTIFIER* sig_algorithm =
|
| + signature_field.GetAs<CSSM_X509_ALGORITHM_IDENTIFIER>();
|
| + if (!sig_algorithm)
|
| + continue;
|
|
|
| - CSSM_X509_ALGORITHM_IDENTIFIER* signature_algorithm =
|
| - reinterpret_cast<CSSM_X509_ALGORITHM_IDENTIFIER*>(
|
| - fields[field].FieldValue.Data);
|
| - // Match the behaviour of OS X system tools and defensively check that
|
| - // sizes are appropriate. This would indicate a critical failure of the
|
| - // OS X certificate library, but based on history, it is best to play it
|
| - // safe.
|
| - if (!signature_algorithm || (fields[field].FieldValue.Length !=
|
| - sizeof(CSSM_X509_ALGORITHM_IDENTIFIER))) {
|
| - break;
|
| - }
|
| - CSSM_OID_PTR alg_oid = &signature_algorithm->algorithm;
|
| - if (CSSMOIDEqual(alg_oid, &CSSMOID_MD2WithRSA)) {
|
| - verify_result->has_md2 = true;
|
| - if (i != 0)
|
| - verify_result->has_md2_ca = true;
|
| - } else if (CSSMOIDEqual(alg_oid, &CSSMOID_MD4WithRSA)) {
|
| - verify_result->has_md4 = true;
|
| - } else if (CSSMOIDEqual(alg_oid, &CSSMOID_MD5WithRSA)) {
|
| - verify_result->has_md5 = true;
|
| - if (i != 0)
|
| - verify_result->has_md5_ca = true;
|
| - }
|
| - break;
|
| + const CSSM_OID* alg_oid = &sig_algorithm->algorithm;
|
| + if (CSSMOIDEqual(alg_oid, &CSSMOID_MD2WithRSA)) {
|
| + verify_result->has_md2 = true;
|
| + if (i != 0)
|
| + verify_result->has_md2_ca = true;
|
| + } else if (CSSMOIDEqual(alg_oid, &CSSMOID_MD4WithRSA)) {
|
| + verify_result->has_md4 = true;
|
| + } else if (CSSMOIDEqual(alg_oid, &CSSMOID_MD5WithRSA)) {
|
| + verify_result->has_md5 = true;
|
| + if (i != 0)
|
| + verify_result->has_md5_ca = true;
|
| }
|
| }
|
| if (!verified_cert)
|
| @@ -610,14 +686,17 @@ void X509Certificate::Initialize() {
|
| if (!status)
|
| issuer_.Parse(name);
|
|
|
| - GetCertDateForOID(cert_handle_, CSSMOID_X509V1ValidityNotBefore,
|
| - &valid_start_);
|
| - GetCertDateForOID(cert_handle_, CSSMOID_X509V1ValidityNotAfter,
|
| - &valid_expiry_);
|
| + CSSMCachedCertificate cached_cert;
|
| + if (cached_cert.Init(cert_handle_) == CSSM_OK) {
|
| + GetCertDateForOID(cached_cert, &CSSMOID_X509V1ValidityNotBefore,
|
| + &valid_start_);
|
| + GetCertDateForOID(cached_cert, &CSSMOID_X509V1ValidityNotAfter,
|
| + &valid_expiry_);
|
| + serial_number_ = GetCertSerialNumber(cached_cert);
|
| + }
|
|
|
| fingerprint_ = CalculateFingerprint(cert_handle_);
|
| ca_fingerprint_ = CalculateCAFingerprint(intermediate_ca_certs_);
|
| - serial_number_ = GetCertSerialNumber(cert_handle_);
|
| }
|
|
|
| // IsIssuedByKnownRoot returns true if the given chain is rooted at a root CA
|
| @@ -780,35 +859,35 @@ void X509Certificate::GetSubjectAltName(
|
| if (ip_addrs)
|
| ip_addrs->clear();
|
|
|
| - CSSMFields fields;
|
| - OSStatus status = GetCertFields(cert_handle_, &fields);
|
| + CSSMCachedCertificate cached_cert;
|
| + OSStatus status = cached_cert.Init(cert_handle_);
|
| if (status)
|
| return;
|
| -
|
| - for (size_t field = 0; field < fields.num_of_fields; ++field) {
|
| - if (!CSSMOIDEqual(&fields.fields[field].FieldOid, &CSSMOID_SubjectAltName))
|
| - continue;
|
| - CSSM_X509_EXTENSION_PTR cssm_ext =
|
| - reinterpret_cast<CSSM_X509_EXTENSION_PTR>(
|
| - fields.fields[field].FieldValue.Data);
|
| - CE_GeneralNames* alt_name =
|
| - reinterpret_cast<CE_GeneralNames*>(cssm_ext->value.parsedValue);
|
| -
|
| - 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 (dns_names && name_struct.nameType == GNT_DNSName) {
|
| - dns_names->push_back(std::string(
|
| - reinterpret_cast<const char*>(name_data.Data),
|
| - name_data.Length));
|
| - } else if (ip_addrs && name_struct.nameType == GNT_IPAddress) {
|
| - ip_addrs->push_back(std::string(
|
| - reinterpret_cast<const char*>(name_data.Data),
|
| - name_data.Length));
|
| - }
|
| + CSSMFieldValue subject_alt_name;
|
| + status = cached_cert.GetField(&CSSMOID_SubjectAltName, &subject_alt_name);
|
| + if (status || !subject_alt_name.field())
|
| + return;
|
| + const CSSM_X509_EXTENSION* cssm_ext =
|
| + subject_alt_name.GetAs<CSSM_X509_EXTENSION>();
|
| + if (!cssm_ext || !cssm_ext->value.parsedValue)
|
| + return;
|
| + const CE_GeneralNames* alt_name =
|
| + reinterpret_cast<const CE_GeneralNames*>(cssm_ext->value.parsedValue);
|
| +
|
| + 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 (dns_names && name_struct.nameType == GNT_DNSName) {
|
| + dns_names->push_back(std::string(
|
| + reinterpret_cast<const char*>(name_data.Data),
|
| + name_data.Length));
|
| + } else if (ip_addrs && name_struct.nameType == GNT_IPAddress) {
|
| + ip_addrs->push_back(std::string(
|
| + reinterpret_cast<const char*>(name_data.Data),
|
| + name_data.Length));
|
| }
|
| }
|
| }
|
| @@ -1147,26 +1226,11 @@ SHA1Fingerprint X509Certificate::CalculateCAFingerprint(
|
| }
|
|
|
| bool X509Certificate::SupportsSSLClientAuth() const {
|
| - CSSMFields fields;
|
| - if (GetCertFields(cert_handle_, &fields) != noErr)
|
| + CSSMCachedCertificate cached_cert;
|
| + OSStatus status = cached_cert.Init(cert_handle_);
|
| + if (status)
|
| return false;
|
|
|
| - // Gather the extensions we care about. We do not support
|
| - // CSSMOID_NetscapeCertType on OS X.
|
| - const CE_ExtendedKeyUsage* ext_key_usage = NULL;
|
| - const CE_KeyUsage* key_usage = NULL;
|
| - for (unsigned f = 0; f < fields.num_of_fields; ++f) {
|
| - const CSSM_FIELD& field = fields.fields[f];
|
| - const CSSM_X509_EXTENSION* ext =
|
| - reinterpret_cast<const CSSM_X509_EXTENSION*>(field.FieldValue.Data);
|
| - if (CSSMOIDEqual(&field.FieldOid, &CSSMOID_KeyUsage)) {
|
| - key_usage = reinterpret_cast<const CE_KeyUsage*>(ext->value.parsedValue);
|
| - } else if (CSSMOIDEqual(&field.FieldOid, &CSSMOID_ExtendedKeyUsage)) {
|
| - ext_key_usage =
|
| - reinterpret_cast<const CE_ExtendedKeyUsage*>(ext->value.parsedValue);
|
| - }
|
| - }
|
| -
|
| // RFC5280 says to take the intersection of the two extensions.
|
| //
|
| // Our underlying crypto libraries don't expose
|
| @@ -1176,11 +1240,24 @@ bool X509Certificate::SupportsSSLClientAuth() const {
|
| //
|
| // In particular, if a key has the nonRepudiation bit and not the
|
| // digitalSignature one, we will not offer it to the user.
|
| - if (key_usage && !((*key_usage) & CE_KU_DigitalSignature))
|
| - return false;
|
| - if (ext_key_usage && !ExtendedKeyUsageAllows(ext_key_usage,
|
| - &CSSMOID_ClientAuth))
|
| - return false;
|
| + CSSMFieldValue key_usage;
|
| + status = cached_cert.GetField(&CSSMOID_KeyUsage, &key_usage);
|
| + if (status == CSSM_OK && key_usage.field()) {
|
| + const CSSM_X509_EXTENSION* ext = key_usage.GetAs<CSSM_X509_EXTENSION>();
|
| + const CE_KeyUsage* key_usage_value =
|
| + reinterpret_cast<const CE_KeyUsage*>(ext->value.parsedValue);
|
| + if (!((*key_usage_value) & CE_KU_DigitalSignature))
|
| + return false;
|
| + }
|
| +
|
| + status = cached_cert.GetField(&CSSMOID_ExtendedKeyUsage, &key_usage);
|
| + if (status == CSSM_OK && key_usage.field()) {
|
| + const CSSM_X509_EXTENSION* ext = key_usage.GetAs<CSSM_X509_EXTENSION>();
|
| + const CE_ExtendedKeyUsage* ext_key_usage =
|
| + reinterpret_cast<const CE_ExtendedKeyUsage*>(ext->value.parsedValue);
|
| + if (!ExtendedKeyUsageAllows(ext_key_usage, &CSSMOID_ClientAuth))
|
| + return false;
|
| + }
|
| return true;
|
| }
|
|
|
|
|