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 |