diff options
author | bulach@chromium.org <bulach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-08 14:33:11 +0000 |
---|---|---|
committer | bulach@chromium.org <bulach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-08 14:33:11 +0000 |
commit | 1e5fead8aa598e7d834806f6f8eaef8302074f35 (patch) | |
tree | 27d865f1cbc3f810fd0ea0038228057e2dafd814 /net | |
parent | 2db58053f8e04deaf129724bdf8ec0b326fb65ac (diff) | |
download | chromium_src-1e5fead8aa598e7d834806f6f8eaef8302074f35.zip chromium_src-1e5fead8aa598e7d834806f6f8eaef8302074f35.tar.gz chromium_src-1e5fead8aa598e7d834806f6f8eaef8302074f35.tar.bz2 |
Adds X509 certificates for OpenSSL.
Depends on http://codereview.chromium.org/3565006/show
TEST=Existing *X509* tests should pass.
Review URL: http://codereview.chromium.org/3529008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@61955 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/base/cert_database_openssl.cc | 52 | ||||
-rw-r--r-- | net/base/cert_test_util.cc | 31 | ||||
-rw-r--r-- | net/base/keygen_handler_openssl.cc | 18 | ||||
-rw-r--r-- | net/base/openssl_util.cc | 61 | ||||
-rw-r--r-- | net/base/openssl_util.h | 51 | ||||
-rw-r--r-- | net/base/x509_certificate.cc | 37 | ||||
-rw-r--r-- | net/base/x509_certificate.h | 9 | ||||
-rw-r--r-- | net/base/x509_certificate_mac.cc | 15 | ||||
-rw-r--r-- | net/base/x509_certificate_nss.cc | 10 | ||||
-rw-r--r-- | net/base/x509_certificate_openssl.cc | 410 | ||||
-rw-r--r-- | net/base/x509_certificate_unittest.cc | 13 | ||||
-rw-r--r-- | net/base/x509_certificate_win.cc | 10 | ||||
-rw-r--r-- | net/net.gyp | 34 |
13 files changed, 708 insertions, 43 deletions
diff --git a/net/base/cert_database_openssl.cc b/net/base/cert_database_openssl.cc new file mode 100644 index 0000000..b56d8ba --- /dev/null +++ b/net/base/cert_database_openssl.cc @@ -0,0 +1,52 @@ +// Copyright (c) 2010 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/base/cert_database.h" + +#include "net/base/net_errors.h" +#include "net/base/x509_certificate.h" + +namespace net { + +CertDatabase::CertDatabase() { +} + +int CertDatabase::CheckUserCert(X509Certificate* cert) { + if (!cert) + return ERR_CERT_INVALID; + if (cert->HasExpired()) + return ERR_CERT_DATE_INVALID; + + // TODO(bulach): implement me. + return ERR_NOT_IMPLEMENTED; +} + +int CertDatabase::AddUserCert(X509Certificate* cert) { + // TODO(bulach): implement me. + return ERR_NOT_IMPLEMENTED; +} + +void CertDatabase::ListCerts(CertificateList* certs) { + // TODO(bulach): implement me. +} + +int CertDatabase::ImportFromPKCS12(const std::string& data, + const string16& password) { + // TODO(bulach): implement me. + return ERR_NOT_IMPLEMENTED; +} + +int CertDatabase::ExportToPKCS12(const CertificateList& certs, + const string16& password, + std::string* output) const { + // TODO(bulach): implement me. + return 0; +} + +bool CertDatabase::DeleteCertAndKey(const X509Certificate* cert) { + // TODO(bulach): implement me. + return false; +} + +} // namespace net diff --git a/net/base/cert_test_util.cc b/net/base/cert_test_util.cc index 9fc6573..8815cd5 100644 --- a/net/base/cert_test_util.cc +++ b/net/base/cert_test_util.cc @@ -6,7 +6,11 @@ #include "build/build_config.h" -#if defined(USE_NSS) +#if defined(USE_OPENSSL) +#include <openssl/err.h> +#include <openssl/x509v3.h> +#include "net/base/openssl_util.h" +#elif defined(USE_NSS) #include <cert.h> #include "base/nss_util.h" #elif defined(OS_MACOSX) @@ -21,7 +25,30 @@ namespace net { -#if defined(USE_NSS) +#if defined(USE_OPENSSL) +X509Certificate* LoadTemporaryRootCert(const FilePath& filename) { + OpenSSLInitSingleton* openssl_init = GetOpenSSLInitSingleton(); + + std::string rawcert; + if (!file_util::ReadFileToString(filename, &rawcert)) { + LOG(ERROR) << "Can't load certificate " << filename.value(); + return NULL; + } + + X509Certificate* certificate = X509Certificate::CreateFromBytes( + rawcert.c_str(), rawcert.length()); + + X509* x509_cert = + X509Certificate::DupOSCertHandle(certificate->os_cert_handle()); + if (!X509_STORE_add_cert(openssl_init->x509_store(), x509_cert)) { + LOG(ERROR) << "X509_STORE_add_cert error: " << ERR_get_error(); + X509_free(x509_cert); + return NULL; + } + + return certificate; +} +#elif defined(USE_NSS) X509Certificate* LoadTemporaryRootCert(const FilePath& filename) { base::EnsureNSSInit(); diff --git a/net/base/keygen_handler_openssl.cc b/net/base/keygen_handler_openssl.cc new file mode 100644 index 0000000..0f5d874 --- /dev/null +++ b/net/base/keygen_handler_openssl.cc @@ -0,0 +1,18 @@ +// Copyright (c) 2010 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/base/keygen_handler.h" + +#if defined(USE_OPENSSL) + +namespace net { + +std::string KeygenHandler::GenKeyAndSignChallenge() { + // TODO(bulach): implement me. + return ""; +} + +} // namespace net + +#endif // USE_OPENSSL diff --git a/net/base/openssl_util.cc b/net/base/openssl_util.cc new file mode 100644 index 0000000..54a3df4 --- /dev/null +++ b/net/base/openssl_util.cc @@ -0,0 +1,61 @@ +// Copyright (c) 2006-2008 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/base/openssl_util.h" + +#include <openssl/err.h> +#include <openssl/x509v3.h> + +#include "base/logging.h" + +namespace net { + +X509_STORE* OpenSSLInitSingleton::x509_store() const { + return store_.get(); +} + +OpenSSLInitSingleton::OpenSSLInitSingleton() + : store_(X509_STORE_new()) { + CHECK(store_.get()); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + X509_STORE_set_default_paths(store_.get()); + // TODO(bulach): Enable CRL (see X509_STORE_set_flags(X509_V_FLAG_CRL_CHECK)). + int num_locks = CRYPTO_num_locks(); + for (int i = 0; i < num_locks; ++i) + locks_.push_back(new Lock()); + CRYPTO_set_locking_callback(LockingCallback); +} + +OpenSSLInitSingleton::~OpenSSLInitSingleton() { + CRYPTO_set_locking_callback(NULL); + EVP_cleanup(); + ERR_free_strings(); +} + +OpenSSLInitSingleton* GetOpenSSLInitSingleton() { + return Singleton<OpenSSLInitSingleton>::get(); +} + +// static +void OpenSSLInitSingleton::LockingCallback(int mode, + int n, + const char* file, + int line) { + GetOpenSSLInitSingleton()->OnLockingCallback(mode, n, file, line); +} + +void OpenSSLInitSingleton::OnLockingCallback(int mode, + int n, + const char* file, + int line) { + CHECK_LT(static_cast<size_t>(n), locks_.size()); + if (mode & CRYPTO_LOCK) + locks_[n]->Acquire(); + else + locks_[n]->Release(); +} + +} // namespace net + diff --git a/net/base/openssl_util.h b/net/base/openssl_util.h new file mode 100644 index 0000000..8015917 --- /dev/null +++ b/net/base/openssl_util.h @@ -0,0 +1,51 @@ +// Copyright (c) 2006-2008 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 <openssl/ssl.h> + +#include "base/lock.h" +#include "base/scoped_vector.h" +#include "base/singleton.h" + +namespace net { + +// A helper class that takes care of destroying OpenSSL objects when it goes out +// of scope. +template <typename T, void (*destructor)(T*)> +class ScopedSSL { + public: + explicit ScopedSSL(T* ptr_) : ptr_(ptr_) { } + ~ScopedSSL() { if (ptr_) (*destructor)(ptr_); } + + T* get() const { return ptr_; } + + private: + T* ptr_; +}; + +// Singleton for initializing / cleaning up OpenSSL and holding a X509 store. +// Access it via EnsureOpenSSLInit(). +class OpenSSLInitSingleton { + public: + X509_STORE* x509_store() const; + + private: + friend struct DefaultSingletonTraits<OpenSSLInitSingleton>; + OpenSSLInitSingleton(); + ~OpenSSLInitSingleton(); + + static void LockingCallback(int mode, int n, const char* file, int line); + void OnLockingCallback(int mode, int n, const char* file, int line); + + ScopedSSL<X509_STORE, X509_STORE_free> store_; + // These locks are used and managed by OpenSSL via LockingCallback(). + ScopedVector<Lock> locks_; + + DISALLOW_COPY_AND_ASSIGN(OpenSSLInitSingleton); +}; + +OpenSSLInitSingleton* GetOpenSSLInitSingleton(); + +} // namespace net + diff --git a/net/base/x509_certificate.cc b/net/base/x509_certificate.cc index df62ec8..6395b97 100644 --- a/net/base/x509_certificate.cc +++ b/net/base/x509_certificate.cc @@ -4,12 +4,6 @@ #include "net/base/x509_certificate.h" -#if defined(OS_MACOSX) -#include <Security/Security.h> -#elif defined(USE_NSS) -#include <cert.h> -#endif - #include <map> #include "base/histogram.h" @@ -47,33 +41,6 @@ const char kPKCS7Header[] = "PKCS7"; } // namespace -// static -bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a, - X509Certificate::OSCertHandle b) { - DCHECK(a && b); - if (a == b) - return true; -#if defined(OS_WIN) - return a->cbCertEncoded == b->cbCertEncoded && - memcmp(a->pbCertEncoded, b->pbCertEncoded, a->cbCertEncoded) == 0; -#elif defined(OS_MACOSX) - if (CFEqual(a, b)) - return true; - CSSM_DATA a_data, b_data; - return SecCertificateGetData(a, &a_data) == noErr && - SecCertificateGetData(b, &b_data) == noErr && - a_data.Length == b_data.Length && - memcmp(a_data.Data, b_data.Data, a_data.Length) == 0; -#elif defined(USE_NSS) - return a->derCert.len == b->derCert.len && - memcmp(a->derCert.data, b->derCert.data, a->derCert.len) == 0; -#else - // TODO(snej): not implemented - UNREACHED(); - return false; -#endif -} - bool X509Certificate::LessThan::operator()(X509Certificate* lhs, X509Certificate* rhs) const { if (lhs == rhs) @@ -282,7 +249,7 @@ X509Certificate::X509Certificate(OSCertHandle cert_handle, const OSCertHandles& intermediates) : cert_handle_(DupOSCertHandle(cert_handle)), source_(source) { -#if defined(OS_MACOSX) || defined(OS_WIN) +#if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_OPENSSL) // Copy/retain the intermediate cert handles. for (size_t i = 0; i < intermediates.size(); ++i) intermediate_ca_certs_.push_back(DupOSCertHandle(intermediates[i])); @@ -320,7 +287,7 @@ bool X509Certificate::HasExpired() const { } bool X509Certificate::HasIntermediateCertificate(OSCertHandle cert) { -#if defined(OS_MACOSX) || defined(OS_WIN) +#if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_OPENSSL) for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) { if (IsSameOSCert(cert, intermediate_ca_certs_[i])) return true; diff --git a/net/base/x509_certificate.h b/net/base/x509_certificate.h index 5a9e897..577de92 100644 --- a/net/base/x509_certificate.h +++ b/net/base/x509_certificate.h @@ -22,6 +22,9 @@ #elif defined(OS_MACOSX) #include <CoreFoundation/CFArray.h> #include <Security/SecBase.h> +#elif defined(USE_OPENSSL) +// Forward declaration; real one in <x509.h> +struct x509_st; #elif defined(USE_NSS) // Forward declaration; real one in <cert.h> struct CERTCertificateStr; @@ -45,6 +48,8 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { typedef PCCERT_CONTEXT OSCertHandle; #elif defined(OS_MACOSX) typedef SecCertificateRef OSCertHandle; +#elif defined(USE_OPENSSL) + typedef struct x509_st* OSCertHandle; #elif defined(USE_NSS) typedef struct CERTCertificateStr* OSCertHandle; #else @@ -168,7 +173,7 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { // now. bool HasExpired() const; -#if defined(OS_MACOSX) || defined(OS_WIN) +#if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_OPENSSL) // Returns intermediate certificates added via AddIntermediateCertificate(). // Ownership follows the "get" rule: it is the caller's responsibility to // retain the elements of the result. @@ -287,7 +292,7 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { // A handle to the certificate object in the underlying crypto library. OSCertHandle cert_handle_; -#if defined(OS_MACOSX) || defined(OS_WIN) +#if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_OPENSSL) // Untrusted intermediate certificates associated with this certificate // that may be needed for chain building. (NSS impl does not need these.) OSCertHandles intermediate_ca_certs_; diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc index e4c5c7f..cd8a3ca 100644 --- a/net/base/x509_certificate_mac.cc +++ b/net/base/x509_certificate_mac.cc @@ -728,6 +728,21 @@ bool X509Certificate::VerifyEV() const { } // static +bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a, + X509Certificate::OSCertHandle b) { + DCHECK(a && b); + if (a == b) + return true; + if (CFEqual(a, b)) + return true; + CSSM_DATA a_data, b_data; + return SecCertificateGetData(a, &a_data) == noErr && + SecCertificateGetData(b, &b_data) == noErr && + a_data.Length == b_data.Length && + memcmp(a_data.Data, b_data.Data, a_data.Length) == 0; +} + +// static X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( const char* data, int length) { CSSM_DATA cert_data; diff --git a/net/base/x509_certificate_nss.cc b/net/base/x509_certificate_nss.cc index 00880022..5061238 100644 --- a/net/base/x509_certificate_nss.cc +++ b/net/base/x509_certificate_nss.cc @@ -725,6 +725,16 @@ bool X509Certificate::VerifyEV() const { } // static +bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a, + X509Certificate::OSCertHandle b) { + DCHECK(a && b); + if (a == b) + return true; + return a->derCert.len == b->derCert.len && + memcmp(a->derCert.data, b->derCert.data, a->derCert.len) == 0; +} + +// static X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( const char* data, int length) { if (length < 0) diff --git a/net/base/x509_certificate_openssl.cc b/net/base/x509_certificate_openssl.cc new file mode 100644 index 0000000..afba382 --- /dev/null +++ b/net/base/x509_certificate_openssl.cc @@ -0,0 +1,410 @@ +// Copyright (c) 2006-2008 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/base/x509_certificate.h" + +#include <openssl/asn1.h> +#include <openssl/obj_mac.h> +#include <openssl/pem.h> +#include <openssl/pkcs7.h> +#include <openssl/sha.h> +#include <openssl/ssl.h> +#include <openssl/x509v3.h> + +#include "base/pickle.h" +#include "base/string_number_conversions.h" +#include "net/base/cert_status_flags.h" +#include "net/base/cert_verify_result.h" +#include "net/base/net_errors.h" +#include "net/base/openssl_util.h" + +namespace net { + +namespace { + +void CreateOSCertHandlesFromPKCS7Bytes( + const char* data, int length, + X509Certificate::OSCertHandles* handles) { + const unsigned char* der_data = reinterpret_cast<const unsigned char*>(data); + ScopedSSL<PKCS7, PKCS7_free> pkcs7_cert( + d2i_PKCS7(NULL, &der_data, length)); + if (!pkcs7_cert.get()) + return; + + STACK_OF(X509)* certs = NULL; + int nid = OBJ_obj2nid(pkcs7_cert.get()->type); + if (nid == NID_pkcs7_signed) { + certs = pkcs7_cert.get()->d.sign->cert; + } else if (nid == NID_pkcs7_signedAndEnveloped) { + certs = pkcs7_cert.get()->d.signed_and_enveloped->cert; + } + + if (certs) { + for (int i = 0; i < sk_X509_num(certs); ++i) { + X509* x509_cert = + X509Certificate::DupOSCertHandle(sk_X509_value(certs, i)); + handles->push_back(x509_cert); + } + } +} + +bool ParsePrincipalFieldInternal(X509_NAME* name, + int index, + std::string* field) { + ASN1_STRING* data = + X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, index)); + if (!data) + return false; + + unsigned char* buf = NULL; + int len = ASN1_STRING_to_UTF8(&buf, data); + if (len <= 0) + return false; + + field->assign(reinterpret_cast<const char*>(buf), len); + OPENSSL_free(buf); + return true; +} + +void ParsePrincipalField(X509_NAME* name, int nid, std::string* field) { + int index = X509_NAME_get_index_by_NID(name, nid, -1); + if (index < 0) + return; + + ParsePrincipalFieldInternal(name, index, field); +} + +void ParsePrincipalFields(X509_NAME* name, + int nid, + std::vector<std::string>* fields) { + for (int index = -1; + (index = X509_NAME_get_index_by_NID(name, nid, index)) != -1;) { + std::string field; + if (!ParsePrincipalFieldInternal(name, index, &field)) + break; + fields->push_back(field); + } +} + +void ParsePrincipal(X509Certificate::OSCertHandle cert, + X509_NAME* x509_name, + CertPrincipal* principal) { + if (!x509_name) + return; + + ParsePrincipalFields(x509_name, NID_streetAddress, + &principal->street_addresses); + ParsePrincipalFields(x509_name, NID_organizationName, + &principal->organization_names); + ParsePrincipalFields(x509_name, NID_organizationalUnitName, + &principal->organization_unit_names); + ParsePrincipalFields(x509_name, NID_domainComponent, + &principal->domain_components); + + ParsePrincipalField(x509_name, NID_commonName, &principal->common_name); + ParsePrincipalField(x509_name, NID_localityName, &principal->locality_name); + ParsePrincipalField(x509_name, NID_stateOrProvinceName, + &principal->state_or_province_name); + ParsePrincipalField(x509_name, NID_countryName, &principal->country_name); +} + +void ParseDate(ASN1_TIME* x509_time, base::Time* time) { + if (!x509_time || + (x509_time->type != V_ASN1_UTCTIME && + x509_time->type != V_ASN1_GENERALIZEDTIME)) + return; + + std::string str_date(reinterpret_cast<char*>(x509_time->data), + x509_time->length); + // UTCTime: YYMMDDHHMMSSZ + // GeneralizedTime: YYYYMMDDHHMMSSZ + size_t year_length = x509_time->type == V_ASN1_UTCTIME ? 2 : 4; + size_t fields_offset = x509_time->type == V_ASN1_UTCTIME ? 0 : 2; + + if (str_date.length() < 11 + year_length) + return; + + base::Time::Exploded exploded = {0}; + bool valid = base::StringToInt(str_date.substr(0, year_length), + &exploded.year); + if (valid && year_length == 2) + exploded.year += exploded.year < 50 ? 2000 : 1900; + + valid &= base::StringToInt(str_date.substr(2 + fields_offset, 2), + &exploded.month); + valid &= base::StringToInt(str_date.substr(4 + fields_offset, 2), + &exploded.day_of_month); + valid &= base::StringToInt(str_date.substr(6 + fields_offset, 2), + &exploded.hour); + valid &= base::StringToInt(str_date.substr(8 + fields_offset, 2), + &exploded.minute); + valid &= base::StringToInt(str_date.substr(10 + fields_offset, 2), + &exploded.second); + + DCHECK(valid); + + *time = base::Time::FromUTCExploded(exploded); +} + +void ParseSubjectAltNames(X509Certificate::OSCertHandle cert, + std::vector<std::string>* dns_names) { + int index = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1); + X509_EXTENSION* alt_name_ext = X509_get_ext(cert, index); + if (!alt_name_ext) + return; + + ScopedSSL<GENERAL_NAMES, GENERAL_NAMES_free> alt_names( + reinterpret_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(alt_name_ext))); + if (!alt_names.get()) + return; + + for (int i = 0; i < sk_GENERAL_NAME_num(alt_names.get()); ++i) { + const GENERAL_NAME* name = sk_GENERAL_NAME_value(alt_names.get(), i); + if (name->type == GEN_DNS) { + unsigned char* dns_name = ASN1_STRING_data(name->d.dNSName); + if (!dns_name) + continue; + int dns_name_len = ASN1_STRING_length(name->d.dNSName); + dns_names->push_back( + std::string(reinterpret_cast<char*>(dns_name), dns_name_len)); + } + } +} + +// Maps X509_STORE_CTX_get_error() return values to our cert status flags. +int MapCertErrorToCertStatus(int err) { + switch (err) { + case X509_V_ERR_SUBJECT_ISSUER_MISMATCH: + return CERT_STATUS_COMMON_NAME_INVALID; + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_CERT_HAS_EXPIRED: + case X509_V_ERR_CRL_NOT_YET_VALID: + case X509_V_ERR_CRL_HAS_EXPIRED: + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: + case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: + return CERT_STATUS_DATE_INVALID; + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + case X509_V_ERR_UNABLE_TO_GET_CRL: + case X509_V_ERR_INVALID_CA: + case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: + case X509_V_ERR_INVALID_NON_CA: + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + return CERT_STATUS_AUTHORITY_INVALID; +#if 0 +// TODO(bulach): what should we map to these status? + return CERT_STATUS_NO_REVOCATION_MECHANISM; + return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION; + return CERT_STATUS_NOT_IN_DNS; +#endif + case X509_V_ERR_CERT_REVOKED: + return CERT_STATUS_REVOKED; + case X509_V_ERR_KEYUSAGE_NO_CERTSIGN: + return CERT_STATUS_WEAK_SIGNATURE_ALGORITHM; + // All these status are mapped to CERT_STATUS_INVALID. + case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: + case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: + case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: + case X509_V_ERR_CERT_SIGNATURE_FAILURE: + case X509_V_ERR_CRL_SIGNATURE_FAILURE: + case X509_V_ERR_OUT_OF_MEM: + case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: + case X509_V_ERR_CERT_CHAIN_TOO_LONG: + case X509_V_ERR_PATH_LENGTH_EXCEEDED: + case X509_V_ERR_INVALID_PURPOSE: + case X509_V_ERR_CERT_UNTRUSTED: + case X509_V_ERR_CERT_REJECTED: + case X509_V_ERR_AKID_SKID_MISMATCH: + case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: + case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: + case X509_V_ERR_KEYUSAGE_NO_CRL_SIGN: + case X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION: + case X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED: + case X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE: + case X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED: + case X509_V_ERR_INVALID_EXTENSION: + case X509_V_ERR_INVALID_POLICY_EXTENSION: + case X509_V_ERR_NO_EXPLICIT_POLICY: + case X509_V_ERR_UNNESTED_RESOURCE: + case X509_V_ERR_APPLICATION_VERIFICATION: + return CERT_STATUS_INVALID; + default: + NOTREACHED() << "Invalid X509 err " << err; + return CERT_STATUS_INVALID; + } +} + +// sk_X509_free is a function-style macro, so can't be used as a template +// param directly. +void sk_X509_free_fn(STACK_OF(X509)* st) { + sk_X509_free(st); +} + +} // namespace + +// static +X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle( + OSCertHandle cert_handle) { + return X509_dup(cert_handle); +} + +// static +void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) { + X509_free(cert_handle); +} + +void X509Certificate::Initialize() { + fingerprint_ = CalculateFingerprint(cert_handle_); + ParsePrincipal(cert_handle_, X509_get_subject_name(cert_handle_), &subject_); + ParsePrincipal(cert_handle_, X509_get_issuer_name(cert_handle_), &issuer_); + ParseDate(X509_get_notBefore(cert_handle_), &valid_start_); + ParseDate(X509_get_notAfter(cert_handle_), &valid_expiry_); +} + +SHA1Fingerprint X509Certificate::CalculateFingerprint(OSCertHandle cert) { + SHA1Fingerprint sha1; + unsigned int sha1_size = static_cast<unsigned int>(sizeof(sha1.data)); + int ret = X509_digest(cert, EVP_sha1(), sha1.data, &sha1_size); + CHECK(ret); + CHECK_EQ(sha1_size, sizeof(sha1.data)); + return sha1; +} + +// static +X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( + const char* data, int length) { + if (length < 0) + return NULL; + const unsigned char* d2i_data = + reinterpret_cast<const unsigned char*>(data); + X509* cert = d2i_X509(NULL, &d2i_data, length); + return cert; +} + +// static +X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes( + const char* data, int length, Format format) { + OSCertHandles results; + if (length < 0) + return results; + + switch (format) { + case FORMAT_SINGLE_CERTIFICATE: { + OSCertHandle handle = CreateOSCertHandleFromBytes(data, length); + if (handle) + results.push_back(handle); + break; + } + case FORMAT_PKCS7: { + CreateOSCertHandlesFromPKCS7Bytes(data, length, &results); + break; + } + default: { + NOTREACHED() << "Certificate format " << format << " unimplemented"; + break; + } + } + + return results; +} + +X509Certificate* X509Certificate::CreateFromPickle(const Pickle& pickle, + void** pickle_iter) { + const char* data; + int length; + if (!pickle.ReadData(pickle_iter, &data, &length)) + return NULL; + + return CreateFromBytes(data, length); +} + +void X509Certificate::Persist(Pickle* pickle) { + unsigned char* data = NULL; + int data_length = i2d_X509(cert_handle_, &data); + if (data_length <= 0 || !data) + return; + + pickle->WriteData(reinterpret_cast<const char*>(data), data_length); + OPENSSL_free(data); +} + +void X509Certificate::GetDNSNames(std::vector<std::string>* dns_names) const { + dns_names->clear(); + + ParseSubjectAltNames(cert_handle_, dns_names); + + if (dns_names->empty()) + dns_names->push_back(subject_.common_name); +} + +int X509Certificate::Verify(const std::string& hostname, + int flags, + CertVerifyResult* verify_result) const { + verify_result->Reset(); + + ScopedSSL<X509_STORE_CTX, X509_STORE_CTX_free> ctx(X509_STORE_CTX_new()); + + ScopedSSL<STACK_OF(X509), sk_X509_free_fn> intermediates(sk_X509_new_null()); + if (!intermediates.get()) + return ERR_OUT_OF_MEMORY; + + for (OSCertHandles::const_iterator it = intermediate_ca_certs_.begin(); + it != intermediate_ca_certs_.end(); ++it) { + if (!sk_X509_push(intermediates.get(), *it)) + return ERR_OUT_OF_MEMORY; + } + int rv = X509_STORE_CTX_init(ctx.get(), + GetOpenSSLInitSingleton()->x509_store(), + cert_handle_, intermediates.get()); + CHECK_EQ(1, rv); + + if (X509_verify_cert(ctx.get()) == 1) { + return OK; + } + + int x509_error = X509_STORE_CTX_get_error(ctx.get()); + int cert_status = MapCertErrorToCertStatus(x509_error); + LOG(ERROR) << "X509 Verification error " + << X509_verify_cert_error_string(x509_error) + << " : " << x509_error + << " : " << X509_STORE_CTX_get_error_depth(ctx.get()) + << " : " << cert_status; + verify_result->cert_status |= cert_status; + return MapCertStatusToNetError(verify_result->cert_status); +} + +// static +bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a, + X509Certificate::OSCertHandle b) { + DCHECK(a && b); + if (a == b) + return true; + + // TODO(bulach): re-encoding the certificate is an expensive operation. + // Consider 'tagging' each X509* we create using X509_set_ex_data, storing the + // original DER data in an index. Then, when comparing two handles, see if + // X509_get_ex_data() returns the DER form. + unsigned char* data_a = NULL; + int data_length_a = i2d_X509(a, &data_a); + if (data_length_a <= 0 || !data_a) + return false; + + bool ret = true; + unsigned char* data_b = NULL; + int data_length_b = i2d_X509(b, &data_b); + if (data_length_b <= 0 || !data_b) + ret = false; + + ret = ret && data_length_a == data_length_b; + ret = ret && memcmp(data_a, data_b, data_length_a) == 0; + + OPENSSL_free(data_a); + OPENSSL_free(data_b); + return ret; +} + +} // namespace net diff --git a/net/base/x509_certificate_unittest.cc b/net/base/x509_certificate_unittest.cc index e1319d7..9bb81cb 100644 --- a/net/base/x509_certificate_unittest.cc +++ b/net/base/x509_certificate_unittest.cc @@ -399,7 +399,7 @@ TEST(X509CertificateTest, PaypalNullCertParsing) { // Either the system crypto library should correctly report a certificate // name mismatch, or our certificate blacklist should cause us to report an // invalid certificate. -#if !defined(OS_MACOSX) +#if !defined(OS_MACOSX) && !defined(USE_OPENSSL) EXPECT_NE(0, verify_result.cert_status & (CERT_STATUS_COMMON_NAME_INVALID | CERT_STATUS_INVALID)); #endif @@ -427,7 +427,7 @@ TEST(X509CertificateTest, UnoSoftCertParsing) { EXPECT_NE(0, verify_result.cert_status & CERT_STATUS_AUTHORITY_INVALID); } -#if defined(USE_NSS) +#if defined(USE_NSS) || defined(USE_OPENSSL) // A regression test for http://crbug.com/31497. // This certificate will expire on 2012-04-08. // TODO(wtc): we can't run this test on Mac because MacTrustedCertificates @@ -452,9 +452,16 @@ TEST(X509CertificateTest, IntermediateCARequireExplicitPolicy) { LoadTemporaryRootCert(root_cert_path); ASSERT_NE(static_cast<X509Certificate*>(NULL), root_cert); + X509Certificate::OSCertHandles intermediates; + intermediates.push_back(intermediate_cert->os_cert_handle()); + scoped_refptr<X509Certificate> cert_chain = + X509Certificate::CreateFromHandle(server_cert->os_cert_handle(), + X509Certificate::SOURCE_FROM_NETWORK, + intermediates); + int flags = 0; CertVerifyResult verify_result; - int error = server_cert->Verify("www.us.army.mil", flags, &verify_result); + int error = cert_chain->Verify("www.us.army.mil", flags, &verify_result); EXPECT_EQ(OK, error); EXPECT_EQ(0, verify_result.cert_status); } diff --git a/net/base/x509_certificate_win.cc b/net/base/x509_certificate_win.cc index 03434bb..380ff3c 100644 --- a/net/base/x509_certificate_win.cc +++ b/net/base/x509_certificate_win.cc @@ -787,6 +787,16 @@ bool X509Certificate::VerifyEV() const { } // static +bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a, + X509Certificate::OSCertHandle b) { + DCHECK(a && b); + if (a == b) + return true; + return a->cbCertEncoded == b->cbCertEncoded && + memcmp(a->pbCertEncoded, b->pbCertEncoded, a->cbCertEncoded) == 0; +} + +// static X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( const char* data, int length) { OSCertHandle cert_handle = NULL; diff --git a/net/net.gyp b/net/net.gyp index c4cef18..79f6c66 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -36,6 +36,7 @@ 'base/cert_database.h', 'base/cert_database_mac.cc', 'base/cert_database_nss.cc', + 'base/cert_database_openssl.cc', 'base/cert_database_win.cc', 'base/cert_status_flags.cc', 'base/cert_status_flags.h', @@ -98,6 +99,7 @@ 'base/keygen_handler.h', 'base/keygen_handler_mac.cc', 'base/keygen_handler_nss.cc', + 'base/keygen_handler_openssl.cc', 'base/keygen_handler_win.cc', 'base/listen_socket.cc', 'base/listen_socket.h', @@ -140,6 +142,8 @@ 'base/network_config_watcher_mac.h', 'base/nss_memio.c', 'base/nss_memio.h', + 'base/openssl_util.cc', + 'base/openssl_util.h', 'base/pem_tokenizer.cc', 'base/pem_tokenizer.h', 'base/platform_mime_util.h', @@ -187,6 +191,7 @@ 'base/x509_certificate.h', 'base/x509_certificate_mac.cc', 'base/x509_certificate_nss.cc', + 'base/x509_certificate_openssl.cc', 'base/x509_certificate_win.cc', 'base/x509_cert_types.cc', 'base/x509_cert_types.h', @@ -247,6 +252,33 @@ ], }, ], + [ 'use_openssl == 1 and OS == "linux"', { + # When building for OpenSSL, we need to exclude some NSS files. + # TODO(bulach): remove once we fully support OpenSSL. + 'sources!': [ + 'base/cert_database_nss.cc', + 'base/keygen_handler_nss.cc', + 'base/x509_certificate_nss.cc', + 'third_party/mozilla_security_manager/nsKeygenHandler.cpp', + 'third_party/mozilla_security_manager/nsKeygenHandler.h', + 'third_party/mozilla_security_manager/nsNSSCertificateDB.cpp', + 'third_party/mozilla_security_manager/nsNSSCertificateDB.h', + 'third_party/mozilla_security_manager/nsNSSCertTrust.cpp', + 'third_party/mozilla_security_manager/nsNSSCertTrust.h', + 'third_party/mozilla_security_manager/nsPKCS12Blob.cpp', + 'third_party/mozilla_security_manager/nsPKCS12Blob.h', + ], + }, + { # else: not using openssl. + 'sources!': [ + 'base/cert_database_openssl.cc', + 'base/keygen_handler_openssl.cc', + 'base/openssl_util.cc', + 'base/openssl_util.h', + 'base/x509_certificate_openssl.cc', + ], + }, + ], [ 'OS == "win"', { 'dependencies': [ '../third_party/nss/nss.gyp:nss', @@ -1079,7 +1111,7 @@ }], ], }], - ['use_openssl==1 and OS == "linux"', { + ['use_openssl == 1 and OS == "linux"', { 'dependencies': [ '../build/linux/system.gyp:openssl', ] |