You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
477 lines
12 KiB
477 lines
12 KiB
/************************************************************************************************
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
File Name: NTAuth.cpp
|
|
Abstract: Implementation of CAuthServer class to do NTLM/Kerberos authentication
|
|
Notes:
|
|
History: 10/10/2001 Created by Hao Yu (haoyu)
|
|
|
|
************************************************************************************************/
|
|
|
|
#include "stdafx.h"
|
|
#include "NTAuth.h"
|
|
#include "uuencode.h"
|
|
#include <atlbase.h>
|
|
|
|
|
|
//Initialize static members
|
|
long CAuthServer::m_glInit=0;
|
|
PSecurityFunctionTable CAuthServer::m_gpFuncs=NULL;
|
|
HINSTANCE CAuthServer::m_ghLib=NULL;
|
|
IP3Config *CAuthServer::m_gpIConfig=NULL;
|
|
|
|
//Static fuction to load SSPI Provider function table
|
|
HRESULT CAuthServer::GlobalInit()
|
|
{
|
|
HRESULT hr=S_OK;
|
|
FARPROC pInit=NULL;
|
|
PSecPkgInfo pPkgInfo;
|
|
if( 0==InterlockedCompareExchange(&m_glInit, 2, 0))
|
|
{
|
|
//Initialization needed
|
|
TCHAR tszWinDir[MAX_PATH+sizeof(NT_SEC_DLL_NAME)+1];
|
|
if(0!=GetWindowsDirectory(tszWinDir, MAX_PATH+1))
|
|
{
|
|
tszWinDir[MAX_PATH]=0;
|
|
_tcscat(tszWinDir, NT_SEC_DLL_NAME );
|
|
m_ghLib=LoadLibrary(tszWinDir);
|
|
if(NULL == m_ghLib)
|
|
{
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr=E_FAIL;
|
|
}
|
|
if(S_OK==hr)
|
|
{
|
|
pInit=GetProcAddress(m_ghLib, SECURITY_ENTRYPOINT_ANSI);
|
|
if(NULL == pInit)
|
|
{
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
|
|
if(S_OK==hr)
|
|
{
|
|
m_gpFuncs = (PSecurityFunctionTable) pInit();
|
|
if(NULL == m_gpFuncs)
|
|
{
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
|
|
if(S_OK==hr)
|
|
{
|
|
//Make sure the security package is available
|
|
if(SEC_SUCCESS(m_gpFuncs->QuerySecurityPackageInfo(NTLM_PACKAGE,
|
|
&pPkgInfo)))
|
|
{
|
|
m_gpFuncs->FreeContextBuffer(pPkgInfo);
|
|
}
|
|
else
|
|
{
|
|
hr=E_FAIL;
|
|
}
|
|
}
|
|
if(S_OK==hr)
|
|
{
|
|
//To circumvent the ADsGetObject leak
|
|
hr = CoCreateInstance( __uuidof( P3Config ), NULL, CLSCTX_ALL, __uuidof( IP3Config ),reinterpret_cast<LPVOID *>( &m_gpIConfig ));
|
|
}
|
|
|
|
if(S_OK==hr)
|
|
{
|
|
//Set the flag to be 1
|
|
InterlockedExchange(&m_glInit, 1);
|
|
}
|
|
else
|
|
{
|
|
//Cleanup
|
|
m_gpFuncs=NULL;
|
|
if(NULL != m_ghLib)
|
|
{
|
|
FreeLibrary(m_ghLib);
|
|
m_ghLib=NULL;
|
|
}
|
|
//Set the falg to be 0
|
|
InterlockedExchange(&m_glInit, 0);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
while(1!=m_glInit)
|
|
{
|
|
Sleep(50); //Wait for the initialization to be done
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
void CAuthServer::GlobalUninit()
|
|
{
|
|
|
|
long lRet=InterlockedCompareExchange(&m_glInit, -1, 1);
|
|
switch (lRet)
|
|
{
|
|
case 2:while(1!=m_glInit)
|
|
{
|
|
Sleep(50);
|
|
}
|
|
case 1:m_gpFuncs=NULL;
|
|
if(NULL != m_ghLib)
|
|
{
|
|
FreeLibrary(m_ghLib);
|
|
m_ghLib=NULL;
|
|
}
|
|
if(m_gpIConfig!=NULL)
|
|
{
|
|
m_gpIConfig->Release();
|
|
m_gpIConfig=NULL;
|
|
}
|
|
break;
|
|
case 0://Nothing needed to be done
|
|
default: break;
|
|
};
|
|
}
|
|
|
|
|
|
|
|
CAuthServer::CAuthServer()
|
|
{
|
|
|
|
m_bInit=FALSE;
|
|
m_bHaveSecContext=FALSE;
|
|
m_bFirstCall=TRUE;
|
|
|
|
}
|
|
|
|
|
|
CAuthServer::~CAuthServer()
|
|
{
|
|
Cleanup();
|
|
}
|
|
|
|
void CAuthServer::Cleanup()
|
|
{
|
|
if(NULL != m_gpFuncs)
|
|
{
|
|
|
|
if(m_bInit)
|
|
{
|
|
m_bInit=FALSE;
|
|
m_bFirstCall=TRUE;
|
|
m_gpFuncs->FreeCredentialHandle(&m_hCredHandle);
|
|
if(m_bHaveSecContext)
|
|
{
|
|
m_bHaveSecContext=FALSE;
|
|
m_gpFuncs->DeleteSecurityContext(&m_hSecContext);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//This should never happen!
|
|
g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
|
|
EVENT_POP3_UNEXPECTED_ERROR);
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT CAuthServer::InitCredential()
|
|
{
|
|
HRESULT hr=S_OK;
|
|
SECURITY_STATUS status;
|
|
TimeStamp tsExpire;
|
|
if(!m_bInit)
|
|
{
|
|
if(m_glInit!=1)
|
|
{
|
|
hr=GlobalInit();
|
|
}
|
|
if(S_OK == hr )
|
|
{
|
|
if(!m_bInit)
|
|
{
|
|
status = m_gpFuncs->AcquireCredentialsHandle(
|
|
NULL,
|
|
NTLM_PACKAGE,
|
|
SECPKG_CRED_INBOUND,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&m_hCredHandle,
|
|
&tsExpire);
|
|
if(SEC_E_OK != status)
|
|
{
|
|
hr=E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
m_bInit=TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CAuthServer::HandShake(LPBYTE pInBuf,
|
|
DWORD cbInBufSize,
|
|
LPBYTE pOutBuf,
|
|
PDWORD pcbOutBufSize)
|
|
{
|
|
HRESULT hr=S_OK;
|
|
SECURITY_STATUS status;
|
|
TimeStamp tsExpire;
|
|
ULONG ulContextAttributes;
|
|
BUFFER uuBuf;
|
|
DWORD cbDecoded=0;
|
|
BYTE pBuf[AUTH_BUF_SIZE];
|
|
SecBufferDesc OutBuffDesc;
|
|
SecBuffer OutSecBuff;
|
|
SecBufferDesc InBuffDesc;
|
|
SecBuffer InSecBuff;
|
|
if( (NULL == pInBuf) ||
|
|
(NULL == pOutBuf) ||
|
|
(NULL == pcbOutBufSize) )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
if(!m_bInit)
|
|
{
|
|
hr = InitCredential();
|
|
}
|
|
|
|
uuBuf.pBuf=pBuf;
|
|
uuBuf.cLen=AUTH_BUF_SIZE;
|
|
if( !uudecode((char *)pInBuf, &uuBuf, &cbDecoded))
|
|
{
|
|
hr=E_FAIL;
|
|
}
|
|
|
|
if( S_OK == hr )
|
|
{
|
|
|
|
OutBuffDesc.ulVersion = 0;
|
|
OutBuffDesc.cBuffers = 1;
|
|
OutBuffDesc.pBuffers = &OutSecBuff;
|
|
|
|
OutSecBuff.cbBuffer = *pcbOutBufSize;
|
|
OutSecBuff.BufferType = SECBUFFER_TOKEN;
|
|
OutSecBuff.pvBuffer = pOutBuf;
|
|
|
|
InBuffDesc.ulVersion = 0;
|
|
InBuffDesc.cBuffers = 1;
|
|
InBuffDesc.pBuffers = &InSecBuff;
|
|
|
|
InSecBuff.cbBuffer = cbDecoded;//cbInBufSize;
|
|
InSecBuff.BufferType = SECBUFFER_TOKEN;
|
|
InSecBuff.pvBuffer = pBuf;//pInBuf;
|
|
|
|
status = m_gpFuncs->AcceptSecurityContext (
|
|
&m_hCredHandle,
|
|
m_bFirstCall? NULL:&m_hSecContext,
|
|
&InBuffDesc,
|
|
0,
|
|
SECURITY_NETWORK_DREP,
|
|
&m_hSecContext,
|
|
&OutBuffDesc,
|
|
&ulContextAttributes,
|
|
&tsExpire );
|
|
if( !SEC_SUCCESS(status) )
|
|
{
|
|
hr=E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
m_bHaveSecContext=TRUE;
|
|
m_bFirstCall=FALSE;
|
|
}
|
|
|
|
|
|
|
|
switch(status)
|
|
{
|
|
|
|
case SEC_E_OK:hr=S_OK;
|
|
break;
|
|
case SEC_I_CONTINUE_NEEDED:hr=S_FALSE;
|
|
break;
|
|
case SEC_I_COMPLETE_AND_CONTINUE:hr=S_FALSE;
|
|
//Continue to do the following
|
|
case SEC_I_COMPLETE_NEEDED:if(m_gpFuncs->CompleteAuthToken)
|
|
{
|
|
status=m_gpFuncs->CompleteAuthToken(
|
|
&m_hSecContext,
|
|
&OutBuffDesc);
|
|
if( !(SEC_SUCCESS(status)) )
|
|
{
|
|
hr=E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr=E_FAIL;
|
|
}
|
|
break;
|
|
default: hr=E_FAIL;
|
|
}
|
|
|
|
}
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
uuBuf.cLen=AUTH_BUF_SIZE;
|
|
if(OutSecBuff.cbBuffer > (AUTH_BUF_SIZE-5)*2/3)
|
|
{
|
|
//This is the case where the buffer is not big
|
|
// enough
|
|
hr=E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
if(uuencode(pOutBuf, OutSecBuff.cbBuffer, &uuBuf))
|
|
{
|
|
pOutBuf[AUTH_BUF_SIZE-1]=0;
|
|
if( 0>_snprintf((char *)pOutBuf,
|
|
AUTH_BUF_SIZE-1,
|
|
"+ %s\r\n",
|
|
(char *)(uuBuf.pBuf)) )
|
|
{
|
|
hr=E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
*pcbOutBufSize=strlen((char *)pOutBuf);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr=E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
if(FAILED(hr))
|
|
{
|
|
if(m_bHaveSecContext)
|
|
{
|
|
m_gpFuncs->DeleteSecurityContext(&m_hSecContext);
|
|
m_bHaveSecContext=FALSE;
|
|
m_bFirstCall=TRUE;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CAuthServer::GetUserName(WCHAR *wszUserName)
|
|
{
|
|
SecPkgContext_Names SecUserName;
|
|
SECURITY_STATUS status;
|
|
WCHAR *pUserName=NULL;
|
|
WCHAR *pAt=NULL;
|
|
VARIANT var;
|
|
VariantInit(&var);
|
|
HRESULT hr=S_OK;
|
|
if(NULL==wszUserName)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
if(!m_bHaveSecContext)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
status=QueryContextAttributes( &m_hSecContext,
|
|
SECPKG_ATTR_NAMES,
|
|
&SecUserName);
|
|
if(SEC_E_OK != status)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
if(AUTH_AD==g_dwAuthMethod)
|
|
{
|
|
|
|
var.vt=VT_BSTR;
|
|
var.bstrVal=SysAllocString(SecUserName.sUserName);
|
|
if(NULL == var.bstrVal)
|
|
{
|
|
var.vt=VT_EMPTY;
|
|
hr=E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
hr=g_pAuthMethod->Get(SZ_EMAILADDR, &var);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
if(wcslen(var.bstrVal) < POP3_MAX_ADDRESS_LENGTH)
|
|
{
|
|
wcscpy(wszUserName, var.bstrVal);
|
|
}
|
|
else
|
|
{
|
|
hr=E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
else //SAM case
|
|
{
|
|
|
|
pAt=wcschr(SecUserName.sUserName, L'\\');
|
|
if(NULL == pAt)
|
|
{
|
|
pUserName=SecUserName.sUserName;
|
|
}
|
|
else
|
|
{
|
|
pUserName=pAt+1;
|
|
}
|
|
CComPtr<IP3Config> spIConfig;
|
|
CComPtr<IP3Domains> spIDomains;
|
|
hr = CoCreateInstance( __uuidof( P3Config ), NULL, CLSCTX_ALL, __uuidof( IP3Config ),reinterpret_cast<LPVOID *>( &spIConfig ));
|
|
if( S_OK == hr )
|
|
{
|
|
if( S_OK== hr)
|
|
{
|
|
hr = spIConfig->get_Domains( &spIDomains );
|
|
}
|
|
}
|
|
if( S_OK == hr )
|
|
{
|
|
BSTR bstrDomainName=NULL;
|
|
CComBSTR bstrUserName(pUserName);
|
|
hr = spIDomains->SearchForMailbox(bstrUserName, &bstrDomainName);
|
|
if ( S_OK == hr )
|
|
{
|
|
if(0> _snwprintf(wszUserName, POP3_MAX_ADDRESS_LENGTH-1, L"%s@%s", pUserName, bstrDomainName) )
|
|
{
|
|
hr=E_FAIL;
|
|
}
|
|
wszUserName[POP3_MAX_ADDRESS_LENGTH-1]=L'\0';
|
|
SysFreeString(bstrDomainName);
|
|
}
|
|
}
|
|
}
|
|
|
|
m_gpFuncs->FreeContextBuffer(SecUserName.sUserName);
|
|
VariantClear(&var);
|
|
return hr;
|
|
}
|
|
|
|
|
|
}
|
|
|