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(); } }
/* * 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; }
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; }
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(); }
/* * 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; }
/* * 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; }
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); }
/* * 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, ×tamp); if (status != SEC_E_OK) { NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: AcquireCredentialsHandle [fail] [%x].\n", status); return -1; } return 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"); } }
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); }
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); }
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
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; } } } }
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); }
ZStreamRWCon_SSL_Win::~ZStreamRWCon_SSL_Win() { spPSFT->DeleteSecurityContext(&fCtxtHandle); spPSFT->FreeCredentialsHandle(&fCredHandle); }
/* * 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; }
/** * 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; }
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; }