in src/qpid/sys/windows/SslAsynchIO.cpp [560:653]
void ClientSslAsynchIO::negotiateStep(BufferBase* buff) {
// SEC_CHAR is non-const, so do all the typing here.
SEC_CHAR *host = const_cast<SEC_CHAR *>(serverHost.c_str());
ULONG ctxtRequested = ISC_REQ_STREAM | ISC_REQ_USE_SUPPLIED_CREDS;
ULONG ctxtAttrs;
// tokenBuffs describe the buffer that's coming in. It should have
// a token from the SSL server.
SecBuffer tokenBuffs[2];
tokenBuffs[0].cbBuffer = buff ? buff->dataCount : 0;
tokenBuffs[0].BufferType = SECBUFFER_TOKEN;
tokenBuffs[0].pvBuffer = buff ? buff->bytes : 0;
tokenBuffs[1].cbBuffer = 0;
tokenBuffs[1].BufferType = SECBUFFER_EMPTY;
tokenBuffs[1].pvBuffer = 0;
SecBufferDesc tokenBuffDesc;
tokenBuffDesc.ulVersion = SECBUFFER_VERSION;
tokenBuffDesc.cBuffers = 2;
tokenBuffDesc.pBuffers = tokenBuffs;
// Need a buffer to receive any token to send back to the server.
BufferBase *sendbuff = aio->getQueuedBuffer();
// sendBuffs gets information to forward to the peer.
SecBuffer sendBuffs[2];
sendBuffs[0].cbBuffer = sendbuff->byteCount;
sendBuffs[0].BufferType = SECBUFFER_TOKEN;
sendBuffs[0].pvBuffer = sendbuff->bytes;
sendBuffs[1].cbBuffer = 0;
sendBuffs[1].BufferType = SECBUFFER_EMPTY;
sendBuffs[1].pvBuffer = 0;
SecBufferDesc sendBuffDesc;
sendBuffDesc.ulVersion = SECBUFFER_VERSION;
sendBuffDesc.cBuffers = 2;
sendBuffDesc.pBuffers = sendBuffs;
SECURITY_STATUS status = ::InitializeSecurityContext(&credHandle,
&ctxtHandle,
host,
ctxtRequested,
0,
0,
&tokenBuffDesc,
0,
NULL,
&sendBuffDesc,
&ctxtAttrs,
NULL);
if (status == SEC_E_INCOMPLETE_MESSAGE) {
// Not enough - get more data from the server then try again.
aio->unread(buff);
aio->queueReadBuffer(sendbuff); // Don't need this one for now...
return;
}
// Done with the buffer that came in...
if (buff)
aio->queueReadBuffer(buff);
if (status == SEC_I_CONTINUE_NEEDED) {
// check if server has requested a client certificate
if (!clientCertRequested) {
SecPkgContext_IssuerListInfoEx caList;
memset(&caList, 0, sizeof(caList));
::QueryContextAttributes(&ctxtHandle, SECPKG_ATTR_ISSUER_LIST_EX, &caList);
if (caList.cIssuers > 0)
clientCertRequested = true;
if (caList.aIssuers)
::FreeContextBuffer(caList.aIssuers);
}
sendbuff->dataCount = sendBuffs[0].cbBuffer;
aio->queueWrite(sendbuff);
return;
}
// Nothing to send back to the server...
aio->queueReadBuffer(sendbuff);
if (status == SEC_E_OK && unsafeNegotiatedTlsVersion(ctxtHandle)) {
// Refuse a connection that negotiates to less than TLS 1.0.
QPID_LOG(notice, "client SSL negotiation to unsafe protocol version.");
status = SEC_E_UNSUPPORTED_FUNCTION;
}
// SEC_I_CONTEXT_EXPIRED means session stop complete; SEC_E_OK can be
// either session stop or negotiation done (session up).
if (status == SEC_E_OK || status == SEC_I_CONTEXT_EXPIRED)
negotiationDone();
else {
if (clientCertRequested && status == SEC_E_CERT_UNKNOWN)
// ISC_REQ_USE_SUPPLIED_CREDS makes us reponsible for this case
// (no client cert). Map it to its counterpart:
status = SEC_E_INCOMPLETE_CREDENTIALS;
negotiationFailed(status);
}
}