| Index: net/socket/ssl_client_socket_mac.cc
|
| diff --git a/net/socket/ssl_client_socket_mac.cc b/net/socket/ssl_client_socket_mac.cc
|
| deleted file mode 100644
|
| index 69ef093cfdfba9c0f66e266d16804015a45668f1..0000000000000000000000000000000000000000
|
| --- a/net/socket/ssl_client_socket_mac.cc
|
| +++ /dev/null
|
| @@ -1,1471 +0,0 @@
|
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "net/socket/ssl_client_socket_mac.h"
|
| -
|
| -#include <CoreServices/CoreServices.h>
|
| -#include <netdb.h>
|
| -#include <sys/socket.h>
|
| -#include <sys/types.h>
|
| -
|
| -#include <algorithm>
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/lazy_instance.h"
|
| -#include "base/mac/mac_logging.h"
|
| -#include "base/mac/scoped_cftyperef.h"
|
| -#include "base/string_util.h"
|
| -#include "net/base/address_list.h"
|
| -#include "net/base/cert_verifier.h"
|
| -#include "net/base/io_buffer.h"
|
| -#include "net/base/net_errors.h"
|
| -#include "net/base/net_log.h"
|
| -#include "net/base/single_request_cert_verifier.h"
|
| -#include "net/base/ssl_cert_request_info.h"
|
| -#include "net/base/ssl_connection_status_flags.h"
|
| -#include "net/base/ssl_info.h"
|
| -#include "net/base/x509_certificate_net_log_param.h"
|
| -#include "net/base/x509_util.h"
|
| -#include "net/socket/client_socket_handle.h"
|
| -#include "net/socket/ssl_error_params.h"
|
| -
|
| -// Welcome to Mac SSL. We've been waiting for you.
|
| -//
|
| -// The Mac SSL implementation is, like the Windows and NSS implementations, a
|
| -// giant state machine. This design constraint is due to the asynchronous nature
|
| -// of our underlying transport mechanism. We can call down to read/write on the
|
| -// network, but what happens is that either it completes immediately or returns
|
| -// saying that we'll get a callback sometime in the future. In that case, we
|
| -// have to return to our caller but pick up where we left off when we
|
| -// resume. Thus the fun.
|
| -//
|
| -// On Windows, we use Security Contexts, which are driven by us. We fetch data
|
| -// from the network, we call the context to decrypt the data, and so on. On the
|
| -// Mac, however, we provide Secure Transport with callbacks to get data from the
|
| -// network, and it calls us back to fetch the data from the network for
|
| -// it. Therefore, there are different sets of states in our respective state
|
| -// machines, fewer on the Mac because Secure Transport keeps a lot of its own
|
| -// state. The discussion about what each of the states means lives in comments
|
| -// in the DoHandshakeLoop() function.
|
| -//
|
| -// Secure Transport is designed for use by either blocking or non-blocking
|
| -// network I/O. If, for example, you called SSLRead() to fetch data, Secure
|
| -// Transport will, unless it has some cached data, issue a read to your network
|
| -// callback read function to fetch it some more encrypted data. It's expecting
|
| -// one of two things. If your function is hooked up to a blocking source, then
|
| -// it'll block pending receipt of the data from the other end. That's fine, as
|
| -// when you return with the data, Secure Transport will do its thing. On the
|
| -// other hand, suppose that your socket is non-blocking and tells your function
|
| -// that it would block. Then you let Secure Transport know, and it'll tell the
|
| -// original caller that it would have blocked and that they need to call it
|
| -// "later."
|
| -//
|
| -// When's "later," though? We have fully-asynchronous networking, so we get a
|
| -// callback when our data's ready. But Secure Transport has no way for us to
|
| -// tell it that data has arrived, so we must re-execute the call that triggered
|
| -// the I/O (we rely on our state machine to do this). When we do so Secure
|
| -// Transport will ask once again for the data. Chances are that it'll be the
|
| -// same request as the previous time, but that's not actually guaranteed. But as
|
| -// long as we buffer what we have and keep track of where we were, it works
|
| -// quite well.
|
| -//
|
| -// Except for network writes. They shoot this plan straight to hell.
|
| -//
|
| -// Faking a blocking connection with an asynchronous connection (theoretically
|
| -// more powerful) simply doesn't work for writing. Suppose that Secure Transport
|
| -// requests a write of data to the network. With blocking I/O, we'd just block
|
| -// until the write completed, and with non-blocking I/O we'd know how many bytes
|
| -// we wrote before we would have blocked. But with the asynchronous I/O, the
|
| -// transport underneath us can tell us that it'll let us know sometime "later"
|
| -// whether or not things succeeded, and how many bytes were written. What do we
|
| -// return to Secure Transport? We can't return a byte count, but we can't return
|
| -// "later" as we're not guaranteed to be called in the future with the same data
|
| -// to write.
|
| -//
|
| -// So, like in any good relationship, we're forced to lie. Whenever Secure
|
| -// Transport asks for data to be written, we take it all and lie about it always
|
| -// being written. We spin in a loop (see SSLWriteCallback() and
|
| -// OnTransportWriteComplete()) independent of the main state machine writing
|
| -// the data to the network, and get the data out. The main consequence of this
|
| -// independence from the state machine is that we require a full-duplex
|
| -// transport underneath us since we can't use it to keep our reading and
|
| -// writing straight. Fortunately, the NSS implementation also has this issue
|
| -// to deal with, so we share the same Libevent-based full-duplex TCP socket.
|
| -//
|
| -// A side comment on return values might be in order. Those who haven't taken
|
| -// the time to read the documentation (ahem, header comments) in our various
|
| -// files might be a bit surprised to see result values being treated as both
|
| -// lengths and errors. Like Shimmer, they are both. In both the case of
|
| -// immediate results as well as results returned in callbacks, a negative return
|
| -// value indicates an error, a zero return value indicates end-of-stream (for
|
| -// reads), and a positive return value indicates the number of bytes read or
|
| -// written. Thus, many functions start off with |if (result < 0) return
|
| -// result;|. That gets the error condition out of the way, and from that point
|
| -// forward the result can be treated as a length.
|
| -
|
| -namespace net {
|
| -
|
| -namespace {
|
| -
|
| -// Pause if we have 2MB of data in flight, resume once we're down below 1MB.
|
| -const unsigned int kWriteSizePauseLimit = 2 * 1024 * 1024;
|
| -const unsigned int kWriteSizeResumeLimit = 1 * 1024 * 1024;
|
| -
|
| -#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
|
| -// When compiled against the Mac OS X 10.5 SDK, define symbolic constants for
|
| -// cipher suites added in Mac OS X 10.6.
|
| -enum {
|
| - // ECC cipher suites from RFC 4492.
|
| - TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001,
|
| - TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002,
|
| - TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003,
|
| - TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004,
|
| - TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005,
|
| - TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006,
|
| - TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007,
|
| - TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008,
|
| - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009,
|
| - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A,
|
| - TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B,
|
| - TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C,
|
| - TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D,
|
| - TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00E,
|
| - TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00F,
|
| - TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010,
|
| - TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011,
|
| - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012,
|
| - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013,
|
| - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014,
|
| - TLS_ECDH_anon_WITH_NULL_SHA = 0xC015,
|
| - TLS_ECDH_anon_WITH_RC4_128_SHA = 0xC016,
|
| - TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = 0xC017,
|
| - TLS_ECDH_anon_WITH_AES_128_CBC_SHA = 0xC018,
|
| - TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xC019,
|
| -};
|
| -#endif
|
| -
|
| -// For an explanation of the Mac OS X error codes, please refer to:
|
| -// http://developer.apple.com/mac/library/documentation/Security/Reference/secureTransportRef/Reference/reference.html
|
| -int NetErrorFromOSStatus(OSStatus status) {
|
| - switch (status) {
|
| - case errSSLWouldBlock:
|
| - return ERR_IO_PENDING;
|
| - case paramErr:
|
| - case errSSLBadCipherSuite:
|
| - case errSSLBadConfiguration:
|
| - return ERR_INVALID_ARGUMENT;
|
| - case errSSLClosedNoNotify:
|
| - return ERR_CONNECTION_RESET;
|
| - case errSSLClosedAbort:
|
| - return ERR_CONNECTION_ABORTED;
|
| - case errSSLInternal:
|
| - return ERR_UNEXPECTED;
|
| - case errSSLBadRecordMac:
|
| - case errSSLCrypto:
|
| - case errSSLConnectionRefused:
|
| - case errSSLDecryptionFail:
|
| - case errSSLFatalAlert:
|
| - case errSSLIllegalParam: // Received an illegal_parameter alert.
|
| - case errSSLPeerDecodeError: // Received a decode_error alert.
|
| - case errSSLPeerDecryptError: // Received a decrypt_error alert.
|
| - case errSSLPeerExportRestriction: // Received an export_restriction alert.
|
| - case errSSLPeerHandshakeFail: // Received a handshake_failure alert.
|
| - case errSSLPeerNoRenegotiation: // Received a no_renegotiation alert
|
| - case errSSLPeerUnexpectedMsg: // Received an unexpected_message alert.
|
| - case errSSLProtocol:
|
| - case errSSLRecordOverflow:
|
| - return ERR_SSL_PROTOCOL_ERROR;
|
| - case errSSLHostNameMismatch:
|
| - return ERR_CERT_COMMON_NAME_INVALID;
|
| - case errSSLCertExpired:
|
| - case errSSLCertNotYetValid:
|
| - return ERR_CERT_DATE_INVALID;
|
| - case errSSLNoRootCert:
|
| - case errSSLUnknownRootCert:
|
| - return ERR_CERT_AUTHORITY_INVALID;
|
| - case errSSLXCertChainInvalid:
|
| - case errSSLBadCert:
|
| - return ERR_CERT_INVALID;
|
| -
|
| - case errSSLClosedGraceful:
|
| - case noErr:
|
| - return OK;
|
| -
|
| - // (Note that all errSSLPeer* codes indicate errors reported by the peer,
|
| - // so the cert-related ones refer to my _client_ cert.)
|
| - // TODO(wtc): Add fine-grained error codes for client certificate errors
|
| - // reported by the server using the following SSL/TLS alert messages:
|
| - // access_denied
|
| - // bad_certificate
|
| - // unsupported_certificate
|
| - // certificate_expired
|
| - // certificate_revoked
|
| - // certificate_unknown
|
| - // unknown_ca
|
| - case errSSLPeerCertUnknown...errSSLPeerBadCert:
|
| - case errSSLPeerUnknownCA:
|
| - case errSSLPeerAccessDenied:
|
| - OSSTATUS_LOG(WARNING, status) << "Server rejected client cert";
|
| - return ERR_BAD_SSL_CLIENT_AUTH_CERT;
|
| -
|
| - case errSSLNegotiation:
|
| - case errSSLPeerInsufficientSecurity:
|
| - case errSSLPeerProtocolVersion:
|
| - return ERR_SSL_VERSION_OR_CIPHER_MISMATCH;
|
| -
|
| - case errSSLBufferOverflow:
|
| - case errSSLModuleAttach:
|
| - case errSSLSessionNotFound:
|
| - default:
|
| - OSSTATUS_LOG(WARNING, status)
|
| - << "Unknown error mapped to net::ERR_FAILED";
|
| - return ERR_FAILED;
|
| - }
|
| -}
|
| -
|
| -OSStatus OSStatusFromNetError(int net_error) {
|
| - switch (net_error) {
|
| - case ERR_IO_PENDING:
|
| - return errSSLWouldBlock;
|
| - case ERR_INTERNET_DISCONNECTED:
|
| - case ERR_TIMED_OUT:
|
| - case ERR_CONNECTION_ABORTED:
|
| - case ERR_CONNECTION_RESET:
|
| - case ERR_CONNECTION_REFUSED:
|
| - case ERR_ADDRESS_UNREACHABLE:
|
| - case ERR_ADDRESS_INVALID:
|
| - return errSSLClosedAbort;
|
| - case ERR_UNEXPECTED:
|
| - return errSSLInternal;
|
| - case ERR_INVALID_ARGUMENT:
|
| - return paramErr;
|
| - case OK:
|
| - return noErr;
|
| - default:
|
| - LOG(WARNING) << "Unknown error " << net_error << " mapped to paramErr";
|
| - return paramErr;
|
| - }
|
| -}
|
| -
|
| -// Converts from a cipher suite to its key size. If the suite is marked with a
|
| -// **, it's not actually implemented in Secure Transport and won't be returned
|
| -// (but we'll code for it anyway). The reference here is
|
| -// http://www.opensource.apple.com/darwinsource/10.5.5/libsecurity_ssl-32463/lib/cipherSpecs.c
|
| -// Seriously, though, there has to be an API for this, but I can't find one.
|
| -// Anybody?
|
| -int KeySizeOfCipherSuite(SSLCipherSuite suite) {
|
| - switch (suite) {
|
| - // SSL 2 only
|
| -
|
| - case SSL_RSA_WITH_DES_CBC_MD5:
|
| - return 56;
|
| - case SSL_RSA_WITH_3DES_EDE_CBC_MD5:
|
| - return 112;
|
| - case SSL_RSA_WITH_RC2_CBC_MD5:
|
| - case SSL_RSA_WITH_IDEA_CBC_MD5: // **
|
| - return 128;
|
| - case SSL_NO_SUCH_CIPHERSUITE: // **
|
| - return 0;
|
| -
|
| - // SSL 2, 3, TLS
|
| -
|
| - case SSL_NULL_WITH_NULL_NULL:
|
| - case SSL_RSA_WITH_NULL_MD5:
|
| - case SSL_RSA_WITH_NULL_SHA: // **
|
| - case SSL_FORTEZZA_DMS_WITH_NULL_SHA: // **
|
| - case TLS_ECDH_ECDSA_WITH_NULL_SHA:
|
| - case TLS_ECDHE_ECDSA_WITH_NULL_SHA:
|
| - case TLS_ECDH_RSA_WITH_NULL_SHA:
|
| - case TLS_ECDHE_RSA_WITH_NULL_SHA:
|
| - case TLS_ECDH_anon_WITH_NULL_SHA:
|
| - return 0;
|
| - case SSL_RSA_EXPORT_WITH_RC4_40_MD5:
|
| - case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5:
|
| - case SSL_RSA_EXPORT_WITH_DES40_CBC_SHA:
|
| - case SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA: // **
|
| - case SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA: // **
|
| - case SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA:
|
| - case SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA:
|
| - case SSL_DH_anon_EXPORT_WITH_RC4_40_MD5:
|
| - case SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA:
|
| - return 40;
|
| - case SSL_RSA_WITH_DES_CBC_SHA:
|
| - case SSL_DH_DSS_WITH_DES_CBC_SHA: // **
|
| - case SSL_DH_RSA_WITH_DES_CBC_SHA: // **
|
| - case SSL_DHE_DSS_WITH_DES_CBC_SHA:
|
| - case SSL_DHE_RSA_WITH_DES_CBC_SHA:
|
| - case SSL_DH_anon_WITH_DES_CBC_SHA:
|
| - return 56;
|
| - case SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA: // **
|
| - return 80;
|
| - case SSL_RSA_WITH_3DES_EDE_CBC_SHA:
|
| - case SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA: // **
|
| - case SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA: // **
|
| - case SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
|
| - case SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
|
| - case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA:
|
| - case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
|
| - case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
|
| - case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
|
| - case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
|
| - case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA:
|
| - return 112;
|
| - case SSL_RSA_WITH_RC4_128_MD5:
|
| - case SSL_RSA_WITH_RC4_128_SHA:
|
| - case SSL_RSA_WITH_IDEA_CBC_SHA: // **
|
| - case SSL_DH_anon_WITH_RC4_128_MD5:
|
| - case TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
|
| - case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
|
| - case TLS_ECDH_RSA_WITH_RC4_128_SHA:
|
| - case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
|
| - case TLS_ECDH_anon_WITH_RC4_128_SHA:
|
| - return 128;
|
| -
|
| - // TLS AES options (see RFC 3268 and RFC 4492)
|
| -
|
| - case TLS_RSA_WITH_AES_128_CBC_SHA:
|
| - case TLS_DH_DSS_WITH_AES_128_CBC_SHA: // **
|
| - case TLS_DH_RSA_WITH_AES_128_CBC_SHA: // **
|
| - case TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
|
| - case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
|
| - case TLS_DH_anon_WITH_AES_128_CBC_SHA:
|
| - case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
|
| - case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
|
| - case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
|
| - case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
|
| - case TLS_ECDH_anon_WITH_AES_128_CBC_SHA:
|
| - return 128;
|
| - case TLS_RSA_WITH_AES_256_CBC_SHA:
|
| - case TLS_DH_DSS_WITH_AES_256_CBC_SHA: // **
|
| - case TLS_DH_RSA_WITH_AES_256_CBC_SHA: // **
|
| - case TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
|
| - case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
|
| - case TLS_DH_anon_WITH_AES_256_CBC_SHA:
|
| - case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
|
| - case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
|
| - case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
|
| - case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
|
| - case TLS_ECDH_anon_WITH_AES_256_CBC_SHA:
|
| - return 256;
|
| -
|
| - default:
|
| - return -1;
|
| - }
|
| -}
|
| -
|
| -// Whitelist the cipher suites we want to enable. We disable the following
|
| -// cipher suites.
|
| -// - Null encryption cipher suites.
|
| -// - Weak cipher suites: < 80 bits of security strength.
|
| -// - FORTEZZA cipher suites (obsolete).
|
| -// - IDEA cipher suites (RFC 5469 explains why).
|
| -// - Anonymous cipher suites.
|
| -//
|
| -// Why don't we use a blacklist? A blacklist that isn't updated for a new
|
| -// Mac OS X release is a potential security issue because the new release
|
| -// may have new null encryption or anonymous cipher suites, whereas a
|
| -// whitelist that isn't updated for a new Mac OS X release just means we
|
| -// won't support any new cipher suites in that release.
|
| -bool ShouldEnableCipherSuite(SSLCipherSuite suite) {
|
| - switch (suite) {
|
| - case SSL_RSA_WITH_3DES_EDE_CBC_MD5:
|
| - case SSL_RSA_WITH_RC2_CBC_MD5:
|
| -
|
| - case SSL_RSA_WITH_3DES_EDE_CBC_SHA:
|
| - case SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA: // **
|
| - case SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA: // **
|
| - case SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
|
| - case SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
|
| - case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
|
| - case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
|
| - case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
|
| - case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
|
| -
|
| - case SSL_RSA_WITH_RC4_128_MD5:
|
| - case SSL_RSA_WITH_RC4_128_SHA:
|
| - case TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
|
| - case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
|
| - case TLS_ECDH_RSA_WITH_RC4_128_SHA:
|
| - case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
|
| -
|
| - case TLS_RSA_WITH_AES_128_CBC_SHA:
|
| - case TLS_DH_DSS_WITH_AES_128_CBC_SHA: // **
|
| - case TLS_DH_RSA_WITH_AES_128_CBC_SHA: // **
|
| - case TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
|
| - case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
|
| - case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
|
| - case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
|
| - case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
|
| - case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
|
| -
|
| - case TLS_RSA_WITH_AES_256_CBC_SHA:
|
| - case TLS_DH_DSS_WITH_AES_256_CBC_SHA: // **
|
| - case TLS_DH_RSA_WITH_AES_256_CBC_SHA: // **
|
| - case TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
|
| - case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
|
| - case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
|
| - case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
|
| - case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
|
| - case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
|
| - return true;
|
| -
|
| - default:
|
| - return false;
|
| - }
|
| -}
|
| -
|
| -// Returns the server's certificate. The caller must release a reference
|
| -// to the return value when done. Returns NULL on failure.
|
| -X509Certificate* GetServerCert(SSLContextRef ssl_context) {
|
| - CFArrayRef certs;
|
| - OSStatus status = SSLCopyPeerCertificates(ssl_context, &certs);
|
| - // SSLCopyPeerCertificates may succeed but return a null |certs|
|
| - // (if we're using an anonymous cipher suite or if we call it
|
| - // before the certificate message has arrived and been parsed).
|
| - if (status != noErr || !certs)
|
| - return NULL;
|
| - base::mac::ScopedCFTypeRef<CFArrayRef> scoped_certs(certs);
|
| -
|
| - DCHECK_GT(CFArrayGetCount(certs), 0);
|
| -
|
| - // Add each of the intermediate certificates in the server's chain to the
|
| - // server's X509Certificate object. This makes them available to
|
| - // X509Certificate::Verify() for chain building.
|
| - std::vector<SecCertificateRef> intermediate_ca_certs;
|
| - CFIndex certs_length = CFArrayGetCount(certs);
|
| - for (CFIndex i = 1; i < certs_length; ++i) {
|
| - SecCertificateRef cert_ref = reinterpret_cast<SecCertificateRef>(
|
| - const_cast<void*>(CFArrayGetValueAtIndex(certs, i)));
|
| - intermediate_ca_certs.push_back(cert_ref);
|
| - }
|
| -
|
| - SecCertificateRef server_cert = static_cast<SecCertificateRef>(
|
| - const_cast<void*>(CFArrayGetValueAtIndex(certs, 0)));
|
| - return X509Certificate::CreateFromHandle(server_cert,
|
| - intermediate_ca_certs);
|
| -}
|
| -
|
| -// Dynamically look up a pointer to a function exported by a bundle.
|
| -template <typename FNTYPE>
|
| -FNTYPE LookupFunction(CFStringRef bundleName, CFStringRef fnName) {
|
| - CFBundleRef bundle = CFBundleGetBundleWithIdentifier(bundleName);
|
| - if (!bundle)
|
| - return NULL;
|
| - return reinterpret_cast<FNTYPE>(
|
| - CFBundleGetFunctionPointerForName(bundle, fnName));
|
| -}
|
| -
|
| -struct CipherSuiteIsDisabledFunctor {
|
| - explicit CipherSuiteIsDisabledFunctor(
|
| - const std::vector<uint16>& disabled_cipher_suites)
|
| - : disabled_cipher_suites_(disabled_cipher_suites) {}
|
| -
|
| - // Returns true if the given |cipher_suite| appears within the set of
|
| - // |disabled_cipher_suites|.
|
| - bool operator()(SSLCipherSuite cipher_suite) const {
|
| - return binary_search(disabled_cipher_suites_.begin(),
|
| - disabled_cipher_suites_.end(),
|
| - static_cast<uint16>(cipher_suite));
|
| - }
|
| -
|
| - const std::vector<uint16>& disabled_cipher_suites_;
|
| -};
|
| -
|
| -// Class to determine what cipher suites are available and which cipher
|
| -// suites should be enabled, based on the overall security policy.
|
| -class EnabledCipherSuites {
|
| - public:
|
| - const std::vector<SSLCipherSuite>& ciphers() const { return ciphers_; }
|
| -
|
| - private:
|
| - friend struct base::DefaultLazyInstanceTraits<EnabledCipherSuites>;
|
| - EnabledCipherSuites();
|
| - ~EnabledCipherSuites() {}
|
| -
|
| - std::vector<SSLCipherSuite> ciphers_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(EnabledCipherSuites);
|
| -};
|
| -
|
| -static base::LazyInstance<EnabledCipherSuites> g_enabled_cipher_suites =
|
| - LAZY_INSTANCE_INITIALIZER;
|
| -
|
| -EnabledCipherSuites::EnabledCipherSuites() {
|
| - SSLContextRef ssl_context;
|
| - OSStatus status = SSLNewContext(false, &ssl_context);
|
| - if (status != noErr)
|
| - return;
|
| -
|
| - size_t num_supported_ciphers;
|
| - status = SSLGetNumberSupportedCiphers(ssl_context, &num_supported_ciphers);
|
| - if (status != noErr) {
|
| - SSLDisposeContext(ssl_context);
|
| - return;
|
| - }
|
| - DCHECK_NE(num_supported_ciphers, 0U);
|
| -
|
| - std::vector<SSLCipherSuite> supported_ciphers(num_supported_ciphers);
|
| - status = SSLGetSupportedCiphers(ssl_context, &supported_ciphers[0],
|
| - &num_supported_ciphers);
|
| - SSLDisposeContext(ssl_context);
|
| - if (status != noErr)
|
| - return;
|
| -
|
| - for (size_t i = 0; i < num_supported_ciphers; ++i) {
|
| - if (ShouldEnableCipherSuite(supported_ciphers[i]))
|
| - ciphers_.push_back(supported_ciphers[i]);
|
| - }
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -//-----------------------------------------------------------------------------
|
| -
|
| -SSLClientSocketMac::SSLClientSocketMac(ClientSocketHandle* transport_socket,
|
| - const HostPortPair& host_and_port,
|
| - const SSLConfig& ssl_config,
|
| - const SSLClientSocketContext& context)
|
| - : transport_(transport_socket),
|
| - host_and_port_(host_and_port),
|
| - ssl_config_(ssl_config),
|
| - user_read_buf_len_(0),
|
| - user_write_buf_len_(0),
|
| - next_handshake_state_(STATE_NONE),
|
| - cert_verifier_(context.cert_verifier),
|
| - renegotiating_(false),
|
| - client_cert_requested_(false),
|
| - ssl_context_(NULL),
|
| - bytes_read_after_renegotiation_(0),
|
| - pending_send_error_(OK),
|
| - net_log_(transport_socket->socket()->NetLog()) {
|
| - // Sort the list of ciphers to disable, since disabling ciphers on Mac
|
| - // requires subtracting from a list of enabled ciphers while maintaining
|
| - // ordering, as opposed to merely needing to iterate them as with NSS.
|
| - sort(ssl_config_.disabled_cipher_suites.begin(),
|
| - ssl_config_.disabled_cipher_suites.end());
|
| -}
|
| -
|
| -SSLClientSocketMac::~SSLClientSocketMac() {
|
| - Disconnect();
|
| -}
|
| -
|
| -int SSLClientSocketMac::Connect(const CompletionCallback& callback) {
|
| - DCHECK(transport_.get());
|
| - DCHECK(next_handshake_state_ == STATE_NONE);
|
| - DCHECK(user_connect_callback_.is_null());
|
| -
|
| - net_log_.BeginEvent(NetLog::TYPE_SSL_CONNECT);
|
| -
|
| - int rv = InitializeSSLContext();
|
| - if (rv != OK) {
|
| - net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, rv);
|
| - return rv;
|
| - }
|
| -
|
| - next_handshake_state_ = STATE_HANDSHAKE;
|
| - rv = DoHandshakeLoop(OK);
|
| - if (rv == ERR_IO_PENDING) {
|
| - user_connect_callback_ = callback;
|
| - } else {
|
| - net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, rv);
|
| - }
|
| - return rv;
|
| -}
|
| -
|
| -void SSLClientSocketMac::Disconnect() {
|
| - next_handshake_state_ = STATE_NONE;
|
| -
|
| - if (ssl_context_) {
|
| - SSLClose(ssl_context_);
|
| - SSLDisposeContext(ssl_context_);
|
| - ssl_context_ = NULL;
|
| - VLOG(1) << "----- Disposed SSLContext";
|
| - }
|
| -
|
| - // Shut down anything that may call us back.
|
| - verifier_.reset();
|
| - transport_->socket()->Disconnect();
|
| -}
|
| -
|
| -bool SSLClientSocketMac::IsConnected() const {
|
| - // Ideally, we should also check if we have received the close_notify alert
|
| - // message from the server, and return false in that case. We're not doing
|
| - // that, so this function may return a false positive. Since the upper
|
| - // layer (HttpNetworkTransaction) needs to handle a persistent connection
|
| - // closed by the server when we send a request anyway, a false positive in
|
| - // exchange for simpler code is a good trade-off.
|
| - return completed_handshake() && transport_->socket()->IsConnected();
|
| -}
|
| -
|
| -bool SSLClientSocketMac::IsConnectedAndIdle() const {
|
| - // Unlike IsConnected, this method doesn't return a false positive.
|
| - //
|
| - // Strictly speaking, we should check if we have received the close_notify
|
| - // alert message from the server, and return false in that case. Although
|
| - // the close_notify alert message means EOF in the SSL layer, it is just
|
| - // bytes to the transport layer below, so
|
| - // transport_->socket()->IsConnectedAndIdle() returns the desired false
|
| - // when we receive close_notify.
|
| - return completed_handshake() && transport_->socket()->IsConnectedAndIdle();
|
| -}
|
| -
|
| -int SSLClientSocketMac::GetPeerAddress(IPEndPoint* address) const {
|
| - return transport_->socket()->GetPeerAddress(address);
|
| -}
|
| -
|
| -int SSLClientSocketMac::GetLocalAddress(IPEndPoint* address) const {
|
| - return transport_->socket()->GetLocalAddress(address);
|
| -}
|
| -
|
| -const BoundNetLog& SSLClientSocketMac::NetLog() const {
|
| - return net_log_;
|
| -}
|
| -
|
| -void SSLClientSocketMac::SetSubresourceSpeculation() {
|
| - if (transport_.get() && transport_->socket()) {
|
| - transport_->socket()->SetSubresourceSpeculation();
|
| - } else {
|
| - NOTREACHED();
|
| - }
|
| -}
|
| -
|
| -void SSLClientSocketMac::SetOmniboxSpeculation() {
|
| - if (transport_.get() && transport_->socket()) {
|
| - transport_->socket()->SetOmniboxSpeculation();
|
| - } else {
|
| - NOTREACHED();
|
| - }
|
| -}
|
| -
|
| -bool SSLClientSocketMac::WasEverUsed() const {
|
| - if (transport_.get() && transport_->socket()) {
|
| - return transport_->socket()->WasEverUsed();
|
| - }
|
| - NOTREACHED();
|
| - return false;
|
| -}
|
| -
|
| -bool SSLClientSocketMac::UsingTCPFastOpen() const {
|
| - if (transport_.get() && transport_->socket()) {
|
| - return transport_->socket()->UsingTCPFastOpen();
|
| - }
|
| - NOTREACHED();
|
| - return false;
|
| -}
|
| -
|
| -int64 SSLClientSocketMac::NumBytesRead() const {
|
| - if (transport_.get() && transport_->socket()) {
|
| - return transport_->socket()->NumBytesRead();
|
| - }
|
| - NOTREACHED();
|
| - return -1;
|
| -}
|
| -
|
| -base::TimeDelta SSLClientSocketMac::GetConnectTimeMicros() const {
|
| - if (transport_.get() && transport_->socket()) {
|
| - return transport_->socket()->GetConnectTimeMicros();
|
| - }
|
| - NOTREACHED();
|
| - return base::TimeDelta::FromMicroseconds(-1);
|
| -}
|
| -
|
| -int SSLClientSocketMac::Read(IOBuffer* buf, int buf_len,
|
| - const CompletionCallback& callback) {
|
| - DCHECK(completed_handshake());
|
| - DCHECK(user_read_callback_.is_null());
|
| - DCHECK(!user_read_buf_);
|
| -
|
| - user_read_buf_ = buf;
|
| - user_read_buf_len_ = buf_len;
|
| -
|
| - int rv = DoPayloadRead();
|
| - if (rv == ERR_IO_PENDING) {
|
| - user_read_callback_ = callback;
|
| - } else {
|
| - user_read_buf_ = NULL;
|
| - user_read_buf_len_ = 0;
|
| - }
|
| - return rv;
|
| -}
|
| -
|
| -int SSLClientSocketMac::Write(IOBuffer* buf, int buf_len,
|
| - const CompletionCallback& callback) {
|
| - DCHECK(completed_handshake());
|
| - DCHECK(user_write_callback_.is_null());
|
| - DCHECK(!user_write_buf_);
|
| -
|
| - user_write_buf_ = buf;
|
| - user_write_buf_len_ = buf_len;
|
| -
|
| - int rv = DoPayloadWrite();
|
| - if (rv == ERR_IO_PENDING) {
|
| - user_write_callback_ = callback;
|
| - } else {
|
| - user_write_buf_ = NULL;
|
| - user_write_buf_len_ = 0;
|
| - }
|
| - return rv;
|
| -}
|
| -
|
| -bool SSLClientSocketMac::SetReceiveBufferSize(int32 size) {
|
| - return transport_->socket()->SetReceiveBufferSize(size);
|
| -}
|
| -
|
| -bool SSLClientSocketMac::SetSendBufferSize(int32 size) {
|
| - return transport_->socket()->SetSendBufferSize(size);
|
| -}
|
| -
|
| -bool SSLClientSocketMac::GetSSLInfo(SSLInfo* ssl_info) {
|
| - ssl_info->Reset();
|
| - if (!server_cert_)
|
| - return false;
|
| -
|
| - ssl_info->cert = server_cert_verify_result_.verified_cert;
|
| - ssl_info->cert_status = server_cert_verify_result_.cert_status;
|
| - ssl_info->public_key_hashes = server_cert_verify_result_.public_key_hashes;
|
| - ssl_info->is_issued_by_known_root =
|
| - server_cert_verify_result_.is_issued_by_known_root;
|
| - ssl_info->client_cert_sent =
|
| - ssl_config_.send_client_cert && ssl_config_.client_cert;
|
| - ssl_info->channel_id_sent = WasChannelIDSent();
|
| -
|
| - // security info
|
| - SSLCipherSuite suite;
|
| - OSStatus status = SSLGetNegotiatedCipher(ssl_context_, &suite);
|
| - if (!status) {
|
| - ssl_info->security_bits = KeySizeOfCipherSuite(suite);
|
| - ssl_info->connection_status |=
|
| - (suite & SSL_CONNECTION_CIPHERSUITE_MASK) <<
|
| - SSL_CONNECTION_CIPHERSUITE_SHIFT;
|
| - }
|
| -
|
| - if (ssl_config_.version_fallback)
|
| - ssl_info->connection_status |= SSL_CONNECTION_VERSION_FALLBACK;
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void SSLClientSocketMac::GetSSLCertRequestInfo(
|
| - SSLCertRequestInfo* cert_request_info) {
|
| - cert_request_info->host_and_port = host_and_port_.ToString();
|
| - cert_request_info->cert_authorities.clear();
|
| - cert_request_info->cert_key_types.clear();
|
| - cert_request_info->client_certs.clear();
|
| -
|
| - // Retrieve the cert issuers accepted by the server. This information is
|
| - // currently (temporarily) being saved both in |valid_issuers| and
|
| - // |cert_authorities|, the latter being the target solution. The refactoring
|
| - // effort is being tracked in http://crbug.com/166642.
|
| - std::vector<CertPrincipal> valid_issuers;
|
| - CFArrayRef valid_issuer_names = NULL;
|
| - if (SSLCopyDistinguishedNames(ssl_context_, &valid_issuer_names) == noErr &&
|
| - valid_issuer_names != NULL) {
|
| - VLOG(1) << "Server has " << CFArrayGetCount(valid_issuer_names)
|
| - << " valid issuer names";
|
| - int n = CFArrayGetCount(valid_issuer_names);
|
| - for (int i = 0; i < n; i++) {
|
| - CFDataRef issuer = reinterpret_cast<CFDataRef>(
|
| - CFArrayGetValueAtIndex(valid_issuer_names, i));
|
| - // Add the DER-encoded issuer DistinguishedName to |cert_authorities|.
|
| - cert_request_info->cert_authorities.push_back(std::string(
|
| - reinterpret_cast<const char*>(CFDataGetBytePtr(issuer)),
|
| - static_cast<size_t>(CFDataGetLength(issuer))));
|
| - // Add the CertPrincipal object representing the issuer to
|
| - // |valid_issuers|.
|
| - CertPrincipal p;
|
| - if (p.ParseDistinguishedName(CFDataGetBytePtr(issuer),
|
| - CFDataGetLength(issuer))) {
|
| - valid_issuers.push_back(p);
|
| - }
|
| - }
|
| - CFRelease(valid_issuer_names);
|
| - }
|
| -
|
| - // Now get the available client certs whose issuers are allowed by the server.
|
| - // TODO(rch): we should consider passing a host-port pair as the first
|
| - // argument to X509Certificate::GetSSLClientCertificates.
|
| - X509Certificate::GetSSLClientCertificates(host_and_port_.host(),
|
| - valid_issuers,
|
| - &cert_request_info->client_certs);
|
| - std::sort(cert_request_info->client_certs.begin(),
|
| - cert_request_info->client_certs.end(),
|
| - x509_util::ClientCertSorter());
|
| -
|
| - VLOG(1) << "Asking user to choose between "
|
| - << cert_request_info->client_certs.size() << " client certs...";
|
| -}
|
| -
|
| -int SSLClientSocketMac::ExportKeyingMaterial(const base::StringPiece& label,
|
| - bool has_context,
|
| - const base::StringPiece& context,
|
| - unsigned char* out,
|
| - unsigned int outlen) {
|
| - return ERR_NOT_IMPLEMENTED;
|
| -}
|
| -
|
| -int SSLClientSocketMac::GetTLSUniqueChannelBinding(std::string* out) {
|
| - return ERR_NOT_IMPLEMENTED;
|
| -}
|
| -
|
| -SSLClientSocket::NextProtoStatus
|
| -SSLClientSocketMac::GetNextProto(std::string* proto,
|
| - std::string* server_protos) {
|
| - proto->clear();
|
| - server_protos->clear();
|
| - return kNextProtoUnsupported;
|
| -}
|
| -
|
| -ServerBoundCertService* SSLClientSocketMac::GetServerBoundCertService() const {
|
| - return NULL;
|
| -}
|
| -
|
| -int SSLClientSocketMac::InitializeSSLContext() {
|
| - VLOG(1) << "----- InitializeSSLContext";
|
| - OSStatus status = noErr;
|
| -
|
| - status = SSLNewContext(false, &ssl_context_);
|
| - if (status)
|
| - return NetErrorFromOSStatus(status);
|
| -
|
| - status = SSLSetProtocolVersionEnabled(ssl_context_,
|
| - kSSLProtocol2,
|
| - false);
|
| - if (status)
|
| - return NetErrorFromOSStatus(status);
|
| -
|
| - // If ssl_config_.version_max > SSL_PROTOCOL_VERSION_TLS1, it means the
|
| - // SSLConfigService::SetDefaultVersionMax(SSL_PROTOCOL_VERSION_TLS1) call
|
| - // in ClientSocketFactory::UseSystemSSL() is not effective.
|
| - DCHECK_LE(ssl_config_.version_max, SSL_PROTOCOL_VERSION_TLS1);
|
| -
|
| - bool ssl3_enabled = (ssl_config_.version_min == SSL_PROTOCOL_VERSION_SSL3);
|
| - status = SSLSetProtocolVersionEnabled(ssl_context_,
|
| - kSSLProtocol3,
|
| - ssl3_enabled);
|
| - if (status)
|
| - return NetErrorFromOSStatus(status);
|
| -
|
| - bool tls1_enabled = (ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1 &&
|
| - ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1);
|
| - status = SSLSetProtocolVersionEnabled(ssl_context_,
|
| - kTLSProtocol1,
|
| - tls1_enabled);
|
| - if (status)
|
| - return NetErrorFromOSStatus(status);
|
| -
|
| - std::vector<SSLCipherSuite> enabled_ciphers =
|
| - g_enabled_cipher_suites.Get().ciphers();
|
| -
|
| - CipherSuiteIsDisabledFunctor is_disabled_cipher(
|
| - ssl_config_.disabled_cipher_suites);
|
| - std::vector<SSLCipherSuite>::iterator new_end =
|
| - std::remove_if(enabled_ciphers.begin(), enabled_ciphers.end(),
|
| - is_disabled_cipher);
|
| - if (new_end != enabled_ciphers.end())
|
| - enabled_ciphers.erase(new_end, enabled_ciphers.end());
|
| -
|
| - status = SSLSetEnabledCiphers(
|
| - ssl_context_,
|
| - enabled_ciphers.empty() ? NULL : &enabled_ciphers[0],
|
| - enabled_ciphers.size());
|
| -
|
| - if (status)
|
| - return NetErrorFromOSStatus(status);
|
| -
|
| - status = SSLSetIOFuncs(ssl_context_, SSLReadCallback, SSLWriteCallback);
|
| - if (status)
|
| - return NetErrorFromOSStatus(status);
|
| -
|
| - status = SSLSetConnection(ssl_context_, this);
|
| - if (status)
|
| - return NetErrorFromOSStatus(status);
|
| -
|
| - // Passing the domain name enables the server_name TLS extension (SNI).
|
| - status = SSLSetPeerDomainName(ssl_context_,
|
| - host_and_port_.host().data(),
|
| - host_and_port_.host().length());
|
| - if (status)
|
| - return NetErrorFromOSStatus(status);
|
| -
|
| - // Disable certificate verification within Secure Transport; we'll
|
| - // be handling that ourselves.
|
| - status = SSLSetEnableCertVerify(ssl_context_, false);
|
| - if (status)
|
| - return NetErrorFromOSStatus(status);
|
| -
|
| - if (ssl_config_.send_client_cert) {
|
| - status = SetClientCert();
|
| - if (status)
|
| - return NetErrorFromOSStatus(status);
|
| - return OK;
|
| - }
|
| -
|
| - // Concatenate the hostname and peer address to use as the peer ID. To
|
| - // resume a session, we must connect to the same server on the same port
|
| - // using the same hostname (i.e., localhost and 127.0.0.1 are considered
|
| - // different peers, which puts us through certificate validation again
|
| - // and catches hostname/certificate name mismatches.
|
| - IPEndPoint endpoint;
|
| - int rv = transport_->socket()->GetPeerAddress(&endpoint);
|
| - if (rv != OK)
|
| - return rv;
|
| - std::string peer_id(host_and_port_.ToString());
|
| - peer_id += std::string(reinterpret_cast<const char*>(&endpoint.address()[0]),
|
| - endpoint.address().size());
|
| - // SSLSetPeerID() treats peer_id as a binary blob, and makes its
|
| - // own copy.
|
| - status = SSLSetPeerID(ssl_context_, peer_id.data(), peer_id.length());
|
| - if (status)
|
| - return NetErrorFromOSStatus(status);
|
| -
|
| - return OK;
|
| -}
|
| -
|
| -void SSLClientSocketMac::DoConnectCallback(int rv) {
|
| - DCHECK(rv != ERR_IO_PENDING);
|
| - DCHECK(!user_connect_callback_.is_null());
|
| -
|
| - CompletionCallback c = user_connect_callback_;
|
| - user_connect_callback_.Reset();
|
| - c.Run(rv > OK ? OK : rv);
|
| -}
|
| -
|
| -void SSLClientSocketMac::DoReadCallback(int rv) {
|
| - DCHECK(rv != ERR_IO_PENDING);
|
| - DCHECK(!user_read_callback_.is_null());
|
| -
|
| - // Since Run may result in Read being called, clear user_read_callback_ up
|
| - // front.
|
| - CompletionCallback c = user_read_callback_;
|
| - user_read_callback_.Reset();
|
| - user_read_buf_ = NULL;
|
| - user_read_buf_len_ = 0;
|
| - c.Run(rv);
|
| -}
|
| -
|
| -void SSLClientSocketMac::DoWriteCallback(int rv) {
|
| - DCHECK(rv != ERR_IO_PENDING);
|
| - DCHECK(!user_write_callback_.is_null());
|
| -
|
| - // Since Run may result in Write being called, clear user_write_callback_ up
|
| - // front.
|
| - CompletionCallback c = user_write_callback_;
|
| - user_write_callback_.Reset();
|
| - user_write_buf_ = NULL;
|
| - user_write_buf_len_ = 0;
|
| - c.Run(rv);
|
| -}
|
| -
|
| -void SSLClientSocketMac::OnHandshakeIOComplete(int result) {
|
| - int rv = DoHandshakeLoop(result);
|
| - if (rv != ERR_IO_PENDING) {
|
| - // If there is no connect callback available to call, we are
|
| - // renegotiating (which occurs because we are in the middle of a Read
|
| - // when the renegotiation process starts). So we complete the Read
|
| - // here.
|
| - if (user_connect_callback_.is_null()) {
|
| - DoReadCallback(rv);
|
| - return;
|
| - }
|
| - net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, rv);
|
| - DoConnectCallback(rv);
|
| - }
|
| -}
|
| -
|
| -void SSLClientSocketMac::OnTransportReadComplete(int result) {
|
| - if (result > 0) {
|
| - recv_buffer_.insert(recv_buffer_.end(),
|
| - read_io_buf_->data(),
|
| - read_io_buf_->data() + result);
|
| - }
|
| - read_io_buf_ = NULL;
|
| -
|
| - if (!completed_handshake()) {
|
| - OnHandshakeIOComplete(result);
|
| - return;
|
| - }
|
| -
|
| - if (user_read_buf_) {
|
| - if (result < 0) {
|
| - DoReadCallback(result);
|
| - return;
|
| - }
|
| - int rv = DoPayloadRead();
|
| - if (rv != ERR_IO_PENDING)
|
| - DoReadCallback(rv);
|
| - }
|
| -}
|
| -
|
| -void SSLClientSocketMac::OnTransportWriteComplete(int result) {
|
| - write_io_buf_ = NULL;
|
| -
|
| - if (result < 0) {
|
| - pending_send_error_ = result;
|
| - return;
|
| - }
|
| -
|
| - send_buffer_.erase(send_buffer_.begin(),
|
| - send_buffer_.begin() + result);
|
| - if (!send_buffer_.empty())
|
| - SSLWriteCallback(this, NULL, NULL);
|
| -
|
| - if (!completed_handshake()) {
|
| - OnHandshakeIOComplete(result);
|
| - return;
|
| - }
|
| -
|
| - // If paused because too much data is in flight, try writing again and make
|
| - // the promised callback.
|
| - if (user_write_buf_ && send_buffer_.size() < kWriteSizeResumeLimit) {
|
| - int rv = DoPayloadWrite();
|
| - if (rv != ERR_IO_PENDING)
|
| - DoWriteCallback(rv);
|
| - }
|
| -}
|
| -
|
| -int SSLClientSocketMac::DoHandshakeLoop(int last_io_result) {
|
| - DCHECK(next_handshake_state_ != STATE_NONE);
|
| - int rv = last_io_result;
|
| - do {
|
| - State state = next_handshake_state_;
|
| - next_handshake_state_ = STATE_NONE;
|
| - switch (state) {
|
| - case STATE_HANDSHAKE:
|
| - // Do the SSL/TLS handshake.
|
| - rv = DoHandshake();
|
| - break;
|
| - case STATE_VERIFY_CERT:
|
| - // Kick off server certificate validation.
|
| - rv = DoVerifyCert();
|
| - break;
|
| - case STATE_VERIFY_CERT_COMPLETE:
|
| - // Check the results of the server certificate validation.
|
| - rv = DoVerifyCertComplete(rv);
|
| - break;
|
| - case STATE_COMPLETED_RENEGOTIATION:
|
| - // The renegotiation handshake has completed, and the Read() call
|
| - // that was interrupted by the renegotiation needs to be resumed in
|
| - // order to to satisfy the original caller's request.
|
| - rv = DoCompletedRenegotiation(rv);
|
| - break;
|
| - case STATE_COMPLETED_HANDSHAKE:
|
| - next_handshake_state_ = STATE_COMPLETED_HANDSHAKE;
|
| - // This is the end of our state machine, so return.
|
| - return rv;
|
| - default:
|
| - rv = ERR_UNEXPECTED;
|
| - NOTREACHED() << "unexpected state";
|
| - break;
|
| - }
|
| - } while (rv != ERR_IO_PENDING && next_handshake_state_ != STATE_NONE);
|
| - return rv;
|
| -}
|
| -
|
| -int SSLClientSocketMac::DoHandshake() {
|
| - client_cert_requested_ = false;
|
| -
|
| - OSStatus status;
|
| - if (!renegotiating_) {
|
| - status = SSLHandshake(ssl_context_);
|
| - } else {
|
| - // Renegotiation can only be detected by a call to DoPayloadRead(),
|
| - // which means |user_read_buf_| should be valid.
|
| - DCHECK(user_read_buf_);
|
| -
|
| - // On OS X 10.5.x, SSLSetSessionOption with
|
| - // kSSLSessionOptionBreakOnServerAuth is broken for renegotiation, as
|
| - // SSLRead() does not internally handle errSSLServerAuthCompleted being
|
| - // returned during handshake. In order to support certificate validation
|
| - // after a renegotiation, SSLRead() sets |renegotiating_| to be true and
|
| - // returns errSSLWouldBlock when it detects an attempt to read the
|
| - // ServerHello after responding to a HelloRequest. It would be
|
| - // appropriate to call SSLHandshake() at this point to restart the
|
| - // handshake state machine, however, on 10.5.x, SSLHandshake() is buggy
|
| - // and will always return noErr (indicating handshake completion),
|
| - // without doing any actual work. Because of this, the only way to
|
| - // advance SecureTransport's internal handshake state machine is to
|
| - // continuously call SSLRead() until the handshake is marked complete.
|
| - // Once the handshake is completed, if it completed successfully, the
|
| - // user read callback is invoked with |bytes_read_after_renegotiation_|
|
| - // as the callback result. On 10.6.0+, both errSSLServerAuthCompleted
|
| - // and SSLHandshake() work as expected, so this strange workaround is
|
| - // only necessary while OS X 10.5.x is still supported.
|
| - bytes_read_after_renegotiation_ = 0;
|
| - status = SSLRead(ssl_context_, user_read_buf_->data(),
|
| - user_read_buf_len_, &bytes_read_after_renegotiation_);
|
| - if (bytes_read_after_renegotiation_ > 0) {
|
| - // With SecureTransport, as of 10.6.5, if application data is read,
|
| - // then the handshake should be completed. This is because
|
| - // SecureTransport does not (yet) support exchanging application data
|
| - // in the midst of handshakes. This is permitted in the TLS
|
| - // specification, as peers may exchange messages using the previous
|
| - // cipher spec up until they exchange ChangeCipherSpec messages.
|
| - // However, in addition to SecureTransport not supporting this, we do
|
| - // not permit callers to enter Read() or Write() when a handshake is
|
| - // occurring, in part due to the deception that happens in
|
| - // SSLWriteCallback(). Thus we need to make sure the handshake is
|
| - // truly completed before processing application data, and if any was
|
| - // read before the handshake is completed, it will be dropped and the
|
| - // connection aborted.
|
| - SSLSessionState session_state = kSSLIdle;
|
| - status = SSLGetSessionState(ssl_context_, &session_state);
|
| - if (session_state != kSSLConnected)
|
| - status = errSSLProtocol;
|
| - }
|
| - }
|
| -
|
| - SSLClientCertificateState client_cert_state;
|
| - if (SSLGetClientCertificateState(ssl_context_, &client_cert_state) != noErr)
|
| - client_cert_state = kSSLClientCertNone;
|
| - if (client_cert_state > kSSLClientCertNone)
|
| - client_cert_requested_ = true;
|
| -
|
| - int net_error = ERR_FAILED;
|
| - switch (status) {
|
| - case noErr:
|
| - return DidCompleteHandshake();
|
| - case errSSLWouldBlock:
|
| - next_handshake_state_ = STATE_HANDSHAKE;
|
| - return ERR_IO_PENDING;
|
| - case errSSLClosedGraceful:
|
| - // The server unexpectedly closed on us.
|
| - net_error = ERR_SSL_PROTOCOL_ERROR;
|
| - break;
|
| - case errSSLClosedAbort:
|
| - case errSSLPeerHandshakeFail:
|
| - if (client_cert_requested_) {
|
| - if (!ssl_config_.send_client_cert) {
|
| - // The server aborted, likely due to requiring a client certificate
|
| - // and one wasn't sent.
|
| - VLOG(1) << "Server requested SSL cert during handshake";
|
| - net_error = ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
|
| - } else {
|
| - // The server aborted, likely due to not liking the client
|
| - // certificate that was sent.
|
| - LOG(WARNING) << "Server aborted SSL handshake";
|
| - net_error = ERR_BAD_SSL_CLIENT_AUTH_CERT;
|
| - }
|
| - // Don't fall through - the error was intentionally remapped.
|
| - break;
|
| - }
|
| - // Fall through if a client cert wasn't requested.
|
| - default:
|
| - net_error = NetErrorFromOSStatus(status);
|
| - DCHECK(!IsCertificateError(net_error));
|
| - if (!ssl_config_.send_client_cert &&
|
| - (client_cert_state == kSSLClientCertRejected ||
|
| - net_error == ERR_BAD_SSL_CLIENT_AUTH_CERT)) {
|
| - // The server unexpectedly sent a peer certificate error alert when no
|
| - // certificate had been sent.
|
| - net_error = ERR_SSL_PROTOCOL_ERROR;
|
| - }
|
| - break;
|
| - }
|
| -
|
| - net_log_.AddEvent(NetLog::TYPE_SSL_HANDSHAKE_ERROR,
|
| - CreateNetLogSSLErrorCallback(net_error, status));
|
| - return net_error;
|
| -}
|
| -
|
| -int SSLClientSocketMac::DoVerifyCert() {
|
| - next_handshake_state_ = STATE_VERIFY_CERT_COMPLETE;
|
| -
|
| - DCHECK(server_cert_);
|
| -
|
| - VLOG(1) << "DoVerifyCert...";
|
| - CertStatus cert_status;
|
| - if (ssl_config_.IsAllowedBadCert(server_cert_, &cert_status)) {
|
| - VLOG(1) << "Received an expected bad cert with status: " << cert_status;
|
| - server_cert_verify_result_.Reset();
|
| - server_cert_verify_result_.cert_status = cert_status;
|
| - server_cert_verify_result_.verified_cert = server_cert_;
|
| - return OK;
|
| - }
|
| -
|
| - int flags = 0;
|
| - if (ssl_config_.rev_checking_enabled)
|
| - flags |= CertVerifier::VERIFY_REV_CHECKING_ENABLED;
|
| - if (ssl_config_.verify_ev_cert)
|
| - flags |= CertVerifier::VERIFY_EV_CERT;
|
| - if (ssl_config_.cert_io_enabled)
|
| - flags |= CertVerifier::VERIFY_CERT_IO_ENABLED;
|
| - verifier_.reset(new SingleRequestCertVerifier(cert_verifier_));
|
| - return verifier_->Verify(
|
| - server_cert_, host_and_port_.host(), flags,
|
| - NULL /* no CRL set */,
|
| - &server_cert_verify_result_,
|
| - base::Bind(&SSLClientSocketMac::OnHandshakeIOComplete,
|
| - base::Unretained(this)),
|
| - net_log_);
|
| -}
|
| -
|
| -int SSLClientSocketMac::DoVerifyCertComplete(int result) {
|
| - DCHECK(verifier_.get());
|
| - verifier_.reset();
|
| -
|
| - VLOG(1) << "...DoVerifyCertComplete (result=" << result << ")";
|
| - if (result == OK && client_cert_requested_ &&
|
| - !ssl_config_.send_client_cert) {
|
| - // Caller hasn't specified a client cert, so let it know the server is
|
| - // asking for one, and abort the connection.
|
| - return ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
|
| - }
|
| - VLOG(1) << "Handshake finished! (DoVerifyCertComplete)";
|
| -
|
| - if (renegotiating_) {
|
| - DidCompleteRenegotiation();
|
| - return result;
|
| - }
|
| -
|
| - // The initial handshake has completed.
|
| - next_handshake_state_ = STATE_COMPLETED_HANDSHAKE;
|
| -
|
| - return result;
|
| -}
|
| -
|
| -int SSLClientSocketMac::SetClientCert() {
|
| - if (!ssl_config_.send_client_cert || !ssl_config_.client_cert)
|
| - return noErr;
|
| -
|
| - base::mac::ScopedCFTypeRef<CFArrayRef> cert_refs(
|
| - ssl_config_.client_cert->CreateClientCertificateChain());
|
| - VLOG(1) << "SSLSetCertificate(" << CFArrayGetCount(cert_refs) << " certs)";
|
| - OSStatus result = SSLSetCertificate(ssl_context_, cert_refs);
|
| - if (result)
|
| - OSSTATUS_LOG(ERROR, result) << "SSLSetCertificate failed";
|
| - return result;
|
| -}
|
| -
|
| -int SSLClientSocketMac::DoPayloadRead() {
|
| - size_t processed = 0;
|
| - OSStatus status = SSLRead(ssl_context_, user_read_buf_->data(),
|
| - user_read_buf_len_, &processed);
|
| - if (status == errSSLWouldBlock && renegotiating_) {
|
| - CHECK_EQ(static_cast<size_t>(0), processed);
|
| - next_handshake_state_ = STATE_HANDSHAKE;
|
| - return DoHandshakeLoop(OK);
|
| - }
|
| - // There's a subtle difference here in semantics of the "would block" errors.
|
| - // In our code, ERR_IO_PENDING means the whole operation is async, while
|
| - // errSSLWouldBlock means that the stream isn't ending (and is often returned
|
| - // along with partial data). So even though "would block" is returned, if we
|
| - // have data, let's just return it. This is further complicated by the fact
|
| - // that errSSLWouldBlock is also used to short-circuit SSLRead()'s
|
| - // transparent renegotiation, so that we can update our state machine above,
|
| - // which otherwise would get out of sync with the SSLContextRef's internal
|
| - // state machine.
|
| - if (processed > 0) {
|
| - net_log_.AddByteTransferEvent(NetLog::TYPE_SSL_SOCKET_BYTES_RECEIVED,
|
| - processed, user_read_buf_->data());
|
| - return processed;
|
| - }
|
| -
|
| - switch (status) {
|
| - case errSSLClosedNoNotify:
|
| - // TODO(wtc): Unless we have received the close_notify alert, we need to
|
| - // return an error code indicating that the SSL connection ended
|
| - // uncleanly, a potential truncation attack. See http://crbug.com/18586.
|
| - return OK;
|
| -
|
| - default:
|
| - return NetErrorFromOSStatus(status);
|
| - }
|
| -}
|
| -
|
| -int SSLClientSocketMac::DoPayloadWrite() {
|
| - // Too much data in flight?
|
| - if (send_buffer_.size() > kWriteSizePauseLimit)
|
| - return ERR_IO_PENDING;
|
| -
|
| - size_t processed = 0;
|
| - OSStatus status = SSLWrite(ssl_context_,
|
| - user_write_buf_->data(),
|
| - user_write_buf_len_,
|
| - &processed);
|
| -
|
| - if (processed > 0) {
|
| - net_log_.AddByteTransferEvent(NetLog::TYPE_SSL_SOCKET_BYTES_SENT, processed,
|
| - user_write_buf_->data());
|
| - return processed;
|
| - }
|
| -
|
| - return NetErrorFromOSStatus(status);
|
| -}
|
| -
|
| -int SSLClientSocketMac::DoCompletedRenegotiation(int result) {
|
| - // The user had a read in progress, which was interrupted by the
|
| - // renegotiation. Return the application data that was processed after the
|
| - // handshake completed.
|
| - next_handshake_state_ = STATE_COMPLETED_HANDSHAKE;
|
| - if (result != OK)
|
| - return result;
|
| - return bytes_read_after_renegotiation_;
|
| -}
|
| -
|
| -void SSLClientSocketMac::DidCompleteRenegotiation() {
|
| - DCHECK(user_connect_callback_.is_null());
|
| - renegotiating_ = false;
|
| - next_handshake_state_ = STATE_COMPLETED_RENEGOTIATION;
|
| -}
|
| -
|
| -int SSLClientSocketMac::DidCompleteHandshake() {
|
| - DCHECK(!server_cert_ || renegotiating_);
|
| - VLOG(1) << "Handshake completed, next verify cert";
|
| -
|
| - scoped_refptr<X509Certificate> new_server_cert(
|
| - GetServerCert(ssl_context_));
|
| - if (!new_server_cert)
|
| - return ERR_UNEXPECTED;
|
| - net_log_.AddEvent(
|
| - NetLog::TYPE_SSL_CERTIFICATES_RECEIVED,
|
| - base::Bind(&NetLogX509CertificateCallback,
|
| - base::Unretained(new_server_cert.get())));
|
| -
|
| - if (renegotiating_ &&
|
| - X509Certificate::IsSameOSCert(server_cert_->os_cert_handle(),
|
| - new_server_cert->os_cert_handle())) {
|
| - // We already verified the server certificate. Either it is good or the
|
| - // user has accepted the certificate error.
|
| - DidCompleteRenegotiation();
|
| - } else {
|
| - server_cert_ = new_server_cert;
|
| - next_handshake_state_ = STATE_VERIFY_CERT;
|
| - }
|
| - return OK;
|
| -}
|
| -
|
| -// static
|
| -OSStatus SSLClientSocketMac::SSLReadCallback(SSLConnectionRef connection,
|
| - void* data,
|
| - size_t* data_length) {
|
| - DCHECK(data);
|
| - DCHECK(data_length);
|
| - SSLClientSocketMac* us =
|
| - const_cast<SSLClientSocketMac*>(
|
| - static_cast<const SSLClientSocketMac*>(connection));
|
| -
|
| - if (us->read_io_buf_) {
|
| - // We have I/O in flight; promise we'll get back to them and use the
|
| - // existing callback to do so.
|
| - *data_length = 0;
|
| - return errSSLWouldBlock;
|
| - }
|
| - if (us->completed_handshake()) {
|
| - // The state machine for SSLRead, located in libsecurity_ssl's
|
| - // sslTransport.c, will attempt to fully complete the renegotiation
|
| - // transparently in SSLRead once it reads the server's HelloRequest
|
| - // message. In order to make sure that the server certificate is
|
| - // (re-)verified and that any other parameters are logged (eg:
|
| - // certificate request state), we try to detect that the
|
| - // SSLClientSocketMac's state machine is out of sync with the
|
| - // SSLContext's. When that happens, we break out by faking
|
| - // errSSLWouldBlock, and set a flag so that DoPayloadRead() knows that
|
| - // it's not actually blocked. DoPayloadRead() will then restart the
|
| - // handshake state machine, and finally resume the original Read()
|
| - // once it successfully completes, similar to the behaviour of
|
| - // SSLClientSocketWin's DoDecryptPayload() and DoLoop() behave.
|
| - SSLSessionState state;
|
| - OSStatus status = SSLGetSessionState(us->ssl_context_, &state);
|
| - if (status) {
|
| - *data_length = 0;
|
| - return status;
|
| - }
|
| - if (state == kSSLHandshake) {
|
| - *data_length = 0;
|
| - us->renegotiating_ = true;
|
| - return errSSLWouldBlock;
|
| - }
|
| - }
|
| -
|
| - size_t total_read = us->recv_buffer_.size();
|
| -
|
| - int rv = 1; // any old value to spin the loop below
|
| - while (rv > 0 && total_read < *data_length) {
|
| - us->read_io_buf_ = new IOBuffer(*data_length - total_read);
|
| - rv = us->transport_->socket()->Read(
|
| - us->read_io_buf_,
|
| - *data_length - total_read,
|
| - base::Bind(&SSLClientSocketMac::OnTransportReadComplete,
|
| - base::Unretained(us)));
|
| -
|
| - if (rv >= 0) {
|
| - us->recv_buffer_.insert(us->recv_buffer_.end(),
|
| - us->read_io_buf_->data(),
|
| - us->read_io_buf_->data() + rv);
|
| - us->read_io_buf_ = NULL;
|
| - total_read += rv;
|
| - }
|
| - }
|
| -
|
| - *data_length = total_read;
|
| - if (total_read) {
|
| - memcpy(data, &us->recv_buffer_[0], total_read);
|
| - us->recv_buffer_.clear();
|
| - }
|
| -
|
| - if (rv != ERR_IO_PENDING)
|
| - us->read_io_buf_ = NULL;
|
| -
|
| - if (rv < 0)
|
| - return OSStatusFromNetError(rv);
|
| - else if (rv == 0) // stream closed
|
| - return errSSLClosedGraceful;
|
| - else
|
| - return noErr;
|
| -}
|
| -
|
| -// static
|
| -OSStatus SSLClientSocketMac::SSLWriteCallback(SSLConnectionRef connection,
|
| - const void* data,
|
| - size_t* data_length) {
|
| - SSLClientSocketMac* us =
|
| - const_cast<SSLClientSocketMac*>(
|
| - static_cast<const SSLClientSocketMac*>(connection));
|
| -
|
| - if (us->pending_send_error_ != OK) {
|
| - OSStatus status = OSStatusFromNetError(us->pending_send_error_);
|
| - us->pending_send_error_ = OK;
|
| - return status;
|
| - }
|
| -
|
| - if (data)
|
| - us->send_buffer_.insert(us->send_buffer_.end(),
|
| - static_cast<const char*>(data),
|
| - static_cast<const char*>(data) + *data_length);
|
| -
|
| - if (us->write_io_buf_) {
|
| - // If we have I/O in flight, just add the data to the end of the buffer and
|
| - // return to our caller. The existing callback will trigger the write of the
|
| - // new data when it sees that data remains in the buffer after removing the
|
| - // sent data. As always, lie to our caller.
|
| - return noErr;
|
| - }
|
| -
|
| - int rv;
|
| - do {
|
| - us->write_io_buf_ = new IOBuffer(us->send_buffer_.size());
|
| - memcpy(us->write_io_buf_->data(), &us->send_buffer_[0],
|
| - us->send_buffer_.size());
|
| - rv = us->transport_->socket()->Write(
|
| - us->write_io_buf_,
|
| - us->send_buffer_.size(),
|
| - base::Bind(&SSLClientSocketMac::OnTransportWriteComplete,
|
| - base::Unretained(us)));
|
| - if (rv > 0) {
|
| - us->send_buffer_.erase(us->send_buffer_.begin(),
|
| - us->send_buffer_.begin() + rv);
|
| - us->write_io_buf_ = NULL;
|
| - }
|
| - } while (rv > 0 && !us->send_buffer_.empty());
|
| -
|
| - if (rv < 0 && rv != ERR_IO_PENDING) {
|
| - us->write_io_buf_ = NULL;
|
| - return OSStatusFromNetError(rv);
|
| - }
|
| -
|
| - // always lie to our caller
|
| - return noErr;
|
| -}
|
| -
|
| -} // namespace net
|
|
|