OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 // OpenSSL binding for SSLClientSocket. The class layout and general principle | 5 // OpenSSL binding for SSLClientSocket. The class layout and general principle |
6 // of operation is derived from SSLClientSocketNSS. | 6 // of operation is derived from SSLClientSocketNSS. |
7 | 7 |
8 #include "net/socket/ssl_client_socket_openssl.h" | 8 #include "net/socket/ssl_client_socket_openssl.h" |
9 | 9 |
10 #include <openssl/ssl.h> | 10 #include <openssl/ssl.h> |
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
193 | 193 |
194 // We do certificate verification after handshake, so we disable the default | 194 // We do certificate verification after handshake, so we disable the default |
195 // by registering a no-op verify function. | 195 // by registering a no-op verify function. |
196 int NoOpVerifyCallback(X509_STORE_CTX*, void *) { | 196 int NoOpVerifyCallback(X509_STORE_CTX*, void *) { |
197 DVLOG(3) << "skipping cert verify"; | 197 DVLOG(3) << "skipping cert verify"; |
198 return 1; | 198 return 1; |
199 } | 199 } |
200 | 200 |
201 // OpenSSL manages a cache of SSL_SESSION, this class provides the application | 201 // OpenSSL manages a cache of SSL_SESSION, this class provides the application |
202 // side policy for that cache about session re-use: we retain one session per | 202 // side policy for that cache about session re-use: we retain one session per |
203 // unique HostPortPair. | 203 // unique HostPortPair, per shard. |
204 class SSLSessionCache { | 204 class SSLSessionCache { |
205 public: | 205 public: |
206 SSLSessionCache() {} | 206 SSLSessionCache() {} |
207 | 207 |
208 void OnSessionAdded(const HostPortPair& host_and_port, SSL_SESSION* session) { | 208 void OnSessionAdded(const HostPortPair& host_and_port, |
| 209 const std::string& shard, |
| 210 SSL_SESSION* session) { |
209 // Declare the session cleaner-upper before the lock, so any call into | 211 // Declare the session cleaner-upper before the lock, so any call into |
210 // OpenSSL to free the session will happen after the lock is released. | 212 // OpenSSL to free the session will happen after the lock is released. |
211 crypto::ScopedOpenSSL<SSL_SESSION, SSL_SESSION_free> session_to_free; | 213 crypto::ScopedOpenSSL<SSL_SESSION, SSL_SESSION_free> session_to_free; |
212 base::AutoLock lock(lock_); | 214 base::AutoLock lock(lock_); |
213 | 215 |
214 DCHECK_EQ(0U, session_map_.count(session)); | 216 DCHECK_EQ(0U, session_map_.count(session)); |
| 217 const std::string cache_key = GetCacheKey(host_and_port, shard); |
| 218 |
215 std::pair<HostPortMap::iterator, bool> res = | 219 std::pair<HostPortMap::iterator, bool> res = |
216 host_port_map_.insert(std::make_pair(host_and_port, session)); | 220 host_port_map_.insert(std::make_pair(cache_key, session)); |
217 if (!res.second) { // Already exists: replace old entry. | 221 if (!res.second) { // Already exists: replace old entry. |
218 session_to_free.reset(res.first->second); | 222 session_to_free.reset(res.first->second); |
219 session_map_.erase(session_to_free.get()); | 223 session_map_.erase(session_to_free.get()); |
220 res.first->second = session; | 224 res.first->second = session; |
221 } | 225 } |
222 DVLOG(2) << "Adding session " << session << " => " | 226 DVLOG(2) << "Adding session " << session << " => " |
223 << host_and_port.ToString() << ", new entry = " << res.second; | 227 << cache_key << ", new entry = " << res.second; |
224 DCHECK(host_port_map_[host_and_port] == session); | 228 DCHECK(host_port_map_[cache_key] == session); |
225 session_map_[session] = res.first; | 229 session_map_[session] = res.first; |
226 DCHECK_EQ(host_port_map_.size(), session_map_.size()); | 230 DCHECK_EQ(host_port_map_.size(), session_map_.size()); |
227 DCHECK_LE(host_port_map_.size(), kSessionCacheMaxEntires); | 231 DCHECK_LE(host_port_map_.size(), kSessionCacheMaxEntires); |
228 } | 232 } |
229 | 233 |
230 void OnSessionRemoved(SSL_SESSION* session) { | 234 void OnSessionRemoved(SSL_SESSION* session) { |
231 // Declare the session cleaner-upper before the lock, so any call into | 235 // Declare the session cleaner-upper before the lock, so any call into |
232 // OpenSSL to free the session will happen after the lock is released. | 236 // OpenSSL to free the session will happen after the lock is released. |
233 crypto::ScopedOpenSSL<SSL_SESSION, SSL_SESSION_free> session_to_free; | 237 crypto::ScopedOpenSSL<SSL_SESSION, SSL_SESSION_free> session_to_free; |
234 base::AutoLock lock(lock_); | 238 base::AutoLock lock(lock_); |
235 | 239 |
236 SessionMap::iterator it = session_map_.find(session); | 240 SessionMap::iterator it = session_map_.find(session); |
237 if (it == session_map_.end()) | 241 if (it == session_map_.end()) |
238 return; | 242 return; |
239 DVLOG(2) << "Remove session " << session << " => " | 243 DVLOG(2) << "Remove session " << session << " => " << it->second->first; |
240 << it->second->first.ToString(); | |
241 DCHECK(it->second->second == session); | 244 DCHECK(it->second->second == session); |
242 host_port_map_.erase(it->second); | 245 host_port_map_.erase(it->second); |
243 session_map_.erase(it); | 246 session_map_.erase(it); |
244 session_to_free.reset(session); | 247 session_to_free.reset(session); |
245 DCHECK_EQ(host_port_map_.size(), session_map_.size()); | 248 DCHECK_EQ(host_port_map_.size(), session_map_.size()); |
246 } | 249 } |
247 | 250 |
248 // Looks up the host:port in the cache, and if a session is found it is added | 251 // Looks up the host:port in the cache, and if a session is found it is added |
249 // to |ssl|, returning true on success. | 252 // to |ssl|, returning true on success. |
250 bool SetSSLSession(SSL* ssl, const HostPortPair& host_and_port) { | 253 bool SetSSLSession(SSL* ssl, const HostPortPair& host_and_port, |
| 254 const std::string& shard) { |
251 base::AutoLock lock(lock_); | 255 base::AutoLock lock(lock_); |
252 HostPortMap::iterator it = host_port_map_.find(host_and_port); | 256 const std::string cache_key = GetCacheKey(host_and_port, shard); |
| 257 HostPortMap::iterator it = host_port_map_.find(cache_key); |
253 if (it == host_port_map_.end()) | 258 if (it == host_port_map_.end()) |
254 return false; | 259 return false; |
255 DVLOG(2) << "Lookup session: " << it->second << " => " | 260 DVLOG(2) << "Lookup session: " << it->second << " => " << cache_key; |
256 << host_and_port.ToString(); | |
257 SSL_SESSION* session = it->second; | 261 SSL_SESSION* session = it->second; |
258 DCHECK(session); | 262 DCHECK(session); |
259 DCHECK(session_map_[session] == it); | 263 DCHECK(session_map_[session] == it); |
260 // Ideally we'd release |lock_| before calling into OpenSSL here, however | 264 // Ideally we'd release |lock_| before calling into OpenSSL here, however |
261 // that opens a small risk |session| will go out of scope before it is used. | 265 // that opens a small risk |session| will go out of scope before it is used. |
262 // Alternatively we would take a temporary local refcount on |session|, | 266 // Alternatively we would take a temporary local refcount on |session|, |
263 // except OpenSSL does not provide a public API for adding a ref (c.f. | 267 // except OpenSSL does not provide a public API for adding a ref (c.f. |
264 // SSL_SESSION_free which decrements the ref). | 268 // SSL_SESSION_free which decrements the ref). |
265 return SSL_set_session(ssl, session) == 1; | 269 return SSL_set_session(ssl, session) == 1; |
266 } | 270 } |
267 | 271 |
| 272 // Flush removes all entries from the cache. This is called when a client |
| 273 // certificate is added. |
| 274 void Flush() { |
| 275 for (HostPortMap::iterator i = host_port_map_.begin(); |
| 276 i != host_port_map_.end(); i++) { |
| 277 SSL_SESSION_free(i->second); |
| 278 } |
| 279 host_port_map_.clear(); |
| 280 session_map_.clear(); |
| 281 } |
| 282 |
268 private: | 283 private: |
| 284 static std::string GetCacheKey(const HostPortPair& host_and_port, |
| 285 const std::string& shard) { |
| 286 return host_and_port.ToString() + "/" + shard; |
| 287 } |
| 288 |
269 // A pair of maps to allow bi-directional lookups between host:port and an | 289 // A pair of maps to allow bi-directional lookups between host:port and an |
270 // associated session. | 290 // associated session. |
271 // TODO(joth): When client certificates are implemented we should key the | 291 typedef std::map<std::string, SSL_SESSION*> HostPortMap; |
272 // cache on the client certificate used in addition to the host-port pair. | |
273 typedef std::map<HostPortPair, SSL_SESSION*> HostPortMap; | |
274 typedef std::map<SSL_SESSION*, HostPortMap::iterator> SessionMap; | 292 typedef std::map<SSL_SESSION*, HostPortMap::iterator> SessionMap; |
275 HostPortMap host_port_map_; | 293 HostPortMap host_port_map_; |
276 SessionMap session_map_; | 294 SessionMap session_map_; |
277 | 295 |
278 // Protects access to both the above maps. | 296 // Protects access to both the above maps. |
279 base::Lock lock_; | 297 base::Lock lock_; |
280 | 298 |
281 DISALLOW_COPY_AND_ASSIGN(SSLSessionCache); | 299 DISALLOW_COPY_AND_ASSIGN(SSLSessionCache); |
282 }; | 300 }; |
283 | 301 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
322 NULL); | 340 NULL); |
323 #endif | 341 #endif |
324 } | 342 } |
325 | 343 |
326 static int NewSessionCallbackStatic(SSL* ssl, SSL_SESSION* session) { | 344 static int NewSessionCallbackStatic(SSL* ssl, SSL_SESSION* session) { |
327 return GetInstance()->NewSessionCallback(ssl, session); | 345 return GetInstance()->NewSessionCallback(ssl, session); |
328 } | 346 } |
329 | 347 |
330 int NewSessionCallback(SSL* ssl, SSL_SESSION* session) { | 348 int NewSessionCallback(SSL* ssl, SSL_SESSION* session) { |
331 SSLClientSocketOpenSSL* socket = GetClientSocketFromSSL(ssl); | 349 SSLClientSocketOpenSSL* socket = GetClientSocketFromSSL(ssl); |
332 session_cache_.OnSessionAdded(socket->host_and_port(), session); | 350 session_cache_.OnSessionAdded(socket->host_and_port(), |
| 351 socket->ssl_session_cache_shard(), |
| 352 session); |
333 return 1; // 1 => We took ownership of |session|. | 353 return 1; // 1 => We took ownership of |session|. |
334 } | 354 } |
335 | 355 |
336 static void RemoveSessionCallbackStatic(SSL_CTX* ctx, SSL_SESSION* session) { | 356 static void RemoveSessionCallbackStatic(SSL_CTX* ctx, SSL_SESSION* session) { |
337 return GetInstance()->RemoveSessionCallback(ctx, session); | 357 return GetInstance()->RemoveSessionCallback(ctx, session); |
338 } | 358 } |
339 | 359 |
340 void RemoveSessionCallback(SSL_CTX* ctx, SSL_SESSION* session) { | 360 void RemoveSessionCallback(SSL_CTX* ctx, SSL_SESSION* session) { |
341 DCHECK(ctx == ssl_ctx()); | 361 DCHECK(ctx == ssl_ctx()); |
342 session_cache_.OnSessionRemoved(session); | 362 session_cache_.OnSessionRemoved(session); |
(...skipping 10 matching lines...) Expand all Loading... |
353 const unsigned char* in, | 373 const unsigned char* in, |
354 unsigned int inlen, void* arg) { | 374 unsigned int inlen, void* arg) { |
355 SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl); | 375 SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl); |
356 return socket->SelectNextProtoCallback(out, outlen, in, inlen); | 376 return socket->SelectNextProtoCallback(out, outlen, in, inlen); |
357 } | 377 } |
358 | 378 |
359 // This is the index used with SSL_get_ex_data to retrieve the owner | 379 // This is the index used with SSL_get_ex_data to retrieve the owner |
360 // SSLClientSocketOpenSSL object from an SSL instance. | 380 // SSLClientSocketOpenSSL object from an SSL instance. |
361 int ssl_socket_data_index_; | 381 int ssl_socket_data_index_; |
362 | 382 |
| 383 // session_cache_ must appear before |ssl_ctx_| because the destruction of |
| 384 // |ssl_ctx_| may trigger callbacks into |session_cache_|. Therefore, |
| 385 // |session_cache_| must be destructed after |ssl_ctx_|. |
| 386 SSLSessionCache session_cache_; |
363 crypto::ScopedOpenSSL<SSL_CTX, SSL_CTX_free> ssl_ctx_; | 387 crypto::ScopedOpenSSL<SSL_CTX, SSL_CTX_free> ssl_ctx_; |
364 SSLSessionCache session_cache_; | |
365 }; | 388 }; |
366 | 389 |
367 // Utility to construct the appropriate set & clear masks for use the OpenSSL | 390 // Utility to construct the appropriate set & clear masks for use the OpenSSL |
368 // options and mode configuration functions. (SSL_set_options etc) | 391 // options and mode configuration functions. (SSL_set_options etc) |
369 struct SslSetClearMask { | 392 struct SslSetClearMask { |
370 SslSetClearMask() : set_mask(0), clear_mask(0) {} | 393 SslSetClearMask() : set_mask(0), clear_mask(0) {} |
371 void ConfigureFlag(long flag, bool state) { | 394 void ConfigureFlag(long flag, bool state) { |
372 (state ? set_mask : clear_mask) |= flag; | 395 (state ? set_mask : clear_mask) |= flag; |
373 // Make sure we haven't got any intersection in the set & clear options. | 396 // Make sure we haven't got any intersection in the set & clear options. |
374 DCHECK_EQ(0, set_mask & clear_mask) << flag << ":" << state; | 397 DCHECK_EQ(0, set_mask & clear_mask) << flag << ":" << state; |
375 } | 398 } |
376 long set_mask; | 399 long set_mask; |
377 long clear_mask; | 400 long clear_mask; |
378 }; | 401 }; |
379 | 402 |
380 } // namespace | 403 } // namespace |
381 | 404 |
| 405 // static |
| 406 void SSLClientSocket::ClearSessionCache() { |
| 407 SSLContext* context = SSLContext::GetInstance(); |
| 408 context->session_cache()->Flush(); |
| 409 } |
| 410 |
382 SSLClientSocketOpenSSL::SSLClientSocketOpenSSL( | 411 SSLClientSocketOpenSSL::SSLClientSocketOpenSSL( |
383 ClientSocketHandle* transport_socket, | 412 ClientSocketHandle* transport_socket, |
384 const HostPortPair& host_and_port, | 413 const HostPortPair& host_and_port, |
385 const SSLConfig& ssl_config, | 414 const SSLConfig& ssl_config, |
386 const SSLClientSocketContext& context) | 415 const SSLClientSocketContext& context) |
387 : transport_send_busy_(false), | 416 : transport_send_busy_(false), |
388 transport_recv_busy_(false), | 417 transport_recv_busy_(false), |
389 completed_handshake_(false), | 418 completed_handshake_(false), |
390 client_auth_cert_needed_(false), | 419 client_auth_cert_needed_(false), |
391 cert_verifier_(context.cert_verifier), | 420 cert_verifier_(context.cert_verifier), |
392 ssl_(NULL), | 421 ssl_(NULL), |
393 transport_bio_(NULL), | 422 transport_bio_(NULL), |
394 transport_(transport_socket), | 423 transport_(transport_socket), |
395 host_and_port_(host_and_port), | 424 host_and_port_(host_and_port), |
396 ssl_config_(ssl_config), | 425 ssl_config_(ssl_config), |
| 426 ssl_session_cache_shard_(context.ssl_session_cache_shard), |
397 trying_cached_session_(false), | 427 trying_cached_session_(false), |
398 npn_status_(kNextProtoUnsupported), | 428 npn_status_(kNextProtoUnsupported), |
399 net_log_(transport_socket->socket()->NetLog()) { | 429 net_log_(transport_socket->socket()->NetLog()) { |
400 } | 430 } |
401 | 431 |
402 SSLClientSocketOpenSSL::~SSLClientSocketOpenSSL() { | 432 SSLClientSocketOpenSSL::~SSLClientSocketOpenSSL() { |
403 Disconnect(); | 433 Disconnect(); |
404 } | 434 } |
405 | 435 |
406 bool SSLClientSocketOpenSSL::Init() { | 436 bool SSLClientSocketOpenSSL::Init() { |
407 DCHECK(!ssl_); | 437 DCHECK(!ssl_); |
408 DCHECK(!transport_bio_); | 438 DCHECK(!transport_bio_); |
409 | 439 |
410 SSLContext* context = SSLContext::GetInstance(); | 440 SSLContext* context = SSLContext::GetInstance(); |
411 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | 441 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
412 | 442 |
413 ssl_ = SSL_new(context->ssl_ctx()); | 443 ssl_ = SSL_new(context->ssl_ctx()); |
414 if (!ssl_ || !context->SetClientSocketForSSL(ssl_, this)) | 444 if (!ssl_ || !context->SetClientSocketForSSL(ssl_, this)) |
415 return false; | 445 return false; |
416 | 446 |
417 if (!SSL_set_tlsext_host_name(ssl_, host_and_port_.host().c_str())) | 447 if (!SSL_set_tlsext_host_name(ssl_, host_and_port_.host().c_str())) |
418 return false; | 448 return false; |
419 | 449 |
420 trying_cached_session_ = | 450 trying_cached_session_ = |
421 context->session_cache()->SetSSLSession(ssl_, host_and_port_); | 451 context->session_cache()->SetSSLSession(ssl_, host_and_port_, |
| 452 ssl_session_cache_shard_); |
422 | 453 |
423 BIO* ssl_bio = NULL; | 454 BIO* ssl_bio = NULL; |
424 // 0 => use default buffer sizes. | 455 // 0 => use default buffer sizes. |
425 if (!BIO_new_bio_pair(&ssl_bio, 0, &transport_bio_, 0)) | 456 if (!BIO_new_bio_pair(&ssl_bio, 0, &transport_bio_, 0)) |
426 return false; | 457 return false; |
427 DCHECK(ssl_bio); | 458 DCHECK(ssl_bio); |
428 DCHECK(transport_bio_); | 459 DCHECK(transport_bio_); |
429 | 460 |
430 SSL_set_bio(ssl_, ssl_bio, ssl_bio); | 461 SSL_set_bio(ssl_, ssl_bio, ssl_bio); |
431 | 462 |
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
644 user_connect_callback_ = callback; | 675 user_connect_callback_ = callback; |
645 } else { | 676 } else { |
646 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, rv); | 677 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, rv); |
647 } | 678 } |
648 | 679 |
649 return rv > OK ? OK : rv; | 680 return rv > OK ? OK : rv; |
650 } | 681 } |
651 | 682 |
652 void SSLClientSocketOpenSSL::Disconnect() { | 683 void SSLClientSocketOpenSSL::Disconnect() { |
653 if (ssl_) { | 684 if (ssl_) { |
| 685 // Calling SSL_shutdown prevents the session from being marked as |
| 686 // unresumable. |
| 687 SSL_shutdown(ssl_); |
654 SSL_free(ssl_); | 688 SSL_free(ssl_); |
655 ssl_ = NULL; | 689 ssl_ = NULL; |
656 } | 690 } |
657 if (transport_bio_) { | 691 if (transport_bio_) { |
658 BIO_free_all(transport_bio_); | 692 BIO_free_all(transport_bio_); |
659 transport_bio_ = NULL; | 693 transport_bio_ = NULL; |
660 } | 694 } |
661 | 695 |
662 // Shut down anything that may call us back. | 696 // Shut down anything that may call us back. |
663 verifier_.reset(); | 697 verifier_.reset(); |
(...skipping 581 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1245 net_log_.AddByteTransferEvent(NetLog::TYPE_SSL_SOCKET_BYTES_SENT, rv, | 1279 net_log_.AddByteTransferEvent(NetLog::TYPE_SSL_SOCKET_BYTES_SENT, rv, |
1246 user_write_buf_->data()); | 1280 user_write_buf_->data()); |
1247 return rv; | 1281 return rv; |
1248 } | 1282 } |
1249 | 1283 |
1250 int err = SSL_get_error(ssl_, rv); | 1284 int err = SSL_get_error(ssl_, rv); |
1251 return MapOpenSSLError(err, err_tracer); | 1285 return MapOpenSSLError(err, err_tracer); |
1252 } | 1286 } |
1253 | 1287 |
1254 } // namespace net | 1288 } // namespace net |
OLD | NEW |