summaryrefslogtreecommitdiffstats
path: root/net/base
diff options
context:
space:
mode:
Diffstat (limited to 'net/base')
-rw-r--r--net/base/cert_verify_result.cc3
-rw-r--r--net/base/cert_verify_result.h19
-rw-r--r--net/base/x509_certificate.cc1
-rw-r--r--net/base/x509_certificate_mac.cc17
-rw-r--r--net/base/x509_certificate_nss.cc20
-rw-r--r--net/base/x509_certificate_openssl.cc13
-rw-r--r--net/base/x509_certificate_unittest.cc142
-rw-r--r--net/base/x509_certificate_win.cc20
8 files changed, 229 insertions, 6 deletions
diff --git a/net/base/cert_verify_result.cc b/net/base/cert_verify_result.cc
index 54e15f5..d51566b 100644
--- a/net/base/cert_verify_result.cc
+++ b/net/base/cert_verify_result.cc
@@ -4,6 +4,8 @@
#include "net/base/cert_verify_result.h"
+#include "net/base/x509_certificate.h"
+
namespace net {
CertVerifyResult::CertVerifyResult() {
@@ -14,6 +16,7 @@ CertVerifyResult::~CertVerifyResult() {
}
void CertVerifyResult::Reset() {
+ verified_cert = NULL;
cert_status = 0;
has_md5 = false;
has_md2 = false;
diff --git a/net/base/cert_verify_result.h b/net/base/cert_verify_result.h
index e4fb84a..2e4c250 100644
--- a/net/base/cert_verify_result.h
+++ b/net/base/cert_verify_result.h
@@ -9,12 +9,14 @@
#include <vector>
#include "net/base/net_api.h"
+#include "base/memory/ref_counted.h"
#include "net/base/x509_cert_types.h"
namespace net {
-// The result of certificate verification. Eventually this may contain the
-// certificate chain that was constructed during certificate verification.
+class X509Certificate;
+
+// The result of certificate verification.
class NET_API CertVerifyResult {
public:
CertVerifyResult();
@@ -22,7 +24,18 @@ class NET_API CertVerifyResult {
void Reset();
- // Bitmask of CERT_STATUS_* from net/base/cert_status_flags.h
+ // The certificate and chain that was constructed during verification.
+ // Note that the though the verified certificate will match the originally
+ // supplied certificate, the intermediate certificates stored within may
+ // be substantially different. In the event of a verification failure, this
+ // will contain the chain as supplied by the server. This may be NULL if
+ // running within the sandbox.
+ scoped_refptr<X509Certificate> verified_cert;
+
+ // Bitmask of CERT_STATUS_* from net/base/cert_status_flags.h. Note that
+ // these status flags apply to the certificate chain returned in
+ // |verified_cert|, rather than the originally supplied certificate
+ // chain.
int cert_status;
// Properties of the certificate chain.
diff --git a/net/base/x509_certificate.cc b/net/base/x509_certificate.cc
index 9fbf702..0c52de7 100644
--- a/net/base/x509_certificate.cc
+++ b/net/base/x509_certificate.cc
@@ -592,6 +592,7 @@ bool X509Certificate::VerifyHostname(
int X509Certificate::Verify(const std::string& hostname, int flags,
CertVerifyResult* verify_result) const {
verify_result->Reset();
+ verify_result->verified_cert = const_cast<X509Certificate*>(this);
if (IsBlacklisted()) {
verify_result->cert_status |= CERT_STATUS_REVOKED;
diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc
index 88ff95f..ef69748 100644
--- a/net/base/x509_certificate_mac.cc
+++ b/net/base/x509_certificate_mac.cc
@@ -832,6 +832,23 @@ int X509Certificate::VerifyInternal(const std::string& hostname,
return NetErrorFromOSStatus(status);
ScopedCFTypeRef<CFArrayRef> scoped_completed_chain(completed_chain);
+ SecCertificateRef verified_cert = NULL;
+ std::vector<SecCertificateRef> verified_chain;
+ for (CFIndex i = 0, count = CFArrayGetCount(completed_chain);
+ i < count; ++i) {
+ SecCertificateRef chain_cert = reinterpret_cast<SecCertificateRef>(
+ const_cast<void*>(CFArrayGetValueAtIndex(completed_chain, i)));
+ if (i == 0) {
+ verified_cert = chain_cert;
+ } else {
+ verified_chain.push_back(chain_cert);
+ }
+ }
+ if (verified_cert) {
+ verify_result->verified_cert = CreateFromHandle(verified_cert,
+ verified_chain);
+ }
+
// Evaluate the results
OSStatus cssm_result;
switch (trust_result) {
diff --git a/net/base/x509_certificate_nss.cc b/net/base/x509_certificate_nss.cc
index 0162663..7224020 100644
--- a/net/base/x509_certificate_nss.cc
+++ b/net/base/x509_certificate_nss.cc
@@ -168,19 +168,27 @@ int MapCertErrorToCertStatus(int err) {
// Saves some information about the certificate chain cert_list in
// *verify_result. The caller MUST initialize *verify_result before calling
// this function.
-// Note that cert_list[0] is the end entity certificate and cert_list doesn't
-// contain the root CA certificate.
+// Note that cert_list[0] is the end entity certificate.
void GetCertChainInfo(CERTCertList* cert_list,
+ CERTCertificate* root_cert,
CertVerifyResult* verify_result) {
// NOTE: Using a NSS library before 3.12.3.1 will crash below. To see the
// NSS version currently in use:
// 1. use ldd on the chrome executable for NSS's location (ie. libnss3.so*)
// 2. use ident libnss3.so* for the library's version
DCHECK(cert_list);
+
+ CERTCertificate* verified_cert = NULL;
+ std::vector<CERTCertificate*> verified_chain;
int i = 0;
for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
!CERT_LIST_END(node, cert_list);
- node = CERT_LIST_NEXT(node), i++) {
+ node = CERT_LIST_NEXT(node), ++i) {
+ if (i == 0) {
+ verified_cert = node->cert;
+ } else {
+ verified_chain.push_back(node->cert);
+ }
SECAlgorithmID& signature = node->cert->signature;
SECOidTag oid_tag = SECOID_FindOIDTag(&signature.algorithm);
switch (oid_tag) {
@@ -201,6 +209,11 @@ void GetCertChainInfo(CERTCertList* cert_list,
break;
}
}
+
+ if (root_cert)
+ verified_chain.push_back(root_cert);
+ verify_result->verified_cert =
+ X509Certificate::CreateFromHandle(verified_cert, verified_chain);
}
// IsKnownRoot returns true if the given certificate is one that we believe
@@ -811,6 +824,7 @@ int X509Certificate::VerifyInternal(const std::string& hostname,
}
GetCertChainInfo(cvout[cvout_cert_list_index].value.pointer.chain,
+ cvout[cvout_trust_anchor_index].value.pointer.cert,
verify_result);
if (IsCertStatusError(verify_result->cert_status))
return MapCertStatusToNetError(verify_result->cert_status);
diff --git a/net/base/x509_certificate_openssl.cc b/net/base/x509_certificate_openssl.cc
index d3cad61..5416a76 100644
--- a/net/base/x509_certificate_openssl.cc
+++ b/net/base/x509_certificate_openssl.cc
@@ -462,8 +462,16 @@ int X509Certificate::VerifyInternal(const std::string& hostname,
return MapCertStatusToNetError(verify_result->cert_status);
STACK_OF(X509)* chain = X509_STORE_CTX_get_chain(ctx.get());
+ X509* verified_cert = NULL;
+ std::vector<X509*> verified_chain;
for (int i = 0; i < sk_X509_num(chain); ++i) {
X509* cert = sk_X509_value(chain, i);
+ if (i == 0) {
+ verified_cert = cert;
+ } else {
+ verified_chain.push_back(verified_cert);
+ }
+
DERCache der_cache;
if (!GetDERAndCacheIfNeeded(cert, &der_cache))
continue;
@@ -480,6 +488,11 @@ int X509Certificate::VerifyInternal(const std::string& hostname,
verify_result->public_key_hashes.push_back(hash);
}
+ if (verified_cert) {
+ verify_result->verified_cert = CreateFromHandle(verified_cert,
+ verified_chain);
+ }
+
// Currently we only ues OpenSSL's default root CA paths, so treat all
// correctly verified certs as being from a known root. TODO(joth): if the
// motivations described in http://src.chromium.org/viewvc/chrome?view=rev&revision=80778
diff --git a/net/base/x509_certificate_unittest.cc b/net/base/x509_certificate_unittest.cc
index 7adad4f..32417ac 100644
--- a/net/base/x509_certificate_unittest.cc
+++ b/net/base/x509_certificate_unittest.cc
@@ -820,6 +820,148 @@ TEST(X509CertificateTest, IntermediateCertificates) {
X509Certificate::FreeOSCertHandle(google_handle);
}
+// Basic test for returning the chain in CertVerifyResult. Note that the
+// returned chain may just be a reflection of the originally supplied chain;
+// that is, if any errors occur, the default chain returned is an exact copy
+// of the certificate to be verified. The remaining VerifyReturn* tests are
+// used to ensure that the actual, verified chain is being returned by
+// Verify().
+TEST(X509CertificateTest, VerifyReturnChainBasic) {
+ FilePath certs_dir = GetTestCertsDirectory();
+ CertificateList certs = CreateCertificateListFromFile(
+ certs_dir, "x509_verify_results.chain.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(3U, certs.size());
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(certs[1]->os_cert_handle());
+ intermediates.push_back(certs[2]->os_cert_handle());
+
+ TestRootCerts::GetInstance()->Add(certs[2]);
+
+ scoped_refptr<X509Certificate> google_full_chain =
+ X509Certificate::CreateFromHandle(certs[0]->os_cert_handle(),
+ intermediates);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), google_full_chain);
+ ASSERT_EQ(2U, google_full_chain->GetIntermediateCertificates().size());
+
+ CertVerifyResult verify_result;
+ EXPECT_EQ(static_cast<X509Certificate*>(NULL), verify_result.verified_cert);
+ int error = google_full_chain->Verify("127.0.0.1", 0, &verify_result);
+ EXPECT_EQ(OK, error);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), verify_result.verified_cert);
+
+ EXPECT_NE(google_full_chain, verify_result.verified_cert);
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(
+ google_full_chain->os_cert_handle(),
+ verify_result.verified_cert->os_cert_handle()));
+ const X509Certificate::OSCertHandles& return_intermediates =
+ verify_result.verified_cert->GetIntermediateCertificates();
+ ASSERT_EQ(2U, return_intermediates.size());
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(return_intermediates[0],
+ certs[1]->os_cert_handle()));
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(return_intermediates[1],
+ certs[2]->os_cert_handle()));
+
+ TestRootCerts::GetInstance()->Clear();
+}
+
+// Test that the certificate returned in CertVerifyResult is able to reorder
+// certificates that are not ordered from end-entity to root. While this is
+// a protocol violation if sent during a TLS handshake, if multiple sources
+// of intermediate certificates are combined, it's possible that order may
+// not be maintained.
+TEST(X509CertificateTest, VerifyReturnChainProperlyOrdered) {
+ FilePath certs_dir = GetTestCertsDirectory();
+ CertificateList certs = CreateCertificateListFromFile(
+ certs_dir, "x509_verify_results.chain.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(3U, certs.size());
+
+ // Construct the chain out of order.
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(certs[2]->os_cert_handle());
+ intermediates.push_back(certs[1]->os_cert_handle());
+
+ TestRootCerts::GetInstance()->Add(certs[2]);
+
+ scoped_refptr<X509Certificate> google_full_chain =
+ X509Certificate::CreateFromHandle(certs[0]->os_cert_handle(),
+ intermediates);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), google_full_chain);
+ ASSERT_EQ(2U, google_full_chain->GetIntermediateCertificates().size());
+
+ CertVerifyResult verify_result;
+ EXPECT_EQ(static_cast<X509Certificate*>(NULL), verify_result.verified_cert);
+ int error = google_full_chain->Verify("127.0.0.1", 0, &verify_result);
+ EXPECT_EQ(OK, error);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), verify_result.verified_cert);
+
+ EXPECT_NE(google_full_chain, verify_result.verified_cert);
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(
+ google_full_chain->os_cert_handle(),
+ verify_result.verified_cert->os_cert_handle()));
+ const X509Certificate::OSCertHandles& return_intermediates =
+ verify_result.verified_cert->GetIntermediateCertificates();
+ ASSERT_EQ(2U, return_intermediates.size());
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(return_intermediates[0],
+ certs[1]->os_cert_handle()));
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(return_intermediates[1],
+ certs[2]->os_cert_handle()));
+
+ TestRootCerts::GetInstance()->Clear();
+}
+
+// Test that Verify() filters out certificates which are not related to
+// or part of the certificate chain being verified.
+TEST(X509CertificateTest, VerifyReturnChainFiltersUnrelatedCerts) {
+ FilePath certs_dir = GetTestCertsDirectory();
+ CertificateList certs = CreateCertificateListFromFile(
+ certs_dir, "x509_verify_results.chain.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(3U, certs.size());
+ TestRootCerts::GetInstance()->Add(certs[2]);
+
+ scoped_refptr<X509Certificate> unrelated_dod_certificate =
+ ImportCertFromFile(certs_dir, "dod_ca_17_cert.der");
+ scoped_refptr<X509Certificate> unrelated_dod_certificate2 =
+ ImportCertFromFile(certs_dir, "dod_root_ca_2_cert.der");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), unrelated_dod_certificate);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), unrelated_dod_certificate2);
+
+ // Interject unrelated certificates into the list of intermediates.
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(unrelated_dod_certificate->os_cert_handle());
+ intermediates.push_back(certs[1]->os_cert_handle());
+ intermediates.push_back(unrelated_dod_certificate2->os_cert_handle());
+ intermediates.push_back(certs[2]->os_cert_handle());
+
+ scoped_refptr<X509Certificate> google_full_chain =
+ X509Certificate::CreateFromHandle(certs[0]->os_cert_handle(),
+ intermediates);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), google_full_chain);
+ ASSERT_EQ(4U, google_full_chain->GetIntermediateCertificates().size());
+
+ CertVerifyResult verify_result;
+ EXPECT_EQ(static_cast<X509Certificate*>(NULL), verify_result.verified_cert);
+ int error = google_full_chain->Verify("127.0.0.1", 0, &verify_result);
+ EXPECT_EQ(OK, error);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), verify_result.verified_cert);
+
+ EXPECT_NE(google_full_chain, verify_result.verified_cert);
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(
+ google_full_chain->os_cert_handle(),
+ verify_result.verified_cert->os_cert_handle()));
+ const X509Certificate::OSCertHandles& return_intermediates =
+ verify_result.verified_cert->GetIntermediateCertificates();
+ ASSERT_EQ(2U, return_intermediates.size());
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(return_intermediates[0],
+ certs[1]->os_cert_handle()));
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(return_intermediates[1],
+ certs[2]->os_cert_handle()));
+ TestRootCerts::GetInstance()->Clear();
+}
+
#if defined(OS_MACOSX)
TEST(X509CertificateTest, IsIssuedBy) {
FilePath certs_dir = GetTestCertsDirectory();
diff --git a/net/base/x509_certificate_win.cc b/net/base/x509_certificate_win.cc
index 306bd28..5dfc285 100644
--- a/net/base/x509_certificate_win.cc
+++ b/net/base/x509_certificate_win.cc
@@ -296,16 +296,28 @@ bool CertSubjectCommonNameHasNull(PCCERT_CONTEXT cert) {
// this function.
void GetCertChainInfo(PCCERT_CHAIN_CONTEXT chain_context,
CertVerifyResult* verify_result) {
+ if (chain_context->cChain == 0)
+ return;
+
PCERT_SIMPLE_CHAIN first_chain = chain_context->rgpChain[0];
int num_elements = first_chain->cElement;
PCERT_CHAIN_ELEMENT* element = first_chain->rgpElement;
+ PCCERT_CONTEXT verified_cert = NULL;
+ std::vector<PCCERT_CONTEXT> verified_chain;
+
// Each chain starts with the end entity certificate (i = 0) and ends with
// the root CA certificate (i = num_elements - 1). Do not inspect the
// signature algorithm of the root CA certificate because the signature on
// the trust anchor is not important.
for (int i = 0; i < num_elements - 1; ++i) {
PCCERT_CONTEXT cert = element[i]->pCertContext;
+ if (i == 0) {
+ verified_cert = cert;
+ } else {
+ verified_chain.push_back(cert);
+ }
+
const char* algorithm = cert->pCertInfo->SignatureAlgorithm.pszObjId;
if (strcmp(algorithm, szOID_RSA_MD5RSA) == 0) {
// md5WithRSAEncryption: 1.2.840.113549.1.1.4
@@ -322,6 +334,14 @@ void GetCertChainInfo(PCCERT_CHAIN_CONTEXT chain_context,
verify_result->has_md4 = true;
}
}
+
+ if (verified_cert) {
+ // Add the root certificate, if present, as it was not added above.
+ if (num_elements > 1)
+ verified_chain.push_back(element[num_elements - 1]->pCertContext);
+ verify_result->verified_cert =
+ X509Certificate::CreateFromHandle(verified_cert, verified_chain);
+ }
}
// Decodes the cert's certificatePolicies extension into a CERT_POLICIES_INFO