OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 ©); | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |