summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorwtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-03-20 23:17:06 +0000
committerwtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-03-20 23:17:06 +0000
commit30c473ac6ffeabd6ba09b3710323817636f30719 (patch)
tree61abb6c9f5c86d873b3b2b7ced02df27a87178fa /net
parent9d384037786ac2912d2ffc82cd1718d8383d8d9f (diff)
downloadchromium_src-30c473ac6ffeabd6ba09b3710323817636f30719.zip
chromium_src-30c473ac6ffeabd6ba09b3710323817636f30719.tar.gz
chromium_src-30c473ac6ffeabd6ba09b3710323817636f30719.tar.bz2
Implement SSL renegotiation.
In the Windows Schannel API, a server requests renegotiation when DecryptMessage (decrypting received data) returns SEC_I_RENEGOTIATE. We need to jump to the handshake sequence, and when handshake completes, come back to reading data. I also cleaned up the code. I created the SetNextStateForRead and FreeSendBuffer functions to share common code, and made sure our handshake sequence is completely equivalent to the handshake sequence in the Platform SDK WebClient.c sample. R=rvargas BUG=6893 TEST=Visit these sites, which request SSL renegotiation: https://secure.skandiabanken.se/Skbsecure/LoginInternet/SKBLoginInternet.aspx https://secure.skandiabanken.no/SkbSecure/Authentication/Otp/Default.ashx https://www.myopenid.com/signin_certificate Review URL: http://codereview.chromium.org/42380 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@12229 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/base/ssl_client_socket_win.cc182
-rw-r--r--net/base/ssl_client_socket_win.h17
2 files changed, 122 insertions, 77 deletions
diff --git a/net/base/ssl_client_socket_win.cc b/net/base/ssl_client_socket_win.cc
index b754393..832186f 100644
--- a/net/base/ssl_client_socket_win.cc
+++ b/net/base/ssl_client_socket_win.cc
@@ -216,17 +216,20 @@ SSLClientSocketWin::SSLClientSocketWin(ClientSocket* transport_socket,
user_buf_len_(0),
next_state_(STATE_NONE),
creds_(NULL),
+ isc_status_(SEC_E_OK),
payload_send_buffer_len_(0),
bytes_sent_(0),
decrypted_ptr_(NULL),
bytes_decrypted_(0),
received_ptr_(NULL),
bytes_received_(0),
+ writing_first_token_(false),
completed_handshake_(false),
- complete_handshake_on_write_complete_(false),
ignore_ok_result_(false),
- no_client_cert_(false) {
+ no_client_cert_(false),
+ renegotiating_(false) {
memset(&stream_sizes_, 0, sizeof(stream_sizes_));
+ memset(in_buffers_, 0, sizeof(in_buffers_));
memset(&send_buffer_, 0, sizeof(send_buffer_));
memset(&ctxt_, 0, sizeof(ctxt_));
}
@@ -288,10 +291,8 @@ void SSLClientSocketWin::Disconnect() {
completed_handshake_ = false;
transport_->Disconnect();
- if (send_buffer_.pvBuffer) {
- FreeContextBuffer(send_buffer_.pvBuffer);
- memset(&send_buffer_, 0, sizeof(send_buffer_));
- }
+ if (send_buffer_.pvBuffer)
+ FreeSendBuffer();
if (ctxt_.dwLower || ctxt_.dwUpper) {
DeleteSecurityContext(&ctxt_);
memset(&ctxt_, 0, sizeof(ctxt_));
@@ -302,6 +303,8 @@ void SSLClientSocketWin::Disconnect() {
// TODO(wtc): reset more members?
bytes_decrypted_ = 0;
bytes_received_ = 0;
+ writing_first_token_ = false;
+ renegotiating_ = false;
}
bool SSLClientSocketWin::IsConnected() const {
@@ -351,12 +354,7 @@ int SSLClientSocketWin::Read(char* buf, int buf_len,
user_buf_ = buf;
user_buf_len_ = buf_len;
- if (bytes_received_ == 0) {
- next_state_ = STATE_PAYLOAD_READ;
- } else {
- next_state_ = STATE_PAYLOAD_READ_COMPLETE;
- ignore_ok_result_ = true; // OK doesn't mean EOF.
- }
+ SetNextStateForRead();
int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING)
user_callback_ = callback;
@@ -499,6 +497,7 @@ int SSLClientSocketWin::DoConnectComplete(int result) {
return MapSecurityError(status);
}
+ writing_first_token_ = true;
next_state_ = STATE_HANDSHAKE_WRITE;
return OK;
}
@@ -531,7 +530,6 @@ int SSLClientSocketWin::DoHandshakeReadComplete(int result) {
bytes_received_ += result;
// Process the contents of recv_buffer_.
- SECURITY_STATUS status;
TimeStamp expiry;
DWORD out_flags;
@@ -553,19 +551,18 @@ int SSLClientSocketWin::DoHandshakeReadComplete(int result) {
flags |= ISC_REQ_USE_SUPPLIED_CREDS;
SecBufferDesc in_buffer_desc, out_buffer_desc;
- SecBuffer in_buffers[2];
in_buffer_desc.cBuffers = 2;
- in_buffer_desc.pBuffers = in_buffers;
+ in_buffer_desc.pBuffers = in_buffers_;
in_buffer_desc.ulVersion = SECBUFFER_VERSION;
- in_buffers[0].pvBuffer = &recv_buffer_[0];
- in_buffers[0].cbBuffer = bytes_received_;
- in_buffers[0].BufferType = SECBUFFER_TOKEN;
+ in_buffers_[0].pvBuffer = recv_buffer_.get();
+ in_buffers_[0].cbBuffer = bytes_received_;
+ in_buffers_[0].BufferType = SECBUFFER_TOKEN;
- in_buffers[1].pvBuffer = NULL;
- in_buffers[1].cbBuffer = 0;
- in_buffers[1].BufferType = SECBUFFER_EMPTY;
+ in_buffers_[1].pvBuffer = NULL;
+ in_buffers_[1].cbBuffer = 0;
+ in_buffers_[1].BufferType = SECBUFFER_EMPTY;
out_buffer_desc.cBuffers = 1;
out_buffer_desc.pBuffers = &send_buffer_;
@@ -575,7 +572,7 @@ int SSLClientSocketWin::DoHandshakeReadComplete(int result) {
send_buffer_.BufferType = SECBUFFER_TOKEN;
send_buffer_.cbBuffer = 0;
- status = InitializeSecurityContext(
+ isc_status_ = InitializeSecurityContext(
creds_,
&ctxt_,
NULL,
@@ -589,43 +586,39 @@ int SSLClientSocketWin::DoHandshakeReadComplete(int result) {
&out_flags,
&expiry);
- if (status == SEC_E_INCOMPLETE_MESSAGE) {
- DCHECK(FAILED(status));
- DCHECK(send_buffer_.cbBuffer == 0 ||
- !(out_flags & ISC_RET_EXTENDED_ERROR));
- next_state_ = STATE_HANDSHAKE_READ;
+ if (send_buffer_.cbBuffer != 0 &&
+ (isc_status_ == SEC_E_OK ||
+ isc_status_ == SEC_I_CONTINUE_NEEDED ||
+ (FAILED(isc_status_) && (out_flags & ISC_RET_EXTENDED_ERROR)))) {
+ next_state_ = STATE_HANDSHAKE_WRITE;
return OK;
}
+ return DidCallInitializeSecurityContext();
+}
- if (send_buffer_.cbBuffer != 0 &&
- (status == SEC_E_OK ||
- status == SEC_I_CONTINUE_NEEDED ||
- FAILED(status) && (out_flags & ISC_RET_EXTENDED_ERROR))) {
- // If FAILED(status) is true, we should terminate the connection after
- // sending send_buffer_.
- if (status == SEC_E_OK)
- complete_handshake_on_write_complete_ = true;
- // We only handle these cases correctly.
- DCHECK(status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED);
- next_state_ = STATE_HANDSHAKE_WRITE;
- bytes_received_ = 0;
+int SSLClientSocketWin::DidCallInitializeSecurityContext() {
+ if (isc_status_ == SEC_E_INCOMPLETE_MESSAGE) {
+ next_state_ = STATE_HANDSHAKE_READ;
return OK;
}
- if (status == SEC_E_OK) {
- if (in_buffers[1].BufferType == SECBUFFER_EXTRA) {
- // TODO(darin) need to save this data for later.
- NOTREACHED() << "should not occur for HTTPS traffic";
- return ERR_FAILED;
+ if (isc_status_ == SEC_E_OK) {
+ if (in_buffers_[1].BufferType == SECBUFFER_EXTRA) {
+ // Save this data for later.
+ memmove(recv_buffer_.get(),
+ recv_buffer_.get() + (bytes_received_ - in_buffers_[1].cbBuffer),
+ in_buffers_[1].cbBuffer);
+ bytes_received_ = in_buffers_[1].cbBuffer;
+ } else {
+ bytes_received_ = 0;
}
- bytes_received_ = 0;
return DidCompleteHandshake();
}
- if (FAILED(status))
- return MapSecurityError(status);
+ if (FAILED(isc_status_))
+ return MapSecurityError(isc_status_);
- if (status == SEC_I_INCOMPLETE_CREDENTIALS) {
+ if (isc_status_ == SEC_I_INCOMPLETE_CREDENTIALS) {
// We don't support SSL client authentication yet. For now we just set
// no_client_cert_ to true and call InitializeSecurityContext again.
no_client_cert_ = true;
@@ -634,12 +627,12 @@ int SSLClientSocketWin::DoHandshakeReadComplete(int result) {
return OK;
}
- DCHECK(status == SEC_I_CONTINUE_NEEDED);
- if (in_buffers[1].BufferType == SECBUFFER_EXTRA) {
- memmove(&recv_buffer_[0],
- &recv_buffer_[0] + (bytes_received_ - in_buffers[1].cbBuffer),
- in_buffers[1].cbBuffer);
- bytes_received_ = in_buffers[1].cbBuffer;
+ DCHECK(isc_status_ == SEC_I_CONTINUE_NEEDED);
+ if (in_buffers_[1].BufferType == SECBUFFER_EXTRA) {
+ memmove(recv_buffer_.get(),
+ recv_buffer_.get() + (bytes_received_ - in_buffers_[1].cbBuffer),
+ in_buffers_[1].cbBuffer);
+ bytes_received_ = in_buffers_[1].cbBuffer;
next_state_ = STATE_HANDSHAKE_READ_COMPLETE;
ignore_ok_result_ = true; // OK doesn't mean EOF.
return OK;
@@ -674,20 +667,21 @@ int SSLClientSocketWin::DoHandshakeWriteComplete(int result) {
if (bytes_sent_ >= static_cast<int>(send_buffer_.cbBuffer)) {
bool overflow = (bytes_sent_ > static_cast<int>(send_buffer_.cbBuffer));
- SECURITY_STATUS status = FreeContextBuffer(send_buffer_.pvBuffer);
- DCHECK(status == SEC_E_OK);
- memset(&send_buffer_, 0, sizeof(send_buffer_));
+ FreeSendBuffer();
bytes_sent_ = 0;
if (overflow) // Bug!
return ERR_UNEXPECTED;
- if (complete_handshake_on_write_complete_)
- return DidCompleteHandshake();
- next_state_ = STATE_HANDSHAKE_READ;
- } else {
- // Send the remaining bytes.
- next_state_ = STATE_HANDSHAKE_WRITE;
+ if (writing_first_token_) {
+ writing_first_token_ = false;
+ DCHECK(bytes_received_ == 0);
+ next_state_ = STATE_HANDSHAKE_READ;
+ return OK;
+ }
+ return DidCallInitializeSecurityContext();
}
+ // Send the remaining bytes.
+ next_state_ = STATE_HANDSHAKE_WRITE;
return OK;
}
@@ -703,6 +697,12 @@ int SSLClientSocketWin::DoVerifyCert() {
int SSLClientSocketWin::DoVerifyCertComplete(int result) {
LogConnectionTypeMetrics();
+ if (renegotiating_) {
+ renegotiating_ = false;
+ // Pick up where we left off. Go back to reading data.
+ if (result == OK)
+ SetNextStateForRead();
+ }
return result;
}
@@ -806,21 +806,38 @@ int SSLClientSocketWin::DoPayloadReadComplete(int result) {
}
if (status == SEC_I_RENEGOTIATE) {
- // TODO(wtc): support renegotiation.
- // Should ideally send a no_renegotiation alert to the server.
- return ERR_SSL_RENEGOTIATION_REQUESTED;
+ if (bytes_received_ != 0) {
+ // The server requested renegotiation, but there are some data yet to
+ // be decrypted. The Platform SDK WebClient.c sample doesn't handle
+ // this, so we don't know how to handle this. Assume this cannot
+ // happen.
+ LOG(ERROR) << "DecryptMessage returned SEC_I_RENEGOTIATE with a buffer "
+ << "of type SECBUFFER_EXTRA.";
+ return ERR_SSL_RENEGOTIATION_REQUESTED;
+ }
+ if (len != 0) {
+ // The server requested renegotiation, but there are some decrypted
+ // data. We can't start renegotiation until we have returned all
+ // decrypted data to the caller.
+ //
+ // This hasn't happened during testing. Assume this cannot happen even
+ // though we know how to handle this.
+ LOG(ERROR) << "DecryptMessage returned SEC_I_RENEGOTIATE with a buffer "
+ << "of type SECBUFFER_DATA.";
+ return ERR_SSL_RENEGOTIATION_REQUESTED;
+ }
+ // Jump to the handshake sequence. Will come back when the rehandshake is
+ // done.
+ renegotiating_ = true;
+ next_state_ = STATE_HANDSHAKE_READ_COMPLETE;
+ ignore_ok_result_ = true; // OK doesn't mean EOF.
+ return len;
}
// If we decrypted 0 bytes, don't report 0 bytes read, which would be
// mistaken for EOF. Continue decrypting or read more.
- if (len == 0) {
- if (bytes_received_ == 0) {
- next_state_ = STATE_PAYLOAD_READ;
- } else {
- next_state_ = STATE_PAYLOAD_READ_COMPLETE;
- ignore_ok_result_ = true; // OK doesn't mean EOF.
- }
- }
+ if (len == 0)
+ SetNextStateForRead();
return len;
}
@@ -918,7 +935,7 @@ int SSLClientSocketWin::DidCompleteHandshake() {
DLOG(ERROR) << "QueryContextAttributes failed: " << status;
return MapSecurityError(status);
}
- DCHECK(!server_cert_);
+ DCHECK(!server_cert_ || renegotiating_);
PCCERT_CONTEXT server_cert_handle = NULL;
status = QueryContextAttributes(
&ctxt_, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &server_cert_handle);
@@ -948,4 +965,19 @@ void SSLClientSocketWin::LogConnectionTypeMetrics() const {
UpdateConnectionTypeHistograms(CONNECTION_SSL_MD2_CA);
}
+void SSLClientSocketWin::SetNextStateForRead() {
+ if (bytes_received_ == 0) {
+ next_state_ = STATE_PAYLOAD_READ;
+ } else {
+ next_state_ = STATE_PAYLOAD_READ_COMPLETE;
+ ignore_ok_result_ = true; // OK doesn't mean EOF.
+ }
+}
+
+void SSLClientSocketWin::FreeSendBuffer() {
+ SECURITY_STATUS status = FreeContextBuffer(send_buffer_.pvBuffer);
+ DCHECK(status == SEC_E_OK);
+ memset(&send_buffer_, 0, sizeof(send_buffer_));
+}
+
} // namespace net
diff --git a/net/base/ssl_client_socket_win.h b/net/base/ssl_client_socket_win.h
index 0ae9374..68eb24d 100644
--- a/net/base/ssl_client_socket_win.h
+++ b/net/base/ssl_client_socket_win.h
@@ -67,8 +67,11 @@ class SSLClientSocketWin : public SSLClientSocket {
int DoPayloadWrite();
int DoPayloadWriteComplete(int result);
+ int DidCallInitializeSecurityContext();
int DidCompleteHandshake();
void LogConnectionTypeMetrics() const;
+ void SetNextStateForRead();
+ void FreeSendBuffer();
CompletionCallbackImpl<SSLClientSocketWin> io_callback_;
scoped_ptr<ClientSocket> transport_;
@@ -106,7 +109,9 @@ class SSLClientSocketWin : public SSLClientSocket {
CredHandle* creds_;
CtxtHandle ctxt_;
- SecBuffer send_buffer_;
+ SecBuffer in_buffers_[2]; // Input buffers for InitializeSecurityContext.
+ SecBuffer send_buffer_; // Output buffer for InitializeSecurityContext.
+ SECURITY_STATUS isc_status_; // Return value of InitializeSecurityContext.
scoped_array<char> payload_send_buffer_;
int payload_send_buffer_len_;
int bytes_sent_;
@@ -124,8 +129,13 @@ class SSLClientSocketWin : public SSLClientSocket {
char* received_ptr_; // Points to the received ciphertext in recv_buffer_
int bytes_received_; // The number of bytes of received ciphertext.
+ // True if we're writing the first token (handshake message) to the server,
+ // false if we're writing a subsequent token. After we have written a token
+ // successfully, DoHandshakeWriteComplete checks this member to set the next
+ // state.
+ bool writing_first_token_;
+
bool completed_handshake_;
- bool complete_handshake_on_write_complete_;
// Only used in the STATE_HANDSHAKE_READ_COMPLETE and
// STATE_PAYLOAD_READ_COMPLETE states. True if a 'result' argument of OK
@@ -140,6 +150,9 @@ class SSLClientSocketWin : public SSLClientSocket {
// True if the user has no client certificate.
bool no_client_cert_;
+
+ // Renegotiation is in progress.
+ bool renegotiating_;
};
} // namespace net