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