//-------------------------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation, 1996 // // Description: // // Microsoft LDAP SSPI Support // // Authors: // // davidsan 05/08/96 hacked to pieces and started over // //-------------------------------------------------------------------------------------------- #include "ldappch.h" #include "ldapsspi.h" #include "lclilist.h" #include "lclixd.h" HRESULT g_hrInitSSPI; HINSTANCE g_hinstSecDll = NULL; PSecurityFunctionTable g_ptblpfnSec; //$ TODO: Possibly return more descriptive errors so clients know why SSPI //$ isn't working HRESULT HrInitializeSSPI() { char *szDll; OSVERSIONINFO ovi; INIT_SECURITY_INTERFACE pfnISI = NULL; if (g_ptblpfnSec) return NOERROR; Assert(!g_hinstSecDll); ovi.dwOSVersionInfoSize = sizeof(ovi); if (!GetVersionEx(&ovi)) return E_FAIL; if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) szDll = "security.dll"; else if (ovi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) szDll = "secur32.dll"; else return E_FAIL; g_hinstSecDll = LoadLibrary(szDll); pfnISI = (INIT_SECURITY_INTERFACE)GetProcAddress(g_hinstSecDll, SECURITY_ENTRYPOINT); if (!pfnISI) { LBail: FreeLibrary(g_hinstSecDll); g_hinstSecDll = NULL; return LDAP_E_AUTHNOTAVAIL; } g_ptblpfnSec = (*pfnISI)(); if (!g_ptblpfnSec) goto LBail; return NOERROR; } HRESULT HrTerminateSSPI() { g_ptblpfnSec = NULL; if (g_hinstSecDll) FreeLibrary(g_hinstSecDll); g_hinstSecDll = NULL; return NOERROR; } HRESULT CLdapClient::HrGetCredentials(char *szUser, char *szPass) { HRESULT hr; SECURITY_STATUS stat; TimeStamp tsLifetime; SEC_WINNT_AUTH_IDENTITY authdata; if (FAILED(g_hrInitSSPI)) return g_hrInitSSPI; Assert(g_ptblpfnSec); if (!g_ptblpfnSec) return LDAP_E_AUTHNOTAVAIL; ::EnterCriticalSection(&m_cs); if (m_fHasCred) { ::LeaveCriticalSection(&m_cs); return NOERROR; } if (szUser && szPass) { authdata.User = (BYTE *)szUser; authdata.UserLength = lstrlen(szUser); authdata.Password = (BYTE *)szPass; authdata.PasswordLength = lstrlen(szPass); authdata.Domain = (BYTE *)""; authdata.DomainLength = 0; authdata.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; } stat = (*g_ptblpfnSec->AcquireCredentialsHandle) (NULL, "MSN", //$ does this ever change? SECPKG_CRED_OUTBOUND, NULL, ((szUser && szPass) ? &authdata : NULL), NULL, NULL, &m_hCred, &tsLifetime); if (stat == SEC_E_OK) { m_fHasCred = TRUE; hr = NOERROR; } else hr = LDAP_E_AUTHNOTAVAIL; ::LeaveCriticalSection(&m_cs); return hr; } STDMETHODIMP CLdapClient::HrSendSSPINegotiate(char *szDN, char *szUser, char *szPass, BOOL fPrompt, PXID pxid) { HRESULT hr; SECURITY_STATUS stat; DWORD fContextAttrib; TimeStamp tsExpireTime; SecBufferDesc outSecDesc; SecBuffer outSecBuffer; DWORD grfReq = ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY; PXD pxd; BYTE rgb[512]; pxd = g_xl.PxdNewXaction(xtypeBindSSPINegotiate); if (!pxd) return E_OUTOFMEMORY; if (FAILED(hr = this->HrGetCredentials(szUser, szPass))) return hr; outSecDesc.ulVersion = 0; outSecDesc.cBuffers = 1; outSecDesc.pBuffers = &outSecBuffer; outSecBuffer.cbBuffer = sizeof(rgb); outSecBuffer.BufferType = SECBUFFER_TOKEN; outSecBuffer.pvBuffer = rgb; if (szUser && szPass) grfReq |= ISC_REQ_USE_SUPPLIED_CREDS; else grfReq |= ISC_REQ_PROMPT_FOR_CREDS; stat = (*g_ptblpfnSec->InitializeSecurityContext) (&m_hCred, NULL, // phCurrContext NULL, // pszTargetName grfReq, 0L, SECURITY_NATIVE_DREP, NULL, 0L, &m_hCtxt, &outSecDesc, &fContextAttrib, &tsExpireTime); if (FAILED(stat)) { //$ TODO: determine what errors InitializeSecurityContext can return and //$ return appropriate errors to client return E_FAIL; } m_fHasCtxt = TRUE; hr = HrSendBindMsg( pxd->Xid(), szDN, BIND_SSPI_NEGOTIATE, outSecBuffer.pvBuffer, outSecBuffer.cbBuffer ); if (SUCCEEDED(hr)) *pxid = pxd->Xid(); return hr; } STDMETHODIMP CLdapClient::HrGetSSPIChallenge(XID xid, BYTE *pbBuf, int cbBuf, int *pcbChallenge, DWORD timeout) { PXD pxd; BOOL fDel; HRESULT hr = NOERROR; BYTE *pbData; int cbData; LBER lber; ULONG ulTag; LONG lResult; pxd = g_xl.PxdForXid(xid); if (!pxd) return LDAP_E_INVALIDXID; if (pxd->Xtype() != xtypeBindSSPINegotiate) return LDAP_E_INVALIDXTYPE; if (pxd->FCancelled()) return LDAP_E_CANCELLED; if (pxd->FOOM()) return E_OUTOFMEMORY; if (pxd->FHasData()) fDel = TRUE; else { if (FAILED(hr = this->HrWaitForPxd(pxd, timeout, &fDel))) goto LBail; } if (!pxd->FGetBuffer(&pbData, &cbData)) { //$ what's the right error here? hr = LDAP_E_UNEXPECTEDDATA; goto LBail; } VERIFY(lber.HrLoadBer(pbData, cbData)); VERIFY(lber.HrStartReadSequence(LDAP_BIND_RES | BER_FORM_CONSTRUCTED | BER_CLASS_APPLICATION)); VERIFY(lber.HrPeekTag(&ulTag)); if (ulTag == BER_SEQUENCE) { Assert(FALSE); // i want to see if any server returns explicit sequences VERIFY(lber.HrStartReadSequence()); } VERIFY(lber.HrGetEnumValue(&lResult)); if (!lResult) { // we have 0 for success--the matchedDN field is the server's challenge. VERIFY(lber.HrGetStringLength(pcbChallenge)); if (*pcbChallenge > cbBuf) { hr = LDAP_E_BUFFERTOOSMALL; goto LBail; } VERIFY(lber.HrGetBinaryValue(pbBuf, cbBuf)); } if (ulTag == BER_SEQUENCE) { VERIFY(lber.HrEndReadSequence()); } VERIFY(lber.HrEndReadSequence()); hr = this->HrFromLdapResult(lResult); LBail: if (fDel) g_xl.RemovePxd(pxd); return hr; } STDMETHODIMP CLdapClient::HrSendSSPIResponse(BYTE *pbChallenge, int cbChallenge, PXID pxid) { HRESULT hr; SECURITY_STATUS stat; DWORD fContextAttrib; TimeStamp tsExpireTime; SecBufferDesc inSecDesc, outSecDesc; SecBuffer inSecBuffer, outSecBuffer; DWORD grfReq = ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY; PXD pxd; BYTE rgb[512]; if (!m_fHasCtxt || !m_fHasCred) return LDAP_E_OUTOFSEQUENCE; pxd = g_xl.PxdNewXaction(xtypeBind); if (!pxd) return E_OUTOFMEMORY; inSecDesc.ulVersion = 0; inSecDesc.cBuffers = 1; inSecDesc.pBuffers = &inSecBuffer; inSecBuffer.cbBuffer = cbChallenge; inSecBuffer.BufferType = SECBUFFER_TOKEN; inSecBuffer.pvBuffer = (PVOID)pbChallenge; outSecDesc.ulVersion = 0; outSecDesc.cBuffers = 1; outSecDesc.pBuffers = &outSecBuffer; outSecBuffer.cbBuffer = sizeof(rgb); outSecBuffer.BufferType = SECBUFFER_TOKEN; outSecBuffer.pvBuffer = rgb; stat = (*g_ptblpfnSec->InitializeSecurityContext) (&m_hCred, &m_hCtxt, NULL, // pszTargetName ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY, 0L, SECURITY_NATIVE_DREP, &inSecDesc, 0L, &m_hCtxt, &outSecDesc, &fContextAttrib, &tsExpireTime); if (FAILED(stat)) { //$ TODO: determine what errors InitializeSecurityContext can return and //$ return appropriate errors to client return E_FAIL; } m_fHasCtxt = TRUE; hr = HrSendBindMsg( pxd->Xid(), "", BIND_SSPI_RESPONSE, outSecBuffer.pvBuffer, outSecBuffer.cbBuffer ); if (SUCCEEDED(hr)) *pxid = pxd->Xid(); return hr; } STDMETHODIMP CLdapClient::HrBindSSPI(char *szDN, char *szUser, char *szPass, BOOL fPrompt, DWORD timeout) { XID xid; HRESULT hr; BYTE rgb[512]; int cbChallenge; if (FAILED(hr = this->HrSendSSPINegotiate(szDN, szUser, szPass, fPrompt, &xid))) return hr; if (FAILED(hr = this->HrGetSSPIChallenge(xid, rgb, sizeof(rgb), &cbChallenge, timeout))) return hr; if (FAILED(hr = this->HrSendSSPIResponse(rgb, cbChallenge, &xid))) return hr; return this->HrGetBindResponse(xid, timeout); }