diff options
-rw-r--r-- | net/base/x509_cert_types.cc | 48 | ||||
-rw-r--r-- | net/base/x509_cert_types.h | 22 | ||||
-rw-r--r-- | net/base/x509_certificate.h | 2 | ||||
-rw-r--r-- | net/base/x509_certificate_mac.cc | 116 | ||||
-rw-r--r-- | net/base/x509_openssl_util.cc | 53 |
5 files changed, 125 insertions, 116 deletions
diff --git a/net/base/x509_cert_types.cc b/net/base/x509_cert_types.cc index cdfbdaa..6beb3ec 100644 --- a/net/base/x509_cert_types.cc +++ b/net/base/x509_cert_types.cc @@ -6,9 +6,27 @@ #include "net/base/x509_certificate.h" #include "base/logging.h" +#include "base/string_number_conversions.h" +#include "base/string_piece.h" +#include "base/time.h" namespace net { +namespace { + +// Helper for ParseCertificateDate. |*field| must contain at least +// |field_len| characters. |*field| will be advanced by |field_len| on exit. +// |*ok| is set to false if there is an error in parsing the number, but left +// untouched otherwise. Returns the parsed integer. +int ParseIntAndAdvance(const char** field, size_t field_len, bool* ok) { + int result = 0; + *ok &= base::StringToInt(*field, *field + field_len, &result); + *field += field_len; + return result; +} + +} // namespace + CertPrincipal::CertPrincipal() { } @@ -75,4 +93,34 @@ bool CertPolicy::HasDeniedCert() const { return !denied_.empty(); } +bool ParseCertificateDate(const base::StringPiece& raw_date, + CertDateFormat format, + base::Time* time) { + size_t year_length = format == CERT_DATE_FORMAT_UTC_TIME ? 2 : 4; + + if (raw_date.length() < 11 + year_length) + return false; + + const char* field = raw_date.data(); + bool valid = true; + base::Time::Exploded exploded = {0}; + + exploded.year = ParseIntAndAdvance(&field, year_length, &valid); + exploded.month = ParseIntAndAdvance(&field, 2, &valid); + exploded.day_of_month = ParseIntAndAdvance(&field, 2, &valid); + exploded.hour = ParseIntAndAdvance(&field, 2, &valid); + exploded.minute = ParseIntAndAdvance(&field, 2, &valid); + exploded.second = ParseIntAndAdvance(&field, 2, &valid); + if (valid && year_length == 2) + exploded.year += exploded.year < 50 ? 2000 : 1900; + + valid &= exploded.HasValidValues(); + + if (!valid) + return false; + + *time = base::Time::FromUTCExploded(exploded); + return true; +} + } // namespace net diff --git a/net/base/x509_cert_types.h b/net/base/x509_cert_types.h index f762e56..eb3ad60 100644 --- a/net/base/x509_cert_types.h +++ b/net/base/x509_cert_types.h @@ -18,6 +18,11 @@ #include <Security/x509defs.h> #endif +namespace base { +class Time; +class StringPiece; +} // namespace base + namespace net { class X509Certificate; @@ -127,6 +132,23 @@ inline bool CSSMOIDEqual(const CSSM_OID* oid1, const CSSM_OID* oid2) { } #endif +// A list of ASN.1 date/time formats that ParseCertificateDate() supports, +// encoded in the canonical forms specified in RFC 2459/3280/5280. +enum CertDateFormat { + // UTCTime: Format is YYMMDDHHMMSSZ + CERT_DATE_FORMAT_UTC_TIME, + + // GeneralizedTime: Format is YYYYMMDDHHMMSSZ + CERT_DATE_FORMAT_GENERALIZED_TIME, +}; + +// Attempts to parse |raw_date|, an ASN.1 date/time string encoded as +// |format|, and writes the result into |*time|. If an invalid date is +// specified, or if parsing fails, returns false, and |*time| will not be +// updated. +bool ParseCertificateDate(const base::StringPiece& raw_date, + CertDateFormat format, + base::Time* time); } // namespace net #endif // NET_BASE_X509_CERT_TYPES_H_ diff --git a/net/base/x509_certificate.h b/net/base/x509_certificate.h index c0342a1..c2a378a 100644 --- a/net/base/x509_certificate.h +++ b/net/base/x509_certificate.h @@ -220,7 +220,7 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { static bool GetSSLClientCertificates( const std::string& server_domain, const std::vector<CertPrincipal>& valid_issuers, - std::vector<scoped_refptr<X509Certificate> >* certs); + CertificateList* certs); // Creates the chain of certs to use for this client identity cert. CFArrayRef CreateClientCertificateChain() const; diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc index 5a5d457c..d4858e6 100644 --- a/net/base/x509_certificate_mac.cc +++ b/net/base/x509_certificate_mac.cc @@ -222,9 +222,10 @@ void GetCertGeneralNamesForOID(X509Certificate::OSCertHandle cert_handle, for (size_t field = 0; field < fields.num_of_fields; ++field) { if (CSSMOIDEqual(&fields.fields[field].FieldOid, &oid)) { CSSM_X509_EXTENSION_PTR cssm_ext = - (CSSM_X509_EXTENSION_PTR)fields.fields[field].FieldValue.Data; + reinterpret_cast<CSSM_X509_EXTENSION_PTR>( + fields.fields[field].FieldValue.Data); CE_GeneralNames* alt_name = - (CE_GeneralNames*) cssm_ext->value.parsedValue; + 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]; @@ -235,10 +236,9 @@ void GetCertGeneralNamesForOID(X509Certificate::OSCertHandle cert_handle, // CE_GeneralNameType for more information. if (name_struct.nameType == name_type) { const CSSM_DATA& name_data = name_struct.name; - std::string value = - std::string(reinterpret_cast<std::string::value_type*> - (name_data.Data), - name_data.Length); + std::string value = std::string( + reinterpret_cast<const char*>(name_data.Data), + name_data.Length); result->push_back(value); } } @@ -257,43 +257,23 @@ void GetCertDateForOID(X509Certificate::OSCertHandle cert_handle, 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); - std::string time_string = - std::string(reinterpret_cast<std::string::value_type*> - (x509_time->time.Data), - x509_time->time.Length); - - DCHECK(x509_time->timeType == BER_TAG_UTC_TIME || - x509_time->timeType == BER_TAG_GENERALIZED_TIME); - - struct tm time; - const char* parse_string; - if (x509_time->timeType == BER_TAG_UTC_TIME) - parse_string = "%y%m%d%H%M%SZ"; - else if (x509_time->timeType == BER_TAG_GENERALIZED_TIME) - parse_string = "%y%m%d%H%M%SZ"; - else { - // Those are the only two BER tags for time; if neither are used then - // this is a rather broken cert. + 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; } - strptime(time_string.c_str(), parse_string, &time); - - Time::Exploded exploded; - exploded.year = time.tm_year + 1900; - exploded.month = time.tm_mon + 1; - exploded.day_of_week = time.tm_wday; - exploded.day_of_month = time.tm_mday; - exploded.hour = time.tm_hour; - exploded.minute = time.tm_min; - exploded.second = time.tm_sec; - exploded.millisecond = 0; - - *result = Time::FromUTCExploded(exploded); - break; + 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; } } } @@ -334,7 +314,8 @@ OSStatus CreatePolicy(const CSSM_OID* policy_OID, // Caller is responsible for releasing the value stored into *out_cert_chain. OSStatus CopyCertChain(SecCertificateRef cert_handle, CFArrayRef* out_cert_chain) { - DCHECK(cert_handle && out_cert_chain); + DCHECK(cert_handle); + DCHECK(out_cert_chain); // Create an SSL policy ref configured for client cert evaluation. SecPolicyRef ssl_policy; OSStatus result = X509Certificate::CreateSSLClientPolicy(&ssl_policy); @@ -343,9 +324,9 @@ OSStatus CopyCertChain(SecCertificateRef cert_handle, ScopedCFTypeRef<SecPolicyRef> scoped_ssl_policy(ssl_policy); // Create a SecTrustRef. - ScopedCFTypeRef<CFArrayRef> input_certs( - CFArrayCreate(NULL, (const void**)&cert_handle, 1, - &kCFTypeArrayCallBacks)); + ScopedCFTypeRef<CFArrayRef> input_certs(CFArrayCreate( + NULL, const_cast<const void**>(reinterpret_cast<void**>(&cert_handle)), + 1, &kCFTypeArrayCallBacks)); SecTrustRef trust_ref = NULL; result = SecTrustCreateWithCertificates(input_certs, ssl_policy, &trust_ref); if (result) @@ -400,8 +381,8 @@ void AddCertificatesFromBytes(const char* data, size_t length, X509Certificate::OSCertHandles* output) { SecExternalFormat input_format = format; ScopedCFTypeRef<CFDataRef> local_data(CFDataCreateWithBytesNoCopy( - kCFAllocatorDefault, reinterpret_cast<const UInt8*>(data), - length, kCFAllocatorNull)); + kCFAllocatorDefault, reinterpret_cast<const UInt8*>(data), length, + kCFAllocatorNull)); CFArrayRef items = NULL; OSStatus status = SecKeychainItemImport(local_data, NULL, &input_format, @@ -452,13 +433,12 @@ void SetMacTestCertificate(X509Certificate* cert) { void X509Certificate::Initialize() { const CSSM_X509_NAME* name; OSStatus status = SecCertificateGetSubject(cert_handle_, &name); - if (!status) { + if (!status) subject_.Parse(name); - } + status = SecCertificateGetIssuer(cert_handle_, &name); - if (!status) { + if (!status) issuer_.Parse(name); - } GetCertDateForOID(cert_handle_, CSSMOID_X509V1ValidityNotBefore, &valid_start_); @@ -659,9 +639,8 @@ int X509Certificate::Verify(const std::string& hostname, int flags, if (cert_status == CERT_STATUS_COMMON_NAME_INVALID) { std::vector<std::string> names; GetDNSNames(&names); - if (OverrideHostnameMismatch(hostname, &names)) { + if (OverrideHostnameMismatch(hostname, &names)) cert_status = 0; - } } verify_result->cert_status |= cert_status; } @@ -682,9 +661,8 @@ int X509Certificate::Verify(const std::string& hostname, int flags, if (status) return NetErrorFromOSStatus(status); verify_result->cert_status |= CertStatusFromOSStatus(cssm_result); - if (!verify_result->cert_status) { + if (!verify_result->cert_status) verify_result->cert_status |= CERT_STATUS_INVALID; - } break; } @@ -818,8 +796,8 @@ SHA1Fingerprint X509Certificate::CalculateFingerprint( if (status) return sha1; - DCHECK(NULL != cert_data.Data); - DCHECK(0 != cert_data.Length); + DCHECK(cert_data.Data); + DCHECK_NE(cert_data.Length, 0U); CC_SHA1(cert_data.Data, cert_data.Length, sha1.data); @@ -870,7 +848,7 @@ bool X509Certificate::IsIssuedBy( CFArrayRef cert_chain = NULL; OSStatus result; result = CopyCertChain(os_cert_handle(), &cert_chain); - if (result != noErr) + if (result) return false; ScopedCFTypeRef<CFArrayRef> scoped_cert_chain(cert_chain); @@ -906,20 +884,22 @@ OSStatus X509Certificate::CreateSSLClientPolicy(SecPolicyRef* out_policy) { } // static -bool X509Certificate::GetSSLClientCertificates ( +bool X509Certificate::GetSSLClientCertificates( const std::string& server_domain, const std::vector<CertPrincipal>& valid_issuers, - std::vector<scoped_refptr<X509Certificate> >* certs) { + CertificateList* certs) { ScopedCFTypeRef<SecIdentityRef> preferred_identity; if (!server_domain.empty()) { // See if there's an identity preference for this domain: ScopedCFTypeRef<CFStringRef> domain_str( base::SysUTF8ToCFStringRef("https://" + server_domain)); SecIdentityRef identity = NULL; - if (SecIdentityCopyPreference(domain_str, - 0, - NULL, // validIssuers argument is ignored :( - &identity) == noErr) + // While SecIdentityCopyPreferences appears to take a list of CA issuers + // to restrict the identity search to, within Security.framework the + // argument is ignored and filtering unimplemented. See + // SecIdentity.cpp in libsecurity_keychain, specifically + // _SecIdentityCopyPreferenceMatchingName(). + if (SecIdentityCopyPreference(domain_str, 0, NULL, &identity) == noErr) preferred_identity.reset(identity); } @@ -997,8 +977,10 @@ CFArrayRef X509Certificate::CreateClientCertificateChain() const { CFArrayRef cert_chain = NULL; result = CopyCertChain(cert_handle_, &cert_chain); - if (result) - goto exit; + if (result) { + LOG(ERROR) << "CreateIdentityCertificateChain error " << result; + return chain.release(); + } // Append the intermediate certs from SecTrust to the result array: if (cert_chain) { @@ -1010,9 +992,7 @@ CFArrayRef X509Certificate::CreateClientCertificateChain() const { } CFRelease(cert_chain); } -exit: - if (result) - LOG(ERROR) << "CreateIdentityCertificateChain error " << result; + return chain.release(); } diff --git a/net/base/x509_openssl_util.cc b/net/base/x509_openssl_util.cc index 0bb4002..81424cc 100644 --- a/net/base/x509_openssl_util.cc +++ b/net/base/x509_openssl_util.cc @@ -5,30 +5,14 @@ #include "net/base/x509_openssl_util.h" #include "base/logging.h" -#include "base/string_number_conversions.h" #include "base/string_piece.h" #include "base/string_util.h" -#include "base/time.h" +#include "net/base/x509_cert_types.h" namespace net { namespace x509_openssl_util { -namespace { - -// Helper for ParseDate. |*field| must contain at least |field_len| characters. -// |*field| will be advanced by |field_len| on exit. |*ok| is set to false if -// there is an error in parsing the number, but left untouched otherwise. -// Returns the parsed integer. -int ParseIntAndAdvance(const char** field, size_t field_len, bool* ok) { - int result = 0; - *ok &= base::StringToInt(*field, *field + field_len, &result); - *field += field_len; - return result; -} - -} // namespace - bool ParsePrincipalKeyAndValueByIndex(X509_NAME* name, int index, std::string* key, @@ -78,35 +62,10 @@ bool ParseDate(ASN1_TIME* x509_time, base::Time* time) { base::StringPiece str_date(reinterpret_cast<const char*>(x509_time->data), x509_time->length); - // UTCTime: YYMMDDHHMMSSZ - // GeneralizedTime: YYYYMMDDHHMMSSZ - size_t year_length = x509_time->type == V_ASN1_UTCTIME ? 2 : 4; - - if (str_date.length() < 11 + year_length) - return false; - - const char* field = str_date.data(); - bool valid = true; - base::Time::Exploded exploded = {0}; - exploded.year = ParseIntAndAdvance(&field, year_length, &valid); - exploded.month = ParseIntAndAdvance(&field, 2, &valid); - exploded.day_of_month = ParseIntAndAdvance(&field, 2, &valid); - exploded.hour = ParseIntAndAdvance(&field, 2, &valid); - exploded.minute = ParseIntAndAdvance(&field, 2, &valid); - exploded.second = ParseIntAndAdvance(&field, 2, &valid); - if (valid && year_length == 2) - exploded.year += exploded.year < 50 ? 2000 : 1900; - - valid &= exploded.HasValidValues(); - - if (!valid) { - NOTREACHED() << "can't parse x509 date " << str_date; - return false; - } - - *time = base::Time::FromUTCExploded(exploded); - return true; + CertDateFormat format = x509_time->type == V_ASN1_UTCTIME ? + CERT_DATE_FORMAT_UTC_TIME : CERT_DATE_FORMAT_GENERALIZED_TIME; + return ParseCertificateDate(str_date, format, time); } // TODO(joth): Investigate if we can upstream this into the OpenSSL library, @@ -225,6 +184,6 @@ bool VerifyHostname(const std::string& hostname, return false; } -} // namespace x509_openssl_util +} // namespace x509_openssl_util -} // namespace net +} // namespace net |