Windows2003-3790/inetsrv/pop3/shared/pop3auth/authmethods.cpp
2020-09-30 16:53:55 +02:00

782 lines
20 KiB
C++

//Implementation of CAuthMethods class
#include "stdafx.h"
#include "Pop3Auth.h"
#include "AuthMethodsEnum.h"
#include <pop3regkeys.h>
#include <p3admin.h>
#include <DSRole.h>
#include <POP3Server.h>
#include <servutil.h>
CAuthMethods::CAuthMethods()
{
m_bIsInitialized=FALSE;
m_lCurrentMethodIndex=-1;
m_bstrServerName=NULL;
m_lMachineRole=0;
InitializeCriticalSection(&m_csVectorGuard);
}
CAuthMethods::~CAuthMethods()
{
CleanUp();
DeleteCriticalSection(&m_csVectorGuard);
}
STDMETHODIMP CAuthMethods::get_Count(LONG *pVal)
{
if(NULL == pVal)
{
return E_POINTER;
}
if(!m_bIsInitialized)
{
if(!Initialize())
{
return E_FAIL;
}
}
*pVal=m_Index.size();
return S_OK;
}
STDMETHODIMP CAuthMethods::get_Item(VARIANT vID, IAuthMethod **ppVal)
{
HRESULT hr=E_INVALIDARG;
if(NULL == ppVal)
{
return E_POINTER;
}
if(! m_bIsInitialized)
{
if(!Initialize())
{
return E_FAIL;
}
}
if(VT_I4 == vID.vt)
{
vID.lVal--; // Collections should be 1-based
if(vID.lVal>=0 && vID.lVal<m_Index.size())
{
hr=m_AuthVector[m_Index[vID.lVal]]->pIAuthMethod
->QueryInterface(IID_IAuthMethod, (void **)ppVal);
}
}
else if(VT_BSTR==vID.vt)
{
for(long i=0; i< m_Index.size(); i++)
{
if( 0==wcscmp(m_AuthVector[m_Index[i]]->bstrName, vID.bstrVal) )
{
hr=m_AuthVector[m_Index[i]]->pIAuthMethod
->QueryInterface(IID_IAuthMethod, (void **)ppVal);
break;
}
}
}
if(S_OK==hr)
{
VARIANT vServer;
vServer.vt=VT_BSTR;
vServer.bstrVal=m_bstrServerName;
if(m_bstrServerName!=NULL)
{
hr = (*ppVal)->Put(SZ_SERVER_NAME, vServer);
}
}
return hr;
}
STDMETHODIMP CAuthMethods::get__NewEnum(IEnumVARIANT **ppVal)
{
if(ppVal==NULL)
{
return E_POINTER;
}
HRESULT hr=S_OK;
CComObject<CAuthMethodsEnum> *pAuthEnum;
*ppVal=NULL;
if(!m_bIsInitialized)
{
if(!Initialize())
{
return E_FAIL;
}
}
hr = CComObject<CAuthMethodsEnum>::CreateInstance(&pAuthEnum); // Reference count still 0
if(SUCCEEDED(hr))
{
hr=pAuthEnum->Init(&m_AuthVector);
if(SUCCEEDED(hr))
{
hr=pAuthEnum->QueryInterface(IID_IEnumVARIANT,(void **)(ppVal) );
}
if(FAILED(hr))
{
delete(pAuthEnum);
}
}
return hr;
}
STDMETHODIMP CAuthMethods::Add(BSTR bstrName, BSTR bstrGUID)
{
HRESULT hr=S_OK;;
PAUTH_METHOD_INFO pAuthMethodInfo=NULL;
if(NULL==bstrName || NULL==bstrGUID)
{
return E_INVALIDARG;
}
pAuthMethodInfo=new(AUTH_METHOD_INFO);
if(pAuthMethodInfo == NULL)
{
return E_OUTOFMEMORY;
}
pAuthMethodInfo->bstrGuid=SysAllocString(bstrGUID);
pAuthMethodInfo->bstrName=SysAllocString(bstrName);
if( (NULL==pAuthMethodInfo->bstrGuid)
||(NULL==pAuthMethodInfo->bstrName))
{
delete pAuthMethodInfo;
hr=E_OUTOFMEMORY;
}
EnterCriticalSection(&m_csVectorGuard);
m_AuthVector.push_back(pAuthMethodInfo);
m_Index.push_back(m_AuthVector.size()-1);
LeaveCriticalSection(&m_csVectorGuard);
return hr;
}
STDMETHODIMP CAuthMethods::Remove(VARIANT vID)
{
HRESULT hr=E_INVALIDARG;
AUTHINDEX::iterator itor=m_Index.begin();
if(!m_bIsInitialized)
{
if(!Initialize())
{
return E_FAIL;
}
}
if(m_Index.size()==1)
{
//Can not remove the last Auth method
return E_FAIL;
}
if(VT_I4 == vID.vt)
{
vID.lVal--; // Collections should be 1-based
if(vID.lVal>=0 && vID.lVal<m_Index.size())
{
itor+=vID.lVal;
m_Index.erase(itor);
if(vID.lVal==m_lCurrentMethodIndex)
{
m_lCurrentMethodIndex=0;
}
else if (vID.lVal<m_lCurrentMethodIndex)
{
m_lCurrentMethodIndex--;
}
}
}
else if(VT_BSTR==vID.vt)
{
for(int i=0; i< m_Index.size(); i++)
{
if(0==wcscmp(m_AuthVector[m_Index[i]]->bstrName, vID.bstrVal))
{
itor+=i;
m_Index.erase(itor);
if(i==m_lCurrentMethodIndex)
{
m_lCurrentMethodIndex=0;
}
else if (i<m_lCurrentMethodIndex)
{
m_lCurrentMethodIndex--;
}
break;
}
}
}
return hr;
}
STDMETHODIMP CAuthMethods::Save(void)
{
int i=0;
int iBufferSize=0;
WCHAR *pBuffer=NULL, *pCurrent;
HRESULT hr=E_FAIL;
HKEY hPop3Key;
if( !m_bIsInitialized )
{
//No change needed for this save
return S_OK;
}
EnterCriticalSection(&m_csVectorGuard);
for(i=0;i<m_AuthVector.size();i++)
{
iBufferSize+=wcslen(m_AuthVector[i]->bstrGuid)+1;
}
if ( 0 != iBufferSize )
{
iBufferSize++;
pBuffer=(WCHAR *)malloc(iBufferSize*sizeof(WCHAR));
if(NULL != pBuffer)
{
pCurrent=pBuffer;
for(i=0;i<m_AuthVector.size();i++)
{
wcscpy(pCurrent,m_AuthVector[i]->bstrGuid);
pCurrent+=wcslen(m_AuthVector[i]->bstrGuid)+1;
}
*pCurrent=0; //Double NULL to terminate
if( ERROR_SUCCESS== RegHKLMOpenKey(
POP3SERVER_AUTH_SUBKEY,
KEY_SET_VALUE,
&hPop3Key,
m_bstrServerName) )
{
if(ERROR_SUCCESS ==
RegSetValueEx(hPop3Key,
VALUENAME_AUTHMETHODS,
NULL,
REG_MULTI_SZ,
(LPBYTE)pBuffer,
iBufferSize*sizeof(WCHAR)))
{
if( ERROR_SUCCESS ==
RegSetValueEx(hPop3Key,
VALUENAME_DEFAULTAUTH,
NULL,
REG_DWORD,
(LPBYTE)&(m_Index[m_lCurrentMethodIndex]),
sizeof(DWORD)))
{ // Need to restart the service (if the Auth method is being changed, which means # domains = 0
long lCount;
CComPtr<IP3Config> spIConfig;
CComPtr<IP3Domains> spIDomains;
hr = CoCreateInstance( __uuidof( P3Config ), NULL, CLSCTX_ALL, __uuidof( IP3Config ),reinterpret_cast<LPVOID *>( &spIConfig ));
if ( S_OK == hr )
{
hr = spIConfig->put_MachineName( m_bstrServerName );
if ( S_OK == hr)
{
hr = spIConfig->get_Domains( &spIDomains );
}
}
if( S_OK == hr )
{
hr = spIDomains->get_Count( &lCount );
if ( S_OK == hr && 0 == lCount && _IsServiceRunning( POP3_SERVICE_NAME ))
hr = _RestartService( POP3_SERVICE_NAME );
}
}
}
RegCloseKey(hPop3Key);
}
free(pBuffer);
}
else //pBuffer == NULL
{
hr=E_OUTOFMEMORY;
}
}
LeaveCriticalSection(&m_csVectorGuard);
return hr;
}
STDMETHODIMP CAuthMethods::put_MachineName(BSTR newVal)
{
if(NULL!=m_bstrServerName)
{
SysFreeString(m_bstrServerName);
m_bstrServerName=NULL;
}
if ( (NULL != newVal) && (0 < wcslen( newVal )))
{
CleanUp();
m_bstrServerName=SysAllocString(newVal);
if(NULL==m_bstrServerName)
{
return E_OUTOFMEMORY;
}
}
// Actually verify we can get the current auth method of the remote machine.
// This enforces that we can only administer remote AD machine's if we are in the same domain.
HRESULT hr;
VARIANT v;
CComPtr<IAuthMethod> spIAuthMethod;
VariantInit( &v );
hr = get_CurrentAuthMethod( &v );
if ( S_OK == hr )
hr = get_Item( v, &spIAuthMethod );
if ( S_OK != hr )
CleanUp();
return hr;
}
STDMETHODIMP CAuthMethods::get_MachineName(BSTR *pVal)
{
if(NULL == pVal)
{
return E_POINTER;
}
if(NULL!=m_bstrServerName)
{
*pVal=SysAllocString(m_bstrServerName);
if(NULL == *pVal)
{
return E_OUTOFMEMORY;
}
}
else
{
*pVal=NULL;
}
return S_OK;
}
STDMETHODIMP CAuthMethods::get_CurrentAuthMethod(VARIANT *pVal)
{
if(NULL == pVal)
{
return E_POINTER;
}
if( !m_bIsInitialized)
{
if(!Initialize())
{
return E_FAIL;
}
}
if(pVal->vt != VT_EMPTY )
{
return E_INVALIDARG;
}
if(m_lCurrentMethodIndex <0 )
{
//The current authmethod is not valid
return HRESULT_FROM_WIN32(ERROR_DS_AUTH_METHOD_NOT_SUPPORTED);
}
pVal->vt=VT_I4;
pVal->lVal=m_lCurrentMethodIndex + 1; // Collections should be 1-based
return S_OK;
}
STDMETHODIMP CAuthMethods::put_CurrentAuthMethod(VARIANT vID)
{
HRESULT hr=E_INVALIDARG;
if(!m_bIsInitialized)
{
if(!Initialize())
{
return E_FAIL;
}
}
EnterCriticalSection(&m_csVectorGuard);
if(VT_I4 == vID.vt)
{
vID.lVal--; // Collections should be 1-based
if(vID.lVal>=0 && vID.lVal<m_Index.size())
{
hr=VerifyCurrentAuthMethod(vID.lVal);
if(S_OK == hr )
{
m_lCurrentMethodIndex=vID.lVal;
}
}
}
else if(VT_BSTR==vID.vt)
{
for(int i=0; i< m_Index.size(); i++)
{
if( 0 == wcscmp(m_AuthVector[m_Index[i]]->bstrName, vID.bstrVal) )
{
hr=VerifyCurrentAuthMethod(i);
if(S_OK == hr)
{
m_lCurrentMethodIndex=i;
}
break;
}
}
}
LeaveCriticalSection(&m_csVectorGuard);
return hr;
}
void CAuthMethods::CleanUp()
{
for(int i=0; i< m_AuthVector.size(); i++)
{
if(NULL != m_AuthVector[i]->pIAuthMethod)
{
m_AuthVector[i]->pIAuthMethod->Release();
}
SysFreeString(m_AuthVector[i]->bstrGuid);
SysFreeString(m_AuthVector[i]->bstrName);
delete m_AuthVector[i];
}
m_AuthVector.clear();
m_Index.clear();
if(NULL!=m_bstrServerName)
{
SysFreeString(m_bstrServerName);
m_bstrServerName=NULL;
}
m_bIsInitialized=FALSE;
}
BOOL CAuthMethods::Initialize()
{
HKEY hPop3Key;
BOOL bRtVal=FALSE;
DWORD cbSize=sizeof(DWORD);
DWORD dwType;
LONG lErr;
LONG lCurrentMethodIndex=0;
WCHAR *wBuffer=NULL;
EnterCriticalSection(&m_csVectorGuard);
if( !m_bIsInitialized )
{
SetMachineRole();
//Load data from the registry
if( ERROR_SUCCESS==RegHKLMOpenKey(
POP3SERVER_AUTH_SUBKEY,
KEY_QUERY_VALUE,
&hPop3Key,
m_bstrServerName) )
{
if(ERROR_SUCCESS==
RegQueryValueEx(hPop3Key,
VALUENAME_DEFAULTAUTH,
NULL,
&dwType,
(LPBYTE)&(lCurrentMethodIndex),
&cbSize))
{
cbSize=0;
if(ERROR_SUCCESS==
RegQueryValueEx(hPop3Key,
VALUENAME_AUTHMETHODS,
NULL,
&dwType,
NULL,
&cbSize))
{
wBuffer=(WCHAR *)malloc(cbSize);
if(NULL!=wBuffer)
{
ZeroMemory(wBuffer, cbSize);
if(ERROR_SUCCESS==
RegQueryValueEx(hPop3Key,
VALUENAME_AUTHMETHODS,
NULL,
&dwType,
(LPBYTE)wBuffer,
&cbSize))
{
if(PopulateAuthMethods(wBuffer, cbSize))
{
for(int i=0;i<m_Index.size();i++)
{
if(m_Index[i]==lCurrentMethodIndex)
{
m_lCurrentMethodIndex=i;
break;
}
}
m_bIsInitialized=TRUE;
bRtVal=TRUE;
}
}
free(wBuffer);
}
}
}
RegCloseKey(hPop3Key);
}
}
LeaveCriticalSection(&m_csVectorGuard);
return bRtVal;
}
BOOL CAuthMethods::PopulateAuthMethods(WCHAR *wBuffer, DWORD cbSize)
{
//Parse the multi-string buffer
DWORD dwSize=cbSize/sizeof(WCHAR);
WCHAR *pStart, *pEnd;
PAUTH_METHOD_INFO pAuthInfo=NULL;
BOOL bRetVal=TRUE;
if(NULL == wBuffer)
{
return FALSE;
}
if(0!=wBuffer[dwSize-1])
{
//Wrong format
return FALSE;
}
pStart=wBuffer;
pEnd=pStart+wcslen(pStart);
while(pEnd<wBuffer+dwSize-1)
{
pAuthInfo=new(AUTH_METHOD_INFO);
if(NULL == pAuthInfo)
{
bRetVal=FALSE;
break;
}
ZeroMemory(pAuthInfo, sizeof(pAuthInfo));
pAuthInfo->bstrGuid=SysAllocString(pStart);
if(NULL == pAuthInfo->bstrGuid)
{
bRetVal=FALSE;
break;
}
if(S_OK != CreateObj(pAuthInfo, &(pAuthInfo->pIAuthMethod)))
{
bRetVal=FALSE;
break;
}
if(S_OK != pAuthInfo->pIAuthMethod->get_Name(&(pAuthInfo->bstrName)) )
{
bRetVal=FALSE;
break;
}
m_AuthVector.push_back(pAuthInfo);
if(SUCCEEDED(VerifyAuthMethod(pAuthInfo->bstrGuid)))
{
//Only show valid auth methods
m_Index.push_back(m_AuthVector.size()-1);
pAuthInfo->bIsValid=TRUE;
}
else
{
pAuthInfo->bIsValid=FALSE;
}
pStart=pEnd+1;
pEnd=pStart+wcslen(pStart);
}
if(!bRetVal)
{
if(NULL !=pAuthInfo)
{
if(NULL!=pAuthInfo->pIAuthMethod)
{
pAuthInfo->pIAuthMethod->Release();
}
SysFreeString(pAuthInfo->bstrGuid);
SysFreeString(pAuthInfo->bstrName);
delete pAuthInfo;
}
CleanUp();
}
return bRetVal;
}
HRESULT CAuthMethods::CreateObj(PAUTH_METHOD_INFO pAuthInfo, IAuthMethod **ppVal)
{
HRESULT hr=S_OK;
UUID uuid;
if( ( NULL == pAuthInfo ) ||
( NULL == ppVal ) )
{
return E_POINTER;
}
if(pAuthInfo->bstrGuid == NULL)
{
return E_INVALIDARG;
}
if(RPC_S_OK != UuidFromString(pAuthInfo->bstrGuid, &uuid))
{
return E_INVALIDARG;
}
hr=CoCreateInstance(uuid,
NULL,
CLSCTX_INPROC_SERVER,
IID_IAuthMethod,
(LPVOID *)ppVal);
return hr;
}
//The index here is the relative index exposed through public interface
STDMETHODIMP CAuthMethods::VerifyCurrentAuthMethod(int iIndex)
{
HRESULT hr=E_FAIL;
long lCount=0;
if(! m_bIsInitialized)
{
if(!Initialize())
{
return E_FAIL;
}
}
if( iIndex < 0 )
{
if(m_lCurrentMethodIndex < 0 )
{
return HRESULT_FROM_WIN32(ERROR_DS_AUTH_METHOD_NOT_SUPPORTED);
}
iIndex=m_lCurrentMethodIndex;
}
if( (NO_DOMAIN==m_lMachineRole) &&
(0==_wcsicmp(m_AuthVector[m_Index[iIndex]]->bstrGuid, SZ_AUTH_ID_DOMAIN_AD)) )
{
return E_INVALIDARG;
}
if( (DOMAIN_CONTROLLER==m_lMachineRole) &&
(0==_wcsicmp(m_AuthVector[m_Index[iIndex]]->bstrGuid, SZ_AUTH_ID_LOCAL_SAM)) )
{
return E_INVALIDARG;
}
if( 0 ==_wcsicmp(m_AuthVector[m_Index[iIndex]]->bstrGuid, SZ_AUTH_ID_MD5_HASH) )
{
// Set the SPA required reg key to 0
RegSetSPARequired(0); //Don't care if the key does not exist
}
if( iIndex != m_lCurrentMethodIndex)
{
// Can not change Auth Method if there is a email domain
CComPtr<IP3Config> spIConfig;
CComPtr<IP3Domains> spIDomains;
hr = CoCreateInstance( __uuidof( P3Config ), NULL, CLSCTX_ALL, __uuidof( IP3Config ),reinterpret_cast<LPVOID *>( &spIConfig ));
if( S_OK == hr )
{
hr = spIConfig->put_MachineName( m_bstrServerName );
if( S_OK== hr)
{
hr = spIConfig->get_Domains( &spIDomains );
}
}
if( S_OK == hr )
{
hr = spIDomains->get_Count( &lCount );
if ( S_OK == hr )
{
if(0==lCount)
{
return S_OK;
}
}
}
return STG_E_ACCESSDENIED;
}
return S_OK;
}
HRESULT CAuthMethods::SetMachineRole()
{
DSROLE_PRIMARY_DOMAIN_INFO_BASIC *pMachineRole=NULL;
//Check the Role of the machine
if( ERROR_SUCCESS==
DsRoleGetPrimaryDomainInformation(
m_bstrServerName,
DsRolePrimaryDomainInfoBasic,
(PBYTE *)(&pMachineRole)) )
{
switch(pMachineRole->MachineRole)
{
case DsRole_RoleStandaloneWorkstation:
case DsRole_RoleStandaloneServer: m_lMachineRole=NO_DOMAIN;
break;
case DsRole_RoleMemberWorkstation:
case DsRole_RoleMemberServer:m_lMachineRole=DOMAIN_NONE_DC;
break;
case DsRole_RoleBackupDomainController:
case DsRole_RolePrimaryDomainController:m_lMachineRole=DOMAIN_CONTROLLER;
break;
}
DsRoleFreeMemory(pMachineRole);
pMachineRole=NULL;
return S_OK;
}
return E_FAIL;
}
HRESULT CAuthMethods::VerifyAuthMethod(BSTR bstrGuid)
{
if(bstrGuid == NULL)
{
return E_POINTER;
}
if( (NO_DOMAIN==m_lMachineRole) &&
(0==_wcsicmp(bstrGuid, SZ_AUTH_ID_DOMAIN_AD)) )
{
return E_FAIL;
}
if( (DOMAIN_CONTROLLER==m_lMachineRole) &&
(0==_wcsicmp(bstrGuid, SZ_AUTH_ID_LOCAL_SAM)) )
{
return E_FAIL;
}
return S_OK;
}