Example #1
0
static void spWriteFreeFlush(SecBuffer& sb, const ZStreamW& w)
{
    if (not sb.pvBuffer)
        return;

    if (not sb.cbBuffer)
    {
        spPSFT->FreeContextBuffer(sb.pvBuffer);
        sb.pvBuffer = nullptr;
    }
    else
    {
        size_t countWritten = 0;
        w.Write(sb.pvBuffer, sb.cbBuffer, &countWritten);

        const bool shortWrite = (countWritten != sb.cbBuffer);
        sb.cbBuffer = 0;

        spPSFT->FreeContextBuffer(sb.pvBuffer);
        sb.pvBuffer = nullptr;

        if (shortWrite)
            ZStreamW::sThrowEndOfStream();
        w.Flush();
    }
}
Example #2
0
/*
 * Resets the context
 */
static void resetContext(SSPIContext * sspiContext)
{
    pSFT->DeleteSecurityContext(&(sspiContext->context));
#if defined(_MSC_VER) && _MSC_VER <= 1200
    pSFT->FreeCredentialHandle(&(sspiContext->credentials));
#else
    pSFT->FreeCredentialsHandle(&(sspiContext->credentials));
#endif
    sspiContext->continueNeeded = 0;
}
Example #3
0
static int
tds_sspi_free(TDSSOCKET * tds, struct tds_authentication * tds_auth)
{
	TDSSSPIAUTH *auth = (TDSSSPIAUTH *) tds_auth;

	sec_fn->DeleteSecurityContext(&auth->cred_ctx);
	sec_fn->FreeCredentialsHandle(&auth->cred);
	free(auth->tds_auth.packet);
	free(auth->sname);
	free(auth);
	return TDS_SUCCEED;
}
Example #4
0
bool ZStreamRWCon_SSL_Win::pConnect()
{
    SecBuffer outSB[1];
    outSB[0].cbBuffer = 0;
    outSB[0].pvBuffer = nullptr;
    outSB[0].BufferType = SECBUFFER_TOKEN;

    SecBufferDesc outSBD;
    outSBD.cBuffers = 1;
    outSBD.pBuffers = outSB;
    outSBD.ulVersion = SECBUFFER_VERSION;

    DWORD attributes;
    if (SEC_I_CONTINUE_NEEDED != spPSFT->InitializeSecurityContextA(
                &fCredHandle,
                nullptr,
                nullptr, // other party, needed if we're verifying
                spRequirements,
                0, // Reserved1
                SECURITY_NATIVE_DREP,
                nullptr,
                0, // Reserved2
                &fCtxtHandle,
                &outSBD,
                &attributes,
                nullptr))
    {
        return false;
    }

    spWriteFreeFlush(outSB[0], fStreamW);

    return this->pHandshake();
}
Example #5
0
/*
 * Wrapper arround initializeSecurityContext.  Supplies several
 * default parameters as well as logging in case of errors.
 */
static SECURITY_STATUS
initializeSecurityContext(CredHandle * credentials, CtxtHandle * context,
                          char *spn, ULONG contextReq,
                          SecBufferDesc * inBuffer, CtxtHandle * newContext,
                          SecBufferDesc * outBuffer)
{
    ULONG contextAttributes;
    SECURITY_STATUS status;

    status =
        pSFT->InitializeSecurityContextA(credentials, context, spn, contextReq,
                                        0, SECURITY_NETWORK_DREP, inBuffer, 0,
                                        newContext, outBuffer,
                                        &contextAttributes, NULL);

    if (!SEC_SUCCESS(status)) {
        if (status == SEC_E_INVALID_TOKEN) {
            NE_DEBUG(NE_DBG_HTTPAUTH,
                     "InitializeSecurityContext [fail] SEC_E_INVALID_TOKEN.\n");
        } else if (status == SEC_E_UNSUPPORTED_FUNCTION) {
            NE_DEBUG(NE_DBG_HTTPAUTH,
                     "InitializeSecurityContext [fail] SEC_E_UNSUPPORTED_FUNCTION.\n");
        } else {
            NE_DEBUG(NE_DBG_HTTPAUTH,
                     "InitializeSecurityContext [fail] [%x].\n", status);
        }
    }

    return status;
}
Example #6
0
/*
 * Query specified package for it's maximum token size.
 */
static int getMaxTokenSize(char *package, ULONG * maxTokenSize)
{
    SECURITY_STATUS status;
    SecPkgInfoA *packageSecurityInfo = NULL;

    status = pSFT->QuerySecurityPackageInfoA(package, &packageSecurityInfo);
    if (status == SEC_E_OK) {
        *maxTokenSize = packageSecurityInfo->cbMaxToken;
        if (pSFT->FreeContextBuffer(packageSecurityInfo) != SEC_E_OK) {
            NE_DEBUG(NE_DBG_HTTPAUTH,
                     "sspi: Unable to free security package info.");
        }
    } else {
        NE_DEBUG(NE_DBG_HTTPAUTH,
                 "sspi: QuerySecurityPackageInfo [failed] [%x].", status);
        return -1;
    }

    return 0;
}
Example #7
0
static int
tds_sspi_handle_next(TDSSOCKET * tds, struct tds_authentication * tds_auth, size_t len)
{
	SecBuffer in_buf, out_buf;
	SecBufferDesc in_desc, out_desc;
	SECURITY_STATUS status;
	ULONG attrs;
	TimeStamp ts;
	TDS_UCHAR *auth_buf;

	TDSSSPIAUTH *auth = (TDSSSPIAUTH *) tds_auth;

	if (len < 32 || len > NTLMBUF_LEN)
		return TDS_FAIL;

	auth_buf = (TDS_UCHAR *) malloc(len);
	if (!auth_buf)
		return TDS_FAIL;
	tds_get_n(tds, auth_buf, (int)len);

	in_desc.ulVersion  = out_desc.ulVersion  = SECBUFFER_VERSION;
	in_desc.cBuffers   = out_desc.cBuffers   = 1;
	in_desc.pBuffers   = &in_buf;
	out_desc.pBuffers   = &out_buf;

	in_buf.BufferType = SECBUFFER_TOKEN;
	in_buf.pvBuffer   = auth_buf;
	in_buf.cbBuffer   = (ULONG)len;

	out_buf.BufferType = SECBUFFER_TOKEN;
	out_buf.pvBuffer   = auth->tds_auth.packet;
	out_buf.cbBuffer   = NTLMBUF_LEN;

	status = sec_fn->InitializeSecurityContextA(&auth->cred, &auth->cred_ctx, auth->sname,
		ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONNECTION,
		0, SECURITY_NETWORK_DREP, &in_desc,
		0, &auth->cred_ctx, &out_desc,
		&attrs, &ts);

	free(auth_buf);

	if (status != SEC_E_OK)
		return TDS_FAIL;
	if (out_buf.cbBuffer == 0)
		return TDS_SUCCEED;

	tds_put_n(tds, auth->tds_auth.packet, out_buf.cbBuffer);

	return tds_flush_packet(tds);
}
Example #8
0
/*
 * Simplification wrapper arround AcquireCredentialsHandle as most of
 * the parameters do not change.
 */
