Index: net/socket/ssl_client_socket_mac.cc |
=================================================================== |
--- net/socket/ssl_client_socket_mac.cc (revision 23320) |
+++ net/socket/ssl_client_socket_mac.cc (working copy) |
@@ -241,6 +241,24 @@ |
} |
} |
+// 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); |
+ if (status != noErr) |
+ return NULL; |
+ |
+ DCHECK_GT(CFArrayGetCount(certs), 0); |
+ |
+ SecCertificateRef server_cert = static_cast<SecCertificateRef>( |
+ const_cast<void*>(CFArrayGetValueAtIndex(certs, 0))); |
+ CFRetain(server_cert); |
+ CFRelease(certs); |
+ return X509Certificate::CreateFromHandle( |
+ server_cert, X509Certificate::SOURCE_FROM_NETWORK); |
+} |
+ |
} // namespace |
//----------------------------------------------------------------------------- |
@@ -305,11 +323,24 @@ |
if (status) |
return NetErrorFromOSStatus(status); |
- status = SSLSetPeerDomainName(ssl_context_, hostname_.c_str(), |
- hostname_.length()); |
- if (status) |
- return NetErrorFromOSStatus(status); |
+ if (ssl_config_.allowed_bad_certs.empty()) { |
+ // We're going to use the default certificate verification that the system |
+ // does, and accept its answer for the cert status. |
+ status = SSLSetPeerDomainName(ssl_context_, hostname_.data(), |
+ hostname_.length()); |
+ if (status) |
+ return NetErrorFromOSStatus(status); |
+ // TODO(wtc): for now, always check revocation. |
+ server_cert_status_ = CERT_STATUS_REV_CHECKING_ENABLED; |
+ } else { |
+ // Disable certificate chain validation. We will only allow the certs in |
+ // ssl_config_.allowed_bad_certs. |
+ status = SSLSetEnableCertVerify(ssl_context_, false); |
+ if (status) |
+ return NetErrorFromOSStatus(status); |
+ } |
+ |
next_state_ = STATE_HANDSHAKE; |
int rv = DoLoop(OK); |
if (rv == ERR_IO_PENDING) |
@@ -394,26 +425,14 @@ |
ssl_info->Reset(); |
// set cert |
- CFArrayRef certs; |
- OSStatus status = SSLCopyPeerCertificates(ssl_context_, &certs); |
- if (!status) { |
- DCHECK(CFArrayGetCount(certs) > 0); |
+ ssl_info->cert = server_cert_; |
- SecCertificateRef client_cert = |
- static_cast<SecCertificateRef>( |
- const_cast<void*>(CFArrayGetValueAtIndex(certs, 0))); |
- CFRetain(client_cert); |
- ssl_info->cert = X509Certificate::CreateFromHandle( |
- client_cert, X509Certificate::SOURCE_FROM_NETWORK); |
- CFRelease(certs); |
- } |
- |
// update status |
ssl_info->cert_status = server_cert_status_; |
// security info |
SSLCipherSuite suite; |
- status = SSLGetNegotiatedCipher(ssl_context_, &suite); |
+ OSStatus status = SSLGetNegotiatedCipher(ssl_context_, &suite); |
if (!status) |
ssl_info->security_bits = KeySizeOfCipherSuite(suite); |
} |
@@ -485,26 +504,36 @@ |
int SSLClientSocketMac::DoHandshake() { |
OSStatus status = SSLHandshake(ssl_context_); |
+ int net_error = NetErrorFromOSStatus(status); |
- if (status == errSSLWouldBlock) |
+ if (status == errSSLWouldBlock) { |
next_state_ = STATE_HANDSHAKE; |
+ } else if (status == noErr) { |
+ completed_handshake_ = true; // We have a connection. |
- if (status == noErr) |
- completed_handshake_ = true; |
+ server_cert_ = GetServerCert(ssl_context_); |
+ DCHECK(server_cert_); |
+ if (!ssl_config_.allowed_bad_certs.empty()) { |
+ // Check server_cert_ because SecureTransport didn't verify it. |
+ // TODO(wtc): If server_cert_ is not one of the allowed bad certificates, |
+ // we should verify server_cert_ ourselves. Since we don't know how to |
+ // do that yet, treat it as an invalid certificate. |
+ net_error = ERR_CERT_INVALID; |
+ server_cert_status_ |= CERT_STATUS_INVALID; |
- int net_error = NetErrorFromOSStatus(status); |
- |
- // At this point we have a connection. For now, we're going to use the default |
- // certificate verification that the system does, and accept its answer for |
- // the cert status. In the future, we'll need to call SSLSetEnableCertVerify |
- // to disable cert verification and do the verification ourselves. This allows |
- // very fine-grained control over what we'll accept for certification. |
- // TODO(avi): ditto |
- |
- // TODO(wtc): for now, always check revocation. |
- server_cert_status_ = CERT_STATUS_REV_CHECKING_ENABLED; |
- if (net_error) |
+ for (size_t i = 0; i < ssl_config_.allowed_bad_certs.size(); ++i) { |
+ if (server_cert_ == ssl_config_.allowed_bad_certs[i].cert) { |
+ net_error = OK; |
+ server_cert_status_ = ssl_config_.allowed_bad_certs[i].cert_status; |
+ break; |
+ } |
+ } |
+ } |
+ } else if (IsCertStatusError(net_error)) { |
+ server_cert_ = GetServerCert(ssl_context_); |
+ DCHECK(server_cert_); |
server_cert_status_ |= MapNetErrorToCertStatus(net_error); |
+ } |
return net_error; |
} |