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. |
joth
2011/12/09 23:28:37
per unique HostPortPair per shard.
agl
2011/12/12 22:18:20
Done.
| |
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 << " => " |
240 << it->second->first.ToString(); | 244 << it->second->first; |
joth
2011/12/09 23:28:37
nit: no need to line break
agl
2011/12/12 22:18:20
Done.
| |
241 DCHECK(it->second->second == session); | 245 DCHECK(it->second->second == session); |
242 host_port_map_.erase(it->second); | 246 host_port_map_.erase(it->second); |
243 session_map_.erase(it); | 247 session_map_.erase(it); |
244 session_to_free.reset(session); | 248 session_to_free.reset(session); |
245 DCHECK_EQ(host_port_map_.size(), session_map_.size()); | 249 DCHECK_EQ(host_port_map_.size(), session_map_.size()); |
246 } | 250 } |
247 | 251 |
248 // Looks up the host:port in the cache, and if a session is found it is added | 252 // Looks up the host:port in the cache, and if a session is found it is added |
249 // to |ssl|, returning true on success. | 253 // to |ssl|, returning true on success. |
250 bool SetSSLSession(SSL* ssl, const HostPortPair& host_and_port) { | 254 bool SetSSLSession(SSL* ssl, const HostPortPair& host_and_port, |
255 const std::string& shard) { | |
251 base::AutoLock lock(lock_); | 256 base::AutoLock lock(lock_); |
252 HostPortMap::iterator it = host_port_map_.find(host_and_port); | 257 const std::string cache_key = GetCacheKey(host_and_port, shard); |
258 HostPortMap::iterator it = host_port_map_.find(cache_key); | |
253 if (it == host_port_map_.end()) | 259 if (it == host_port_map_.end()) |
254 return false; | 260 return false; |
255 DVLOG(2) << "Lookup session: " << it->second << " => " | 261 DVLOG(2) << "Lookup session: " << it->second << " => " |
256 << host_and_port.ToString(); | 262 << cache_key; |
joth
2011/12/09 23:28:37
nit: no need for line break
agl
2011/12/12 22:18:20
Done.
| |
257 SSL_SESSION* session = it->second; | 263 SSL_SESSION* session = it->second; |
258 DCHECK(session); | 264 DCHECK(session); |
259 DCHECK(session_map_[session] == it); | 265 DCHECK(session_map_[session] == it); |
260 // Ideally we'd release |lock_| before calling into OpenSSL here, however | 266 // 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. | 267 // 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|, | 268 // 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. | 269 // except OpenSSL does not provide a public API for adding a ref (c.f. |
264 // SSL_SESSION_free which decrements the ref). | 270 // SSL_SESSION_free which decrements the ref). |
265 return SSL_set_session(ssl, session) == 1; | 271 return SSL_set_session(ssl, session) == 1; |
266 } | 272 } |
267 | 273 |
274 // Flush removes all entries from the cache. This is called when a client | |
275 // certificate is added. | |
276 void Flush() { | |
277 for (HostPortMap::iterator i = host_port_map_.begin(); | |
278 i != host_port_map_.end(); i++) { | |
279 SSL_SESSION_free(i->second); | |
280 } | |
281 host_port_map_.clear(); | |
282 session_map_.clear(); | |
283 } | |
284 | |
268 private: | 285 private: |
286 static std::string GetCacheKey(const HostPortPair& host_and_port, | |
287 const std::string& shard) { | |
288 return host_and_port.ToString() + "/" + shard; | |
289 } | |
290 | |
269 // A pair of maps to allow bi-directional lookups between host:port and an | 291 // A pair of maps to allow bi-directional lookups between host:port and an |
270 // associated session. | 292 // associated session. |
271 // TODO(joth): When client certificates are implemented we should key the | 293 typedef std::map<std::string, SSL_SESSION*> HostPortMap; |
272 // cache on the client certificate used in addition to the host-port pair. | |
joth
2011/12/09 23:28:37
thanks for handling this one too. if I've followed
agl
2011/12/12 22:18:20
Right. And when we add a new client-cert, we flush
| |
273 typedef std::map<HostPortPair, SSL_SESSION*> HostPortMap; | |
274 typedef std::map<SSL_SESSION*, HostPortMap::iterator> SessionMap; | 294 typedef std::map<SSL_SESSION*, HostPortMap::iterator> SessionMap; |
275 HostPortMap host_port_map_; | 295 HostPortMap host_port_map_; |
276 SessionMap session_map_; | 296 SessionMap session_map_; |
277 | 297 |
278 // Protects access to both the above maps. | 298 // Protects access to both the above maps. |
279 base::Lock lock_; | 299 base::Lock lock_; |
280 | 300 |
281 DISALLOW_COPY_AND_ASSIGN(SSLSessionCache); | 301 DISALLOW_COPY_AND_ASSIGN(SSLSessionCache); |
282 }; | 302 }; |
283 | 303 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
322 NULL); | 342 NULL); |
323 #endif | 343 #endif |
324 } | 344 } |
325 | 345 |
326 static int NewSessionCallbackStatic(SSL* ssl, SSL_SESSION* session) { | 346 static int NewSessionCallbackStatic(SSL* ssl, SSL_SESSION* session) { |
327 return GetInstance()->NewSessionCallback(ssl, session); | 347 return GetInstance()->NewSessionCallback(ssl, session); |
328 } | 348 } |
329 | 349 |
330 int NewSessionCallback(SSL* ssl, SSL_SESSION* session) { | 350 int NewSessionCallback(SSL* ssl, SSL_SESSION* session) { |
331 SSLClientSocketOpenSSL* socket = GetClientSocketFromSSL(ssl); | 351 SSLClientSocketOpenSSL* socket = GetClientSocketFromSSL(ssl); |
332 session_cache_.OnSessionAdded(socket->host_and_port(), session); | 352 session_cache_.OnSessionAdded(socket->host_and_port(), |
353 socket->session_cache_shard(), | |
354 session); | |
333 return 1; // 1 => We took ownership of |session|. | 355 return 1; // 1 => We took ownership of |session|. |
334 } | 356 } |
335 | 357 |
336 static void RemoveSessionCallbackStatic(SSL_CTX* ctx, SSL_SESSION* session) { | 358 static void RemoveSessionCallbackStatic(SSL_CTX* ctx, SSL_SESSION* session) { |
337 return GetInstance()->RemoveSessionCallback(ctx, session); | 359 return GetInstance()->RemoveSessionCallback(ctx, session); |
338 } | 360 } |
339 | 361 |
340 void RemoveSessionCallback(SSL_CTX* ctx, SSL_SESSION* session) { | 362 void RemoveSessionCallback(SSL_CTX* ctx, SSL_SESSION* session) { |
341 DCHECK(ctx == ssl_ctx()); | 363 DCHECK(ctx == ssl_ctx()); |
342 session_cache_.OnSessionRemoved(session); | 364 session_cache_.OnSessionRemoved(session); |
(...skipping 10 matching lines...) Expand all Loading... | |
353 const unsigned char* in, | 375 const unsigned char* in, |
354 unsigned int inlen, void* arg) { | 376 unsigned int inlen, void* arg) { |
355 SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl); | 377 SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl); |
356 return socket->SelectNextProtoCallback(out, outlen, in, inlen); | 378 return socket->SelectNextProtoCallback(out, outlen, in, inlen); |
357 } | 379 } |
358 | 380 |
359 // This is the index used with SSL_get_ex_data to retrieve the owner | 381 // This is the index used with SSL_get_ex_data to retrieve the owner |
360 // SSLClientSocketOpenSSL object from an SSL instance. | 382 // SSLClientSocketOpenSSL object from an SSL instance. |
361 int ssl_socket_data_index_; | 383 int ssl_socket_data_index_; |
362 | 384 |
385 // session_cache_ must appear before |ssl_ctx_| because the destruction of | |
386 // |ssl_ctx_| may trigger callbacks into |session_cache_|. Therefore, | |
387 // |session_cache_| must be destructed after |ssl_ctx_|. | |
388 SSLSessionCache session_cache_; | |
363 crypto::ScopedOpenSSL<SSL_CTX, SSL_CTX_free> ssl_ctx_; | 389 crypto::ScopedOpenSSL<SSL_CTX, SSL_CTX_free> ssl_ctx_; |
364 SSLSessionCache session_cache_; | |
365 }; | 390 }; |
366 | 391 |
367 // Utility to construct the appropriate set & clear masks for use the OpenSSL | 392 // Utility to construct the appropriate set & clear masks for use the OpenSSL |
368 // options and mode configuration functions. (SSL_set_options etc) | 393 // options and mode configuration functions. (SSL_set_options etc) |
369 struct SslSetClearMask { | 394 struct SslSetClearMask { |
370 SslSetClearMask() : set_mask(0), clear_mask(0) {} | 395 SslSetClearMask() : set_mask(0), clear_mask(0) {} |
371 void ConfigureFlag(long flag, bool state) { | 396 void ConfigureFlag(long flag, bool state) { |
372 (state ? set_mask : clear_mask) |= flag; | 397 (state ? set_mask : clear_mask) |= flag; |
373 // Make sure we haven't got any intersection in the set & clear options. | 398 // Make sure we haven't got any intersection in the set & clear options. |
374 DCHECK_EQ(0, set_mask & clear_mask) << flag << ":" << state; | 399 DCHECK_EQ(0, set_mask & clear_mask) << flag << ":" << state; |
375 } | 400 } |
376 long set_mask; | 401 long set_mask; |
377 long clear_mask; | 402 long clear_mask; |
378 }; | 403 }; |
379 | 404 |
380 } // namespace | 405 } // namespace |
381 | 406 |
407 // static | |
408 void SSLClientSocket::ClearSessionCache() { | |
409 SSLContext* context = SSLContext::GetInstance(); | |
410 context->session_cache()->Flush(); | |
411 } | |
412 | |
382 SSLClientSocketOpenSSL::SSLClientSocketOpenSSL( | 413 SSLClientSocketOpenSSL::SSLClientSocketOpenSSL( |
383 ClientSocketHandle* transport_socket, | 414 ClientSocketHandle* transport_socket, |
384 const HostPortPair& host_and_port, | 415 const HostPortPair& host_and_port, |
385 const SSLConfig& ssl_config, | 416 const SSLConfig& ssl_config, |
386 const SSLClientSocketContext& context) | 417 const SSLClientSocketContext& context) |
387 : ALLOW_THIS_IN_INITIALIZER_LIST(buffer_send_callback_( | 418 : ALLOW_THIS_IN_INITIALIZER_LIST(buffer_send_callback_( |
388 this, &SSLClientSocketOpenSSL::BufferSendComplete)), | 419 this, &SSLClientSocketOpenSSL::BufferSendComplete)), |
389 ALLOW_THIS_IN_INITIALIZER_LIST(buffer_recv_callback_( | 420 ALLOW_THIS_IN_INITIALIZER_LIST(buffer_recv_callback_( |
390 this, &SSLClientSocketOpenSSL::BufferRecvComplete)), | 421 this, &SSLClientSocketOpenSSL::BufferRecvComplete)), |
391 transport_send_busy_(false), | 422 transport_send_busy_(false), |
392 transport_recv_busy_(false), | 423 transport_recv_busy_(false), |
393 old_user_connect_callback_(NULL), | 424 old_user_connect_callback_(NULL), |
394 old_user_read_callback_(NULL), | 425 old_user_read_callback_(NULL), |
395 user_write_callback_(NULL), | 426 user_write_callback_(NULL), |
396 completed_handshake_(false), | 427 completed_handshake_(false), |
397 client_auth_cert_needed_(false), | 428 client_auth_cert_needed_(false), |
398 cert_verifier_(context.cert_verifier), | 429 cert_verifier_(context.cert_verifier), |
399 ssl_(NULL), | 430 ssl_(NULL), |
400 transport_bio_(NULL), | 431 transport_bio_(NULL), |
401 transport_(transport_socket), | 432 transport_(transport_socket), |
402 host_and_port_(host_and_port), | 433 host_and_port_(host_and_port), |
403 ssl_config_(ssl_config), | 434 ssl_config_(ssl_config), |
435 session_cache_shard_(context.session_cache_shard), | |
404 trying_cached_session_(false), | 436 trying_cached_session_(false), |
405 npn_status_(kNextProtoUnsupported), | 437 npn_status_(kNextProtoUnsupported), |
406 net_log_(transport_socket->socket()->NetLog()) { | 438 net_log_(transport_socket->socket()->NetLog()) { |
407 } | 439 } |
408 | 440 |
409 SSLClientSocketOpenSSL::~SSLClientSocketOpenSSL() { | 441 SSLClientSocketOpenSSL::~SSLClientSocketOpenSSL() { |
410 Disconnect(); | 442 Disconnect(); |
411 } | 443 } |
412 | 444 |
413 bool SSLClientSocketOpenSSL::Init() { | 445 bool SSLClientSocketOpenSSL::Init() { |
414 DCHECK(!ssl_); | 446 DCHECK(!ssl_); |
415 DCHECK(!transport_bio_); | 447 DCHECK(!transport_bio_); |
416 | 448 |
417 SSLContext* context = SSLContext::GetInstance(); | 449 SSLContext* context = SSLContext::GetInstance(); |
418 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | 450 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
419 | 451 |
420 ssl_ = SSL_new(context->ssl_ctx()); | 452 ssl_ = SSL_new(context->ssl_ctx()); |
421 if (!ssl_ || !context->SetClientSocketForSSL(ssl_, this)) | 453 if (!ssl_ || !context->SetClientSocketForSSL(ssl_, this)) |
422 return false; | 454 return false; |
423 | 455 |
424 if (!SSL_set_tlsext_host_name(ssl_, host_and_port_.host().c_str())) | 456 if (!SSL_set_tlsext_host_name(ssl_, host_and_port_.host().c_str())) |
425 return false; | 457 return false; |
426 | 458 |
427 trying_cached_session_ = | 459 trying_cached_session_ = |
428 context->session_cache()->SetSSLSession(ssl_, host_and_port_); | 460 context->session_cache()->SetSSLSession(ssl_, host_and_port_, |
461 session_cache_shard_); | |
429 | 462 |
430 BIO* ssl_bio = NULL; | 463 BIO* ssl_bio = NULL; |
431 // 0 => use default buffer sizes. | 464 // 0 => use default buffer sizes. |
432 if (!BIO_new_bio_pair(&ssl_bio, 0, &transport_bio_, 0)) | 465 if (!BIO_new_bio_pair(&ssl_bio, 0, &transport_bio_, 0)) |
433 return false; | 466 return false; |
434 DCHECK(ssl_bio); | 467 DCHECK(ssl_bio); |
435 DCHECK(transport_bio_); | 468 DCHECK(transport_bio_); |
436 | 469 |
437 SSL_set_bio(ssl_, ssl_bio, ssl_bio); | 470 SSL_set_bio(ssl_, ssl_bio, ssl_bio); |
438 | 471 |
(...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
683 user_connect_callback_ = callback; | 716 user_connect_callback_ = callback; |
684 } else { | 717 } else { |
685 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, rv); | 718 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, rv); |
686 } | 719 } |
687 | 720 |
688 return rv > OK ? OK : rv; | 721 return rv > OK ? OK : rv; |
689 } | 722 } |
690 | 723 |
691 void SSLClientSocketOpenSSL::Disconnect() { | 724 void SSLClientSocketOpenSSL::Disconnect() { |
692 if (ssl_) { | 725 if (ssl_) { |
726 // Calling SSL_shutdown prevents the session from being marked as | |
727 // unresumable. | |
728 SSL_shutdown(ssl_); | |
693 SSL_free(ssl_); | 729 SSL_free(ssl_); |
694 ssl_ = NULL; | 730 ssl_ = NULL; |
695 } | 731 } |
696 if (transport_bio_) { | 732 if (transport_bio_) { |
697 BIO_free_all(transport_bio_); | 733 BIO_free_all(transport_bio_); |
698 transport_bio_ = NULL; | 734 transport_bio_ = NULL; |
699 } | 735 } |
700 | 736 |
701 // Shut down anything that may call us back. | 737 // Shut down anything that may call us back. |
702 verifier_.reset(); | 738 verifier_.reset(); |
(...skipping 600 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1303 net_log_.AddByteTransferEvent(NetLog::TYPE_SSL_SOCKET_BYTES_SENT, rv, | 1339 net_log_.AddByteTransferEvent(NetLog::TYPE_SSL_SOCKET_BYTES_SENT, rv, |
1304 user_write_buf_->data()); | 1340 user_write_buf_->data()); |
1305 return rv; | 1341 return rv; |
1306 } | 1342 } |
1307 | 1343 |
1308 int err = SSL_get_error(ssl_, rv); | 1344 int err = SSL_get_error(ssl_, rv); |
1309 return MapOpenSSLError(err, err_tracer); | 1345 return MapOpenSSLError(err, err_tracer); |
1310 } | 1346 } |
1311 | 1347 |
1312 } // namespace net | 1348 } // namespace net |
OLD | NEW |