static int acquireCredentialsHandle(CredHandle * credentials, char *package)
{
    SECURITY_STATUS status;
    TimeStamp timestamp;

    status =
        pSFT->AcquireCredentialsHandleA(NULL, package, SECPKG_CRED_OUTBOUND,
                                       NULL, NULL, NULL, NULL, credentials,
                                       &timestamp);

    if (status != SEC_E_OK) {
        NE_DEBUG(NE_DBG_HTTPAUTH,
                 "sspi: AcquireCredentialsHandle [fail] [%x].\n", status);
        return -1;
    }

    return 0;
}
Example #9
0
ZStreamRWCon_SSL_Win::ZStreamRWCon_SSL_Win(const ZStreamR& iStreamR, const ZStreamW& iStreamW)
    :	fStreamR(iStreamR)
    ,	fStreamW(iStreamW)
    ,	fSendOpen(true)
    ,	fReceiveOpen(true)
{
    bool iVerify = false;
    bool iCheckName = false;

    SecInvalidateHandle(&fCredHandle);
    SecInvalidateHandle(&fCtxtHandle);

    if (not spAcquireCredentials(iVerify, iCheckName, fCredHandle))
        throw runtime_error("ZStreamRWCon_SSL_Win, couldn't acquire credentials");

    if (not this->pConnect())
    {
        spPSFT->FreeCredentialsHandle(&fCredHandle);
        throw runtime_error("ZStreamRWCon_SSL_Win, couldn't handshake");
    }
}
Example #10
0
void ZStreamRWCon_SSL_Win::Imp_SendDisconnect()
{
    ZAcqMtx acq(fMtx_W);

    if (not fSendOpen)
        return;

    fSendOpen = false;

    SecBuffer outSB[1];
    outSB[0].cbBuffer = 0;
    outSB[0].pvBuffer = nullptr;
    outSB[0].BufferType= SECBUFFER_TOKEN;

    SecBufferDesc outSBD;
    outSBD.cBuffers = 1;
    outSBD.pBuffers = outSB;
    outSBD.ulVersion = SECBUFFER_VERSION;

    DWORD attributes;
    SECURITY_STATUS scRet = spPSFT->InitializeSecurityContextA(
                                &fCredHandle,
                                &fCtxtHandle,
                                nullptr,
                                spRequirements,
                                0, // Reserved1
                                SECURITY_NATIVE_DREP,
                                nullptr,
                                0, // Reserved2
                                nullptr,
                                &outSBD,
                                &attributes,
                                nullptr);

    if (FAILED(scRet))
        return;

    spWriteFreeFlush(outSB[0], fStreamW);
}
Example #11
0
static bool spAcquireCredentials(bool iVerify, bool iCheckName, CredHandle& oCredHandle)
{
    SCHANNEL_CRED theSCC = {};
    theSCC.dwVersion = SCHANNEL_CRED_VERSION;
    theSCC.grbitEnabledProtocols = SP_PROT_SSL3TLS1_CLIENTS | 0xA00;

    theSCC.dwFlags = SCH_CRED_NO_DEFAULT_CREDS;

    if (not iVerify)
        theSCC.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
    else if (not iCheckName)
        theSCC.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;

    return SEC_E_OK == spPSFT->AcquireCredentialsHandleA(
               nullptr,
               const_cast<SEC_CHAR*>(UNISP_NAME_A),
               SECPKG_CRED_OUTBOUND,
               nullptr,
               &theSCC,
               nullptr,
               nullptr,
               &oCredHandle,
               nullptr);
}
Example #12
0
namespace ZooLib {

// =================================================================================================
// MARK: - Factory functions

namespace { // anonymous

class Make_SSL
    :	public ZFunctionChain_T<ZRef<ZStreamerRWCon>, MakeSSLParam_t>
{
    virtual bool Invoke(Result_t& oResult, Param_t iParam)
    {
        oResult = new ZStreamerRWCon_SSL_Win(iParam.fStreamerR, iParam.fStreamerW);
        return true;
    }
} sMaker0;

} // anonymous namespace

// =================================================================================================
// MARK: - Helpers

static PSecurityFunctionTableA spPSFT = ::InitSecurityInterfaceA();

static void spWriteFreeFlush(SecBuffer& sb, const ZStreamW& w)
{
    if (not sb.pvBuffer)
        return;

    if (not sb.cbBuffer)
    {
        spPSFT->FreeContextBuffer(sb.pvBuffer);
        sb.pvBuffer = nullptr;
    }
    else
    {
        size_t countWritten = 0;
        w.Write(sb.pvBuffer, sb.cbBuffer, &countWritten);

        const bool shortWrite = (countWritten != sb.cbBuffer);
        sb.cbBuffer = 0;

        spPSFT->FreeContextBuffer(sb.pvBuffer);
        sb.pvBuffer = nullptr;

        if (shortWrite)
            ZStreamW::sThrowEndOfStream();
        w.Flush();
    }
}

static bool spWriteFully(const SecBuffer& sb, const ZStreamW& w)
{
    size_t countWritten;
    w.Write(sb.pvBuffer, sb.cbBuffer, &countWritten);
    return countWritten == sb.cbBuffer;
}

static bool spReadMore(vector<char>& ioBuf, const ZStreamR& r)
{
    const size_t priorSize = ioBuf.size();
    const size_t newSize = priorSize + 4096;
    ioBuf.resize(newSize);
    size_t countRead;
    r.Read(&ioBuf[priorSize], newSize - priorSize, &countRead);
    ioBuf.resize(priorSize + countRead);
    return countRead != 0;
}

static bool spAcquireCredentials(bool iVerify, bool iCheckName, CredHandle& oCredHandle)
{
    SCHANNEL_CRED theSCC = {};
    theSCC.dwVersion = SCHANNEL_CRED_VERSION;
    theSCC.grbitEnabledProtocols = SP_PROT_SSL3TLS1_CLIENTS | 0xA00;

    theSCC.dwFlags = SCH_CRED_NO_DEFAULT_CREDS;

    if (not iVerify)
        theSCC.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
    else if (not iCheckName)
        theSCC.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;

    return SEC_E_OK == spPSFT->AcquireCredentialsHandleA(
               nullptr,
               const_cast<SEC_CHAR*>(UNISP_NAME_A),
               SECPKG_CRED_OUTBOUND,
               nullptr,
               &theSCC,
               nullptr,
               nullptr,
               &oCredHandle,
               nullptr);
}

// =================================================================================================
// MARK: - ZStreamRWCon_SSL_Win

static const DWORD spRequirements =
    ISC_REQ_SEQUENCE_DETECT
    | ISC_REQ_REPLAY_DETECT
    | ISC_REQ_CONFIDENTIALITY
    | ISC_REQ_EXTENDED_ERROR
    | ISC_REQ_ALLOCATE_MEMORY
    | ISC_REQ_STREAM;

ZStreamRWCon_SSL_Win::ZStreamRWCon_SSL_Win(const ZStreamR& iStreamR, const ZStreamW& iStreamW)
    :	fStreamR(iStreamR)
    ,	fStreamW(iStreamW)
    ,	fSendOpen(true)
    ,	fReceiveOpen(true)
{
    bool iVerify = false;
    bool iCheckName = false;

    SecInvalidateHandle(&fCredHandle);
    SecInvalidateHandle(&fCtxtHandle);

    if (not spAcquireCredentials(iVerify, iCheckName, fCredHandle))
        throw runtime_error("ZStreamRWCon_SSL_Win, couldn't acquire credentials");

    if (not this->pConnect())
    {
        spPSFT->FreeCredentialsHandle(&fCredHandle);
        throw runtime_error("ZStreamRWCon_SSL_Win, couldn't handshake");
    }
}

ZStreamRWCon_SSL_Win::~ZStreamRWCon_SSL_Win()
{
    spPSFT->DeleteSecurityContext(&fCtxtHandle);
    spPSFT->FreeCredentialsHandle(&fCredHandle);
}

void ZStreamRWCon_SSL_Win::Imp_Read(void* oDest, size_t iCount, size_t* oCountRead)
{
    ZAcqMtx acq(fMtx_R);

    char* localDest = static_cast<char*>(oDest);

    while (iCount)
    {
        if (fBufferPlain.size())
        {
            // We've got some data to return.
            size_t countToRead = min(iCount, fBufferPlain.size());
            deque<char>::iterator begin = fBufferPlain.begin();
            deque<char>::iterator end = begin + countToRead;
            std::copy(begin, end, static_cast<char*>(localDest));
            fBufferPlain.erase(begin, end);
            localDest += countToRead;
            iCount -= countToRead;
        }

        if (localDest != oDest)
        {
            // We've somehow managed to read some data, just above or down
            // below, and so can bail from the loop.
            break;
        }

        if (not fReceiveOpen)
            break;

        // We pass four buffers. inSB[0] references the available encrypted
        // data. inSB[1] through inSB[3] are marked as being empty, and may
        // be modified by DecryptMessage.
        SecBuffer inSB[4];
        inSB[0].cbBuffer = fBufferEnc.size();
        inSB[0].pvBuffer = ZUtil_STL::sFirstOrNil(fBufferEnc);
        inSB[0].BufferType = SECBUFFER_DATA;

        inSB[1].BufferType = SECBUFFER_EMPTY;
        inSB[2].BufferType = SECBUFFER_EMPTY;
        inSB[3].BufferType = SECBUFFER_EMPTY;

        SecBufferDesc inSBD;
        inSBD.cBuffers = 4;
        inSBD.pBuffers = inSB;
        inSBD.ulVersion = SECBUFFER_VERSION;

        SECURITY_STATUS scRet = spPSFT->DecryptMessage(&fCtxtHandle, &inSBD, 0, nullptr);

        if (scRet == SEC_E_INCOMPLETE_MESSAGE || (FAILED(scRet) && fBufferEnc.empty()))
        {
            // fBufferEnc holds an incomplete chunk, DecryptMessage needs
            // more data before it can do the decrypt.
            if (not spReadMore(fBufferEnc, fStreamR))
            {
                // We couldn't read any more encrypted data. Mark
                // our receive as being closed.
                fReceiveOpen = false;
            }
            continue;
        }

        if (scRet == SEC_I_CONTEXT_EXPIRED)
        {
            // SSL-level disconnect has been sent by the other side.
            fReceiveOpen = false;
            continue;
        }

        if (scRet == SEC_I_RENEGOTIATE)
        {
            // We need to re-handshake.
            if (not this->pHandshake())
            {
                fReceiveOpen = false;
                fSendOpen = false;
            }
            continue;
        }

        if (FAILED(scRet))
        {
            // We failed for some other reason.
            fReceiveOpen = false;
            continue;
        }

        // If DecryptMessage did any work, inSB[0] through inSB[2] will now reference
        // the header, decrypted data and trailer. inSB[3] will indicate how many
        // bytes at the end of fBufferEnc were unused by this decrypt, and so
        // must be preserved for subsequent use.
        // This assignment of information to buffers is only something I've determined
        // by inspection, so for safety we walk through them to find the two we care
        // about -- the decrypted data and any unused encrypted data.

        SecBuffer* decrypted = nullptr;
        SecBuffer* encrypted = nullptr;

        // Pickup any decrypted data
        for (size_t x = 0; x < 4; ++x)
        {
            if (inSB[x].BufferType == SECBUFFER_DATA && ! decrypted)
                decrypted = &inSB[x];
            if (inSB[x].BufferType == SECBUFFER_EXTRA && ! encrypted)
                encrypted = &inSB[x];
        }

        // The decryption happens in-place, ie in fBufferEnc. Therefore
        // we must copy out the decrypted data before munging fBufferEnc to
        // reference only the unused data.

        if (decrypted)
        {
            // Copy some decrypted data to our destination.
            const size_t countToCopy = std::min(iCount, size_t(decrypted->cbBuffer));
            sMemCopy(localDest, decrypted->pvBuffer, countToCopy);
            localDest += countToCopy;
            iCount -= countToCopy;

            // Anything remaining we put in fBufferPlain, which must be
            // empty otherwise we wouldn't have got to this point.
            const char* data = static_cast<const char*>(decrypted->pvBuffer);
            fBufferPlain.insert(fBufferPlain.begin(),
                                data + countToCopy, data + decrypted->cbBuffer);
        }

        if (encrypted)
        {
            // There is some unused data, move it to the front of fBufferEnc,
            // and resize fBufferEnc to reference only that data.
            sMemMove(&fBufferEnc[0], encrypted->pvBuffer, encrypted->cbBuffer);
            fBufferEnc.resize(encrypted->cbBuffer);
        }
        else
        {
            // There was no unused data.
            fBufferEnc.clear();
        }
    }

    if (oCountRead)
        *oCountRead = localDest - static_cast<char*>(oDest);
}

size_t ZStreamRWCon_SSL_Win::Imp_CountReadable()
{
    return fBufferPlain.size();
}

bool ZStreamRWCon_SSL_Win::Imp_ReceiveDisconnect(double iTimeout)
{
    for (;;)
    {
        uint64 countSkipped;
        this->SkipAll(&countSkipped);
        if (0 == countSkipped)
            return true;
    }
}

void ZStreamRWCon_SSL_Win::Imp_Write(const void* iSource, size_t iCount, size_t* oCountWritten)
{
    ZAcqMtx acq(fMtx_W);

    if (oCountWritten)
        *oCountWritten = 0;

    SecPkgContext_StreamSizes theSizes;
    if (FAILED(spPSFT->QueryContextAttributesA(
                   &fCtxtHandle, SECPKG_ATTR_STREAM_SIZES, &theSizes)))
    {
        // QueryContextAttributesA really shouldn't ever fail.
        ZAssert(false);
        return;
    }

    // Local buffer that will hold the message header, cyphertext and message trailer
    vector<char> buffer(theSizes.cbHeader
                        + min(iCount, size_t(theSizes.cbMaximumMessage))
                        + theSizes.cbTrailer,
                        '\0');

    // Encryption happens in-place, copy the plaintext to the appropriate offset of the buffer.
    const size_t countToEncrypt = min(iCount, size_t(theSizes.cbMaximumMessage));
    sMemCopy(&buffer[theSizes.cbHeader], iSource, countToEncrypt);

    SecBuffer outSB[4];
    outSB[0].cbBuffer = theSizes.cbHeader;
    outSB[0].pvBuffer = &buffer[0];
    outSB[0].BufferType = SECBUFFER_STREAM_HEADER;

    outSB[1].cbBuffer = countToEncrypt;
    outSB[1].pvBuffer = &buffer[theSizes.cbHeader];
    outSB[1].BufferType = SECBUFFER_DATA;

    outSB[2].cbBuffer = theSizes.cbTrailer;
    outSB[2].pvBuffer = &buffer[theSizes.cbHeader + countToEncrypt];
    outSB[2].BufferType = SECBUFFER_STREAM_TRAILER;

    outSB[3].BufferType = SECBUFFER_EMPTY;

    SecBufferDesc outSBD;
    outSBD.pBuffers = outSB;
    outSBD.cBuffers = 4;
    outSBD.ulVersion = SECBUFFER_VERSION;

    SECURITY_STATUS result = spPSFT->EncryptMessage(&fCtxtHandle, 0, &outSBD, 0);
    if (SUCCEEDED(result))
    {
        if (spWriteFully(outSB[0], fStreamW)
                && spWriteFully(outSB[1], fStreamW)
                && spWriteFully(outSB[2], fStreamW))
        {
            if (oCountWritten)
                *oCountWritten = countToEncrypt;
            return;
        }
    }

    fSendOpen = false;
}

void ZStreamRWCon_SSL_Win::Imp_Flush()
{
    fStreamW.Flush();
}

void ZStreamRWCon_SSL_Win::Imp_SendDisconnect()
{
    ZAcqMtx acq(fMtx_W);

    if (not fSendOpen)
        return;

    fSendOpen = false;

    SecBuffer outSB[1];
    outSB[0].cbBuffer = 0;
    outSB[0].pvBuffer = nullptr;
    outSB[0].BufferType= SECBUFFER_TOKEN;

    SecBufferDesc outSBD;
    outSBD.cBuffers = 1;
    outSBD.pBuffers = outSB;
    outSBD.ulVersion = SECBUFFER_VERSION;

    DWORD attributes;
    SECURITY_STATUS scRet = spPSFT->InitializeSecurityContextA(
                                &fCredHandle,
                                &fCtxtHandle,
                                nullptr,
                                spRequirements,
                                0, // Reserved1
                                SECURITY_NATIVE_DREP,
                                nullptr,
                                0, // Reserved2
                                nullptr,
                                &outSBD,
                                &attributes,
                                nullptr);

    if (FAILED(scRet))
        return;

    spWriteFreeFlush(outSB[0], fStreamW);
}

void ZStreamRWCon_SSL_Win::Imp_Abort()
{
    // Hmm. We should really abort our source stream.
    this->Imp_SendDisconnect();
}

bool ZStreamRWCon_SSL_Win::pConnect()
{
    SecBuffer outSB[1];
    outSB[0].cbBuffer = 0;
    outSB[0].pvBuffer = nullptr;
    outSB[0].BufferType = SECBUFFER_TOKEN;

    SecBufferDesc outSBD;
    outSBD.cBuffers = 1;
    outSBD.pBuffers = outSB;
    outSBD.ulVersion = SECBUFFER_VERSION;

    DWORD attributes;
    if (SEC_I_CONTINUE_NEEDED != spPSFT->InitializeSecurityContextA(
                &fCredHandle,
                nullptr,
                nullptr, // other party, needed if we're verifying
                spRequirements,
                0, // Reserved1
                SECURITY_NATIVE_DREP,
                nullptr,
                0, // Reserved2
                &fCtxtHandle,
                &outSBD,
                &attributes,
                nullptr))
    {
        return false;
    }

    spWriteFreeFlush(outSB[0], fStreamW);

    return this->pHandshake();
}

bool ZStreamRWCon_SSL_Win::pHandshake()
{
    for (;;)
    {
        SecBuffer inSB[2];

        // Data from peer is in fBufferEnc, and passed to schannel via inSB[0].
        inSB[0].cbBuffer = fBufferEnc.size();
        inSB[0].pvBuffer = ZUtil_STL::sFirstOrNil(fBufferEnc);
        inSB[0].BufferType = SECBUFFER_TOKEN;

        // After calling schannel, any unused data in fBufferEnc is described by inSB[1].
        inSB[1].cbBuffer = 0;
        inSB[1].pvBuffer = nullptr;
        inSB[1].BufferType = SECBUFFER_EMPTY;

        SecBufferDesc inSBD;
        inSBD.cBuffers = 2;
        inSBD.pBuffers = inSB;
        inSBD.ulVersion = SECBUFFER_VERSION;

        // Will hold data to be sent to peer
        SecBuffer outSB[1];
        outSB[0].cbBuffer = 0;
        outSB[0].pvBuffer = nullptr;
        outSB[0].BufferType= SECBUFFER_TOKEN;

        SecBufferDesc outSBD;
        outSBD.cBuffers = 1;
        outSBD.pBuffers = outSB;
        outSBD.ulVersion = SECBUFFER_VERSION;

        DWORD attributes;
        const SECURITY_STATUS result = spPSFT->InitializeSecurityContextA(
                                           &fCredHandle,
                                           &fCtxtHandle,
                                           nullptr,
                                           spRequirements,
                                           0, // Reserved1
                                           SECURITY_NATIVE_DREP,
                                           &inSBD,
                                           0, // Reserved2
                                           nullptr,
                                           &outSBD,
                                           &attributes,
                                           nullptr);

        if (result == SEC_E_INCOMPLETE_MESSAGE)
        {
            if (not spReadMore(fBufferEnc, fStreamR))
                return false;
        }
        else
        {
            // Send anything that was generated.
            spWriteFreeFlush(outSB[0], fStreamW);

            if (FAILED(result))
                return false;

            // Remove consumed data.
            if (inSB[1].BufferType == SECBUFFER_EXTRA)
                fBufferEnc.erase(fBufferEnc.begin(), fBufferEnc.end() - inSB[1].cbBuffer);
            else
                fBufferEnc.clear();

            if (result == SEC_E_OK)
            {
                // Handshake completed successfully.
                return true;
            }
        }
    }
}

// =================================================================================================
// MARK: - ZStreamerRWCon_SSL_Win

ZStreamerRWCon_SSL_Win::ZStreamerRWCon_SSL_Win(
    ZRef<ZStreamerR> iStreamerR, ZRef<ZStreamerW> iStreamerW)
    :	fStreamerR(iStreamerR)
    ,	fStreamerW(iStreamerW)
    ,	fStream(fStreamerR->GetStreamR(), fStreamerW->GetStreamW())
{}

ZStreamerRWCon_SSL_Win::~ZStreamerRWCon_SSL_Win()
{}

const ZStreamRCon& ZStreamerRWCon_SSL_Win::GetStreamRCon()
{
    return fStream;
}

const ZStreamWCon& ZStreamerRWCon_SSL_Win::GetStreamWCon()
{
    return fStream;
}

ZStreamRWCon_SSL_Win& ZStreamerRWCon_SSL_Win::GetStream()
{
    return fStream;
}

} // namespace ZooLib
Example #13
0
bool ZStreamRWCon_SSL_Win::pHandshake()
{
    for (;;)
    {
        SecBuffer inSB[2];

        // Data from peer is in fBufferEnc, and passed to schannel via inSB[0].
        inSB[0].cbBuffer = fBufferEnc.size();
        inSB[0].pvBuffer = ZUtil_STL::sFirstOrNil(fBufferEnc);
        inSB[0].BufferType = SECBUFFER_TOKEN;

        // After calling schannel, any unused data in fBufferEnc is described by inSB[1].
        inSB[1].cbBuffer = 0;
        inSB[1].pvBuffer = nullptr;
        inSB[1].BufferType = SECBUFFER_EMPTY;

        SecBufferDesc inSBD;
        inSBD.cBuffers = 2;
        inSBD.pBuffers = inSB;
        inSBD.ulVersion = SECBUFFER_VERSION;

        // Will hold data to be sent to peer
        SecBuffer outSB[1];
        outSB[0].cbBuffer = 0;
        outSB[0].pvBuffer = nullptr;
        outSB[0].BufferType= SECBUFFER_TOKEN;

        SecBufferDesc outSBD;
        outSBD.cBuffers = 1;
        outSBD.pBuffers = outSB;
        outSBD.ulVersion = SECBUFFER_VERSION;

        DWORD attributes;
        const SECURITY_STATUS result = spPSFT->InitializeSecurityContextA(
                                           &fCredHandle,
                                           &fCtxtHandle,
                                           nullptr,
                                           spRequirements,
                                           0, // Reserved1
                                           SECURITY_NATIVE_DREP,
                                           &inSBD,
                                           0, // Reserved2
                                           nullptr,
                                           &outSBD,
                                           &attributes,
                                           nullptr);

        if (result == SEC_E_INCOMPLETE_MESSAGE)
        {
            if (not spReadMore(fBufferEnc, fStreamR))
                return false;
        }
        else
        {
            // Send anything that was generated.
            spWriteFreeFlush(outSB[0], fStreamW);

            if (FAILED(result))
                return false;

            // Remove consumed data.
            if (inSB[1].BufferType == SECBUFFER_EXTRA)
                fBufferEnc.erase(fBufferEnc.begin(), fBufferEnc.end() - inSB[1].cbBuffer);
            else
                fBufferEnc.clear();

            if (result == SEC_E_OK)
            {
                // Handshake completed successfully.
                return true;
            }
        }
    }
}
Example #14
0
void ZStreamRWCon_SSL_Win::Imp_Read(void* oDest, size_t iCount, size_t* oCountRead)
{
    ZAcqMtx acq(fMtx_R);

    char* localDest = static_cast<char*>(oDest);

    while (iCount)
    {
        if (fBufferPlain.size())
        {
            // We've got some data to return.
            size_t countToRead = min(iCount, fBufferPlain.size());
            deque<char>::iterator begin = fBufferPlain.begin();
            deque<char>::iterator end = begin + countToRead;
            std::copy(begin, end, static_cast<char*>(localDest));
            fBufferPlain.erase(begin, end);
            localDest += countToRead;
            iCount -= countToRead;
        }

        if (localDest != oDest)
        {
            // We've somehow managed to read some data, just above or down
            // below, and so can bail from the loop.
            break;
        }

        if (not fReceiveOpen)
            break;

        // We pass four buffers. inSB[0] references the available encrypted
        // data. inSB[1] through inSB[3] are marked as being empty, and may
        // be modified by DecryptMessage.
        SecBuffer inSB[4];
        inSB[0].cbBuffer = fBufferEnc.size();
        inSB[0].pvBuffer = ZUtil_STL::sFirstOrNil(fBufferEnc);
        inSB[0].BufferType = SECBUFFER_DATA;

        inSB[1].BufferType = SECBUFFER_EMPTY;
        inSB[2].BufferType = SECBUFFER_EMPTY;
        inSB[3].BufferType = SECBUFFER_EMPTY;

        SecBufferDesc inSBD;
        inSBD.cBuffers = 4;
        inSBD.pBuffers = inSB;
        inSBD.ulVersion = SECBUFFER_VERSION;

        SECURITY_STATUS scRet = spPSFT->DecryptMessage(&fCtxtHandle, &inSBD, 0, nullptr);

        if (scRet == SEC_E_INCOMPLETE_MESSAGE || (FAILED(scRet) && fBufferEnc.empty()))
        {
            // fBufferEnc holds an incomplete chunk, DecryptMessage needs
            // more data before it can do the decrypt.
            if (not spReadMore(fBufferEnc, fStreamR))
            {
                // We couldn't read any more encrypted data. Mark
                // our receive as being closed.
                fReceiveOpen = false;
            }
            continue;
        }

        if (scRet == SEC_I_CONTEXT_EXPIRED)
        {
            // SSL-level disconnect has been sent by the other side.
            fReceiveOpen = false;
            continue;
        }

        if (scRet == SEC_I_RENEGOTIATE)
        {
            // We need to re-handshake.
            if (not this->pHandshake())
            {
                fReceiveOpen = false;
                fSendOpen = false;
            }
            continue;
        }

        if (FAILED(scRet))
        {
            // We failed for some other reason.
            fReceiveOpen = false;
            continue;
        }

        // If DecryptMessage did any work, inSB[0] through inSB[2] will now reference
        // the header, decrypted data and trailer. inSB[3] will indicate how many
        // bytes at the end of fBufferEnc were unused by this decrypt, and so
        // must be preserved for subsequent use.
        // This assignment of information to buffers is only something I've determined
        // by inspection, so for safety we walk through them to find the two we care
        // about -- the decrypted data and any unused encrypted data.

        SecBuffer* decrypted = nullptr;
        SecBuffer* encrypted = nullptr;

        // Pickup any decrypted data
        for (size_t x = 0; x < 4; ++x)
        {
            if (inSB[x].BufferType == SECBUFFER_DATA && ! decrypted)
                decrypted = &inSB[x];
            if (inSB[x].BufferType == SECBUFFER_EXTRA && ! encrypted)
                encrypted = &inSB[x];
        }

        // The decryption happens in-place, ie in fBufferEnc. Therefore
        // we must copy out the decrypted data before munging fBufferEnc to
        // reference only the unused data.

        if (decrypted)
        {
            // Copy some decrypted data to our destination.
            const size_t countToCopy = std::min(iCount, size_t(decrypted->cbBuffer));
            sMemCopy(localDest, decrypted->pvBuffer, countToCopy);
            localDest += countToCopy;
            iCount -= countToCopy;

            // Anything remaining we put in fBufferPlain, which must be
            // empty otherwise we wouldn't have got to this point.
            const char* data = static_cast<const char*>(decrypted->pvBuffer);
            fBufferPlain.insert(fBufferPlain.begin(),
                                data + countToCopy, data + decrypted->cbBuffer);
        }

        if (encrypted)
        {
            // There is some unused data, move it to the front of fBufferEnc,
            // and resize fBufferEnc to reference only that data.
            sMemMove(&fBufferEnc[0], encrypted->pvBuffer, encrypted->cbBuffer);
            fBufferEnc.resize(encrypted->cbBuffer);
        }
        else
        {
            // There was no unused data.
            fBufferEnc.clear();
        }
    }

    if (oCountRead)
        *oCountRead = localDest - static_cast<char*>(oDest);
}
Example #15
0
ZStreamRWCon_SSL_Win::~ZStreamRWCon_SSL_Win()
{
    spPSFT->DeleteSecurityContext(&fCtxtHandle);
    spPSFT->FreeCredentialsHandle(&fCredHandle);
}
Example #16
0
/*
 * Processes received authentication tokens as well as supplies the
 * response token.
 */
