Chromium Code Reviews| Index: net/base/ssl_client_socket_win.cc |
| =================================================================== |
| --- net/base/ssl_client_socket_win.cc (revision 18735) |
| +++ net/base/ssl_client_socket_win.cc (working copy) |
| @@ -12,6 +12,7 @@ |
| #include "net/base/connection_type_histograms.h" |
| #include "net/base/io_buffer.h" |
| #include "net/base/net_errors.h" |
| +#include "net/base/ssl_cert_request_info.h" |
| #include "net/base/ssl_info.h" |
| #pragma comment(lib, "secur32.lib") |
| @@ -62,8 +63,9 @@ |
| // Returns true if the two CERT_CONTEXTs contain the same certificate. |
| bool SameCert(PCCERT_CONTEXT a, PCCERT_CONTEXT b) { |
| - return a->cbCertEncoded == b->cbCertEncoded && |
| - memcmp(a->pbCertEncoded, b->pbCertEncoded, b->cbCertEncoded) == 0; |
| + return a == b || |
| + (a->cbCertEncoded == b->cbCertEncoded && |
| + memcmp(a->pbCertEncoded, b->pbCertEncoded, b->cbCertEncoded) == 0); |
| } |
| //----------------------------------------------------------------------------- |
| @@ -77,44 +79,80 @@ |
| SSL_VERSION_MASKS = 1 << 3 // The number of SSL version bitmasks. |
| }; |
| -// A table of CredHandles for all possible combinations of SSL versions. |
| -class CredHandleTable { |
| +// CredHandleClass simply gives a default constructor and a destructor to |
| +// SSPI's CredHandle type (a C struct). The default constuctor is required |
| +// by STL containers. |
| +class CredHandleClass : public CredHandle { |
| public: |
| - CredHandleTable() { |
| - memset(creds_, 0, sizeof(creds_)); |
| + CredHandleClass() { |
| + dwLower = 0; |
| + dwUpper = 0; |
| } |
| - // Frees the CredHandles. |
| - ~CredHandleTable() { |
| - for (int i = 0; i < arraysize(creds_); ++i) { |
| - if (creds_[i].dwLower || creds_[i].dwUpper) |
| - FreeCredentialsHandle(&creds_[i]); |
| + ~CredHandleClass() { |
| + if (dwLower || dwUpper) { |
|
eroman
2009/06/19 21:42:24
heh, been a while since i seen hungarian notation
|
| + SECURITY_STATUS status = FreeCredentialsHandle(this); |
| + DCHECK(status == SEC_E_OK); |
| } |
| } |
| +}; |
| - CredHandle* GetHandle(int ssl_version_mask) { |
| - DCHECK(0 < ssl_version_mask && ssl_version_mask < arraysize(creds_)); |
| - CredHandle* handle = &creds_[ssl_version_mask]; |
| - { |
| - AutoLock lock(lock_); |
| - if (!handle->dwLower && !handle->dwUpper) |
| - InitializeHandle(handle, ssl_version_mask); |
| +// A table of CredHandles. |
| +class CredHandleTable { |
| + public: |
| + CredHandleTable() {} |
| + |
| + ~CredHandleTable() {} |
| + |
| + CredHandle* GetHandle(PCCERT_CONTEXT client_cert, int ssl_version_mask) { |
| + DCHECK(0 < ssl_version_mask && |
|
eroman
2009/06/19 21:42:24
nit: I would suggest writing this as two DCHECKs:
wtc
2009/06/19 23:11:53
I didn't make this change because the second DCHEC
|
| + ssl_version_mask < arraysize(anonymous_creds_)); |
| + CredHandle* handle; |
| + AutoLock lock(lock_); |
| + if (client_cert) { |
| + handle = &client_cert_creds_[ |
| + std::make_pair(client_cert, ssl_version_mask)]; |
| + } else { |
| + handle = &anonymous_creds_[ssl_version_mask]; |
| } |
| + if (!handle->dwLower && !handle->dwUpper) |
| + InitializeHandle(handle, client_cert, ssl_version_mask); |
| return handle; |
| } |
| private: |
| - static void InitializeHandle(CredHandle* handle, int ssl_version_mask); |
| + // CredHandleMapKey is a std::pair consisting of these two components: |
| + // PCCERT_CONTEXT client_cert |
| + // int ssl_version_mask |
| + typedef std::pair<PCCERT_CONTEXT, int> CredHandleMapKey; |
| + typedef std::map<CredHandleMapKey, CredHandleClass> CredHandleMap; |
|
eroman
2009/06/19 21:42:24
Hmm, I dunno about this...
Most of the STL contai
|
| + |
| + static void InitializeHandle(CredHandle* handle, |
| + PCCERT_CONTEXT client_cert, |
| + int ssl_version_mask); |
| + |
| Lock lock_; |
| - CredHandle creds_[SSL_VERSION_MASKS]; |
| + |
| + // Anonymous (no client certificate) CredHandles for all possible |
| + // combinations of SSL versions. Defined as an array for fast lookup. |
| + CredHandleClass anonymous_creds_[SSL_VERSION_MASKS]; |
| + |
| + // CredHandles that use a client certificate. |
| + CredHandleMap client_cert_creds_; |
| }; |
| // static |
| void CredHandleTable::InitializeHandle(CredHandle* handle, |
| + PCCERT_CONTEXT client_cert, |
| int ssl_version_mask) { |
| SCHANNEL_CRED schannel_cred = {0}; |
| schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; |
| + if (client_cert) { |
| + schannel_cred.cCreds = 1; |
| + schannel_cred.paCred = &client_cert; |
| + // Schannel will make its own copy of client_cert. |
| + } |
| // The global system registry settings take precedence over the value of |
| // schannel_cred.grbitEnabledProtocols. |
| @@ -180,14 +218,9 @@ |
| // versions (for example, SSL3 | TLS1 for normal use, plus SSL3 when visiting |
| // TLS-intolerant servers). These CredHandles are initialized only when |
| // needed. |
| -// |
| -// NOTE: Since the client authentication certificate is also a property of the |
| -// CredHandle, SSL sockets won't be able to use the shared CredHandles when we |
| -// support SSL client authentication. So we will need to refine the way we |
| -// share SSL sessions. For now the simple solution of using shared |
| -// CredHandles is good enough. |
| -static CredHandle* GetCredHandle(int ssl_version_mask) { |
| +static CredHandle* GetCredHandle(PCCERT_CONTEXT client_cert, |
| + int ssl_version_mask) { |
| // It doesn't matter whether GetCredHandle returns NULL or a pointer to an |
| // uninitialized CredHandle on failure. Both of them cause |
| // InitializeSecurityContext to fail with SEC_E_INVALID_HANDLE. |
| @@ -195,11 +228,43 @@ |
| NOTREACHED(); |
| return NULL; |
| } |
| - return Singleton<CredHandleTable>::get()->GetHandle(ssl_version_mask); |
| + return Singleton<CredHandleTable>::get()->GetHandle(client_cert, |
| + ssl_version_mask); |
| } |
| //----------------------------------------------------------------------------- |
| +// A memory certificate store for client certificates. This allows us to |
| +// close the "MY" system certificate store when we finish searching for |
| +// client certificates. |
| +class ClientCertStore { |
| + public: |
| + ClientCertStore() { |
| + store_ = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL); |
| + } |
| + |
| + ~ClientCertStore() { |
| + if (store_) { |
| + BOOL ok = CertCloseStore(store_, CERT_CLOSE_STORE_CHECK_FLAG); |
| + DCHECK(ok); |
| + } |
| + } |
| + |
| + PCCERT_CONTEXT CopyCertContext(PCCERT_CONTEXT client_cert) { |
| + PCCERT_CONTEXT copy; |
| + BOOL ok = CertAddCertificateContextToStore(store_, client_cert, |
| + CERT_STORE_ADD_USE_EXISTING, |
| + ©); |
| + DCHECK(ok); |
| + return ok ? copy : NULL; |
| + } |
| + |
| + private: |
| + HCERTSTORE store_; |
| +}; |
| + |
| +//----------------------------------------------------------------------------- |
| + |
| // Size of recv_buffer_ |
| // |
| // Ciphertext is decrypted one SSL record at a time, so recv_buffer_ needs to |
| @@ -262,7 +327,73 @@ |
| void SSLClientSocketWin::GetSSLCertRequestInfo( |
| SSLCertRequestInfo* cert_request_info) { |
| - // TODO(wtc): implement this. |
| + cert_request_info->host_and_port = hostname_; // TODO(wtc): no port! |
| + cert_request_info->client_certs.clear(); |
| + |
| + // Get the certificate_authorities field of the CertificateRequest message. |
| + // Schannel doesn't return the certificate_types field of the |
| + // CertificateRequest message to us, so we can't filter the client |
| + // certificates properly. :-( |
| + SecPkgContext_IssuerListInfoEx issuer_list; |
| + SECURITY_STATUS status = QueryContextAttributes( |
| + &ctxt_, SECPKG_ATTR_ISSUER_LIST_EX, &issuer_list); |
| + if (status != SEC_E_OK) { |
| + DLOG(ERROR) << "QueryContextAttributes (issuer list) failed: " << status; |
| + return; |
| + } |
| + |
| + // Client certificates of the user are in the "MY" system certificate store. |
| + HCERTSTORE my_cert_store = CertOpenSystemStore(NULL, L"MY"); |
| + if (!my_cert_store) { |
| + FreeContextBuffer(issuer_list.aIssuers); |
| + return; |
| + } |
| + |
| + // 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 = issuer_list.cIssuers; |
| + find_by_issuer_para.rgIssuer = issuer_list.aIssuers; |
| + |
| + PCCERT_CHAIN_CONTEXT chain_context = NULL; |
| + |
| + for (;;) { |
| + // Find a certificate chain. |
| + chain_context = CertFindChainInStore(my_cert_store, |
| + X509_ASN_ENCODING, |
| + 0, |
| + CERT_CHAIN_FIND_BY_ISSUER, |
| + &find_by_issuer_para, |
| + chain_context); |
| + if (!chain_context) { |
| + DWORD err = GetLastError(); |
| + if (err != CRYPT_E_NOT_FOUND) |
| + DLOG(ERROR) << "CertFindChainInStore failed: " << err; |
| + break; |
| + } |
| + |
| + // Get the leaf certificate. |
| + PCCERT_CONTEXT cert_context = |
| + chain_context->rgpChain[0]->rgpElement[0]->pCertContext; |
| + // Copy it to our own certificate store, so that we can close the "MY" |
| + // certificate store before returning from this function. |
| + PCCERT_CONTEXT cert_context2 = |
| + Singleton<ClientCertStore>::get()->CopyCertContext(cert_context); |
| + if (!cert_context2) { |
| + NOTREACHED(); |
| + continue; |
| + } |
| + scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle( |
| + cert_context2, X509Certificate::SOURCE_LONE_CERT_IMPORT); |
| + cert_request_info->client_certs.push_back(cert); |
| + } |
| + |
| + FreeContextBuffer(issuer_list.aIssuers); |
| + |
| + BOOL ok = CertCloseStore(my_cert_store, CERT_CLOSE_STORE_CHECK_FLAG); |
| + DCHECK(ok); |
| } |
| int SSLClientSocketWin::Connect(CompletionCallback* callback) { |
| @@ -281,7 +412,10 @@ |
| // rather than enabling no protocols. So we have to fail here. |
| if (ssl_version_mask == 0) |
| return ERR_NO_SSL_VERSIONS_ENABLED; |
| - creds_ = GetCredHandle(ssl_version_mask); |
| + PCCERT_CONTEXT cert_context = NULL; |
| + if (ssl_config_.client_cert) |
| + cert_context = ssl_config_.client_cert->os_cert_handle(); |
| + creds_ = GetCredHandle(cert_context, ssl_version_mask); |
| memset(&ctxt_, 0, sizeof(ctxt_)); |