void ClientSslAsynchIO::negotiateStep()

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);
    }
}