int ne_sspi_authenticate(void *context, const char *base64Token, char **responseToken)
{
    SecBufferDesc outBufferDesc;
    SecBuffer outBuffer;
    int status;
    SECURITY_STATUS securityStatus;
    ULONG contextFlags;

    SSPIContext *sspiContext;
    if (initialized <= 0) {
        return -1;
    }

    status = getContext(context, &sspiContext);
    if (status) {
        return status;
    }

    /* TODO: Not sure what flags should be set. joe: this needs to be
     * driven by the ne_auth interface; the GSSAPI code needs similar
     * flags. */
    contextFlags = ISC_REQ_CONFIDENTIALITY | ISC_REQ_MUTUAL_AUTH;

    initSingleEmptyBuffer(&outBufferDesc, &outBuffer);
    status = makeBuffer(&outBufferDesc, sspiContext->maxTokenSize);
    if (status) {
        return status;
    }

    if (base64Token) {
        SecBufferDesc inBufferDesc;
        SecBuffer inBuffer;

        if (!sspiContext->continueNeeded) {
            freeBuffer(&outBufferDesc);
            NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Got an unexpected token.\n");
            return -1;
        }

        initSingleEmptyBuffer(&inBufferDesc, &inBuffer);

        status = base64ToBuffer(base64Token, &inBufferDesc);
        if (status) {
            freeBuffer(&outBufferDesc);
            return status;
        }

        securityStatus =
            initializeSecurityContext(&sspiContext->credentials,
                                      &(sspiContext->context),
                                      sspiContext->serverName, contextFlags,
                                      &inBufferDesc, &(sspiContext->context),
                                      &outBufferDesc);
        if (securityStatus == SEC_E_OK)
        {
            sspiContext->authfinished = 1;
        }
        freeBuffer(&inBufferDesc);
    } else {
        if (sspiContext->continueNeeded) {
            freeBuffer(&outBufferDesc);
            NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Expected a token from server.\n");
            return -1;
        }
        if (sspiContext->authfinished && (sspiContext->credentials.dwLower || sspiContext->credentials.dwUpper)) {
            if (sspiContext->authfinished)
            {
                freeBuffer(&outBufferDesc);
                sspiContext->authfinished = 0;
                NE_DEBUG(NE_DBG_HTTPAUTH,"sspi: failing because starting over from failed try.\n");
                return -1;
            }
            sspiContext->authfinished = 0;
        }

        /* Reset any existing context since we are starting over */
        resetContext(sspiContext);

        if (acquireCredentialsHandle
            (&sspiContext->credentials, sspiContext->mechanism) != SEC_E_OK) {
                freeBuffer(&outBufferDesc);
                NE_DEBUG(NE_DBG_HTTPAUTH,
                    "sspi: acquireCredentialsHandle failed.\n");
                return -1;
        }

        securityStatus =
            initializeSecurityContext(&sspiContext->credentials, NULL,
                                      sspiContext->serverName, contextFlags,
                                      NULL, &(sspiContext->context),
                                      &outBufferDesc);
    }

    if (securityStatus == SEC_I_COMPLETE_AND_CONTINUE
        || securityStatus == SEC_I_COMPLETE_NEEDED) {
        SECURITY_STATUS compleStatus =
            pSFT->CompleteAuthToken(&(sspiContext->context), &outBufferDesc);

        if (compleStatus != SEC_E_OK) {
            freeBuffer(&outBufferDesc);
            NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: CompleteAuthToken failed.\n");
            return -1;
        }
    }

    if (securityStatus == SEC_I_COMPLETE_AND_CONTINUE
        || securityStatus == SEC_I_CONTINUE_NEEDED) {
        sspiContext->continueNeeded = 1;
    } else {
        sspiContext->continueNeeded = 0;
    }

    if (!(securityStatus == SEC_I_COMPLETE_AND_CONTINUE
          || securityStatus == SEC_I_COMPLETE_NEEDED
          || securityStatus == SEC_I_CONTINUE_NEEDED
          || securityStatus == SEC_E_OK)) {
        NE_DEBUG(NE_DBG_HTTPAUTH,
                 "sspi: initializeSecurityContext [failed] [%x].\n",
                 securityStatus);
        freeBuffer(&outBufferDesc);
        return -1;
    }

    *responseToken = ne_base64(outBufferDesc.pBuffers->pvBuffer,
                               outBufferDesc.pBuffers->cbBuffer);
    freeBuffer(&outBufferDesc);

    return 0;
}
Example #17
0
/**
 * Build a SSPI packet to send to server
 * @param tds     A pointer to the TDSSOCKET structure managing a client/server operation.
 */
