| OLD | NEW | 
|    1 // Copyright 2013 The Chromium Authors. All rights reserved. |    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 |    2 // Use of this source code is governed by a BSD-style license that can be | 
|    3 // found in the LICENSE file. |    3 // found in the LICENSE file. | 
|    4  |    4  | 
|    5 #include "net/ssl/client_cert_store_win.h" |    5 #include "net/ssl/client_cert_store_win.h" | 
|    6  |    6  | 
|    7 #include <algorithm> |    7 #include <algorithm> | 
|    8 #include <string> |    8 #include <string> | 
|    9  |    9  | 
|   10 #define SECURITY_WIN32  // Needs to be defined before including security.h |   10 #define SECURITY_WIN32  // Needs to be defined before including security.h | 
|   11 #include <windows.h> |   11 #include <windows.h> | 
|   12 #include <security.h> |   12 #include <security.h> | 
|   13  |   13  | 
 |   14 #include "base/bind.h" | 
 |   15 #include "base/bind_helpers.h" | 
|   14 #include "base/callback.h" |   16 #include "base/callback.h" | 
|   15 #include "base/logging.h" |   17 #include "base/logging.h" | 
 |   18 #include "base/memory/ptr_util.h" | 
 |   19 #include "base/task_runner_util.h" | 
 |   20 #include "base/threading/thread_task_runner_handle.h" | 
|   16 #include "crypto/wincrypt_shim.h" |   21 #include "crypto/wincrypt_shim.h" | 
|   17 #include "net/cert/x509_util.h" |   22 #include "net/cert/x509_util.h" | 
 |   23 #include "net/ssl/ssl_platform_key_win.h" | 
 |   24 #include "net/ssl/ssl_private_key.h" | 
