Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(366)

Side by Side Diff: net/base/ssl_client_socket_win.cc

Issue 131086: Implement the backend of SSL client authentication for... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Upload before checkin Created 11 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2006-2009 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/base/ssl_client_socket_win.h" 5 #include "net/base/ssl_client_socket_win.h"
6 6
7 #include <schnlsp.h> 7 #include <schnlsp.h>
8 8
9 #include "base/lock.h" 9 #include "base/lock.h"
10 #include "base/singleton.h" 10 #include "base/singleton.h"
11 #include "base/string_util.h" 11 #include "base/string_util.h"
12 #include "net/base/connection_type_histograms.h" 12 #include "net/base/connection_type_histograms.h"
13 #include "net/base/io_buffer.h" 13 #include "net/base/io_buffer.h"
14 #include "net/base/net_errors.h" 14 #include "net/base/net_errors.h"
15 #include "net/base/ssl_cert_request_info.h"
15 #include "net/base/ssl_info.h" 16 #include "net/base/ssl_info.h"
16 17
17 #pragma comment(lib, "secur32.lib") 18 #pragma comment(lib, "secur32.lib")
18 19
19 namespace net { 20 namespace net {
20 21
21 //----------------------------------------------------------------------------- 22 //-----------------------------------------------------------------------------
22 23
23 // TODO(wtc): See http://msdn.microsoft.com/en-us/library/aa377188(VS.85).aspx 24 // TODO(wtc): See http://msdn.microsoft.com/en-us/library/aa377188(VS.85).aspx
24 // for the other error codes we may need to map. 25 // for the other error codes we may need to map.
(...skipping 30 matching lines...) Expand all
55 case SEC_E_OK: 56 case SEC_E_OK:
56 return OK; 57 return OK;
57 default: 58 default:
58 LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED"; 59 LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED";
59 return ERR_FAILED; 60 return ERR_FAILED;
60 } 61 }
61 } 62 }
62 63
63 // Returns true if the two CERT_CONTEXTs contain the same certificate. 64 // Returns true if the two CERT_CONTEXTs contain the same certificate.
64 bool SameCert(PCCERT_CONTEXT a, PCCERT_CONTEXT b) { 65 bool SameCert(PCCERT_CONTEXT a, PCCERT_CONTEXT b) {
65 return a->cbCertEncoded == b->cbCertEncoded && 66 return a == b ||
66 memcmp(a->pbCertEncoded, b->pbCertEncoded, b->cbCertEncoded) == 0; 67 (a->cbCertEncoded == b->cbCertEncoded &&
68 memcmp(a->pbCertEncoded, b->pbCertEncoded, b->cbCertEncoded) == 0);
67 } 69 }
68 70
69 //----------------------------------------------------------------------------- 71 //-----------------------------------------------------------------------------
70 72
71 // A bitmask consisting of these bit flags encodes which versions of the SSL 73 // A bitmask consisting of these bit flags encodes which versions of the SSL
72 // protocol (SSL 2.0, SSL 3.0, and TLS 1.0) are enabled. 74 // protocol (SSL 2.0, SSL 3.0, and TLS 1.0) are enabled.
73 enum { 75 enum {
74 SSL2 = 1 << 0, 76 SSL2 = 1 << 0,
75 SSL3 = 1 << 1, 77 SSL3 = 1 << 1,
76 TLS1 = 1 << 2, 78 TLS1 = 1 << 2,
77 SSL_VERSION_MASKS = 1 << 3 // The number of SSL version bitmasks. 79 SSL_VERSION_MASKS = 1 << 3 // The number of SSL version bitmasks.
78 }; 80 };
79 81
80 // A table of CredHandles for all possible combinations of SSL versions. 82 // CredHandleClass simply gives a default constructor and a destructor to
83 // SSPI's CredHandle type (a C struct). The default constuctor is required
84 // by STL containers.
85 class CredHandleClass : public CredHandle {
86 public:
87 CredHandleClass() {
88 dwLower = 0;
89 dwUpper = 0;
90 }
91
92 ~CredHandleClass() {
93 if (dwLower || dwUpper) {
eroman 2009/06/19 21:42:24 heh, been a while since i seen hungarian notation
94 SECURITY_STATUS status = FreeCredentialsHandle(this);
95 DCHECK(status == SEC_E_OK);
96 }
97 }
98 };
99
100 // A table of CredHandles.
81 class CredHandleTable { 101 class CredHandleTable {
82 public: 102 public:
83 CredHandleTable() { 103 CredHandleTable() {}
84 memset(creds_, 0, sizeof(creds_));
85 }
86 104
87 // Frees the CredHandles. 105 ~CredHandleTable() {}
88 ~CredHandleTable() { 106
89 for (int i = 0; i < arraysize(creds_); ++i) { 107 CredHandle* GetHandle(PCCERT_CONTEXT client_cert, int ssl_version_mask) {
90 if (creds_[i].dwLower || creds_[i].dwUpper) 108 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
91 FreeCredentialsHandle(&creds_[i]); 109 ssl_version_mask < arraysize(anonymous_creds_));
110 CredHandle* handle;
111 AutoLock lock(lock_);
112 if (client_cert) {
113 handle = &client_cert_creds_[
114 std::make_pair(client_cert, ssl_version_mask)];
115 } else {
116 handle = &anonymous_creds_[ssl_version_mask];
92 } 117 }
93 } 118 if (!handle->dwLower && !handle->dwUpper)
94 119 InitializeHandle(handle, client_cert, ssl_version_mask);
95 CredHandle* GetHandle(int ssl_version_mask) {
96 DCHECK(0 < ssl_version_mask && ssl_version_mask < arraysize(creds_));
97 CredHandle* handle = &creds_[ssl_version_mask];
98 {
99 AutoLock lock(lock_);
100 if (!handle->dwLower && !handle->dwUpper)
101 InitializeHandle(handle, ssl_version_mask);
102 }
103 return handle; 120 return handle;
104 } 121 }
105 122
106 private: 123 private:
107 static void InitializeHandle(CredHandle* handle, int ssl_version_mask); 124 // CredHandleMapKey is a std::pair consisting of these two components:
125 // PCCERT_CONTEXT client_cert
126 // int ssl_version_mask
127 typedef std::pair<PCCERT_CONTEXT, int> CredHandleMapKey;
128
129 typedef std::map<CredHandleMapKey, CredHandleClass> CredHandleMap;
eroman 2009/06/19 21:42:24 Hmm, I dunno about this... Most of the STL contai
130
131 static void InitializeHandle(CredHandle* handle,
132 PCCERT_CONTEXT client_cert,
133 int ssl_version_mask);
108 134
109 Lock lock_; 135 Lock lock_;
110 CredHandle creds_[SSL_VERSION_MASKS]; 136
137 // Anonymous (no client certificate) CredHandles for all possible
138 // combinations of SSL versions. Defined as an array for fast lookup.
139 CredHandleClass anonymous_creds_[SSL_VERSION_MASKS];
140
141 // CredHandles that use a client certificate.
142 CredHandleMap client_cert_creds_;
111 }; 143 };
112 144
113 // static 145 // static
114 void CredHandleTable::InitializeHandle(CredHandle* handle, 146 void CredHandleTable::InitializeHandle(CredHandle* handle,
147 PCCERT_CONTEXT client_cert,
115 int ssl_version_mask) { 148 int ssl_version_mask) {
116 SCHANNEL_CRED schannel_cred = {0}; 149 SCHANNEL_CRED schannel_cred = {0};
117 schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; 150 schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
151 if (client_cert) {
152 schannel_cred.cCreds = 1;
153 schannel_cred.paCred = &client_cert;
154 // Schannel will make its own copy of client_cert.
155 }
118 156
119 // The global system registry settings take precedence over the value of 157 // The global system registry settings take precedence over the value of
120 // schannel_cred.grbitEnabledProtocols. 158 // schannel_cred.grbitEnabledProtocols.
121 schannel_cred.grbitEnabledProtocols = 0; 159 schannel_cred.grbitEnabledProtocols = 0;
122 if (ssl_version_mask & SSL2) 160 if (ssl_version_mask & SSL2)
123 schannel_cred.grbitEnabledProtocols |= SP_PROT_SSL2; 161 schannel_cred.grbitEnabledProtocols |= SP_PROT_SSL2;
124 if (ssl_version_mask & SSL3) 162 if (ssl_version_mask & SSL3)
125 schannel_cred.grbitEnabledProtocols |= SP_PROT_SSL3; 163 schannel_cred.grbitEnabledProtocols |= SP_PROT_SSL3;
126 if (ssl_version_mask & TLS1) 164 if (ssl_version_mask & TLS1)
127 schannel_cred.grbitEnabledProtocols |= SP_PROT_TLS1; 165 schannel_cred.grbitEnabledProtocols |= SP_PROT_TLS1;
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
173 // For the SSL sockets to share SSL sessions by session resumption handshakes, 211 // For the SSL sockets to share SSL sessions by session resumption handshakes,
174 // they need to use the same CredHandle. The GetCredHandle function creates 212 // they need to use the same CredHandle. The GetCredHandle function creates
175 // and returns a shared CredHandle. 213 // and returns a shared CredHandle.
176 // 214 //
177 // The versions of the SSL protocol enabled are a property of the CredHandle. 215 // The versions of the SSL protocol enabled are a property of the CredHandle.
178 // So we need a separate CredHandle for each combination of SSL versions. 216 // So we need a separate CredHandle for each combination of SSL versions.
179 // Most of the time Chromium will use only one or two combinations of SSL 217 // Most of the time Chromium will use only one or two combinations of SSL
180 // versions (for example, SSL3 | TLS1 for normal use, plus SSL3 when visiting 218 // versions (for example, SSL3 | TLS1 for normal use, plus SSL3 when visiting
181 // TLS-intolerant servers). These CredHandles are initialized only when 219 // TLS-intolerant servers). These CredHandles are initialized only when
182 // needed. 220 // needed.
183 //
184 // NOTE: Since the client authentication certificate is also a property of the
185 // CredHandle, SSL sockets won't be able to use the shared CredHandles when we
186 // support SSL client authentication. So we will need to refine the way we
187 // share SSL sessions. For now the simple solution of using shared
188 // CredHandles is good enough.
189 221
190 static CredHandle* GetCredHandle(int ssl_version_mask) { 222 static CredHandle* GetCredHandle(PCCERT_CONTEXT client_cert,
223 int ssl_version_mask) {
191 // It doesn't matter whether GetCredHandle returns NULL or a pointer to an 224 // It doesn't matter whether GetCredHandle returns NULL or a pointer to an
192 // uninitialized CredHandle on failure. Both of them cause 225 // uninitialized CredHandle on failure. Both of them cause
193 // InitializeSecurityContext to fail with SEC_E_INVALID_HANDLE. 226 // InitializeSecurityContext to fail with SEC_E_INVALID_HANDLE.
194 if (ssl_version_mask <= 0 || ssl_version_mask >= SSL_VERSION_MASKS) { 227 if (ssl_version_mask <= 0 || ssl_version_mask >= SSL_VERSION_MASKS) {
195 NOTREACHED(); 228 NOTREACHED();
196 return NULL; 229 return NULL;
197 } 230 }
198 return Singleton<CredHandleTable>::get()->GetHandle(ssl_version_mask); 231 return Singleton<CredHandleTable>::get()->GetHandle(client_cert,
232 ssl_version_mask);
199 } 233 }
200 234
201 //----------------------------------------------------------------------------- 235 //-----------------------------------------------------------------------------
202 236
237 // A memory certificate store for client certificates. This allows us to
238 // close the "MY" system certificate store when we finish searching for
239 // client certificates.
240 class ClientCertStore {
241 public:
242 ClientCertStore() {
243 store_ = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL);
244 }
245
246 ~ClientCertStore() {
247 if (store_) {
248 BOOL ok = CertCloseStore(store_, CERT_CLOSE_STORE_CHECK_FLAG);
249 DCHECK(ok);
250 }
251 }
252
253 PCCERT_CONTEXT CopyCertContext(PCCERT_CONTEXT client_cert) {
254 PCCERT_CONTEXT copy;
255 BOOL ok = CertAddCertificateContextToStore(store_, client_cert,
256 CERT_STORE_ADD_USE_EXISTING,
257 &copy);
258 DCHECK(ok);
259 return ok ? copy : NULL;
260 }
261
262 private:
263 HCERTSTORE store_;
264 };
265
266 //-----------------------------------------------------------------------------
267
203 // Size of recv_buffer_ 268 // Size of recv_buffer_
204 // 269 //
205 // Ciphertext is decrypted one SSL record at a time, so recv_buffer_ needs to 270 // Ciphertext is decrypted one SSL record at a time, so recv_buffer_ needs to
206 // have room for a full SSL record, with the header and trailer. Here is the 271 // have room for a full SSL record, with the header and trailer. Here is the
207 // breakdown of the size: 272 // breakdown of the size:
208 // 5: SSL record header 273 // 5: SSL record header
209 // 16K: SSL record maximum size 274 // 16K: SSL record maximum size
210 // 64: >= SSL record trailer (16 or 20 have been observed) 275 // 64: >= SSL record trailer (16 or 20 have been observed)
211 static const int kRecvBufferSize = (5 + 16*1024 + 64); 276 static const int kRecvBufferSize = (5 + 16*1024 + 64);
212 277
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
255 if (status == SEC_E_OK) { 320 if (status == SEC_E_OK) {
256 // TODO(wtc): compute the overall security strength, taking into account 321 // TODO(wtc): compute the overall security strength, taking into account
257 // dwExchStrength and dwHashStrength. dwExchStrength needs to be 322 // dwExchStrength and dwHashStrength. dwExchStrength needs to be
258 // normalized. 323 // normalized.
259 ssl_info->security_bits = connection_info.dwCipherStrength; 324 ssl_info->security_bits = connection_info.dwCipherStrength;
260 } 325 }
261 } 326 }
262 327
263 void SSLClientSocketWin::GetSSLCertRequestInfo( 328 void SSLClientSocketWin::GetSSLCertRequestInfo(
264 SSLCertRequestInfo* cert_request_info) { 329 SSLCertRequestInfo* cert_request_info) {
265 // TODO(wtc): implement this. 330 cert_request_info->host_and_port = hostname_; // TODO(wtc): no port!
331 cert_request_info->client_certs.clear();
332
333 // Get the certificate_authorities field of the CertificateRequest message.
334 // Schannel doesn't return the certificate_types field of the
335 // CertificateRequest message to us, so we can't filter the client
336 // certificates properly. :-(
337 SecPkgContext_IssuerListInfoEx issuer_list;
338 SECURITY_STATUS status = QueryContextAttributes(
339 &ctxt_, SECPKG_ATTR_ISSUER_LIST_EX, &issuer_list);
340 if (status != SEC_E_OK) {
341 DLOG(ERROR) << "QueryContextAttributes (issuer list) failed: " << status;
342 return;
343 }
344
345 // Client certificates of the user are in the "MY" system certificate store.
346 HCERTSTORE my_cert_store = CertOpenSystemStore(NULL, L"MY");
347 if (!my_cert_store) {
348 FreeContextBuffer(issuer_list.aIssuers);
349 return;
350 }
351
352 // Enumerate the client certificates.
353 CERT_CHAIN_FIND_BY_ISSUER_PARA find_by_issuer_para;
354 memset(&find_by_issuer_para, 0, sizeof(find_by_issuer_para));
355 find_by_issuer_para.cbSize = sizeof(find_by_issuer_para);
356 find_by_issuer_para.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH;
357 find_by_issuer_para.cIssuer = issuer_list.cIssuers;
358 find_by_issuer_para.rgIssuer = issuer_list.aIssuers;
359
360 PCCERT_CHAIN_CONTEXT chain_context = NULL;
361
362 for (;;) {
363 // Find a certificate chain.
364 chain_context = CertFindChainInStore(my_cert_store,
365 X509_ASN_ENCODING,
366 0,
367 CERT_CHAIN_FIND_BY_ISSUER,
368 &find_by_issuer_para,
369 chain_context);
370 if (!chain_context) {
371 DWORD err = GetLastError();
372 if (err != CRYPT_E_NOT_FOUND)
373 DLOG(ERROR) << "CertFindChainInStore failed: " << err;
374 break;
375 }
376
377 // Get the leaf certificate.
378 PCCERT_CONTEXT cert_context =
379 chain_context->rgpChain[0]->rgpElement[0]->pCertContext;
380 // Copy it to our own certificate store, so that we can close the "MY"
381 // certificate store before returning from this function.
382 PCCERT_CONTEXT cert_context2 =
383 Singleton<ClientCertStore>::get()->CopyCertContext(cert_context);
384 if (!cert_context2) {
385 NOTREACHED();
386 continue;
387 }
388 scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle(
389 cert_context2, X509Certificate::SOURCE_LONE_CERT_IMPORT);
390 cert_request_info->client_certs.push_back(cert);
391 }
392
393 FreeContextBuffer(issuer_list.aIssuers);
394
395 BOOL ok = CertCloseStore(my_cert_store, CERT_CLOSE_STORE_CHECK_FLAG);
396 DCHECK(ok);
266 } 397 }
267 398
268 int SSLClientSocketWin::Connect(CompletionCallback* callback) { 399 int SSLClientSocketWin::Connect(CompletionCallback* callback) {
269 DCHECK(transport_.get()); 400 DCHECK(transport_.get());
270 DCHECK(next_state_ == STATE_NONE); 401 DCHECK(next_state_ == STATE_NONE);
271 DCHECK(!user_callback_); 402 DCHECK(!user_callback_);
272 403
273 int ssl_version_mask = 0; 404 int ssl_version_mask = 0;
274 if (ssl_config_.ssl2_enabled) 405 if (ssl_config_.ssl2_enabled)
275 ssl_version_mask |= SSL2; 406 ssl_version_mask |= SSL2;
276 if (ssl_config_.ssl3_enabled) 407 if (ssl_config_.ssl3_enabled)
277 ssl_version_mask |= SSL3; 408 ssl_version_mask |= SSL3;
278 if (ssl_config_.tls1_enabled) 409 if (ssl_config_.tls1_enabled)
279 ssl_version_mask |= TLS1; 410 ssl_version_mask |= TLS1;
280 // If we pass 0 to GetCredHandle, we will let Schannel select the protocols, 411 // If we pass 0 to GetCredHandle, we will let Schannel select the protocols,
281 // rather than enabling no protocols. So we have to fail here. 412 // rather than enabling no protocols. So we have to fail here.
282 if (ssl_version_mask == 0) 413 if (ssl_version_mask == 0)
283 return ERR_NO_SSL_VERSIONS_ENABLED; 414 return ERR_NO_SSL_VERSIONS_ENABLED;
284 creds_ = GetCredHandle(ssl_version_mask); 415 PCCERT_CONTEXT cert_context = NULL;
416 if (ssl_config_.client_cert)
417 cert_context = ssl_config_.client_cert->os_cert_handle();
418 creds_ = GetCredHandle(cert_context, ssl_version_mask);
285 419
286 memset(&ctxt_, 0, sizeof(ctxt_)); 420 memset(&ctxt_, 0, sizeof(ctxt_));
287 421
288 SecBufferDesc buffer_desc; 422 SecBufferDesc buffer_desc;
289 DWORD out_flags; 423 DWORD out_flags;
290 DWORD flags = ISC_REQ_SEQUENCE_DETECT | 424 DWORD flags = ISC_REQ_SEQUENCE_DETECT |
291 ISC_REQ_REPLAY_DETECT | 425 ISC_REQ_REPLAY_DETECT |
292 ISC_REQ_CONFIDENTIALITY | 426 ISC_REQ_CONFIDENTIALITY |
293 ISC_RET_EXTENDED_ERROR | 427 ISC_RET_EXTENDED_ERROR |
294 ISC_REQ_ALLOCATE_MEMORY | 428 ISC_REQ_ALLOCATE_MEMORY |
(...skipping 743 matching lines...) Expand 10 before | Expand all | Expand 10 after
1038 } 1172 }
1039 } 1173 }
1040 1174
1041 void SSLClientSocketWin::FreeSendBuffer() { 1175 void SSLClientSocketWin::FreeSendBuffer() {
1042 SECURITY_STATUS status = FreeContextBuffer(send_buffer_.pvBuffer); 1176 SECURITY_STATUS status = FreeContextBuffer(send_buffer_.pvBuffer);
1043 DCHECK(status == SEC_E_OK); 1177 DCHECK(status == SEC_E_OK);
1044 memset(&send_buffer_, 0, sizeof(send_buffer_)); 1178 memset(&send_buffer_, 0, sizeof(send_buffer_));
1045 } 1179 }
1046 1180
1047 } // namespace net 1181 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698