Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / net / ssl / client_cert_store_win.cc
blob2e9763ffdb269b77ac58c18735ece8f04ceb053f
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/ssl/client_cert_store_win.h"
7 #include <algorithm>
8 #include <string>
10 #define SECURITY_WIN32 // Needs to be defined before including security.h
11 #include <windows.h>
12 #include <security.h>
14 #include "base/callback.h"
15 #include "base/logging.h"
16 #include "crypto/scoped_capi_types.h"
17 #include "crypto/wincrypt_shim.h"
18 #include "net/cert/x509_util.h"
20 namespace net {
22 namespace {
24 // Callback required by Windows API function CertFindChainInStore(). In addition
25 // to filtering by extended/enhanced key usage, we do not show expired
26 // certificates and require digital signature usage in the key usage extension.
28 // This matches our behavior on Mac OS X and that of NSS. It also matches the
29 // default behavior of IE8. See http://support.microsoft.com/kb/890326 and
30 // http://blogs.msdn.com/b/askie/archive/2009/06/09/my-expired-client-certifica
31 // tes-no-longer-display-when-connecting-to-my-web-server-using-ie8.aspx
32 static BOOL WINAPI ClientCertFindCallback(PCCERT_CONTEXT cert_context,
33 void* find_arg) {
34 // Verify the certificate key usage is appropriate or not specified.
35 BYTE key_usage;
36 if (CertGetIntendedKeyUsage(X509_ASN_ENCODING, cert_context->pCertInfo,
37 &key_usage, 1)) {
38 if (!(key_usage & CERT_DIGITAL_SIGNATURE_KEY_USAGE))
39 return FALSE;
40 } else {
41 DWORD err = GetLastError();
42 // If |err| is non-zero, it's an actual error. Otherwise the extension
43 // just isn't present, and we treat it as if everything was allowed.
44 if (err) {
45 DLOG(ERROR) << "CertGetIntendedKeyUsage failed: " << err;
46 return FALSE;
50 // Verify the current time is within the certificate's validity period.
51 if (CertVerifyTimeValidity(NULL, cert_context->pCertInfo) != 0)
52 return FALSE;
54 // Verify private key metadata is associated with this certificate.
55 // TODO(ppi): Is this really needed? Isn't it equivalent to leaving
56 // CERT_CHAIN_FIND_BY_ISSUER_NO_KEY_FLAG not set in |find_flags| argument of
57 // CertFindChainInStore()?
58 DWORD size = 0;
59 if (!CertGetCertificateContextProperty(
60 cert_context, CERT_KEY_PROV_INFO_PROP_ID, NULL, &size)) {
61 return FALSE;
64 return TRUE;
67 void GetClientCertsImpl(HCERTSTORE cert_store,
68 const SSLCertRequestInfo& request,
69 CertificateList* selected_certs) {
70 selected_certs->clear();
72 const size_t auth_count = request.cert_authorities.size();
73 std::vector<CERT_NAME_BLOB> issuers(auth_count);
74 for (size_t i = 0; i < auth_count; ++i) {
75 issuers[i].cbData = static_cast<DWORD>(request.cert_authorities[i].size());
76 issuers[i].pbData = reinterpret_cast<BYTE*>(
77 const_cast<char*>(request.cert_authorities[i].data()));
80 // Enumerate the client certificates.
81 CERT_CHAIN_FIND_BY_ISSUER_PARA find_by_issuer_para;
82 memset(&find_by_issuer_para, 0, sizeof(find_by_issuer_para));
83 find_by_issuer_para.cbSize = sizeof(find_by_issuer_para);
84 find_by_issuer_para.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH;
85 find_by_issuer_para.cIssuer = static_cast<DWORD>(auth_count);
86 find_by_issuer_para.rgIssuer =
87 reinterpret_cast<CERT_NAME_BLOB*>(issuers.data());
88 find_by_issuer_para.pfnFindCallback = ClientCertFindCallback;
90 PCCERT_CHAIN_CONTEXT chain_context = NULL;
91 DWORD find_flags = CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_FLAG |
92 CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_URL_FLAG;
93 for (;;) {
94 // Find a certificate chain.
95 chain_context = CertFindChainInStore(cert_store,
96 X509_ASN_ENCODING,
97 find_flags,
98 CERT_CHAIN_FIND_BY_ISSUER,
99 &find_by_issuer_para,
100 chain_context);
101 if (!chain_context) {
102 if (GetLastError() != CRYPT_E_NOT_FOUND)
103 DPLOG(ERROR) << "CertFindChainInStore failed: ";
104 break;
107 // Get the leaf certificate.
108 PCCERT_CONTEXT cert_context =
109 chain_context->rgpChain[0]->rgpElement[0]->pCertContext;
110 // Copy the certificate, so that it is valid after |cert_store| is closed.
111 PCCERT_CONTEXT cert_context2 = NULL;
112 BOOL ok = CertAddCertificateContextToStore(NULL, cert_context,
113 CERT_STORE_ADD_USE_EXISTING,
114 &cert_context2);
115 if (!ok) {
116 NOTREACHED();
117 continue;
120 // Grab the intermediates, if any.
121 X509Certificate::OSCertHandles intermediates;
122 for (DWORD i = 1; i < chain_context->rgpChain[0]->cElement; ++i) {
123 PCCERT_CONTEXT chain_intermediate =
124 chain_context->rgpChain[0]->rgpElement[i]->pCertContext;
125 PCCERT_CONTEXT copied_intermediate = NULL;
126 ok = CertAddCertificateContextToStore(NULL, chain_intermediate,
127 CERT_STORE_ADD_USE_EXISTING,
128 &copied_intermediate);
129 if (ok)
130 intermediates.push_back(copied_intermediate);
132 scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle(
133 cert_context2, intermediates);
134 selected_certs->push_back(cert);
135 CertFreeCertificateContext(cert_context2);
136 for (size_t i = 0; i < intermediates.size(); ++i)
137 CertFreeCertificateContext(intermediates[i]);
140 std::sort(selected_certs->begin(), selected_certs->end(),
141 x509_util::ClientCertSorter());
144 } // namespace
146 ClientCertStoreWin::ClientCertStoreWin() {}
148 ClientCertStoreWin::~ClientCertStoreWin() {}
150 void ClientCertStoreWin::GetClientCerts(const SSLCertRequestInfo& request,
151 CertificateList* selected_certs,
152 const base::Closure& callback) {
153 // Client certificates of the user are in the "MY" system certificate store.
154 HCERTSTORE my_cert_store = CertOpenSystemStore(NULL, L"MY");
155 if (!my_cert_store) {
156 PLOG(ERROR) << "Could not open the \"MY\" system certificate store: ";
157 selected_certs->clear();
158 callback.Run();
159 return;
162 GetClientCertsImpl(my_cert_store, request, selected_certs);
163 if (!CertCloseStore(my_cert_store, CERT_CLOSE_STORE_CHECK_FLAG))
164 PLOG(ERROR) << "Could not close the \"MY\" system certificate store: ";
165 callback.Run();
168 bool ClientCertStoreWin::SelectClientCertsForTesting(
169 const CertificateList& input_certs,
170 const SSLCertRequestInfo& request,
171 CertificateList* selected_certs) {
172 typedef crypto::ScopedCAPIHandle<
173 HCERTSTORE,
174 crypto::CAPIDestroyerWithFlags<HCERTSTORE,
175 CertCloseStore, 0> > ScopedHCERTSTORE;
177 ScopedHCERTSTORE test_store(CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0,
178 NULL));
179 if (!test_store)
180 return false;
182 // Add available certificates to the test store.
183 for (size_t i = 0; i < input_certs.size(); ++i) {
184 // Add the certificate to the test store.
185 PCCERT_CONTEXT cert = NULL;
186 if (!CertAddCertificateContextToStore(test_store,
187 input_certs[i]->os_cert_handle(),
188 CERT_STORE_ADD_NEW, &cert)) {
189 return false;
191 // Add dummy private key data to the certificate - otherwise the certificate
192 // would be discarded by the filtering routines.
193 CRYPT_KEY_PROV_INFO private_key_data;
194 memset(&private_key_data, 0, sizeof(private_key_data));
195 if (!CertSetCertificateContextProperty(cert,
196 CERT_KEY_PROV_INFO_PROP_ID,
197 0, &private_key_data)) {
198 return false;
200 // Decrement the reference count of the certificate (since we requested a
201 // copy).
202 if (!CertFreeCertificateContext(cert))
203 return false;
206 GetClientCertsImpl(test_store.get(), request, selected_certs);
207 return true;
210 } // namespace net