OLD | NEW |
1 // Copyright (c) 2008-2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2008-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/socket/ssl_client_socket_mac.h" | 5 #include "net/socket/ssl_client_socket_mac.h" |
6 | 6 |
7 #include "base/scoped_cftyperef.h" | 7 #include "base/scoped_cftyperef.h" |
8 #include "base/singleton.h" | 8 #include "base/singleton.h" |
9 #include "base/string_util.h" | 9 #include "base/string_util.h" |
10 #include "net/base/cert_verifier.h" | 10 #include "net/base/cert_verifier.h" |
11 #include "net/base/io_buffer.h" | 11 #include "net/base/io_buffer.h" |
| 12 #include "net/base/load_log.h" |
12 #include "net/base/net_errors.h" | 13 #include "net/base/net_errors.h" |
13 #include "net/base/ssl_info.h" | 14 #include "net/base/ssl_info.h" |
14 | 15 |
15 // Welcome to Mac SSL. We've been waiting for you. | 16 // Welcome to Mac SSL. We've been waiting for you. |
16 // | 17 // |
17 // The Mac SSL implementation is, like the Windows and NSS implementations, a | 18 // The Mac SSL implementation is, like the Windows and NSS implementations, a |
18 // giant state machine. This design constraint is due to the asynchronous nature | 19 // giant state machine. This design constraint is due to the asynchronous nature |
19 // of our underlying transport mechanism. We can call down to read/write on the | 20 // of our underlying transport mechanism. We can call down to read/write on the |
20 // network, but what happens is that either it completes immediately or returns | 21 // network, but what happens is that either it completes immediately or returns |
21 // saying that we'll get a callback sometime in the future. In that case, we | 22 // saying that we'll get a callback sometime in the future. In that case, we |
(...skipping 297 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
319 ssl_context_(NULL), | 320 ssl_context_(NULL), |
320 pending_send_error_(OK), | 321 pending_send_error_(OK), |
321 recv_buffer_head_slop_(0), | 322 recv_buffer_head_slop_(0), |
322 recv_buffer_tail_slop_(0) { | 323 recv_buffer_tail_slop_(0) { |
323 } | 324 } |
324 | 325 |
325 SSLClientSocketMac::~SSLClientSocketMac() { | 326 SSLClientSocketMac::~SSLClientSocketMac() { |
326 Disconnect(); | 327 Disconnect(); |
327 } | 328 } |
328 | 329 |
329 int SSLClientSocketMac::Connect(CompletionCallback* callback) { | 330 int SSLClientSocketMac::Connect(CompletionCallback* callback, |
| 331 LoadLog* load_log) { |
330 DCHECK(transport_.get()); | 332 DCHECK(transport_.get()); |
331 DCHECK(next_handshake_state_ == STATE_NONE); | 333 DCHECK(next_handshake_state_ == STATE_NONE); |
332 DCHECK(!user_connect_callback_); | 334 DCHECK(!user_connect_callback_); |
333 | 335 |
334 OSStatus status = noErr; | 336 LoadLog::BeginEvent(load_log, LoadLog::TYPE_SSL_CONNECT); |
335 | 337 |
336 status = SSLNewContext(false, &ssl_context_); | 338 int rv = InitializeSSLContext(); |
337 if (status) | 339 if (rv != OK) { |
338 return NetErrorFromOSStatus(status); | 340 LoadLog::EndEvent(load_log, LoadLog::TYPE_SSL_CONNECT); |
339 | 341 return rv; |
340 status = SSLSetProtocolVersionEnabled(ssl_context_, | |
341 kSSLProtocol2, | |
342 ssl_config_.ssl2_enabled); | |
343 if (status) | |
344 return NetErrorFromOSStatus(status); | |
345 | |
346 status = SSLSetProtocolVersionEnabled(ssl_context_, | |
347 kSSLProtocol3, | |
348 ssl_config_.ssl3_enabled); | |
349 if (status) | |
350 return NetErrorFromOSStatus(status); | |
351 | |
352 status = SSLSetProtocolVersionEnabled(ssl_context_, | |
353 kTLSProtocol1, | |
354 ssl_config_.tls1_enabled); | |
355 if (status) | |
356 return NetErrorFromOSStatus(status); | |
357 | |
358 status = SSLSetIOFuncs(ssl_context_, SSLReadCallback, SSLWriteCallback); | |
359 if (status) | |
360 return NetErrorFromOSStatus(status); | |
361 | |
362 status = SSLSetConnection(ssl_context_, this); | |
363 if (status) | |
364 return NetErrorFromOSStatus(status); | |
365 | |
366 // Disable certificate verification within Secure Transport; we'll | |
367 // be handling that ourselves. | |
368 status = SSLSetEnableCertVerify(ssl_context_, false); | |
369 if (status) | |
370 return NetErrorFromOSStatus(status); | |
371 | |
372 // SSLSetSessionOption() was introduced in Mac OS X 10.5.7. It allows us | |
373 // to perform certificate validation during the handshake, which is | |
374 // required in order to properly enable session resumption. | |
375 // | |
376 // With the kSSLSessionOptionBreakOnServerAuth option set, SSLHandshake() | |
377 // will return errSSLServerAuthCompleted after receiving the server's | |
378 // Certificate during the handshake. That gives us an opportunity to verify | |
379 // the server certificate and then re-enter that handshake (assuming the | |
380 // certificate successfully validated). | |
381 // | |
382 // If SSLSetSessionOption() is not present, we do not enable session | |
383 // resumption, because in that case we are verifying the server's certificate | |
384 // after the handshake completes (but before any application data is | |
385 // exchanged). If we were to enable session resumption in this situation, | |
386 // the session would be cached before we verified the certificate, leaving | |
387 // the potential for a session in which the certificate failed to validate | |
388 // to still be able to be resumed. | |
389 CFBundleRef bundle = | |
390 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security")); | |
391 if (bundle) { | |
392 SSLSetSessionOptionFuncPtr ssl_set_session_options = | |
393 reinterpret_cast<SSLSetSessionOptionFuncPtr>( | |
394 CFBundleGetFunctionPointerForName(bundle, | |
395 CFSTR("SSLSetSessionOption"))); | |
396 if (ssl_set_session_options) { | |
397 status = ssl_set_session_options(ssl_context_, | |
398 kSSLSessionOptionBreakOnServerAuthFlag, | |
399 true); | |
400 if (status) | |
401 return NetErrorFromOSStatus(status); | |
402 | |
403 // Concatenate the hostname and peer address to use as the peer ID. To | |
404 // resume a session, we must connect to the same server on the same port | |
405 // using the same hostname (i.e., localhost and 127.0.0.1 are considered | |
406 // different peers, which puts us through certificate validation again | |
407 // and catches hostname/certificate name mismatches. | |
408 struct sockaddr_storage addr; | |
409 socklen_t addr_length = sizeof(struct sockaddr_storage); | |
410 memset(&addr, 0, sizeof(addr)); | |
411 if (!transport_->GetPeerName(reinterpret_cast<struct sockaddr*>(&addr), | |
412 &addr_length)) { | |
413 // Assemble the socket hostname and address into a single buffer. | |
414 std::vector<char> peer_id(hostname_.begin(), hostname_.end()); | |
415 peer_id.insert(peer_id.end(), reinterpret_cast<char*>(&addr), | |
416 reinterpret_cast<char*>(&addr) + addr_length); | |
417 | |
418 // SSLSetPeerID() treats peer_id as a binary blob, and makes its | |
419 // own copy. | |
420 status = SSLSetPeerID(ssl_context_, &peer_id[0], peer_id.size()); | |
421 if (status) | |
422 return NetErrorFromOSStatus(status); | |
423 } | |
424 } | |
425 } | 342 } |
426 | 343 |
427 next_handshake_state_ = STATE_HANDSHAKE_START; | 344 next_handshake_state_ = STATE_HANDSHAKE_START; |
428 int rv = DoHandshakeLoop(OK); | 345 rv = DoHandshakeLoop(OK); |
429 if (rv == ERR_IO_PENDING) | 346 if (rv == ERR_IO_PENDING) { |
| 347 load_log_ = load_log; |
430 user_connect_callback_ = callback; | 348 user_connect_callback_ = callback; |
| 349 } else { |
| 350 LoadLog::EndEvent(load_log, LoadLog::TYPE_SSL_CONNECT); |
| 351 } |
431 return rv; | 352 return rv; |
432 } | 353 } |
433 | 354 |
434 void SSLClientSocketMac::Disconnect() { | 355 void SSLClientSocketMac::Disconnect() { |
435 completed_handshake_ = false; | 356 completed_handshake_ = false; |
436 | 357 |
437 if (ssl_context_) { | 358 if (ssl_context_) { |
438 SSLClose(ssl_context_); | 359 SSLClose(ssl_context_); |
439 SSLDisposeContext(ssl_context_); | 360 SSLDisposeContext(ssl_context_); |
440 ssl_context_ = NULL; | 361 ssl_context_ = NULL; |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
528 OSStatus status = SSLGetNegotiatedCipher(ssl_context_, &suite); | 449 OSStatus status = SSLGetNegotiatedCipher(ssl_context_, &suite); |
529 if (!status) | 450 if (!status) |
530 ssl_info->security_bits = KeySizeOfCipherSuite(suite); | 451 ssl_info->security_bits = KeySizeOfCipherSuite(suite); |
531 } | 452 } |
532 | 453 |
533 void SSLClientSocketMac::GetSSLCertRequestInfo( | 454 void SSLClientSocketMac::GetSSLCertRequestInfo( |
534 SSLCertRequestInfo* cert_request_info) { | 455 SSLCertRequestInfo* cert_request_info) { |
535 // TODO(wtc): implement this. | 456 // TODO(wtc): implement this. |
536 } | 457 } |
537 | 458 |
| 459 int SSLClientSocketMac::InitializeSSLContext() { |
| 460 OSStatus status = noErr; |
| 461 |
| 462 status = SSLNewContext(false, &ssl_context_); |
| 463 if (status) |
| 464 return NetErrorFromOSStatus(status); |
| 465 |
| 466 status = SSLSetProtocolVersionEnabled(ssl_context_, |
| 467 kSSLProtocol2, |
| 468 ssl_config_.ssl2_enabled); |
| 469 if (status) |
| 470 return NetErrorFromOSStatus(status); |
| 471 |
| 472 status = SSLSetProtocolVersionEnabled(ssl_context_, |
| 473 kSSLProtocol3, |
| 474 ssl_config_.ssl3_enabled); |
| 475 if (status) |
| 476 return NetErrorFromOSStatus(status); |
| 477 |
| 478 status = SSLSetProtocolVersionEnabled(ssl_context_, |
| 479 kTLSProtocol1, |
| 480 ssl_config_.tls1_enabled); |
| 481 if (status) |
| 482 return NetErrorFromOSStatus(status); |
| 483 |
| 484 status = SSLSetIOFuncs(ssl_context_, SSLReadCallback, SSLWriteCallback); |
| 485 if (status) |
| 486 return NetErrorFromOSStatus(status); |
| 487 |
| 488 status = SSLSetConnection(ssl_context_, this); |
| 489 if (status) |
| 490 return NetErrorFromOSStatus(status); |
| 491 |
| 492 // Disable certificate verification within Secure Transport; we'll |
| 493 // be handling that ourselves. |
| 494 status = SSLSetEnableCertVerify(ssl_context_, false); |
| 495 if (status) |
| 496 return NetErrorFromOSStatus(status); |
| 497 |
| 498 // SSLSetSessionOption() was introduced in Mac OS X 10.5.7. It allows us |
| 499 // to perform certificate validation during the handshake, which is |
| 500 // required in order to properly enable session resumption. |
| 501 // |
| 502 // With the kSSLSessionOptionBreakOnServerAuth option set, SSLHandshake() |
| 503 // will return errSSLServerAuthCompleted after receiving the server's |
| 504 // Certificate during the handshake. That gives us an opportunity to verify |
| 505 // the server certificate and then re-enter that handshake (assuming the |
| 506 // certificate successfully validated). |
| 507 // |
| 508 // If SSLSetSessionOption() is not present, we do not enable session |
| 509 // resumption, because in that case we are verifying the server's certificate |
| 510 // after the handshake completes (but before any application data is |
| 511 // exchanged). If we were to enable session resumption in this situation, |
| 512 // the session would be cached before we verified the certificate, leaving |
| 513 // the potential for a session in which the certificate failed to validate |
| 514 // to still be able to be resumed. |
| 515 CFBundleRef bundle = |
| 516 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security")); |
| 517 if (bundle) { |
| 518 SSLSetSessionOptionFuncPtr ssl_set_session_options = |
| 519 reinterpret_cast<SSLSetSessionOptionFuncPtr>( |
| 520 CFBundleGetFunctionPointerForName(bundle, |
| 521 CFSTR("SSLSetSessionOption"))); |
| 522 if (ssl_set_session_options) { |
| 523 status = ssl_set_session_options(ssl_context_, |
| 524 kSSLSessionOptionBreakOnServerAuthFlag, |
| 525 true); |
| 526 if (status) |
| 527 return NetErrorFromOSStatus(status); |
| 528 |
| 529 // Concatenate the hostname and peer address to use as the peer ID. To |
| 530 // resume a session, we must connect to the same server on the same port |
| 531 // using the same hostname (i.e., localhost and 127.0.0.1 are considered |
| 532 // different peers, which puts us through certificate validation again |
| 533 // and catches hostname/certificate name mismatches. |
| 534 struct sockaddr_storage addr; |
| 535 socklen_t addr_length = sizeof(struct sockaddr_storage); |
| 536 memset(&addr, 0, sizeof(addr)); |
| 537 if (!transport_->GetPeerName(reinterpret_cast<struct sockaddr*>(&addr), |
| 538 &addr_length)) { |
| 539 // Assemble the socket hostname and address into a single buffer. |
| 540 std::vector<char> peer_id(hostname_.begin(), hostname_.end()); |
| 541 peer_id.insert(peer_id.end(), reinterpret_cast<char*>(&addr), |
| 542 reinterpret_cast<char*>(&addr) + addr_length); |
| 543 |
| 544 // SSLSetPeerID() treats peer_id as a binary blob, and makes its |
| 545 // own copy. |
| 546 status = SSLSetPeerID(ssl_context_, &peer_id[0], peer_id.size()); |
| 547 if (status) |
| 548 return NetErrorFromOSStatus(status); |
| 549 } |
| 550 } |
| 551 } |
| 552 |
| 553 return OK; |
| 554 } |
| 555 |
538 void SSLClientSocketMac::DoConnectCallback(int rv) { | 556 void SSLClientSocketMac::DoConnectCallback(int rv) { |
539 DCHECK(rv != ERR_IO_PENDING); | 557 DCHECK(rv != ERR_IO_PENDING); |
540 DCHECK(user_connect_callback_); | 558 DCHECK(user_connect_callback_); |
541 DCHECK(next_handshake_state_ == STATE_NONE); | 559 DCHECK(next_handshake_state_ == STATE_NONE); |
542 | 560 |
543 CompletionCallback* c = user_connect_callback_; | 561 CompletionCallback* c = user_connect_callback_; |
544 user_connect_callback_ = NULL; | 562 user_connect_callback_ = NULL; |
545 c->Run(rv > OK ? OK : rv); | 563 c->Run(rv > OK ? OK : rv); |
546 } | 564 } |
547 | 565 |
(...skipping 19 matching lines...) Expand all Loading... |
567 CompletionCallback* c = user_write_callback_; | 585 CompletionCallback* c = user_write_callback_; |
568 user_write_callback_ = NULL; | 586 user_write_callback_ = NULL; |
569 user_write_buf_ = NULL; | 587 user_write_buf_ = NULL; |
570 user_write_buf_len_ = 0; | 588 user_write_buf_len_ = 0; |
571 c->Run(rv); | 589 c->Run(rv); |
572 } | 590 } |
573 | 591 |
574 void SSLClientSocketMac::OnHandshakeIOComplete(int result) { | 592 void SSLClientSocketMac::OnHandshakeIOComplete(int result) { |
575 DCHECK(next_handshake_state_ != STATE_NONE); | 593 DCHECK(next_handshake_state_ != STATE_NONE); |
576 int rv = DoHandshakeLoop(result); | 594 int rv = DoHandshakeLoop(result); |
577 if (rv != ERR_IO_PENDING) | 595 if (rv != ERR_IO_PENDING) { |
| 596 LoadLog::EndEvent(load_log_, LoadLog::TYPE_SSL_CONNECT); |
| 597 load_log_ = NULL; |
578 DoConnectCallback(rv); | 598 DoConnectCallback(rv); |
| 599 } |
579 } | 600 } |
580 | 601 |
581 void SSLClientSocketMac::OnTransportReadComplete(int result) { | 602 void SSLClientSocketMac::OnTransportReadComplete(int result) { |
582 if (result > 0) { | 603 if (result > 0) { |
583 char* buffer = | 604 char* buffer = |
584 &recv_buffer_[recv_buffer_.size() - recv_buffer_tail_slop_]; | 605 &recv_buffer_[recv_buffer_.size() - recv_buffer_tail_slop_]; |
585 memcpy(buffer, read_io_buf_->data(), result); | 606 memcpy(buffer, read_io_buf_->data(), result); |
586 recv_buffer_tail_slop_ -= result; | 607 recv_buffer_tail_slop_ -= result; |
587 } | 608 } |
588 read_io_buf_ = NULL; | 609 read_io_buf_ = NULL; |
589 | 610 |
590 if (next_handshake_state_ != STATE_NONE) { | 611 if (next_handshake_state_ != STATE_NONE) { |
591 int rv = DoHandshakeLoop(result); | 612 int rv = DoHandshakeLoop(result); |
592 if (rv != ERR_IO_PENDING) | 613 if (rv != ERR_IO_PENDING) { |
| 614 LoadLog::EndEvent(load_log_, LoadLog::TYPE_SSL_CONNECT); |
| 615 load_log_ = NULL; |
593 DoConnectCallback(rv); | 616 DoConnectCallback(rv); |
| 617 } |
594 return; | 618 return; |
595 } | 619 } |
596 if (user_read_buf_) { | 620 if (user_read_buf_) { |
597 if (result < 0) { | 621 if (result < 0) { |
598 DoReadCallback(result); | 622 DoReadCallback(result); |
599 return; | 623 return; |
600 } | 624 } |
601 int rv = DoPayloadRead(); | 625 int rv = DoPayloadRead(); |
602 if (rv != ERR_IO_PENDING) | 626 if (rv != ERR_IO_PENDING) |
603 DoReadCallback(rv); | 627 DoReadCallback(rv); |
(...skipping 357 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
961 | 985 |
962 if (rv < 0 && rv != ERR_IO_PENDING) { | 986 if (rv < 0 && rv != ERR_IO_PENDING) { |
963 return OSStatusFromNetError(rv); | 987 return OSStatusFromNetError(rv); |
964 } | 988 } |
965 | 989 |
966 // always lie to our caller | 990 // always lie to our caller |
967 return noErr; | 991 return noErr; |
968 } | 992 } |
969 | 993 |
970 } // namespace net | 994 } // namespace net |
OLD | NEW |