Chromium Code Reviews| 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 |
| index d6fca9b48bd60d01534c0321e03a88108c754971..6ef573c1939c4195948a1bb782eab5ee3f3d9a0d 100644 |
| --- a/net/socket/ssl_client_socket_mac.cc |
| +++ b/net/socket/ssl_client_socket_mac.cc |
| @@ -9,6 +9,8 @@ |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| +#include <algorithm> |
| + |
| #include "base/mac/scoped_cftyperef.h" |
| #include "base/singleton.h" |
| #include "base/string_util.h" |
| @@ -21,6 +23,7 @@ |
| #include "net/base/ssl_connection_status_flags.h" |
| #include "net/base/ssl_info.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. |
| // |
| @@ -143,6 +146,7 @@ int NetErrorFromOSStatus(OSStatus status) { |
| switch (status) { |
| case errSSLWouldBlock: |
| return ERR_IO_PENDING; |
| + case paramErr: |
| case errSSLBadCipherSuite: |
| case errSSLBadConfiguration: |
| return ERR_INVALID_ARGUMENT; |
| @@ -200,13 +204,13 @@ int NetErrorFromOSStatus(OSStatus status) { |
| LOG(WARNING) << "Server rejected client cert (OSStatus=" << status << ")"; |
| 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 errSSLNegotiation: |
| case errSSLSessionNotFound: |
| default: |
| LOG(WARNING) << "Unknown error " << status << |
| @@ -448,20 +452,33 @@ FNTYPE LookupFunction(CFStringRef bundleName, CFStringRef fnName) { |
| CFBundleGetFunctionPointerForName(bundle, fnName)); |
| } |
| -// A class that wraps an array of enabled cipher suites that can be passed to |
| -// SSLSetEnabledCiphers. |
| -// |
| -// Used as a singleton. |
| +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: |
| - EnabledCipherSuites(); |
| - |
| - const SSLCipherSuite* ciphers() const { |
| - return ciphers_.empty() ? NULL : &ciphers_[0]; |
| - } |
| - size_t num_ciphers() const { return ciphers_.size(); } |
| + const std::vector<SSLCipherSuite>& ciphers() const { return ciphers_; } |
| private: |
| + friend struct DefaultSingletonTraits<EnabledCipherSuites>; |
| + EnabledCipherSuites(); |
| + ~EnabledCipherSuites() {} |
| + |
| std::vector<SSLCipherSuite> ciphers_; |
| DISALLOW_COPY_AND_ASSIGN(EnabledCipherSuites); |
| @@ -520,6 +537,11 @@ SSLClientSocketMac::SSLClientSocketMac(ClientSocketHandle* transport_socket, |
| ssl_context_(NULL), |
| 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() { |
| @@ -761,10 +783,22 @@ int SSLClientSocketMac::InitializeSSLContext() { |
| if (status) |
| return NetErrorFromOSStatus(status); |
| - const EnabledCipherSuites* enabled_ciphers = |
| - Singleton<EnabledCipherSuites>::get(); |
| - status = SSLSetEnabledCiphers(ssl_context_, enabled_ciphers->ciphers(), |
| - enabled_ciphers->num_ciphers()); |
| + std::vector<SSLCipherSuite> enabled_ciphers = |
| + Singleton<EnabledCipherSuites>::get()->ciphers(); |
|
wtc
2010/11/10 00:43:37
My concern about the sorted disabled cipher list i
|
| + |
| + 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); |
| @@ -970,39 +1004,50 @@ int SSLClientSocketMac::DoHandshake() { |
| 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; |
| - break; |
| + return ERR_IO_PENDING; |
| case errSSLClosedGraceful: |
| // The server unexpectedly closed on us. |
| - return ERR_SSL_PROTOCOL_ERROR; |
| + net_error = ERR_SSL_PROTOCOL_ERROR; |
| + break; |
| case errSSLClosedAbort: |
| case errSSLPeerHandshakeFail: |
| if (client_cert_requested_) { |
| - // See if the server aborted due to client cert checking. |
| 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"; |
| - return ERR_SSL_CLIENT_AUTH_CERT_NEEDED; |
| + 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; |
| } |
| - LOG(WARNING) << "Server aborted SSL handshake"; |
| - return 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; |
| } |
| - int 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; |
| - } |
| + net_log_.AddEvent(NetLog::TYPE_SSL_HANDSHAKE_ERROR, |
| + new SSLErrorParams(net_error, status)); |
| return net_error; |
| } |