|   18  |   25  | 
|   19 namespace net { |   26 namespace net { | 
|   20  |   27  | 
|   21 namespace { |   28 namespace { | 
|   22  |   29  | 
 |   30 class ClientCertIdentityWin : public ClientCertIdentity { | 
 |   31  public: | 
 |   32   // Takes ownership of |cert_context|. | 
 |   33   ClientCertIdentityWin( | 
 |   34       scoped_refptr<net::X509Certificate> cert, | 
 |   35       PCCERT_CONTEXT cert_context, | 
 |   36       scoped_refptr<base::SingleThreadTaskRunner> key_task_runner) | 
 |   37       : ClientCertIdentity(std::move(cert)), | 
 |   38         cert_context_(cert_context), | 
 |   39         key_task_runner_(key_task_runner) {} | 
 |   40   ~ClientCertIdentityWin() override { | 
 |   41     CertFreeCertificateContext(cert_context_); | 
 |   42   } | 
 |   43  | 
 |   44   void AcquirePrivateKey( | 
 |   45       const base::Callback<void(scoped_refptr<SSLPrivateKey>)>& | 
 |   46           private_key_callback) override { | 
 |   47     if (base::PostTaskAndReplyWithResult( | 
 |   48             key_task_runner_.get(), FROM_HERE, | 
 |   49             base::Bind(&FetchClientCertPrivateKey, | 
 |   50                        base::Unretained(certificate()), cert_context_), | 
 |   51             private_key_callback)) { | 
 |   52       return; | 
 |   53     } | 
 |   54     // If the task could not be posted, behave as if there was no key. | 
 |   55     private_key_callback.Run(nullptr); | 
 |   56   } | 
 |   57  | 
 |   58  private: | 
 |   59   PCCERT_CONTEXT cert_context_; | 
 |   60   scoped_refptr<base::SingleThreadTaskRunner> key_task_runner_; | 
 |   61 }; | 
 |   62  | 
|   23 // Callback required by Windows API function CertFindChainInStore(). In addition |   63 // Callback required by Windows API function CertFindChainInStore(). In addition | 
|   24 // to filtering by extended/enhanced key usage, we do not show expired |   64 // to filtering by extended/enhanced key usage, we do not show expired | 
|   25 // certificates and require digital signature usage in the key usage extension. |   65 // certificates and require digital signature usage in the key usage extension. | 
|   26 // |   66 // | 
|   27 // This matches our behavior on Mac OS X and that of NSS. It also matches the |   67 // 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 |   68 // 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 |   69 // 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 |   70 //     tes-no-longer-display-when-connecting-to-my-web-server-using-ie8.aspx | 
|   31 static BOOL WINAPI ClientCertFindCallback(PCCERT_CONTEXT cert_context, |   71 static BOOL WINAPI ClientCertFindCallback(PCCERT_CONTEXT cert_context, | 
|   32                                           void* find_arg) { |   72                                           void* find_arg) { | 
| (...skipping 25 matching lines...) Expand all  Loading... | 
|   58   if (!CertGetCertificateContextProperty( |   98   if (!CertGetCertificateContextProperty( | 
|   59           cert_context, CERT_KEY_PROV_INFO_PROP_ID, NULL, &size)) { |   99           cert_context, CERT_KEY_PROV_INFO_PROP_ID, NULL, &size)) { | 
|   60     return FALSE; |  100     return FALSE; | 
|   61   } |  101   } | 
|   62  |  102  | 
|   63   return TRUE; |  103   return TRUE; | 
|   64 } |  104 } | 
|   65  |  105  | 
|   66 void GetClientCertsImpl(HCERTSTORE cert_store, |  106 void GetClientCertsImpl(HCERTSTORE cert_store, | 
|   67                         const SSLCertRequestInfo& request, |  107                         const SSLCertRequestInfo& request, | 
|   68                         CertificateList* selected_certs) { |  108                         ClientCertIdentityList* selected_identities) { | 
|   69   selected_certs->clear(); |  109   selected_identities->clear(); | 
 |  110  | 
 |  111   scoped_refptr<base::SingleThreadTaskRunner> current_thread = | 
 |  112       base::ThreadTaskRunnerHandle::Get(); | 
|   70  |  113  | 
|   71   const size_t auth_count = request.cert_authorities.size(); |  114   const size_t auth_count = request.cert_authorities.size(); | 
|   72   std::vector<CERT_NAME_BLOB> issuers(auth_count); |  115   std::vector<CERT_NAME_BLOB> issuers(auth_count); | 
|   73   for (size_t i = 0; i < auth_count; ++i) { |  116   for (size_t i = 0; i < auth_count; ++i) { | 
|   74     issuers[i].cbData = static_cast<DWORD>(request.cert_authorities[i].size()); |  117     issuers[i].cbData = static_cast<DWORD>(request.cert_authorities[i].size()); | 
|   75     issuers[i].pbData = reinterpret_cast<BYTE*>( |  118     issuers[i].pbData = reinterpret_cast<BYTE*>( | 
|   76         const_cast<char*>(request.cert_authorities[i].data())); |  119         const_cast<char*>(request.cert_authorities[i].data())); | 
|   77   } |  120   } | 
|   78  |  121  | 
|   79   // Enumerate the client certificates. |  122   // Enumerate the client certificates. | 
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  142       intermediates.pop_back(); |  185       intermediates.pop_back(); | 
|  143     } |  186     } | 
|  144  |  187  | 
|  145     // TODO(svaldez): cert currently wraps cert_context2 which may be backed |  188     // TODO(svaldez): cert currently wraps cert_context2 which may be backed | 
|  146     // by a smartcard with threading difficulties. Instead, create a fresh |  189     // by a smartcard with threading difficulties. Instead, create a fresh | 
|  147     // X509Certificate with CreateFromBytes and route cert_context2 into the |  190     // X509Certificate with CreateFromBytes and route cert_context2 into the | 
|  148     // SSLPrivateKey. Probably changing CertificateList to be a |  191     // SSLPrivateKey. Probably changing CertificateList to be a | 
|  149     // pair<X509Certificate, SSLPrivateKeyCallback>. |  192     // pair<X509Certificate, SSLPrivateKeyCallback>. | 
|  150     scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle( |  193     scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle( | 
|  151         cert_context2, intermediates); |  194         cert_context2, intermediates); | 
|  152     if (cert) |  195     if (cert) { | 
|  153       selected_certs->push_back(std::move(cert)); |  196       selected_identities->push_back(base::MakeUnique<ClientCertIdentityWin>( | 
|  154     CertFreeCertificateContext(cert_context2); |  197           std::move(cert), | 
 |  198           cert_context2,     // Takes ownership of |cert_context2|. | 
 |  199           current_thread));  // The key must be acquired on the same thread, as | 
 |  200                              // the PCCERT_CONTEXT may not be thread safe. | 
 |  201     } | 
|  155     for (size_t i = 0; i < intermediates.size(); ++i) |  202     for (size_t i = 0; i < intermediates.size(); ++i) | 
|  156       CertFreeCertificateContext(intermediates[i]); |  203       CertFreeCertificateContext(intermediates[i]); | 
|  157   } |  204   } | 
|  158  |  205  | 
|  159   std::sort(selected_certs->begin(), selected_certs->end(), |  206   std::sort(selected_identities->begin(), selected_identities->end(), | 
|  160             x509_util::ClientCertSorter()); |  207             ClientCertIdentitySorter()); | 
|  161 } |  208 } | 
|  162  |  209  | 
|  163 }  // namespace |  210 }  // namespace | 
|  164  |  211  | 
|  165 ClientCertStoreWin::ClientCertStoreWin() {} |  212 ClientCertStoreWin::ClientCertStoreWin() {} | 
|  166  |  213  | 
|  167 ClientCertStoreWin::ClientCertStoreWin(HCERTSTORE cert_store) { |  214 ClientCertStoreWin::ClientCertStoreWin(HCERTSTORE cert_store) { | 
|  168   DCHECK(cert_store); |  215   DCHECK(cert_store); | 
|  169   cert_store_.reset(cert_store); |  216   cert_store_.reset(cert_store); | 
|  170 } |  217 } | 
|  171  |  218  | 
|  172 ClientCertStoreWin::~ClientCertStoreWin() {} |  219 ClientCertStoreWin::~ClientCertStoreWin() {} | 
|  173  |  220  | 
|  174 void ClientCertStoreWin::GetClientCerts( |  221 void ClientCertStoreWin::GetClientCerts( | 
|  175     const SSLCertRequestInfo& request, |  222     const SSLCertRequestInfo& request, | 
|  176     const ClientCertListCallback& callback) { |  223     const ClientCertListCallback& callback) { | 
|  177   CertificateList selected_certs; |  224   ClientCertIdentityList selected_identities; | 
|  178   if (cert_store_) { |  225   if (cert_store_) { | 
|  179     // Use the existing client cert store. Note: Under some situations, |  226     // Use the existing client cert store. Note: Under some situations, | 
|  180     // it's possible for this to return certificates that aren't usable |  227     // it's possible for this to return certificates that aren't usable | 
|  181     // (see below). |  228     // (see below). | 
|  182     GetClientCertsImpl(cert_store_, request, &selected_certs); |  229     GetClientCertsImpl(cert_store_, request, &selected_identities); | 
|  183     callback.Run(std::move(selected_certs)); |  230     callback.Run(std::move(selected_identities)); | 
|  184     return; |  231     return; | 
|  185   } |  232   } | 
|  186  |  233  | 
|  187   // Always open a new instance of the "MY" store, to ensure that there |  234   // Always open a new instance of the "MY" store, to ensure that there | 
|  188   // are no previously cached certificates being reused after they're |  235   // are no previously cached certificates being reused after they're | 
|  189   // no longer available (some smartcard providers fail to update the "MY" |  236   // no longer available (some smartcard providers fail to update the "MY" | 
|  190   // store handles and instead interpose CertOpenSystemStore). |  237   // store handles and instead interpose CertOpenSystemStore). | 
|  191   ScopedHCERTSTORE my_cert_store(CertOpenSystemStore(NULL, L"MY")); |  238   ScopedHCERTSTORE my_cert_store(CertOpenSystemStore(NULL, L"MY")); | 
|  192   if (!my_cert_store) { |  239   if (!my_cert_store) { | 
|  193     PLOG(ERROR) << "Could not open the \"MY\" system certificate store: "; |  240     PLOG(ERROR) << "Could not open the \"MY\" system certificate store: "; | 
|  194     callback.Run(CertificateList()); |  241     callback.Run(ClientCertIdentityList()); | 
|  195     return; |  242     return; | 
|  196   } |  243   } | 
|  197  |  244  | 
|  198   GetClientCertsImpl(my_cert_store, request, &selected_certs); |  245   GetClientCertsImpl(my_cert_store, request, &selected_identities); | 
|  199   callback.Run(std::move(selected_certs)); |  246   callback.Run(std::move(selected_identities)); | 
|  200 } |  247 } | 
|  201  |  248  | 
|  202 bool ClientCertStoreWin::SelectClientCertsForTesting( |  249 bool ClientCertStoreWin::SelectClientCertsForTesting( | 
|  203     const CertificateList& input_certs, |  250     const CertificateList& input_certs, | 
|  204     const SSLCertRequestInfo& request, |  251     const SSLCertRequestInfo& request, | 
|  205     CertificateList* selected_certs) { |  252     ClientCertIdentityList* selected_identities) { | 
|  206   ScopedHCERTSTORE test_store(CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, |  253   ScopedHCERTSTORE test_store(CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, | 
|  207                                             NULL)); |  254                                             NULL)); | 
|  208   if (!test_store) |  255   if (!test_store) | 
|  209     return false; |  256     return false; | 
|  210  |  257  | 
|  211   // Add available certificates to the test store. |  258   // Add available certificates to the test store. | 
|  212   for (size_t i = 0; i < input_certs.size(); ++i) { |  259   for (size_t i = 0; i < input_certs.size(); ++i) { | 
|  213     // Add the certificate to the test store. |  260     // Add the certificate to the test store. | 
|  214     PCCERT_CONTEXT cert = NULL; |  261     PCCERT_CONTEXT cert = NULL; | 
|  215     if (!CertAddCertificateContextToStore(test_store, |  262     if (!CertAddCertificateContextToStore(test_store, | 
|  216                                           input_certs[i]->os_cert_handle(), |  263                                           input_certs[i]->os_cert_handle(), | 
|  217                                           CERT_STORE_ADD_NEW, &cert)) { |  264                                           CERT_STORE_ADD_NEW, &cert)) { | 
|  218       return false; |  265       return false; | 
|  219     } |  266     } | 
|  220     // Add dummy private key data to the certificate - otherwise the certificate |  267     // Add dummy private key data to the certificate - otherwise the certificate | 
|  221     // would be discarded by the filtering routines. |  268     // would be discarded by the filtering routines. | 
|  222     CRYPT_KEY_PROV_INFO private_key_data; |  269     CRYPT_KEY_PROV_INFO private_key_data; | 
|  223     memset(&private_key_data, 0, sizeof(private_key_data)); |  270     memset(&private_key_data, 0, sizeof(private_key_data)); | 
|  224     if (!CertSetCertificateContextProperty(cert, |  271     if (!CertSetCertificateContextProperty(cert, | 
|  225                                            CERT_KEY_PROV_INFO_PROP_ID, |  272                                            CERT_KEY_PROV_INFO_PROP_ID, | 
|  226                                            0, &private_key_data)) { |  273                                            0, &private_key_data)) { | 
|  227       return false; |  274       return false; | 
|  228     } |  275     } | 
|  229     // Decrement the reference count of the certificate (since we requested a |  276     // Decrement the reference count of the certificate (since we requested a | 
|  230     // copy). |  277     // copy). | 
|  231     if (!CertFreeCertificateContext(cert)) |  278     if (!CertFreeCertificateContext(cert)) | 
|  232       return false; |  279       return false; | 
|  233   } |  280   } | 
|  234  |  281  | 
|  235   GetClientCertsImpl(test_store.get(), request, selected_certs); |  282   GetClientCertsImpl(test_store.get(), request, selected_identities); | 
|  236   return true; |  283   return true; | 
|  237 } |  284 } | 
|  238  |  285  | 
|  239 }  // namespace net |  286 }  // namespace net | 
| OLD | NEW |