TDSAUTHENTICATION *
tds_sspi_get_auth(TDSSOCKET * tds)
{
	SecBuffer buf;
	SecBufferDesc desc;
	SECURITY_STATUS status;
	ULONG attrs;
	TimeStamp ts;
	SEC_WINNT_AUTH_IDENTITY identity;
	const char *p, *user_name, *server_name;

	TDSSSPIAUTH *auth;
	TDSCONNECTION *connection = tds->connection;

	/* check connection */
	if (!connection)
		return NULL;

	if (!tds_init_secdll())
		return NULL;

	/* parse username/password informations */
	memset(&identity, 0, sizeof(identity));
	user_name = tds_dstr_cstr(&connection->user_name);
	if ((p = strchr(user_name, '\\')) != NULL) {
		identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
		identity.Password = (void *) tds_dstr_cstr(&connection->password);
		identity.PasswordLength = tds_dstr_len(&connection->password);
		identity.Domain = (void *) user_name;
		identity.DomainLength = p - user_name;
		user_name = p + 1;
		identity.User = (void *) user_name;
		identity.UserLength = strlen(user_name);
	}

	auth = (TDSSSPIAUTH *) calloc(1, sizeof(TDSSSPIAUTH));
	if (!auth || !tds->connection)
		return NULL;

	auth->tds_auth.free = tds_sspi_free;
	auth->tds_auth.handle_next = tds_sspi_handle_next;

	/* using Negotiate system will use proper protocol (either NTLM or Kerberos) */
	if (sec_fn->AcquireCredentialsHandleA(NULL, (char *)"Negotiate", SECPKG_CRED_OUTBOUND,
		NULL, identity.Domain ? &identity : NULL,
		NULL, NULL, &auth->cred, &ts) != SEC_E_OK) {
		free(auth);
		return NULL;
	}

	/* allocate buffer */
	auth->tds_auth.packet = (TDS_UCHAR *) malloc(NTLMBUF_LEN);
	if (!auth->tds_auth.packet) {
		sec_fn->FreeCredentialsHandle(&auth->cred);
		free(auth);
		return NULL;
	}
	desc.ulVersion = SECBUFFER_VERSION;
	desc.cBuffers  = 1;
	desc.pBuffers  = &buf;

	buf.cbBuffer   = NTLMBUF_LEN;
	buf.BufferType = SECBUFFER_TOKEN;
	buf.pvBuffer   = auth->tds_auth.packet;

	/* build SPN */
	server_name = tds_dstr_cstr(&connection->server_host_name);
	if (strchr(server_name, '.') == NULL) {
		struct hostent *host = gethostbyname(server_name);
		if (host && strchr(host->h_name, '.') != NULL)
			server_name = host->h_name;
	}
	if (strchr(server_name, '.') != NULL) {
		if (asprintf(&auth->sname, "MSSQLSvc/%s:%d", server_name, connection->port) < 0) {
			free(auth->tds_auth.packet);
			sec_fn->FreeCredentialsHandle(&auth->cred);
			free(auth);
			return NULL;
		}
		tdsdump_log(TDS_DBG_NETWORK, "kerberos name %s\n", auth->sname);
	}

	status = sec_fn->InitializeSecurityContextA(&auth->cred, NULL, auth->sname,
		ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONNECTION,
		0, SECURITY_NETWORK_DREP,
		NULL, 0,
		&auth->cred_ctx, &desc,
		&attrs, &ts);

	if (status == SEC_I_COMPLETE_AND_CONTINUE || status == SEC_I_CONTINUE_NEEDED) {
		sec_fn->CompleteAuthToken(&auth->cred_ctx, &desc);
	} else if(status != SEC_E_OK) {
		free(auth->sname);
		free(auth->tds_auth.packet);
		sec_fn->FreeCredentialsHandle(&auth->cred);
		free(auth);
		return NULL;
	}

	auth->tds_auth.packet_len = buf.cbBuffer;
	return &auth->tds_auth;
}
Example #18
0
void ZStreamRWCon_SSL_Win::Imp_Write(const void* iSource, size_t iCount, size_t* oCountWritten)
{
    ZAcqMtx acq(fMtx_W);

    if (oCountWritten)
        *oCountWritten = 0;

    SecPkgContext_StreamSizes theSizes;
    if (FAILED(spPSFT->QueryContextAttributesA(
                   &fCtxtHandle, SECPKG_ATTR_STREAM_SIZES, &theSizes)))
    {
        // QueryContextAttributesA really shouldn't ever fail.
        ZAssert(false);
        return;
    }

    // Local buffer that will hold the message header, cyphertext and message trailer
    vector<char> buffer(theSizes.cbHeader
                        + min(iCount, size_t(theSizes.cbMaximumMessage))
                        + theSizes.cbTrailer,
                        '\0');

    // Encryption happens in-place, copy the plaintext to the appropriate offset of the buffer.
    const size_t countToEncrypt = min(iCount, size_t(theSizes.cbMaximumMessage));
    sMemCopy(&buffer[theSizes.cbHeader], iSource, countToEncrypt);

    SecBuffer outSB[4];
    outSB[0].cbBuffer = theSizes.cbHeader;
    outSB[0].pvBuffer = &buffer[0];
    outSB[0].BufferType = SECBUFFER_STREAM_HEADER;

    outSB[1].cbBuffer = countToEncrypt;
    outSB[1].pvBuffer = &buffer[theSizes.cbHeader];
    outSB[1].BufferType = SECBUFFER_DATA;

    outSB[2].cbBuffer = theSizes.cbTrailer;
    outSB[2].pvBuffer = &buffer[theSizes.cbHeader + countToEncrypt];
    outSB[2].BufferType = SECBUFFER_STREAM_TRAILER;

    outSB[3].BufferType = SECBUFFER_EMPTY;

    SecBufferDesc outSBD;
    outSBD.pBuffers = outSB;
    outSBD.cBuffers = 4;
    outSBD.ulVersion = SECBUFFER_VERSION;

    SECURITY_STATUS result = spPSFT->EncryptMessage(&fCtxtHandle, 0, &outSBD, 0);
    if (SUCCEEDED(result))
    {
        if (spWriteFully(outSB[0], fStreamW)
                && spWriteFully(outSB[1], fStreamW)
                && spWriteFully(outSB[2], fStreamW))
        {
            if (oCountWritten)
                *oCountWritten = countToEncrypt;
            return;
        }
    }

    fSendOpen = false;
}