| 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 |