summaryrefslogtreecommitdiffstats
path: root/net/socket
diff options
context:
space:
mode:
authorsnej@chromium.org <snej@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-18 22:45:05 +0000
committersnej@chromium.org <snej@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-18 22:45:05 +0000
commit3e1fc8ea295d450e864b8e156617399f4f2f672f (patch)
tree25cf9b717968f6236d0ce467ac2fc63411ef7867 /net/socket
parente5430c9883c5196cf768b4c9998d1e5209ed7606 (diff)
downloadchromium_src-3e1fc8ea295d450e864b8e156617399f4f2f672f.zip
chromium_src-3e1fc8ea295d450e864b8e156617399f4f2f672f.tar.gz
chromium_src-3e1fc8ea295d450e864b8e156617399f4f2f672f.tar.bz2
Client-side SSL cert support for Mac.
This includes sending an existing identity cert, and asking the user which cert to use. Doesn't yet handle SSL renegotiation, or key-gen. BUG=16831 TEST=none Review URL: http://codereview.chromium.org/604067 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@39389 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/socket')
-rw-r--r--net/socket/ssl_client_socket_mac.cc177
-rw-r--r--net/socket/ssl_client_socket_mac.h2
2 files changed, 123 insertions, 56 deletions
diff --git a/net/socket/ssl_client_socket_mac.cc b/net/socket/ssl_client_socket_mac.cc
index 2fb43d1..c9c2123 100644
--- a/net/socket/ssl_client_socket_mac.cc
+++ b/net/socket/ssl_client_socket_mac.cc
@@ -13,6 +13,7 @@
#include "net/base/io_buffer.h"
#include "net/base/load_log.h"
#include "net/base/net_errors.h"
+#include "net/base/ssl_cert_request_info.h"
#include "net/base/ssl_info.h"
// Welcome to Mac SSL. We've been waiting for you.
@@ -93,19 +94,21 @@ namespace net {
namespace {
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
// Declarations needed to call the 10.5.7 and later SSLSetSessionOption()
// function when building with the 10.5.0 SDK.
typedef enum {
- kSSLSessionOptionBreakOnServerAuthFlag
+ kSSLSessionOptionBreakOnServerAuth,
+ kSSLSessionOptionBreakOnCertRequested,
} SSLSetSessionOptionType;
enum {
- errSSLServerAuthCompletedFlag = -9841
+ errSSLServerAuthCompleted = -9841,
+ errSSLClientCertRequested = -9842,
};
// When compiled against the Mac OS X 10.5 SDK, define symbolic constants for
// cipher suites added in Mac OS X 10.6.
-#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
enum {
// ECC cipher suites from RFC 4492.
TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001,
@@ -173,13 +176,17 @@ int NetErrorFromOSStatus(OSStatus status) {
case errSSLXCertChainInvalid:
case errSSLBadCert:
return ERR_CERT_INVALID;
- case errSSLPeerCertRevoked:
- return ERR_CERT_REVOKED;
case errSSLClosedGraceful:
case noErr:
return OK;
+ case errSSLPeerCertUnknown...errSSLPeerBadCert:
+ case errSSLPeerInsufficientSecurity...errSSLPeerUnknownCA:
+ // (Note that all errSSLPeer* codes indicate errors reported by the
+ // peer, so the cert-related ones refer to my _client_ cert.)
+ return ERR_BAD_SSL_CLIENT_AUTH_CERT;
+
case errSSLBadRecordMac:
case errSSLBufferOverflow:
case errSSLDecryptionFail:
@@ -425,6 +432,16 @@ X509Certificate* GetServerCert(SSLContextRef ssl_context) {
return x509_cert;
}
+// 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));
+}
+
// A class that wraps an array of enabled cipher suites that can be passed to
// SSLSetEnabledCiphers.
//
@@ -634,7 +651,10 @@ void SSLClientSocketMac::GetSSLInfo(SSLInfo* ssl_info) {
void SSLClientSocketMac::GetSSLCertRequestInfo(
SSLCertRequestInfo* cert_request_info) {
- // TODO(wtc): implement this.
+ // I'm being asked for available client certs (identities).
+ cert_request_info->host_and_port = hostname_;
+ cert_request_info->client_certs.clear();
+ X509Certificate::GetSSLClientCertificates(&cert_request_info->client_certs);
}
SSLClientSocket::NextProtoStatus
@@ -706,42 +726,46 @@ int SSLClientSocketMac::InitializeSSLContext() {
// the session would be cached before we verified the certificate, leaving
// the potential for a session in which the certificate failed to validate
// to still be able to be resumed.
- CFBundleRef bundle =
- CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security"));
- if (bundle) {
- SSLSetSessionOptionFuncPtr ssl_set_session_options =
- reinterpret_cast<SSLSetSessionOptionFuncPtr>(
- CFBundleGetFunctionPointerForName(bundle,
- CFSTR("SSLSetSessionOption")));
- if (ssl_set_session_options) {
+ SSLSetSessionOptionFuncPtr ssl_set_session_options =
+ LookupFunction<SSLSetSessionOptionFuncPtr>(CFSTR("com.apple.security"),
+ CFSTR("SSLSetSessionOption"));
+ if (ssl_set_session_options) {
+ status = ssl_set_session_options(ssl_context_,
+ kSSLSessionOptionBreakOnServerAuth,
+ true);
+ if (!status)
status = ssl_set_session_options(ssl_context_,
- kSSLSessionOptionBreakOnServerAuthFlag,
+ kSSLSessionOptionBreakOnCertRequested,
true);
+ if (status)
+ return NetErrorFromOSStatus(status);
+
+ // 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.
+ struct sockaddr_storage addr;
+ socklen_t addr_length = sizeof(struct sockaddr_storage);
+ memset(&addr, 0, sizeof(addr));
+ if (!transport_->GetPeerName(reinterpret_cast<struct sockaddr*>(&addr),
+ &addr_length)) {
+ // Assemble the socket hostname and address into a single buffer.
+ std::vector<char> peer_id(hostname_.begin(), hostname_.end());
+ peer_id.insert(peer_id.end(), reinterpret_cast<char*>(&addr),
+ reinterpret_cast<char*>(&addr) + addr_length);
+
+ // SSLSetPeerID() treats peer_id as a binary blob, and makes its
+ // own copy.
+ status = SSLSetPeerID(ssl_context_, &peer_id[0], peer_id.size());
if (status)
return NetErrorFromOSStatus(status);
-
- // 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.
- struct sockaddr_storage addr;
- socklen_t addr_length = sizeof(struct sockaddr_storage);
- memset(&addr, 0, sizeof(addr));
- if (!transport_->GetPeerName(reinterpret_cast<struct sockaddr*>(&addr),
- &addr_length)) {
- // Assemble the socket hostname and address into a single buffer.
- std::vector<char> peer_id(hostname_.begin(), hostname_.end());
- peer_id.insert(peer_id.end(), reinterpret_cast<char*>(&addr),
- reinterpret_cast<char*>(&addr) + addr_length);
-
- // SSLSetPeerID() treats peer_id as a binary blob, and makes its
- // own copy.
- status = SSLSetPeerID(ssl_context_, &peer_id[0], peer_id.size());
- if (status)
- return NetErrorFromOSStatus(status);
- }
}
+ } else {
+ // If I can't break on cert-requested, then set the cert up-front:
+ status = SetClientCert();
+ if (status)
+ return NetErrorFromOSStatus(status);
}
return OK;
@@ -877,14 +901,14 @@ int SSLClientSocketMac::DoHandshakeStart() {
if (status == errSSLWouldBlock)
next_handshake_state_ = STATE_HANDSHAKE_START;
- if (status == noErr || status == errSSLServerAuthCompletedFlag) {
+ if (status == noErr || status == errSSLServerAuthCompleted) {
// TODO(hawk): we verify the certificate chain even on resumed sessions
// so that we have the certificate status (valid, expired but overridden
// by the user, EV, etc.) available. Eliminate this step once we have
// a certificate validation result cache.
next_handshake_state_ = STATE_VERIFY_CERT;
- if (status == errSSLServerAuthCompletedFlag) {
- // Override errSSLServerAuthCompletedFlag as it's not actually an error,
+ if (status == errSSLServerAuthCompleted) {
+ // Override errSSLServerAuthCompleted as it's not actually an error,
// but rather an indication that we're only half way through the
// handshake.
handshake_interrupted_ = true;
@@ -946,18 +970,50 @@ int SSLClientSocketMac::DoVerifyCertComplete(int result) {
return result;
}
-int SSLClientSocketMac::DoHandshakeFinish() {
- OSStatus status = SSLHandshake(ssl_context_);
+int SSLClientSocketMac::SetClientCert() {
+ if (!ssl_config_.send_client_cert || !ssl_config_.client_cert)
+ return noErr;
- if (status == errSSLWouldBlock)
- next_handshake_state_ = STATE_HANDSHAKE_FINISH;
+ scoped_cftyperef<CFArrayRef> cert_refs(
+ ssl_config_.client_cert->CreateClientCertificateChain());
+ OSStatus result = SSLSetCertificate(ssl_context_, cert_refs);
+ if (result)
+ LOG(ERROR) << "SSLSetCertificate returned OSStatus " << result;
+ return result;
+}
- if (status == errSSLClosedGraceful)
- return ERR_SSL_PROTOCOL_ERROR;
+int SSLClientSocketMac::DoHandshakeFinish() {
+ OSStatus status = SSLHandshake(ssl_context_);
- if (status == noErr) {
- completed_handshake_ = true;
- DCHECK(next_handshake_state_ == STATE_NONE);
+ switch (status) {
+ case errSSLWouldBlock:
+ next_handshake_state_ = STATE_HANDSHAKE_FINISH;
+ break;
+ case errSSLClientCertRequested:
+ status = SetClientCert();
+ next_handshake_state_ = STATE_HANDSHAKE_FINISH;
+ break;
+ case errSSLClosedGraceful:
+ return ERR_SSL_PROTOCOL_ERROR;
+ case errSSLClosedAbort:
+ case errSSLPeerHandshakeFail: {
+ // See if the server aborted due to client cert checking.
+ SSLClientCertificateState clientState;
+ if (SSLGetClientCertificateState(ssl_context_, &clientState) == noErr &&
+ clientState > kSSLClientCertNone) {
+ if (clientState == kSSLClientCertRequested &&
+ !ssl_config_.send_client_cert)
+ return ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
+ return ERR_BAD_SSL_CLIENT_AUTH_CERT;
+ }
+ break;
+ }
+ case noErr:
+ completed_handshake_ = true;
+ DCHECK(next_handshake_state_ == STATE_NONE);
+ break;
+ default:
+ break;
}
return NetErrorFromOSStatus(status);
@@ -979,14 +1035,23 @@ int SSLClientSocketMac::DoPayloadRead() {
if (processed > 0)
return processed;
- if (status == 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;
- }
+ 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;
- return NetErrorFromOSStatus(status);
+ case errSSLServerAuthCompleted:
+ case errSSLClientCertRequested:
+ // Server wants to renegotiate, probably to ask for a client cert.
+ // We don't support this yet, so abort.
+ // TODO(snej): Work around the SecureTransport issues.
+ return ERR_BAD_SSL_CLIENT_AUTH_CERT;
+
+ default:
+ return NetErrorFromOSStatus(status);
+ }
}
int SSLClientSocketMac::DoPayloadWrite() {
diff --git a/net/socket/ssl_client_socket_mac.h b/net/socket/ssl_client_socket_mac.h
index 3176116..6b2eb48 100644
--- a/net/socket/ssl_client_socket_mac.h
+++ b/net/socket/ssl_client_socket_mac.h
@@ -71,6 +71,8 @@ class SSLClientSocketMac : public SSLClientSocket {
int DoVerifyCertComplete(int result);
int DoHandshakeFinish();
+ int SetClientCert();
+
static OSStatus SSLReadCallback(SSLConnectionRef connection,
void* data,
size_t* data_length);