summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/base/cert_verify_result.h9
-rw-r--r--net/base/x509_certificate.cc19
-rw-r--r--net/base/x509_certificate.h12
-rw-r--r--net/base/x509_certificate_mac.cc20
-rw-r--r--net/base/x509_certificate_nss.cc20
-rw-r--r--net/base/x509_certificate_unittest.cc29
-rw-r--r--net/base/x509_certificate_win.cc22
-rw-r--r--net/data/ssl/certificates/nist_intermediate.derbin0 -> 1520 bytes
8 files changed, 126 insertions, 5 deletions
diff --git a/net/base/cert_verify_result.h b/net/base/cert_verify_result.h
index faac17a..2939c3a 100644
--- a/net/base/cert_verify_result.h
+++ b/net/base/cert_verify_result.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -21,6 +21,7 @@ class CertVerifyResult {
has_md4 = false;
has_md5_ca = false;
has_md2_ca = false;
+ is_issued_by_known_root = false;
}
// Bitmask of CERT_STATUS_* from net/base/cert_status_flags.h
@@ -32,6 +33,12 @@ class CertVerifyResult {
bool has_md4;
bool has_md5_ca;
bool has_md2_ca;
+
+ // is_issued_by_known_root is true if we recognise the root CA as a standard
+ // root. If it isn't then it's probably the case that this certificate was
+ // generated by a MITM proxy whose root has been installed locally. This is
+ // meaningless if the certificate was not trusted.
+ bool is_issued_by_known_root;
};
} // namespace net
diff --git a/net/base/x509_certificate.cc b/net/base/x509_certificate.cc
index 089bf096..91ed346 100644
--- a/net/base/x509_certificate.cc
+++ b/net/base/x509_certificate.cc
@@ -4,6 +4,8 @@
#include "net/base/x509_certificate.h"
+#include <stdlib.h>
+
#include <map>
#include <string>
#include <vector>
@@ -12,6 +14,7 @@
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/metrics/histogram.h"
+#include "base/sha1.h"
#include "base/string_piece.h"
#include "base/string_util.h"
#include "base/time.h"
@@ -113,6 +116,12 @@ X509Certificate* X509CertificateCache::Find(
return pos->second;
};
+// CompareSHA1Hashes is a helper function for using bsearch() with an array of
+// SHA1 hashes.
+static int CompareSHA1Hashes(const void* a, const void* b) {
+ return memcmp(a, b, base::SHA1_LENGTH);
+}
+
} // namespace
bool X509Certificate::LessThan::operator()(X509Certificate* lhs,
@@ -528,4 +537,14 @@ bool X509Certificate::IsBlacklisted() const {
return false;
}
+// static
+bool X509Certificate::IsSHA1HashInSortedArray(const SHA1Fingerprint& hash,
+ const uint8* array,
+ size_t array_byte_len) {
+ DCHECK_EQ(0u, array_byte_len % base::SHA1_LENGTH);
+ const unsigned arraylen = array_byte_len / base::SHA1_LENGTH;
+ return NULL != bsearch(hash.data, array, arraylen, base::SHA1_LENGTH,
+ CompareSHA1Hashes);
+}
+
} // namespace net
diff --git a/net/base/x509_certificate.h b/net/base/x509_certificate.h
index 52bb9f1..986f867 100644
--- a/net/base/x509_certificate.h
+++ b/net/base/x509_certificate.h
@@ -167,7 +167,7 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> {
// An example:
// CN=Michael Wong,O=FooBar Corporation,DC=foobar,DC=com
//
- // SECURUITY WARNING
+ // SECURITY WARNING
//
// Using self-signed certificates has the following security risks:
// 1. Encryption without authentication and thus vulnerable to
@@ -344,6 +344,10 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> {
#if defined(OS_WIN)
bool CheckEV(PCCERT_CHAIN_CONTEXT chain_context,
const char* policy_oid) const;
+ static bool IsIssuedByKnownRoot(PCCERT_CHAIN_CONTEXT chain_context);
+#endif
+#if defined(OS_MACOSX)
+ static bool IsIssuedByKnownRoot(CFArrayRef chain);
#endif
bool VerifyEV() const;
@@ -378,6 +382,12 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> {
// IsBlacklisted returns true if this certificate is explicitly blacklisted.
bool IsBlacklisted() const;
+ // IsSHA1HashInSortedArray returns true iff |hash| is in |array|, a sorted
+ // array of SHA1 hashes.
+ static bool IsSHA1HashInSortedArray(const SHA1Fingerprint& hash,
+ const uint8* array,
+ size_t array_byte_len);
+
// The subject of the certificate.
CertPrincipal subject_;
diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc
index 4cecb50..fd3f665 100644
--- a/net/base/x509_certificate_mac.cc
+++ b/net/base/x509_certificate_mac.cc
@@ -14,15 +14,17 @@
#include "base/crypto/rsa_private_key.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
+#include "base/mac/scoped_cftyperef.h"
#include "base/memory/singleton.h"
#include "base/nss_util.h"
#include "base/pickle.h"
-#include "base/mac/scoped_cftyperef.h"
+#include "base/sha1.h"
#include "base/sys_string_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/test_root_certs.h"
+#include "net/base/x509_certificate_known_roots_mac.h"
#include "third_party/nss/mozilla/security/nss/lib/certdb/cert.h"
using base::mac::ScopedCFTypeRef;
@@ -514,6 +516,20 @@ void X509Certificate::Initialize() {
serial_number_ = GetCertSerialNumber(cert_handle_);
}
+// IsIssuedByKnownRoot returns true if the given chain is rooted at a root CA
+// that we recognise as a standard root.
+// static
+bool X509Certificate::IsIssuedByKnownRoot(CFArrayRef chain) {
+ int n = CFArrayGetCount(chain);
+ if (n < 1)
+ return false;
+ SecCertificateRef root_ref = reinterpret_cast<SecCertificateRef>(
+ const_cast<void*>(CFArrayGetValueAtIndex(chain, n - 1)));
+ SHA1Fingerprint hash = X509Certificate::CalculateFingerprint(root_ref);
+ return IsSHA1HashInSortedArray(
+ hash, &kKnownRootCertSHA1Hashes[0][0], sizeof(kKnownRootCertSHA1Hashes));
+}
+
// static
X509Certificate* X509Certificate::CreateFromPickle(const Pickle& pickle,
void** pickle_iter) {
@@ -908,6 +924,8 @@ int X509Certificate::Verify(const std::string& hostname, int flags,
}
}
+ verify_result->is_issued_by_known_root = IsIssuedByKnownRoot(completed_chain);
+
return OK;
}
diff --git a/net/base/x509_certificate_nss.cc b/net/base/x509_certificate_nss.cc
index d71381a..a14690a 100644
--- a/net/base/x509_certificate_nss.cc
+++ b/net/base/x509_certificate_nss.cc
@@ -203,6 +203,18 @@ void GetCertChainInfo(CERTCertList* cert_list,
}
}
+// IsKnownRoot returns true if the given certificate is one that we believe
+// is a standard (as opposed to user-installed) root.
+bool IsKnownRoot(CERTCertificate* root) {
+ if (!root->slot)
+ return false;
+
+ // This magic name is taken from
+ // http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/security/nss/lib/ckfw/builtins/constants.c&rev=1.13&mark=86,89#79
+ return 0 == strcmp(PK11_GetSlotName(root->slot),
+ "NSS Builtin Objects");
+}
+
typedef char* (*CERTGetNameFunc)(CERTName* name);
void ParsePrincipal(CERTName* name,
@@ -769,11 +781,14 @@ int X509Certificate::Verify(const std::string& hostname,
CERTValOutParam cvout[3];
int cvout_index = 0;
- // We don't need the trust anchor for the first PKIXVerifyCert call.
cvout[cvout_index].type = cert_po_certList;
cvout[cvout_index].value.pointer.chain = NULL;
int cvout_cert_list_index = cvout_index;
cvout_index++;
+ cvout[cvout_index].type = cert_po_trustAnchor;
+ cvout[cvout_index].value.pointer.cert = NULL;
+ int cvout_trust_anchor_index = cvout_index;
+ cvout_index++;
cvout[cvout_index].type = cert_po_end;
ScopedCERTValOutParam scoped_cvout(cvout);
@@ -808,6 +823,9 @@ int X509Certificate::Verify(const std::string& hostname,
if (IsCertStatusError(verify_result->cert_status))
return MapCertStatusToNetError(verify_result->cert_status);
+ verify_result->is_issued_by_known_root =
+ IsKnownRoot(cvout[cvout_trust_anchor_index].value.pointer.cert);
+
if ((flags & VERIFY_EV_CERT) && VerifyEV())
verify_result->cert_status |= CERT_STATUS_IS_EV;
return OK;
diff --git a/net/base/x509_certificate_unittest.cc b/net/base/x509_certificate_unittest.cc
index 53a131f..c7dd8fb 100644
--- a/net/base/x509_certificate_unittest.cc
+++ b/net/base/x509_certificate_unittest.cc
@@ -481,6 +481,35 @@ TEST(X509CertificateTest, IntermediateCARequireExplicitPolicy) {
root_certs->Clear();
}
+TEST(X509CertificateTest, TestKnownRoot) {
+ FilePath certs_dir = GetTestCertsDirectory();
+ scoped_refptr<X509Certificate> cert =
+ ImportCertFromFile(certs_dir, "nist.der");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), cert);
+
+ // This intermediate is only needed for old Linux machines. Modern NSS
+ // includes it as a root already.
+ scoped_refptr<X509Certificate> intermediate_cert =
+ ImportCertFromFile(certs_dir, "nist_intermediate.der");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate_cert);
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(intermediate_cert->os_cert_handle());
+ scoped_refptr<X509Certificate> cert_chain =
+ X509Certificate::CreateFromHandle(cert->os_cert_handle(),
+ X509Certificate::SOURCE_FROM_NETWORK,
+ intermediates);
+
+ int flags = 0;
+ CertVerifyResult verify_result;
+ // This is going to blow up in Feb 2012. Sorry! Disable and file a bug
+ // against agl.
+ int error = cert_chain->Verify("www.nist.gov", flags, &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_EQ(0, verify_result.cert_status);
+ EXPECT_TRUE(verify_result.is_issued_by_known_root);
+}
+
// A regression test for http://crbug.com/70293.
// The Key Usage extension in this RSA SSL server certificate does not have
// the keyEncipherment bit.
diff --git a/net/base/x509_certificate_win.cc b/net/base/x509_certificate_win.cc
index a9d6606..3fb9a5c 100644
--- a/net/base/x509_certificate_win.cc
+++ b/net/base/x509_certificate_win.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,6 +9,7 @@
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/pickle.h"
+#include "base/sha1.h"
#include "base/string_tokenizer.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
@@ -18,6 +19,7 @@
#include "net/base/net_errors.h"
#include "net/base/scoped_cert_chain_context.h"
#include "net/base/test_root_certs.h"
+#include "net/base/x509_certificate_known_roots_win.h"
#pragma comment(lib, "crypt32.lib")
@@ -504,6 +506,22 @@ void X509Certificate::Initialize() {
serial_number_ = serial_number_.substr(1, serial_number_.size() - 1);
}
+// IsIssuedByKnownRoot returns true if the given chain is rooted at a root CA
+// which we recognise as a standard root.
+// static
+bool X509Certificate::IsIssuedByKnownRoot(PCCERT_CHAIN_CONTEXT chain_context) {
+ PCERT_SIMPLE_CHAIN first_chain = chain_context->rgpChain[0];
+ int num_elements = first_chain->cElement;
+ if (num_elements < 1)
+ return false;
+ PCERT_CHAIN_ELEMENT* element = first_chain->rgpElement;
+ PCCERT_CONTEXT cert = element[num_elements - 1]->pCertContext;
+
+ SHA1Fingerprint hash = CalculateFingerprint(cert);
+ return IsSHA1HashInSortedArray(
+ hash, &kKnownRootCertSHA1Hashes[0][0], sizeof(kKnownRootCertSHA1Hashes));
+}
+
// static
X509Certificate* X509Certificate::CreateFromPickle(const Pickle& pickle,
void** pickle_iter) {
@@ -867,6 +885,8 @@ int X509Certificate::Verify(const std::string& hostname,
if (IsCertStatusError(verify_result->cert_status))
return MapCertStatusToNetError(verify_result->cert_status);
+ verify_result->is_issued_by_known_root = IsIssuedByKnownRoot(chain_context);
+
if (ev_policy_oid && CheckEV(chain_context, ev_policy_oid))
verify_result->cert_status |= CERT_STATUS_IS_EV;
return OK;
diff --git a/net/data/ssl/certificates/nist_intermediate.der b/net/data/ssl/certificates/nist_intermediate.der
new file mode 100644
index 0000000..55923a0
--- /dev/null
+++ b/net/data/ssl/certificates/nist_intermediate.der
Binary files differ