summaryrefslogtreecommitdiff
path: root/chromium/net/ssl/client_cert_store_impl_win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/ssl/client_cert_store_impl_win.cc')
-rw-r--r--chromium/net/ssl/client_cert_store_impl_win.cc205
1 files changed, 205 insertions, 0 deletions
diff --git a/chromium/net/ssl/client_cert_store_impl_win.cc b/chromium/net/ssl/client_cert_store_impl_win.cc
new file mode 100644
index 00000000000..63ea6e4a875
--- /dev/null
+++ b/chromium/net/ssl/client_cert_store_impl_win.cc
@@ -0,0 +1,205 @@
+// Copyright (c) 2012 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/ssl/client_cert_store_impl.h"
+
+#include <algorithm>
+#include <string>
+
+#define SECURITY_WIN32 // Needs to be defined before including security.h
+#include <windows.h>
+#include <wincrypt.h>
+#include <security.h>
+
+#include "base/logging.h"
+#include "crypto/scoped_capi_types.h"
+#include "net/cert/x509_util.h"
+
+namespace net {
+
+namespace {
+
+// Callback required by Windows API function CertFindChainInStore(). In addition
+// to filtering by extended/enhanced key usage, we do not show expired
+// certificates and require digital signature usage in the key usage extension.
+//
+// This matches our behavior on Mac OS X and that of NSS. It also matches the
+// default behavior of IE8. See http://support.microsoft.com/kb/890326 and
+// http://blogs.msdn.com/b/askie/archive/2009/06/09/my-expired-client-certifica
+// tes-no-longer-display-when-connecting-to-my-web-server-using-ie8.aspx
+static BOOL WINAPI ClientCertFindCallback(PCCERT_CONTEXT cert_context,
+ void* find_arg) {
+ // Verify the certificate key usage is appropriate or not specified.
+ BYTE key_usage;
+ if (CertGetIntendedKeyUsage(X509_ASN_ENCODING, cert_context->pCertInfo,
+ &key_usage, 1)) {
+ if (!(key_usage & CERT_DIGITAL_SIGNATURE_KEY_USAGE))
+ return FALSE;
+ } else {
+ DWORD err = GetLastError();
+ // If |err| is non-zero, it's an actual error. Otherwise the extension
+ // just isn't present, and we treat it as if everything was allowed.
+ if (err) {
+ DLOG(ERROR) << "CertGetIntendedKeyUsage failed: " << err;
+ return FALSE;
+ }
+ }
+
+ // Verify the current time is within the certificate's validity period.
+ if (CertVerifyTimeValidity(NULL, cert_context->pCertInfo) != 0)
+ return FALSE;
+
+ // Verify private key metadata is associated with this certificate.
+ // TODO(ppi): Is this really needed? Isn't it equivalent to leaving
+ // CERT_CHAIN_FIND_BY_ISSUER_NO_KEY_FLAG not set in |find_flags| argument of
+ // CertFindChainInStore()?
+ DWORD size = 0;
+ if (!CertGetCertificateContextProperty(
+ cert_context, CERT_KEY_PROV_INFO_PROP_ID, NULL, &size)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+bool GetClientCertsImpl(HCERTSTORE cert_store,
+ const SSLCertRequestInfo& request,
+ CertificateList* selected_certs) {
+ selected_certs->clear();
+
+ const size_t auth_count = request.cert_authorities.size();
+ std::vector<CERT_NAME_BLOB> issuers(auth_count);
+ for (size_t i = 0; i < auth_count; ++i) {
+ issuers[i].cbData = static_cast<DWORD>(request.cert_authorities[i].size());
+ issuers[i].pbData = reinterpret_cast<BYTE*>(
+ const_cast<char*>(request.cert_authorities[i].data()));
+ }
+
+ // Enumerate the client certificates.
+ CERT_CHAIN_FIND_BY_ISSUER_PARA find_by_issuer_para;
+ memset(&find_by_issuer_para, 0, sizeof(find_by_issuer_para));
+ find_by_issuer_para.cbSize = sizeof(find_by_issuer_para);
+ find_by_issuer_para.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH;
+ find_by_issuer_para.cIssuer = static_cast<DWORD>(auth_count);
+ find_by_issuer_para.rgIssuer =
+ reinterpret_cast<CERT_NAME_BLOB*>(issuers.data());
+ find_by_issuer_para.pfnFindCallback = ClientCertFindCallback;
+
+ PCCERT_CHAIN_CONTEXT chain_context = NULL;
+ DWORD find_flags = CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_FLAG |
+ CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_URL_FLAG;
+ for (;;) {
+ // Find a certificate chain.
+ chain_context = CertFindChainInStore(cert_store,
+ X509_ASN_ENCODING,
+ find_flags,
+ CERT_CHAIN_FIND_BY_ISSUER,
+ &find_by_issuer_para,
+ chain_context);
+ if (!chain_context) {
+ if (GetLastError() != CRYPT_E_NOT_FOUND)
+ DPLOG(ERROR) << "CertFindChainInStore failed: ";
+ break;
+ }
+
+ // Get the leaf certificate.
+ PCCERT_CONTEXT cert_context =
+ chain_context->rgpChain[0]->rgpElement[0]->pCertContext;
+ // Copy the certificate, so that it is valid after |cert_store| is closed.
+ PCCERT_CONTEXT cert_context2 = NULL;
+ BOOL ok = CertAddCertificateContextToStore(NULL, cert_context,
+ CERT_STORE_ADD_USE_EXISTING,
+ &cert_context2);
+ if (!ok) {
+ NOTREACHED();
+ continue;
+ }
+
+ // Grab the intermediates, if any.
+ X509Certificate::OSCertHandles intermediates;
+ for (DWORD i = 1; i < chain_context->rgpChain[0]->cElement; ++i) {
+ PCCERT_CONTEXT chain_intermediate =
+ chain_context->rgpChain[0]->rgpElement[i]->pCertContext;
+ PCCERT_CONTEXT copied_intermediate = NULL;
+ ok = CertAddCertificateContextToStore(NULL, chain_intermediate,
+ CERT_STORE_ADD_USE_EXISTING,
+ &copied_intermediate);
+ if (ok)
+ intermediates.push_back(copied_intermediate);
+ }
+ scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle(
+ cert_context2, intermediates);
+ selected_certs->push_back(cert);
+ CertFreeCertificateContext(cert_context2);
+ for (size_t i = 0; i < intermediates.size(); ++i)
+ CertFreeCertificateContext(intermediates[i]);
+ }
+
+ std::sort(selected_certs->begin(), selected_certs->end(),
+ x509_util::ClientCertSorter());
+ return true;
+}
+
+} // namespace
+
+bool ClientCertStoreImpl::GetClientCerts(const SSLCertRequestInfo& request,
+ CertificateList* selected_certs) {
+ // Client certificates of the user are in the "MY" system certificate store.
+ HCERTSTORE my_cert_store = CertOpenSystemStore(NULL, L"MY");
+ if (!my_cert_store) {
+ PLOG(ERROR) << "Could not open the \"MY\" system certificate store: ";
+ return false;
+ }
+
+ bool rv = GetClientCertsImpl(my_cert_store, request, selected_certs);
+ if (!CertCloseStore(my_cert_store, CERT_CLOSE_STORE_CHECK_FLAG)) {
+ PLOG(ERROR) << "Could not close the \"MY\" system certificate store: ";
+ return false;
+ }
+ return rv;
+}
+
+bool ClientCertStoreImpl::SelectClientCertsForTesting(
+ const CertificateList& input_certs,
+ const SSLCertRequestInfo& request,
+ CertificateList* selected_certs) {
+ typedef crypto::ScopedCAPIHandle<
+ HCERTSTORE,
+ crypto::CAPIDestroyerWithFlags<HCERTSTORE,
+ CertCloseStore, 0> > ScopedHCERTSTORE;
+
+ ScopedHCERTSTORE test_store(CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0,
+ NULL));
+ if (!test_store)
+ return false;
+
+ // Add available certificates to the test store.
+ for (size_t i = 0; i < input_certs.size(); ++i) {
+ // Add the certificate to the test store.
+ PCCERT_CONTEXT cert = NULL;
+ if (!CertAddCertificateContextToStore(test_store,
+ input_certs[i]->os_cert_handle(),
+ CERT_STORE_ADD_NEW, &cert)) {
+ return false;
+ }
+ // Add dummy private key data to the certificate - otherwise the certificate
+ // would be discarded by the filtering routines.
+ CRYPT_KEY_PROV_INFO private_key_data;
+ memset(&private_key_data, 0, sizeof(private_key_data));
+ if (!CertSetCertificateContextProperty(cert,
+ CERT_KEY_PROV_INFO_PROP_ID,
+ 0, &private_key_data)) {
+ return false;
+ }
+ // Decrement the reference count of the certificate (since we requested a
+ // copy).
+ if (!CertFreeCertificateContext(cert))
+ return false;
+ }
+
+ bool rv = GetClientCertsImpl(test_store.get(), request, selected_certs);
+ return rv;
+}
+
+} // namespace net