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.
2100 lines
71 KiB
2100 lines
71 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1995.
|
|
//
|
|
// File:
|
|
// remact.cxx
|
|
//
|
|
// Contents:
|
|
//
|
|
// Implementation of binding handle cache to remote activation services.
|
|
//
|
|
// History:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "act.hxx"
|
|
#include "misc.hxx"
|
|
|
|
CRemoteMachineList * gpRemoteMachineList = NULL;
|
|
CSharedLock * gpRemoteMachineLock = NULL;
|
|
|
|
// These globals are set to their defaults here, but can be overridden at boot
|
|
// time via registry knobs. See ReadRemoteBindingHandleCacheKeys in registry.cxx.
|
|
|
|
// Also note that gdwRemoteBindingHandleCacheMaxSize cannot be adjusted after boot,
|
|
// but gdwRemoteBindingHandleCacheMaxLifetime and gdwRemoteBindingHandleCacheIdleTimeout can
|
|
// be played with in the debugger at will for those so inclined.
|
|
DWORD gdwRemoteBindingHandleCacheMaxSize = 16; // in # of cache elements
|
|
DWORD gdwRemoteBindingHandleCacheMaxLifetime = 0; // in minutes
|
|
DWORD gdwRemoteBindingHandleCacheIdleTimeout = 15; // in minutes
|
|
|
|
|
|
|
|
class CRemActPPing : public CParallelPing
|
|
{
|
|
public:
|
|
CRemActPPing(WCHAR *pMachine) :
|
|
_ndx(0),
|
|
_pMachine(pMachine)
|
|
{}
|
|
|
|
|
|
BOOL NextCall(PROTSEQINFO *pProtseqInfo)
|
|
{
|
|
RPC_STATUS status;
|
|
if (_ndx < cMyProtseqs)
|
|
{
|
|
status = CreateRemoteBinding(_pMachine,
|
|
_ndx,
|
|
&pProtseqInfo->hRpc);
|
|
if (status != RPC_S_OK)
|
|
{
|
|
pProtseqInfo->hRpc = NULL;
|
|
}
|
|
pProtseqInfo->dwUserInfo = _ndx;
|
|
_ndx++;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
void ReleaseCall(PROTSEQINFO *pProtseqInfo)
|
|
{
|
|
if (pProtseqInfo->hRpc)
|
|
{
|
|
RpcBindingFree(&pProtseqInfo->hRpc);
|
|
}
|
|
}
|
|
private:
|
|
DWORD _ndx;
|
|
WCHAR *_pMachine;
|
|
};
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: RemoteActivationCall
|
|
//
|
|
// Synopsis: Finds or creates a machine object to cache binding handles
|
|
// to the server machine and forwards the activation request
|
|
// to it.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
RemoteActivationCall(
|
|
ACTIVATION_PARAMS * pActParams,
|
|
WCHAR * pwszServerName )
|
|
{
|
|
CRemoteMachine * pRemoteMachine;
|
|
WCHAR wszPathForServer[MAX_PATH+1];
|
|
WCHAR * pwszPathForServer;
|
|
HRESULT hr;
|
|
|
|
pActParams->activatedRemote = TRUE;
|
|
|
|
ASSERT( pwszServerName );
|
|
|
|
pwszPathForServer = 0;
|
|
|
|
if ( pActParams->pwszPath )
|
|
{
|
|
hr = GetPathForServer( pActParams->pwszPath, wszPathForServer, &pwszPathForServer );
|
|
if ( hr != S_OK )
|
|
return hr;
|
|
}
|
|
|
|
pRemoteMachine = gpRemoteMachineList->GetOrAdd( pwszServerName );
|
|
if ( ! pRemoteMachine )
|
|
return E_OUTOFMEMORY;
|
|
|
|
ASSERT(pActParams->pActPropsIn);
|
|
BOOL fUseSystemId;
|
|
pActParams->pActPropsIn->GetRemoteActivationFlags(&pActParams->fComplusOnly,
|
|
&fUseSystemId);
|
|
if (!fUseSystemId)
|
|
{
|
|
if (pActParams->pToken != NULL)
|
|
pActParams->pToken->Impersonate();
|
|
else
|
|
{
|
|
pRemoteMachine->Release();
|
|
return HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
|
|
}
|
|
}
|
|
|
|
IServerLocationInfo *pServerLocationInfo = NULL;
|
|
ISpecialSystemProperties* pSSP = NULL;
|
|
ULONG ulCurrentSessionId = 0;
|
|
BOOL bUseConsole = FALSE;
|
|
BOOL fRemoteThisSessionId = FALSE;
|
|
pServerLocationInfo = pActParams->pActPropsIn->GetServerLocationInfo();
|
|
ASSERT(pServerLocationInfo != NULL);
|
|
pServerLocationInfo->SetRemoteServerName(NULL);
|
|
|
|
//
|
|
// Session id's should flow off-machine only when explicitly told to; make sure
|
|
// this is the case:
|
|
//
|
|
hr = pActParams->pActPropsIn->QueryInterface(IID_ISpecialSystemProperties, (void**)&pSSP);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pSSP->GetSessionId2(&ulCurrentSessionId, &bUseConsole, &fRemoteThisSessionId);
|
|
if (!fRemoteThisSessionId)
|
|
{
|
|
hr = pSSP->SetSessionId(INVALID_SESSION_ID, FALSE, FALSE);
|
|
ASSERT(SUCCEEDED(hr) && "SetSessionId failed");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Try the activation:
|
|
//
|
|
hr = pRemoteMachine->Activate( pActParams, pwszPathForServer );
|
|
|
|
//
|
|
// Restore session id just in case the activation ends up being re-tried (for
|
|
// whatever reason) on this machine (eg, if a load-balancing activator is loaded)
|
|
//
|
|
if (!fRemoteThisSessionId)
|
|
{
|
|
HRESULT hrLocal;
|
|
hrLocal = pSSP->SetSessionId(ulCurrentSessionId, bUseConsole, FALSE);
|
|
ASSERT(SUCCEEDED(hrLocal) && "SetSessionId failed");
|
|
}
|
|
|
|
if (pSSP)
|
|
pSSP->Release();
|
|
|
|
pRemoteMachine->Release();
|
|
|
|
if (!fUseSystemId)
|
|
pActParams->pToken->Revert();
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRemoteMachineList::GetOrAdd
|
|
//
|
|
// Synopsis: Scans the machine list for a matching name. Returns it if
|
|
// found. Otherwise, creates a machine list entry. Returned
|
|
// object is refcounted.
|
|
//
|
|
// Changes: jsimmons 4/6/00 Fix for bug 22803 -- cap cache size
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CRemoteMachine *
|
|
CRemoteMachineList::GetOrAdd(
|
|
IN WCHAR * pwszMachine
|
|
)
|
|
{
|
|
CRemoteMachine * pMachine;
|
|
WCHAR * pwszMachineCopy;
|
|
WCHAR * pwszScmSPNCopy;
|
|
|
|
gpRemoteMachineLock->LockExclusive();
|
|
|
|
for ( pMachine = (CRemoteMachine *) First();
|
|
pMachine;
|
|
pMachine = (CRemoteMachine *) pMachine->Next() )
|
|
{
|
|
if ( lstrcmpiW( pMachine->_pwszMachine, pwszMachine ) == 0 )
|
|
{
|
|
pMachine->_dwLastUsedTickCount = GetTickCount();
|
|
pMachine->AddRef(); // add caller reference
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( ! pMachine )
|
|
{
|
|
pwszMachineCopy = (WCHAR *) PrivMemAlloc( (lstrlenW( pwszMachine ) + 1) * sizeof(WCHAR) );
|
|
if (pwszMachineCopy)
|
|
{
|
|
pwszScmSPNCopy = (WCHAR *) PrivMemAlloc( (lstrlenW( pwszMachine ) +
|
|
(sizeof(RPCSS_SPN_PREFIX) / sizeof(WCHAR))
|
|
+ 1) * sizeof(WCHAR) );
|
|
if (pwszScmSPNCopy)
|
|
{
|
|
lstrcpyW( pwszMachineCopy, pwszMachine );
|
|
|
|
// Form server principal name for the remote scm
|
|
lstrcpyW( pwszScmSPNCopy, RPCSS_SPN_PREFIX);
|
|
lstrcatW( pwszScmSPNCopy, pwszMachine);
|
|
|
|
pMachine = new CRemoteMachine( pwszMachineCopy, pwszScmSPNCopy);
|
|
// constructed with refcount of 1, don't addref it again
|
|
if ( pMachine )
|
|
{
|
|
// Only attempt to save new object in cache if cache size > 0:
|
|
if (_dwMaxCacheSize > 0)
|
|
{
|
|
ASSERT(_dwCacheSize <= _dwMaxCacheSize);
|
|
if (_dwCacheSize == _dwMaxCacheSize)
|
|
{
|
|
// Cache has no more room. Dump the oldest one
|
|
RemoveOldestCacheElement();
|
|
|
|
ASSERT(_dwCacheSize < _dwMaxCacheSize);
|
|
}
|
|
|
|
Insert( pMachine );
|
|
pMachine->AddRef();
|
|
_dwCacheSize++;
|
|
ASSERT(_dwCacheSize <= _dwMaxCacheSize);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PrivMemFree( pwszMachineCopy );
|
|
PrivMemFree( pwszScmSPNCopy );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PrivMemFree(pwszMachineCopy);
|
|
}
|
|
}
|
|
}
|
|
|
|
gpRemoteMachineLock->UnlockExclusive();
|
|
|
|
return pMachine;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRemoteMachineList::CRemoteMachine
|
|
//
|
|
// Synopsis: Looks thru the cache for the lru element and removes it.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void CRemoteMachineList::RemoveOldestCacheElement()
|
|
{
|
|
ASSERT(_dwCacheSize > 0);
|
|
ASSERT(gpRemoteMachineLock->HeldExclusive());
|
|
|
|
CRemoteMachine* pLRUMachine = (CRemoteMachine*)First();
|
|
CRemoteMachine* pMachine = (CRemoteMachine*)pLRUMachine->Next();
|
|
|
|
while (pMachine)
|
|
{
|
|
if (pMachine->_dwLastUsedTickCount < pLRUMachine->_dwLastUsedTickCount)
|
|
{
|
|
pLRUMachine = pMachine;
|
|
}
|
|
pMachine = (CRemoteMachine*)pMachine->Next();
|
|
}
|
|
|
|
Remove(pLRUMachine);
|
|
pLRUMachine->Release();
|
|
_dwCacheSize--;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRemoteMachineList::FlushSpecificBindings
|
|
//
|
|
// Synopsis: Looks thru the cache for the specified element and removes it
|
|
// from the cache if found.
|
|
//
|
|
// Arguments: [pszMachine] -- name of the machine to flush from the cache. Can
|
|
// be ""; this means all bindings should be flushed
|
|
//
|
|
// Returns: S_OK -- the specified bindings were found and flushed
|
|
// CO_S_MACHINENAMENOTFOUND -- if "" was passed, means the cache was
|
|
// empty; otherwise means that the specified machine name was not
|
|
// found in the cache.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT CRemoteMachineList::FlushSpecificBindings(WCHAR* pszMachine)
|
|
{
|
|
HRESULT hr = CO_S_MACHINENAMENOTFOUND;
|
|
BOOL bFlushAll = (0 == lstrcmpW(pszMachine, L""));
|
|
CRemoteMachine* pMachine;
|
|
CRemoteMachine* pNextMachine;
|
|
|
|
gpRemoteMachineLock->LockExclusive();
|
|
|
|
if (bFlushAll)
|
|
{
|
|
// Loop thru and release all of them
|
|
while (pMachine = (CRemoteMachine*)First())
|
|
{
|
|
Remove(pMachine);
|
|
pMachine->Release();
|
|
_dwCacheSize--;
|
|
hr = S_OK; // there was at least one item in the cache, so return S_OK
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Loop thru looking for the specified machine name
|
|
pMachine = (CRemoteMachine*)First();
|
|
|
|
while (pMachine)
|
|
{
|
|
if (lstrcmpiW(pszMachine, pMachine->_pwszMachine) == 0)
|
|
{
|
|
// Found it
|
|
Remove(pMachine);
|
|
pMachine->Release();
|
|
_dwCacheSize--;
|
|
hr = S_OK; // found it so return S_OK
|
|
break;
|
|
}
|
|
pMachine = (CRemoteMachine*)pMachine->Next();
|
|
}
|
|
}
|
|
|
|
gpRemoteMachineLock->UnlockExclusive();
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRemoteMachineList::TryToFlushIdleOrTooOldElements
|
|
//
|
|
// Synopsis: Looks thru the cache for elements which have either 1) not been
|
|
// used for a period than the idle timeout period; or 2) been in the cache
|
|
// longer than the maximum allowable period. If any are found they are
|
|
// deleted from the cache.
|
|
//
|
|
// Arguments: none
|
|
//
|
|
// Returns: void
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void CRemoteMachineList::TryToFlushIdleOrTooOldElements()
|
|
{
|
|
CRemoteMachine* pMachine;
|
|
CRemoteMachine* pNextMachine;
|
|
DWORD dwNow = GetTickCount();
|
|
DWORD dwIdleTimeout = gdwRemoteBindingHandleCacheIdleTimeout * 1000 * 60;
|
|
DWORD dwLifetimeTimeout = gdwRemoteBindingHandleCacheMaxLifetime * 1000 * 60;
|
|
|
|
if (!dwIdleTimeout && !dwLifetimeTimeout)
|
|
return; // nothing to do
|
|
|
|
gpRemoteMachineLock->LockExclusive();
|
|
|
|
pMachine = (CRemoteMachine*)First();
|
|
|
|
while (pMachine)
|
|
{
|
|
BOOL bRemoveCurrentItem;
|
|
|
|
bRemoveCurrentItem = FALSE;
|
|
|
|
// Check if it's been idle too long
|
|
if (dwIdleTimeout > 0)
|
|
{
|
|
if (dwNow - pMachine->_dwLastUsedTickCount > dwIdleTimeout)
|
|
{
|
|
bRemoveCurrentItem = TRUE;
|
|
}
|
|
}
|
|
|
|
// Check if it's been around too long, period
|
|
if (dwLifetimeTimeout > 0)
|
|
{
|
|
if (dwNow - pMachine->_dwTickCountAtCreate > dwLifetimeTimeout)
|
|
{
|
|
bRemoveCurrentItem = TRUE;
|
|
}
|
|
}
|
|
|
|
pNextMachine = (CRemoteMachine*)pMachine->Next();
|
|
|
|
if (bRemoveCurrentItem)
|
|
{
|
|
Remove(pMachine);
|
|
pMachine->Release();
|
|
_dwCacheSize--;
|
|
}
|
|
|
|
pMachine = pNextMachine;
|
|
}
|
|
|
|
gpRemoteMachineLock->UnlockExclusive();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: OLESCMBindingHandleFlush
|
|
//
|
|
// Synopsis: This function gets called periodically by objex's worker
|
|
// thread. It gives us a chance to flush idle or too-old cache elements
|
|
// in the remote binding handle cache.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void OLESCMBindingHandleFlush()
|
|
{
|
|
gpRemoteMachineList->TryToFlushIdleOrTooOldElements();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRemoteMachine::CRemoteMachine
|
|
//
|
|
// Synopsis: Constructor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
CRemoteMachine::CRemoteMachine(
|
|
IN WCHAR * pwszMachine,
|
|
IN WCHAR * pwszScmSPN
|
|
)
|
|
{
|
|
_pwszMachine = pwszMachine;
|
|
_pwszScmSPN = pwszScmSPN;
|
|
_dsa = NULL;
|
|
_ulRefCount = 1; // starts with non-zero refcount
|
|
_dwLastUsedTickCount = GetTickCount();
|
|
_dwTickCountAtCreate = _dwLastUsedTickCount;
|
|
|
|
_fUseOldActivationInterface = TRUE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRemoteMachine::CRemoteMachine
|
|
//
|
|
// Synopsis: Destructor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
CRemoteMachine::~CRemoteMachine()
|
|
{
|
|
ASSERT(_ulRefCount == 0);
|
|
|
|
// We don't need to hold a lock to flush the bindings,
|
|
// since no one else has a reference to us.
|
|
FlushBindingsNoLock();
|
|
|
|
if (_pwszMachine)
|
|
PrivMemFree(_pwszMachine);
|
|
if (_pwszScmSPN)
|
|
PrivMemFree(_pwszScmSPN);
|
|
if (_dsa)
|
|
_dsa->Release();
|
|
}
|
|
|
|
// AddRef function
|
|
ULONG CRemoteMachine::AddRef()
|
|
{
|
|
return InterlockedIncrement((PLONG)&_ulRefCount);
|
|
}
|
|
|
|
// Release function
|
|
ULONG CRemoteMachine::Release()
|
|
{
|
|
ULONG ulNewRefCount = InterlockedDecrement((PLONG)&_ulRefCount);
|
|
if (ulNewRefCount == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
return ulNewRefCount;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRemoteMachine::Activate
|
|
//
|
|
// Synopsis: Picks a protocol and authentication service to use to call
|
|
// the server machine. Forwards the activation request to
|
|
// CallRemoteMachine.
|
|
//
|
|
// Description:
|
|
//
|
|
// This method tries several different methods to get a binding handle.
|
|
// Look for auth info match in cache
|
|
// Look for any binding handle in cache
|
|
// Ping server to find valid protocol sequence
|
|
//
|
|
// After it gets a binding handle, it tries to pick auth info.
|
|
// Use client params if specified
|
|
// Use cached params if they exist
|
|
// Try all auth svc valid on client and server
|
|
// Try unsecure
|
|
//
|
|
// This function maintains a cache of binding handles with the following
|
|
// rules.
|
|
// - All binding handles in the cache at any point in time use the same
|
|
// protocol sequence.
|
|
// - The best non-custom authentication info is before any other
|
|
// non-custom authentication info.
|
|
// - The cache is flushed if the protocol is competely invalid.
|
|
// - If a cached entry gets a non security error, it is discarded
|
|
// (security errors may be due to the current credentials rather then
|
|
// the binding handle itself).
|
|
// - Only binding handles that actually worked once are cached.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
CRemoteMachine::Activate(
|
|
IN ACTIVATION_PARAMS * pActParams,
|
|
IN WCHAR * pwszPathForServer
|
|
)
|
|
{
|
|
CMachineBinding * pMachineBinding;
|
|
handle_t hBinding = NULL;
|
|
BOOL bNoEndpoint;
|
|
BOOL bStatus;
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
USHORT AuthnSvc;
|
|
USHORT ProtseqId = 0;
|
|
DWORD AuthnLevel;
|
|
RPC_STATUS Status = RPC_S_INTERNAL_ERROR;
|
|
|
|
// Try to use a cached handle first.
|
|
if (pActParams->pAuthInfo != NULL)
|
|
AuthnSvc = (USHORT) pActParams->pAuthInfo->dwAuthnSvc;
|
|
else
|
|
AuthnSvc = AUTHN_ANY;
|
|
|
|
|
|
hr = GetAuthnLevel(pActParams, &AuthnLevel);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
pMachineBinding = LookupBinding( AuthnSvc, AuthnLevel, pActParams->pAuthInfo );
|
|
if ( pMachineBinding )
|
|
{
|
|
AuthnSvc = pMachineBinding->_AuthnSvc;
|
|
Status = pMachineBinding->GetMarshaledTargetInfo(&pActParams->ulMarshaledTargetInfoLength, &pActParams->pMarshaledTargetInfo);
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
Status = CallRemoteMachine(pMachineBinding->_hBinding,
|
|
pMachineBinding->_ProtseqId,
|
|
pActParams,
|
|
pwszPathForServer,
|
|
_pwszMachine,
|
|
_fUseOldActivationInterface,
|
|
&hr);
|
|
}
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
pActParams->AuthnSvc = AuthnSvc;
|
|
pMachineBinding->Release();
|
|
return hr;
|
|
}
|
|
|
|
// Throw away the binding if it is unlikely to work again in the
|
|
// future.
|
|
else if (Status != RPC_S_ACCESS_DENIED &&
|
|
Status != RPC_S_SEC_PKG_ERROR)
|
|
{
|
|
RemoveBinding( pMachineBinding );
|
|
}
|
|
pMachineBinding->Release();
|
|
}
|
|
|
|
// Throw away all bindings if the protocol is unlikely to work
|
|
// again.
|
|
if (Status == RPC_S_SERVER_UNAVAILABLE ||
|
|
Status == EPT_S_NOT_REGISTERED)
|
|
{
|
|
FlushBindings();
|
|
}
|
|
// Get any binding handle from the cache.
|
|
else
|
|
{
|
|
gpRemoteMachineLock->LockShared();
|
|
pMachineBinding = (CMachineBinding *) _BindingList.First();
|
|
if (pMachineBinding != NULL)
|
|
{
|
|
Status = RpcBindingCopy( pMachineBinding->_hBinding, &hBinding );
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
ASSERT(hBinding != NULL);
|
|
ProtseqId = pMachineBinding->_ProtseqId;
|
|
}
|
|
else
|
|
hBinding = NULL;
|
|
}
|
|
gpRemoteMachineLock->UnlockShared();
|
|
|
|
// Try to find auth info that will work.
|
|
if (hBinding != NULL)
|
|
{
|
|
Assert (pMachineBinding != NULL);
|
|
Status = PickAuthnAndActivate( pActParams, pwszPathForServer,
|
|
&hBinding, AuthnSvc, AuthnLevel,
|
|
ProtseqId, &hr );
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
Assert( hBinding == NULL );
|
|
return hr;
|
|
}
|
|
else
|
|
{
|
|
// Stop if the activation failed but the protocol was
|
|
// probably good.
|
|
Assert( hBinding != NULL );
|
|
RpcBindingFree( &hBinding );
|
|
hBinding = NULL;
|
|
if (Status != RPC_S_SERVER_UNAVAILABLE &&
|
|
Status != EPT_S_NOT_REGISTERED)
|
|
{
|
|
if (Status == RPC_S_ACCESS_DENIED)
|
|
{
|
|
// Don't map security errors as this is just confusing.
|
|
return HRESULT_FROM_WIN32(RPC_S_ACCESS_DENIED);
|
|
}
|
|
else
|
|
{
|
|
LogRemoteSideUnavailable( pActParams->ClsContext, _pwszMachine );
|
|
return HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// No cached binding handles worked. Try to ping for one.
|
|
{
|
|
CRemActPPing ping(_pwszMachine);
|
|
|
|
// This loop only executes twice if we need to try the call without
|
|
// an endpoint specified.
|
|
//
|
|
bNoEndpoint = FALSE;
|
|
for (;;)
|
|
{
|
|
Status = ping.Ping();
|
|
if ( RPC_S_UNKNOWN_IF == Status )
|
|
{
|
|
if ( ! bNoEndpoint )
|
|
{
|
|
for ( ULONG ProtseqIndex = 0; ProtseqIndex < ping.HandleCount(); ProtseqIndex++ )
|
|
{
|
|
RPC_BINDING_HANDLE tmpBinding;
|
|
Status = RpcBindingCopy( ping.Info(ProtseqIndex)->hRpc, &tmpBinding);
|
|
|
|
if (Status != RPC_S_OK)
|
|
break;
|
|
|
|
Status = RpcBindingFree( &(ping.Info(ProtseqIndex)->hRpc));
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
RpcBindingFree(&tmpBinding);
|
|
break;
|
|
}
|
|
|
|
Status = RpcBindingReset(tmpBinding);
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
RpcBindingFree(&tmpBinding);
|
|
break;
|
|
}
|
|
|
|
ping.Info(ProtseqIndex)->hRpc = tmpBinding;
|
|
}
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
bNoEndpoint = TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
gpRemoteMachineLock->LockExclusive();
|
|
if (_dsa != NULL)
|
|
_dsa->Release();
|
|
hBinding = ping.GetWinner()->hRpc;
|
|
ping.GetWinner()->hRpc = NULL;
|
|
_dsa = ping.TakeOrBindings();
|
|
ProtseqId = aMyProtseqs[ping.GetWinner()->dwUserInfo];
|
|
ASSERT( hBinding != NULL );
|
|
|
|
//
|
|
// If this is not a win2k or better system, then use the old activation
|
|
// interface. Otherwise, use the new activation interface.
|
|
//
|
|
if (ping.GetWinner()->comVersion.MinorVersion < 6)
|
|
{
|
|
_fUseOldActivationInterface = TRUE;
|
|
}
|
|
else
|
|
{
|
|
_fUseOldActivationInterface = FALSE;
|
|
}
|
|
|
|
gpRemoteMachineLock->UnlockExclusive();
|
|
}
|
|
|
|
ping.Reset();
|
|
|
|
}
|
|
|
|
// Try auth info with the new binding.
|
|
if (hBinding != NULL)
|
|
{
|
|
// We've had to re-bind. Make sure we've still decided on
|
|
// the right authentication level here.
|
|
hr = GetAuthnLevel(pActParams, &AuthnLevel);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Status = PickAuthnAndActivate( pActParams, pwszPathForServer,
|
|
&hBinding, RPC_C_AUTHN_NONE,
|
|
AuthnLevel, ProtseqId, &hr );
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
// asserts that in success cases binding was added to the cache
|
|
Assert( hBinding == NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// If the call never worked, return a nice error code.
|
|
// except for security errors.
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
if (Status == RPC_S_ACCESS_DENIED)
|
|
hr = HRESULT_FROM_WIN32(RPC_S_ACCESS_DENIED);
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE);
|
|
LogRemoteSideUnavailable( pActParams->ClsContext, _pwszMachine );
|
|
}
|
|
}
|
|
|
|
// Clean up resources.
|
|
if (hBinding != NULL)
|
|
RpcBindingFree( &hBinding );
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRemoteMachine::GetAuthnLevel
|
|
//
|
|
// Synopsis: Figures out what the authentication level for this call ought
|
|
// to be.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT CRemoteMachine::GetAuthnLevel(ACTIVATION_PARAMS *pActParams, DWORD *pdwDefaultAuthnLevel)
|
|
{
|
|
ISpecialSystemProperties *pSpecialSystemProperties;
|
|
HRESULT hr = S_OK;
|
|
|
|
if (pActParams->pAuthInfo)
|
|
{
|
|
*pdwDefaultAuthnLevel = pActParams->pAuthInfo->dwAuthnLevel;
|
|
}
|
|
else
|
|
{
|
|
// Get the default authentication level from the actprops, if we're talking over
|
|
// the "new" style interface.
|
|
if (!_fUseOldActivationInterface)
|
|
{
|
|
hr = pActParams->pActPropsIn->QueryInterface(IID_ISpecialSystemProperties,
|
|
(void **)&pSpecialSystemProperties);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pSpecialSystemProperties->GetDefaultAuthenticationLevel(pdwDefaultAuthnLevel);
|
|
pSpecialSystemProperties->Release();
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (*pdwDefaultAuthnLevel < RPC_C_AUTHN_LEVEL_CONNECT)
|
|
{
|
|
// Don't do the activation at less than connect.
|
|
*pdwDefaultAuthnLevel = RPC_C_AUTHN_LEVEL_CONNECT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Always use RPC_C_AUTHN_LEVEL_CONNECT for talking to < Win2k servers.
|
|
*pdwDefaultAuthnLevel = RPC_C_AUTHN_LEVEL_CONNECT;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRemoteMachine::PickAuthnAndActivate
|
|
//
|
|
// Synopsis: Determine what authentication information to use for the
|
|
// activation. The AuthnSvc parameter indicates an
|
|
// authentication service that was already tried.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
RPC_STATUS CRemoteMachine::PickAuthnAndActivate(
|
|
IN ACTIVATION_PARAMS * pActParams,
|
|
IN WCHAR * pwszPathForServer,
|
|
IN handle_t * pBinding,
|
|
IN USHORT AuthnSvc,
|
|
IN DWORD AuthnLevel,
|
|
IN USHORT ProtseqId,
|
|
OUT HRESULT * phr )
|
|
{
|
|
RPC_SECURITY_QOS Qos;
|
|
DWORD i;
|
|
RPC_STATUS Status;
|
|
CMachineBinding *pMachineBinding;
|
|
BOOL fTry;
|
|
CDualStringArray *pdsa = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
// If the client specified security, try exactly the settings requested.
|
|
Qos.Version = RPC_C_SECURITY_QOS_VERSION;
|
|
pActParams->UnsecureActivation = FALSE;
|
|
if (pActParams->pAuthInfo)
|
|
{
|
|
// Set the requested authentication information.
|
|
AuthnSvc = (USHORT) pActParams->pAuthInfo->dwAuthnSvc;
|
|
pActParams->AuthnSvc = (USHORT) pActParams->pAuthInfo->dwAuthnSvc;
|
|
AuthnLevel = pActParams->pAuthInfo->dwAuthnLevel;
|
|
Qos.Capabilities = pActParams->pAuthInfo->dwCapabilities;
|
|
Qos.ImpersonationType = pActParams->pAuthInfo->dwImpersonationLevel;
|
|
if (pActParams->pAuthInfo->pAuthIdentityData != NULL)
|
|
Qos.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC;
|
|
else
|
|
Qos.IdentityTracking = RPC_C_QOS_IDENTITY_DYNAMIC;
|
|
|
|
Status = RpcBindingSetAuthInfoExW(
|
|
*pBinding,
|
|
pActParams->pAuthInfo->pwszServerPrincName,
|
|
pActParams->pAuthInfo->dwAuthnLevel,
|
|
pActParams->pAuthInfo->dwAuthnSvc,
|
|
pActParams->pAuthInfo->pAuthIdentityData,
|
|
pActParams->pAuthInfo->dwAuthzSvc,
|
|
&Qos );
|
|
|
|
// Try the activation.
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
Status = CallRemoteMachine(*pBinding,
|
|
ProtseqId,
|
|
pActParams,
|
|
pwszPathForServer,
|
|
_pwszMachine,
|
|
_fUseOldActivationInterface,
|
|
phr);
|
|
}
|
|
}
|
|
// Try all authentication services and then try unsecure.
|
|
else
|
|
{
|
|
// Get a reference to the dual string array.
|
|
gpRemoteMachineLock->LockShared();
|
|
pdsa = _dsa;
|
|
if (pdsa) pdsa->AddRef();
|
|
gpRemoteMachineLock->UnlockShared();
|
|
|
|
// Initialize the QOS structure.
|
|
Qos.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH;
|
|
Qos.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE;
|
|
Qos.IdentityTracking = RPC_C_QOS_IDENTITY_DYNAMIC;
|
|
|
|
// Loop over the authentication services.
|
|
Status = RPC_S_INTERNAL_ERROR;
|
|
for (i = 0; i < s_cRpcssSvc; i++)
|
|
{
|
|
// Skip the authentication service that was already tried.
|
|
if (s_aRpcssSvc[i].wId == AuthnSvc)
|
|
continue;
|
|
|
|
// If there are no security bindings, only try NTLM.
|
|
if (pdsa == NULL)
|
|
{
|
|
fTry = s_aRpcssSvc[i].wId == RPC_C_AUTHN_WINNT;
|
|
}
|
|
else
|
|
{
|
|
// If there are security bindings, try the next authentication
|
|
// service if both machines use it.
|
|
fTry = ValidAuthnSvc( pdsa->DSA(), s_aRpcssSvc[i].wId );
|
|
}
|
|
|
|
if (fTry)
|
|
{
|
|
BOOL bSetSecurityCallBack = FALSE;
|
|
USHORT usAuthSvcFromCallback = RPC_C_AUTHN_GSS_NEGOTIATE;
|
|
|
|
// Set the security.
|
|
Status = RPC_S_OK;
|
|
if (s_aRpcssSvc[i].wId == RPC_C_AUTHN_GSS_NEGOTIATE)
|
|
{
|
|
// Using snego, compute list of compatible authnsvcs:
|
|
ASSERT(pdsa);
|
|
// if using snego, we need to know what sec pkg is eventually negotiated:
|
|
if (gpCRpcSecurityCallbackMgr->RegisterForRpcAuthSvcCallBack(*pBinding))
|
|
bSetSecurityCallBack = TRUE;
|
|
}
|
|
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
Status = RpcBindingSetAuthInfoEx(
|
|
*pBinding,
|
|
_pwszScmSPN,
|
|
AuthnLevel,
|
|
s_aRpcssSvc[i].wId,
|
|
NULL,
|
|
0,
|
|
&Qos );
|
|
|
|
}
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
if (bSetSecurityCallBack)
|
|
gpCRpcSecurityCallbackMgr->GetSecurityContextDetailsAndTurnOffCallback(*pBinding, NULL,
|
|
&pActParams->ulMarshaledTargetInfoLength, &pActParams->pMarshaledTargetInfo);
|
|
}
|
|
|
|
// Try the activation.
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
Status = CallRemoteMachine(*pBinding,
|
|
ProtseqId,
|
|
pActParams,
|
|
pwszPathForServer,
|
|
_pwszMachine,
|
|
_fUseOldActivationInterface,
|
|
phr);
|
|
if (bSetSecurityCallBack)
|
|
{
|
|
//
|
|
// Only ask for the result of the callback if the call went through; otherwise
|
|
// just cancel the registration.
|
|
//
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
if (!gpCRpcSecurityCallbackMgr->GetSecurityContextDetailsAndTurnOffCallback(*pBinding,
|
|
&usAuthSvcFromCallback, &pActParams->ulMarshaledTargetInfoLength, &pActParams->pMarshaledTargetInfo))
|
|
{
|
|
// something went wrong. In this case we don't trust what the callback
|
|
// told us. Fall back on the original behavior
|
|
bSetSecurityCallBack = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// cancel the callback
|
|
gpCRpcSecurityCallbackMgr->GetSecurityContextDetailsAndTurnOffCallback(*pBinding, NULL,
|
|
&pActParams->ulMarshaledTargetInfoLength, &pActParams->pMarshaledTargetInfo);
|
|
bSetSecurityCallBack = FALSE;
|
|
}
|
|
}
|
|
|
|
if (Status == RPC_S_OK ||
|
|
Status == RPC_S_SERVER_UNAVAILABLE ||
|
|
Status == EPT_S_NOT_REGISTERED)
|
|
{
|
|
if (bSetSecurityCallBack)
|
|
{
|
|
// snego call, and we succesfully got a callback telling us
|
|
// what the real authentication service was
|
|
if (usAuthSvcFromCallback == RPC_C_AUTHN_GSS_KERBEROS)
|
|
{
|
|
// if we got back kerberos we're going to cache snego anyway; this
|
|
// helps NTLM-only clients who can't use kerberos
|
|
pActParams->AuthnSvc = AuthnSvc = RPC_C_AUTHN_GSS_NEGOTIATE;
|
|
}
|
|
else
|
|
pActParams->AuthnSvc = AuthnSvc = usAuthSvcFromCallback;
|
|
}
|
|
else
|
|
{
|
|
// non-snego, or something went wrong with security callback
|
|
// on a snego call
|
|
pActParams->AuthnSvc = AuthnSvc = s_aRpcssSvc[i].wId;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pdsa)
|
|
{
|
|
pdsa->Release();
|
|
pdsa = NULL;
|
|
}
|
|
|
|
// If no authentication services worked and the protocol doesn't
|
|
// look bad, try no authentication
|
|
if (Status != RPC_S_OK &&
|
|
Status != RPC_S_SERVER_UNAVAILABLE &&
|
|
Status != EPT_S_NOT_REGISTERED)
|
|
{
|
|
// remember that unsecure activation was done.
|
|
// This will be used later when creating the MID
|
|
// so we do unsecure pinging also.
|
|
pActParams->UnsecureActivation = TRUE;
|
|
|
|
// Look for a cached unsecure binding handle.
|
|
pMachineBinding = LookupBinding( RPC_C_AUTHN_NONE, RPC_C_AUTHN_LEVEL_NONE, NULL );
|
|
if ( pMachineBinding )
|
|
{
|
|
Status = CallRemoteMachine(pMachineBinding->_hBinding,
|
|
pMachineBinding->_ProtseqId,
|
|
pActParams,
|
|
pwszPathForServer,
|
|
_pwszMachine,
|
|
_fUseOldActivationInterface,
|
|
phr);
|
|
|
|
// Throw away the binding handle received as a parameter so
|
|
// it doesn't get cached.
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
RpcBindingFree( pBinding );
|
|
*pBinding = NULL;
|
|
}
|
|
|
|
// Throw away the binding if it is unlikely to work again in the
|
|
// future.
|
|
else
|
|
{
|
|
RemoveBinding( pMachineBinding );
|
|
}
|
|
pMachineBinding->Release();
|
|
}
|
|
|
|
// Make the current binding handle unsecure.
|
|
else
|
|
{
|
|
// Set the authentication information.
|
|
Status = RpcBindingSetAuthInfoEx(
|
|
*pBinding,
|
|
NULL,
|
|
RPC_C_AUTHN_LEVEL_NONE,
|
|
RPC_C_AUTHN_NONE,
|
|
NULL,
|
|
0,
|
|
NULL );
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
AuthnSvc = RPC_C_AUTHN_NONE;
|
|
AuthnLevel = RPC_C_AUTHN_LEVEL_NONE;
|
|
|
|
Status = CallRemoteMachine(
|
|
*pBinding,
|
|
ProtseqId,
|
|
pActParams,
|
|
pwszPathForServer,
|
|
_pwszMachine,
|
|
_fUseOldActivationInterface,
|
|
phr );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Status == RPC_S_OK && *pBinding != NULL)
|
|
{
|
|
//
|
|
// The call completed. We now cache this binding handle.
|
|
// Caching is just an optimization so we don't care if this
|
|
// insert fails.
|
|
//
|
|
InsertBinding(
|
|
*pBinding,
|
|
ProtseqId,
|
|
AuthnSvc,
|
|
pActParams->ulMarshaledTargetInfoLength,
|
|
pActParams->pMarshaledTargetInfo,
|
|
AuthnLevel,
|
|
pActParams->pAuthInfo);
|
|
*pBinding = NULL;
|
|
}
|
|
// Throw away all bindings if the protocol is unlikely to work
|
|
// again.
|
|
else if (Status == RPC_S_SERVER_UNAVAILABLE ||
|
|
Status == EPT_S_NOT_REGISTERED)
|
|
{
|
|
FlushBindings();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRemoteMachine::LookupBinding
|
|
//
|
|
// Synopsis: Scan the binding list for a binding with matching
|
|
// authentication information
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
CMachineBinding *
|
|
CRemoteMachine::LookupBinding(
|
|
IN USHORT AuthnSvc,
|
|
IN DWORD AuthnLevel,
|
|
IN COAUTHINFO * pAuthInfo OPTIONAL
|
|
)
|
|
{
|
|
CMachineBinding * pMachineBinding;
|
|
|
|
gpRemoteMachineLock->LockShared();
|
|
|
|
for ( pMachineBinding = (CMachineBinding *) _BindingList.First();
|
|
pMachineBinding;
|
|
pMachineBinding = (CMachineBinding *) pMachineBinding->Next() )
|
|
{
|
|
if ( pMachineBinding->Equal( AuthnSvc, AuthnLevel, pAuthInfo ) )
|
|
{
|
|
pMachineBinding->Reference();
|
|
break;
|
|
}
|
|
}
|
|
|
|
gpRemoteMachineLock->UnlockShared();
|
|
|
|
return pMachineBinding;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRemoteMachine::FlushBindings
|
|
//
|
|
// Synopsis: Release all entries under a lock
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
CRemoteMachine::FlushBindings()
|
|
{
|
|
gpRemoteMachineLock->LockExclusive();
|
|
|
|
FlushBindingsNoLock();
|
|
|
|
gpRemoteMachineLock->UnlockExclusive();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRemoteMachine::FlushBindingsNoLock
|
|
//
|
|
// Synopsis: Release all entries without taking a lock first.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void CRemoteMachine::FlushBindingsNoLock()
|
|
{
|
|
CMachineBinding * pMachineBinding;
|
|
|
|
while ( pMachineBinding = (CMachineBinding *) _BindingList.First() )
|
|
{
|
|
_BindingList.Remove( pMachineBinding );
|
|
pMachineBinding->Release();
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRemoteMachine::InsertBinding
|
|
//
|
|
// Synopsis: Add the specified binding handle to the cache of binding
|
|
// handles for this machine. Free it if the insertion fails.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
CRemoteMachine::InsertBinding(
|
|
IN handle_t hBinding,
|
|
IN USHORT ProtseqId,
|
|
IN USHORT AuthnSvc,
|
|
IN unsigned long ulMarshaledTargetInfoLength,
|
|
IN unsigned char* pMarshaledTargetInfo,
|
|
IN DWORD AuthnLevel,
|
|
IN COAUTHINFO * pAuthInfo OPTIONAL)
|
|
{
|
|
CMachineBinding * pMachineBinding;
|
|
CMachineBinding * pExistingBinding;
|
|
COAUTHINFO * pAuthInfoCopy;
|
|
|
|
pAuthInfoCopy = 0;
|
|
pMachineBinding = 0;
|
|
|
|
if (pAuthInfo && pAuthInfo->pAuthIdentityData)
|
|
{
|
|
// Cannot cache this particular binding.
|
|
// Just free the binding handle.
|
|
RpcBindingFree( &hBinding );
|
|
return;
|
|
}
|
|
|
|
if ( ! pAuthInfo || (CopyAuthInfo( pAuthInfo, &pAuthInfoCopy ) == S_OK) )
|
|
{
|
|
pMachineBinding = new CMachineBinding(
|
|
hBinding,
|
|
ProtseqId,
|
|
AuthnSvc,
|
|
AuthnLevel,
|
|
pAuthInfoCopy);
|
|
}
|
|
|
|
if ( ! pMachineBinding )
|
|
{
|
|
RpcBindingFree( &hBinding );
|
|
return;
|
|
}
|
|
|
|
gpRemoteMachineLock->LockExclusive();
|
|
|
|
for ( pExistingBinding = (CMachineBinding *) _BindingList.First();
|
|
pExistingBinding;
|
|
pExistingBinding = (CMachineBinding *) pExistingBinding->Next() )
|
|
{
|
|
if ( pExistingBinding->Equal( AuthnSvc, AuthnLevel, pAuthInfoCopy ) )
|
|
break;
|
|
}
|
|
|
|
if ( ! pExistingBinding )
|
|
{
|
|
pMachineBinding->SetMarshaledTargetInfo(ulMarshaledTargetInfoLength, pMarshaledTargetInfo);
|
|
_BindingList.Insert( pMachineBinding );
|
|
}
|
|
|
|
gpRemoteMachineLock->UnlockExclusive();
|
|
|
|
if ( pExistingBinding )
|
|
{
|
|
// Will delete the new binding we created above.
|
|
pMachineBinding->Release();
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRemoteMachine::RemoveBinding
|
|
//
|
|
// Synopsis: Remove the specified binding handle from the cache for
|
|
// this machine.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
CRemoteMachine::RemoveBinding(
|
|
IN CMachineBinding * pMachineBinding
|
|
)
|
|
{
|
|
CMachineBinding * pBinding;
|
|
|
|
gpRemoteMachineLock->LockExclusive();
|
|
|
|
for ( pBinding = (CMachineBinding *) _BindingList.First();
|
|
pBinding;
|
|
pBinding = (CMachineBinding *) pBinding->Next() )
|
|
{
|
|
if ( pBinding == pMachineBinding )
|
|
{
|
|
_BindingList.Remove( pMachineBinding );
|
|
pMachineBinding->Release();
|
|
break;
|
|
}
|
|
}
|
|
|
|
gpRemoteMachineLock->UnlockExclusive();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CMachineBinding::CMachineBinding
|
|
//
|
|
// Synopsis: Constructor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
// CMachineBinding
|
|
//
|
|
CMachineBinding::CMachineBinding(
|
|
IN handle_t hBinding,
|
|
IN USHORT ProtseqId,
|
|
IN USHORT AuthnSvc,
|
|
IN DWORD AuthnLevel,
|
|
IN COAUTHINFO * pAuthInfo OPTIONAL
|
|
)
|
|
{
|
|
_hBinding = hBinding;
|
|
_ProtseqId = ProtseqId;
|
|
_AuthnSvc = AuthnSvc;
|
|
_AuthnLevel = AuthnLevel;
|
|
_pAuthInfo = pAuthInfo;
|
|
_ulMarshaledTargetInfoLength = 0;
|
|
_pMarshaledTargetInfo = NULL;
|
|
|
|
ASSERT(pAuthInfo == NULL || pAuthInfo->pAuthIdentityData == NULL);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CMachineBinding::~CMachineBinding
|
|
//
|
|
// Synopsis: Destructor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
CMachineBinding::~CMachineBinding()
|
|
{
|
|
if ( _hBinding )
|
|
RpcBindingFree( &_hBinding );
|
|
|
|
if ( _pAuthInfo )
|
|
{
|
|
PrivMemFree( _pAuthInfo->pwszServerPrincName );
|
|
PrivMemFree( _pAuthInfo );
|
|
}
|
|
if (_ulMarshaledTargetInfoLength)
|
|
{
|
|
MIDL_user_free(_pMarshaledTargetInfo);
|
|
}
|
|
}
|
|
//keeps a copy of the passed in creds
|
|
RPC_STATUS CMachineBinding::SetMarshaledTargetInfo(unsigned long ulMarshaledTargetInfoLength, unsigned char *pMarshaledTargetInfo)
|
|
{
|
|
|
|
// I don't expect any existing creds here
|
|
ASSERT( (_ulMarshaledTargetInfoLength == 0) && (_pMarshaledTargetInfo == NULL));
|
|
// but just in case
|
|
if (_pMarshaledTargetInfo)
|
|
{
|
|
MIDL_user_free(_pMarshaledTargetInfo);
|
|
}
|
|
_ulMarshaledTargetInfoLength = 0;
|
|
_pMarshaledTargetInfo = NULL;
|
|
RPC_STATUS status=OR_OK;
|
|
if (ulMarshaledTargetInfoLength)
|
|
{
|
|
_pMarshaledTargetInfo = (unsigned char *) MIDL_user_allocate(ulMarshaledTargetInfoLength * sizeof(char));
|
|
if (_pMarshaledTargetInfo)
|
|
{
|
|
memcpy(_pMarshaledTargetInfo, pMarshaledTargetInfo, ulMarshaledTargetInfoLength);
|
|
_ulMarshaledTargetInfoLength = ulMarshaledTargetInfoLength;
|
|
}
|
|
else
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
return status;
|
|
|
|
}
|
|
// hands out a copy of my creds
|
|
RPC_STATUS CMachineBinding::GetMarshaledTargetInfo(unsigned long *pulMarshaledTargetInfoLength, unsigned char **pucMarshaledTargetInfo)
|
|
{
|
|
RPC_STATUS status=OR_OK;
|
|
*pulMarshaledTargetInfoLength = 0;
|
|
*pucMarshaledTargetInfo = NULL;
|
|
if (_ulMarshaledTargetInfoLength)
|
|
{
|
|
*pucMarshaledTargetInfo = (unsigned char *) MIDL_user_allocate(_ulMarshaledTargetInfoLength * sizeof(char));
|
|
if (*pucMarshaledTargetInfo)
|
|
{
|
|
memcpy(*pucMarshaledTargetInfo, _pMarshaledTargetInfo, _ulMarshaledTargetInfoLength);
|
|
*pulMarshaledTargetInfoLength = _ulMarshaledTargetInfoLength;
|
|
}
|
|
else
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CMachineBinding::Equal
|
|
//
|
|
// Synopsis: Return TRUE if the specified authentication information
|
|
// matches this binding handle. If the AUTHN_ANY flag is
|
|
// specified, do not check the authentication service but do
|
|
// check the authentication info.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL
|
|
CMachineBinding::Equal(
|
|
IN USHORT AuthnSvc,
|
|
IN DWORD AuthnLevel,
|
|
IN COAUTHINFO * pAuthInfo OPTIONAL
|
|
)
|
|
{
|
|
if (AuthnSvc == _AuthnSvc ||
|
|
(AuthnSvc == AUTHN_ANY && _AuthnSvc != RPC_C_AUTHN_NONE))
|
|
{
|
|
if (AuthnLevel > _AuthnLevel)
|
|
{
|
|
// Asking for an authentication level greater than what this
|
|
// machine binding supports.
|
|
return FALSE;
|
|
}
|
|
|
|
if (!EqualAuthInfo( pAuthInfo, _pAuthInfo ))
|
|
{
|
|
// Specified COAUTHINFO does not match.
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CallRemoteMachine
|
|
//
|
|
// Synopsis: Marshal/Unmarshal the activation parameters. Call the right
|
|
// remote activation interface.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
RPC_STATUS
|
|
CallRemoteMachine(
|
|
handle_t hBinding,
|
|
USHORT ProtseqId,
|
|
ACTIVATION_PARAMS * pActParams,
|
|
WCHAR * pwszPathForServer,
|
|
WCHAR * pwszMachine,
|
|
BOOL fUseOldActivationInterface,
|
|
HRESULT * phr
|
|
)
|
|
{
|
|
RPC_STATUS Status=RPC_S_OK;
|
|
|
|
if (fUseOldActivationInterface)
|
|
{
|
|
Status = CallOldRemoteActivation(hBinding,
|
|
ProtseqId,
|
|
pActParams,
|
|
pwszPathForServer,
|
|
pwszMachine,
|
|
phr);
|
|
}
|
|
else
|
|
{
|
|
Status = CallNewRemoteActivation(hBinding,
|
|
ProtseqId,
|
|
pActParams,
|
|
pwszPathForServer,
|
|
pwszMachine,
|
|
phr);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CallNewRemoteActivation
|
|
//
|
|
// Synopsis: Call the COM+ remote activation functions. Returns the RPC
|
|
// status of the call, not the end result. In other words, the
|
|
// call might fail because of some out of memory, or something,
|
|
// but we will still say "success".
|
|
//
|
|
// If everything works out, then when we are finished, the
|
|
// interface information will be in the actprops, and the
|
|
// ProtseqId will be in the ActParams.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
RPC_STATUS
|
|
CallNewRemoteActivation(
|
|
handle_t hBinding,
|
|
USHORT ProtseqId,
|
|
ACTIVATION_PARAMS * pActParams,
|
|
WCHAR * pwszPathForServer,
|
|
WCHAR * pwszMachine,
|
|
HRESULT * phr)
|
|
{
|
|
RPC_STATUS Status=RPC_S_OK;
|
|
|
|
//
|
|
// Setup the ActProps for the new stuff.
|
|
//
|
|
if (pwszPathForServer && (pwszPathForServer != pActParams->pwszPath))
|
|
{
|
|
ASSERT(pActParams->pInstanceInfo != NULL);
|
|
pActParams->pInstanceInfo->SetFile(pwszPathForServer, pActParams->Mode);
|
|
}
|
|
ASSERT(pActParams->pActPropsIn != NULL);
|
|
|
|
IScmRequestInfo *pRequestInfo;
|
|
*phr = pActParams->pActPropsIn->QueryInterface(IID_IScmRequestInfo,
|
|
(void**) &pRequestInfo);
|
|
|
|
if (*phr != S_OK)
|
|
return Status;
|
|
|
|
REMOTE_REQUEST_SCM_INFO *pRequest;
|
|
pRequest = (REMOTE_REQUEST_SCM_INFO *)
|
|
MIDL_user_allocate(sizeof(REMOTE_REQUEST_SCM_INFO));
|
|
if (pRequest == NULL)
|
|
{
|
|
pRequestInfo->Release();
|
|
*phr = E_OUTOFMEMORY;
|
|
return Status;
|
|
}
|
|
|
|
pRequest->ClientImpLevel = RPC_C_IMP_LEVEL_IDENTIFY;
|
|
pRequest->cRequestedProtseqs = cMyProtseqs;
|
|
pRequest->pRequestedProtseqs = (unsigned short*)
|
|
MIDL_user_allocate(sizeof(short)*pRequest->cRequestedProtseqs);
|
|
|
|
if (pRequest->pRequestedProtseqs==NULL)
|
|
{
|
|
*phr = E_OUTOFMEMORY;
|
|
MIDL_user_free(pRequest);
|
|
pRequestInfo->Release();
|
|
return RPC_S_OK;
|
|
}
|
|
memcpy(pRequest->pRequestedProtseqs, aMyProtseqs, sizeof(short)*cMyProtseqs);
|
|
|
|
pRequestInfo->SetRemoteRequestInfo(pRequest);
|
|
pRequestInfo->Release();
|
|
|
|
MInterfacePointer *pIFDIn, *pIFDOut=NULL;
|
|
*phr = ActPropsMarshalHelper(pActParams->pActPropsIn,
|
|
IID_IActivationPropertiesIn,
|
|
MSHCTX_DIFFERENTMACHINE,
|
|
MSHLFLAGS_NORMAL,
|
|
&pIFDIn);
|
|
if (*phr != S_OK)
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
RpcTryExcept
|
|
{
|
|
if (pActParams->MsgType == GETCLASSOBJECT)
|
|
{
|
|
*phr = RemoteGetClassObject(hBinding,
|
|
pActParams->ORPCthis,
|
|
pActParams->ORPCthat,
|
|
pIFDIn,
|
|
&pIFDOut);
|
|
}
|
|
else
|
|
{
|
|
*phr = RemoteCreateInstance(hBinding,
|
|
pActParams->ORPCthis,
|
|
pActParams->ORPCthat,
|
|
NULL,
|
|
pIFDIn,
|
|
&pIFDOut);
|
|
}
|
|
}
|
|
RpcExcept(TRUE)
|
|
{
|
|
Status = RpcExceptionCode();
|
|
}
|
|
RpcEndExcept;
|
|
|
|
// Don't need the marshalled [in] interface anymore.
|
|
MIDL_user_free(pIFDIn);
|
|
|
|
//
|
|
// If the status we got back was not RPC_S_OK, just return it.
|
|
// Set *phr, too, but depending on the error, the caller might
|
|
// not care.
|
|
//
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
*phr = HRESULT_FROM_WIN32(Status);
|
|
return Status;
|
|
}
|
|
|
|
if (*phr != S_OK)
|
|
{
|
|
// a-sergiv (Sergei O. Ivanov), 6-17-99
|
|
// Fix for com+ 14808/nt 355212
|
|
LogRemoteSideFailure( &pActParams->Clsid, pActParams->ClsContext, pwszMachine, pwszPathForServer, *phr);
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
// AWFUL HACK ALERT: This is too hacky even for the SCM
|
|
ActivationStream ActStream((InterfaceData*)(((BYTE*)pIFDOut)+48));
|
|
|
|
pActParams->pActPropsOut = new ActivationPropertiesOut(FALSE /* fBrokenRefCount */);
|
|
if (pActParams->pActPropsOut==NULL)
|
|
{
|
|
*phr = E_OUTOFMEMORY;
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
IScmReplyInfo *pReplyInfo;
|
|
*phr = pActParams->pActPropsOut->UnmarshalInterface(&ActStream,
|
|
IID_IScmReplyInfo,
|
|
(LPVOID*)&pReplyInfo);
|
|
if (*phr != S_OK)
|
|
{
|
|
pReplyInfo->Release();
|
|
pActParams->pActPropsOut->Release();
|
|
pActParams->pActPropsOut=NULL;
|
|
MIDL_user_free(pIFDOut);
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
// If in a remote LB router, just return
|
|
if (pActParams->RemoteActivation)
|
|
{
|
|
pReplyInfo->Release();
|
|
MIDL_user_free(pIFDOut);
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
REMOTE_REPLY_SCM_INFO *pReply;
|
|
pReplyInfo->GetRemoteReplyInfo(&pReply);
|
|
ASSERT(pReply!=NULL);
|
|
|
|
*pActParams->pOxidServer = pReply->Oxid;
|
|
|
|
//We will not have protocol bindings for custom marshalled objrefs
|
|
if ((pReply->pdsaOxidBindings) && (pReply->pdsaOxidBindings->wNumEntries))
|
|
{
|
|
ASSERT(pActParams->pOxidInfo != NULL);
|
|
pActParams->pOxidInfo->psa = (DUALSTRINGARRAY *)
|
|
MIDL_user_allocate(pReply->pdsaOxidBindings->wNumEntries
|
|
* sizeof(WCHAR) +
|
|
sizeof(DUALSTRINGARRAY));
|
|
if (pActParams->pOxidInfo->psa == NULL)
|
|
{
|
|
pReplyInfo->Release();
|
|
pActParams->pActPropsOut->Release();
|
|
pActParams->pActPropsOut=NULL;
|
|
*phr = E_OUTOFMEMORY;
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
dsaCopy(pActParams->pOxidInfo->psa, pReply->pdsaOxidBindings);
|
|
}
|
|
else
|
|
pActParams->pOxidInfo->psa = NULL;
|
|
|
|
pActParams->ProtseqId = ProtseqId;
|
|
pActParams->pOxidInfo->ipidRemUnknown = pReply->ipidRemUnknown;
|
|
pActParams->pOxidInfo->dwAuthnHint = pReply->authnHint;
|
|
pActParams->pOxidInfo->version = pReply->serverVersion;
|
|
|
|
pActParams->pIIDs = 0;
|
|
pActParams->pResults = 0;
|
|
pActParams->ppIFD = 0;
|
|
|
|
MIDL_user_free(pIFDOut);
|
|
pReplyInfo->Release();
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CallOldRemoteActivation
|
|
//
|
|
// Synopsis: Call the down-level "RemoteActivation" function, for downrev
|
|
// servers. Returns the RPC status of the call, not the end
|
|
// result. In other words, the call might fail because of some
|
|
// out of memory, or something, but we will still say "success".
|
|
//
|
|
// If everything works out, then when we are finished, the
|
|
// interface information will be in the actprops, and the
|
|
// ProtseqId will be in the ActParams.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
RPC_STATUS
|
|
CallOldRemoteActivation(
|
|
handle_t hBinding,
|
|
USHORT ProtseqId,
|
|
ACTIVATION_PARAMS * pActParams,
|
|
WCHAR * pwszPathForServer,
|
|
WCHAR * pwszMachine,
|
|
HRESULT * phr)
|
|
{
|
|
RPC_STATUS Status;
|
|
|
|
// Use Old Down-level interface
|
|
//
|
|
// all remote activations on this interface have to be made with
|
|
// minor version 1, since remote downlevel (version 5.1) servers
|
|
// refuse any other version.
|
|
pActParams->ORPCthis->version.MinorVersion = COM_MINOR_VERSION_1;
|
|
pActParams->ORPCthis->flags = ORPCF_NULL;
|
|
|
|
pActParams->ppIFD = (MInterfacePointer **)
|
|
MIDL_user_allocate(sizeof(MInterfacePointer *) *
|
|
pActParams->Interfaces);
|
|
|
|
pActParams->pResults = (HRESULT*) MIDL_user_allocate(sizeof(HRESULT) *
|
|
pActParams->Interfaces);
|
|
if ((pActParams->ppIFD == NULL) || (pActParams->pResults == NULL))
|
|
{
|
|
MIDL_user_free(pActParams->ppIFD);
|
|
MIDL_user_free(pActParams->pResults);
|
|
*phr = E_OUTOFMEMORY;
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
for (DWORD i=0; i<pActParams->Interfaces;i++)
|
|
{
|
|
pActParams->ppIFD[i] = NULL;
|
|
pActParams->pResults[i] = E_FAIL;
|
|
}
|
|
|
|
Status = RemoteActivation(
|
|
hBinding,
|
|
pActParams->ORPCthis,
|
|
pActParams->ORPCthat,
|
|
&pActParams->Clsid,
|
|
pwszPathForServer,
|
|
pActParams->pIFDStorage,
|
|
RPC_C_IMP_LEVEL_IDENTIFY,
|
|
pActParams->Mode,
|
|
pActParams->Interfaces,
|
|
pActParams->pIIDs,
|
|
cMyProtseqs,
|
|
aMyProtseqs,
|
|
pActParams->pOxidServer,
|
|
&pActParams->pOxidInfo->psa,
|
|
&pActParams->pOxidInfo->ipidRemUnknown,
|
|
&pActParams->pOxidInfo->dwAuthnHint,
|
|
&pActParams->pOxidInfo->version,
|
|
phr,
|
|
pActParams->ppIFD,
|
|
pActParams->pResults );
|
|
|
|
//
|
|
// Note that this will only give us a bad status if there is a
|
|
// communication failure.
|
|
//
|
|
if ( Status != RPC_S_OK )
|
|
return Status;
|
|
|
|
// Tweak the COMVERSION to be the lower of the two.
|
|
Status = NegotiateDCOMVersion( &pActParams->pOxidInfo->version );
|
|
if ( Status != RPC_S_OK )
|
|
return Status;
|
|
|
|
if ( (RPC_S_OK == Status) && FAILED(*phr) )
|
|
LogRemoteSideFailure( &pActParams->Clsid, pActParams->ClsContext, pwszMachine, pwszPathForServer, *phr );
|
|
|
|
if ((pActParams->MsgType == GETCLASSOBJECT) && (*phr == S_OK))
|
|
{
|
|
*pActParams->pResults = *phr;
|
|
}
|
|
|
|
|
|
//
|
|
// If the activation fails we return success for the communication
|
|
// status, but the overall operation has failed and the error will
|
|
// be propogated back to the client.
|
|
//
|
|
if ( FAILED(*phr) )
|
|
return RPC_S_OK;
|
|
|
|
//
|
|
// Anything that fails from here on out is our fault, not the server's.
|
|
// So it is now safe to remember the ProtseqId.
|
|
//
|
|
pActParams->ProtseqId = ProtseqId;
|
|
|
|
ASSERT(pActParams->pActPropsIn != NULL);
|
|
*phr =
|
|
pActParams->pActPropsIn->GetReturnActivationProperties((ActivationPropertiesOut **)
|
|
&pActParams->pActPropsOut);
|
|
if ( FAILED(*phr) )
|
|
return RPC_S_OK;
|
|
|
|
*phr = pActParams->pActPropsOut->SetMarshalledResults(pActParams->Interfaces,
|
|
pActParams->pIIDs,
|
|
pActParams->pResults,
|
|
pActParams->ppIFD);
|
|
|
|
//pActParams->pIIDs belongs to ActPropsIn
|
|
MIDL_user_free(pActParams->pResults);
|
|
for (i=0;i<pActParams->Interfaces ; i++)
|
|
MIDL_user_free(pActParams->ppIFD[i]);
|
|
MIDL_user_free(pActParams->ppIFD);
|
|
pActParams->pResults = NULL;
|
|
pActParams->ppIFD = NULL;
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CreateRemoteBinding
|
|
//
|
|
// Synopsis: Create a binding handle for the specified machine and protseq.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
RPC_STATUS
|
|
CreateRemoteBinding(
|
|
IN WCHAR * pwszMachine,
|
|
IN int ProtseqIndex,
|
|
OUT handle_t * phBinding
|
|
)
|
|
{
|
|
WCHAR * pwszStringBinding;
|
|
RPC_STATUS Status;
|
|
|
|
*phBinding = 0;
|
|
|
|
Status = RpcStringBindingCompose(
|
|
NULL,
|
|
gaProtseqInfo[aMyProtseqs[ProtseqIndex]].pwstrProtseq,
|
|
pwszMachine,
|
|
gaProtseqInfo[aMyProtseqs[ProtseqIndex]].pwstrEndpoint,
|
|
NULL,
|
|
&pwszStringBinding );
|
|
|
|
if ( Status != RPC_S_OK )
|
|
return Status;
|
|
|
|
Status = RpcBindingFromStringBinding( pwszStringBinding, phBinding );
|
|
|
|
RpcStringFree( &pwszStringBinding );
|
|
|
|
return Status;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CopyAuthIdentity
|
|
//
|
|
// Synopsis: Copy an auth identity structure and all its strings.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
CopyAuthIdentity(
|
|
IN COAUTHIDENTITY * pAuthIdentSrc,
|
|
IN COAUTHIDENTITY ** ppAuthIdentDest
|
|
)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
ULONG ulCharLen = 1;
|
|
COAUTHIDENTITY *pAuthIdentTemp = NULL;
|
|
|
|
*ppAuthIdentDest = NULL;
|
|
|
|
// Guard against both being set, although presumably this would have
|
|
// caused grief before we got to this point.
|
|
if ((pAuthIdentSrc->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) &&
|
|
(pAuthIdentSrc->Flags & SEC_WINNT_AUTH_IDENTITY_ANSI))
|
|
{
|
|
ASSERT(0 && "Both string type flags were set!");
|
|
hr = E_UNEXPECTED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pAuthIdentSrc->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE)
|
|
{
|
|
ulCharLen = sizeof(WCHAR);
|
|
}
|
|
else if (pAuthIdentSrc->Flags & SEC_WINNT_AUTH_IDENTITY_ANSI)
|
|
{
|
|
ulCharLen = sizeof(CHAR);
|
|
}
|
|
else
|
|
{
|
|
// The user didn't specify either string bit? How did we get here?
|
|
ASSERT(0 && "String type flag was not set!");
|
|
hr = E_UNEXPECTED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pAuthIdentTemp = (COAUTHIDENTITY*) ActMemAlloc(sizeof(COAUTHIDENTITY));
|
|
if (!pAuthIdentTemp)
|
|
goto Cleanup;
|
|
|
|
CopyMemory(pAuthIdentTemp, pAuthIdentSrc, sizeof(COAUTHIDENTITY));
|
|
|
|
// Strings need to be allocated individually and copied
|
|
pAuthIdentTemp->User = pAuthIdentTemp->Domain = pAuthIdentTemp->Password = NULL;
|
|
|
|
if (pAuthIdentSrc->User)
|
|
{
|
|
pAuthIdentTemp->User = (USHORT *)ActMemAlloc((pAuthIdentTemp->UserLength+1) * ulCharLen);
|
|
|
|
if (!pAuthIdentTemp->User)
|
|
goto Cleanup;
|
|
|
|
CopyMemory(pAuthIdentTemp->User, pAuthIdentSrc->User, (pAuthIdentTemp->UserLength+1) * ulCharLen);
|
|
}
|
|
|
|
if (pAuthIdentSrc->Domain)
|
|
{
|
|
pAuthIdentTemp->Domain = (USHORT *)ActMemAlloc((pAuthIdentTemp->DomainLength+1) * ulCharLen);
|
|
|
|
if (!pAuthIdentTemp->Domain)
|
|
goto Cleanup;
|
|
|
|
CopyMemory(pAuthIdentTemp->Domain, pAuthIdentSrc->Domain, (pAuthIdentTemp->DomainLength+1) * ulCharLen);
|
|
}
|
|
|
|
if (pAuthIdentSrc->Password)
|
|
{
|
|
pAuthIdentTemp->Password = (USHORT *)ActMemAlloc((pAuthIdentTemp->PasswordLength+1) * ulCharLen);
|
|
|
|
if (!pAuthIdentTemp->Password)
|
|
goto Cleanup;
|
|
|
|
CopyMemory(pAuthIdentTemp->Password, pAuthIdentSrc->Password, (pAuthIdentTemp->PasswordLength+1) * ulCharLen);
|
|
}
|
|
|
|
|
|
hr = S_OK;
|
|
|
|
Cleanup:
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*ppAuthIdentDest = pAuthIdentTemp;
|
|
}
|
|
else
|
|
{
|
|
if (pAuthIdentTemp)
|
|
{
|
|
SecurityInfo::freeCOAUTHIDENTITY(pAuthIdentTemp);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CopyAuthInfo
|
|
//
|
|
// Synopsis: Copy an auth info structure and all its sub structures.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
CopyAuthInfo(
|
|
IN COAUTHINFO * pAuthInfoSrc,
|
|
IN COAUTHINFO ** ppAuthInfoDest
|
|
)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
COAUTHINFO *pAuthInfoTemp = NULL;
|
|
|
|
*ppAuthInfoDest = NULL;
|
|
|
|
if (pAuthInfoSrc == NULL)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
pAuthInfoTemp = (COAUTHINFO*)ActMemAlloc(sizeof(COAUTHINFO));
|
|
|
|
if (!pAuthInfoTemp)
|
|
goto Cleanup;
|
|
|
|
CopyMemory(pAuthInfoTemp, pAuthInfoSrc, sizeof(COAUTHINFO));
|
|
|
|
// We need to allocate these fields and make a copy
|
|
pAuthInfoTemp->pwszServerPrincName = NULL;
|
|
pAuthInfoTemp->pAuthIdentityData = NULL;
|
|
|
|
// only alloc space for pwszServerPrincName if its non-null
|
|
if (pAuthInfoSrc->pwszServerPrincName)
|
|
{
|
|
pAuthInfoTemp->pwszServerPrincName =
|
|
(LPWSTR) ActMemAlloc((lstrlenW(pAuthInfoSrc->pwszServerPrincName) + 1) * sizeof(WCHAR));
|
|
|
|
if (!pAuthInfoTemp->pwszServerPrincName)
|
|
goto Cleanup;
|
|
|
|
lstrcpyW(pAuthInfoTemp->pwszServerPrincName, pAuthInfoSrc->pwszServerPrincName);
|
|
}
|
|
|
|
// copy the AuthIdentity if its non-null
|
|
if (pAuthInfoSrc->pAuthIdentityData)
|
|
{
|
|
hr = CopyAuthIdentity(pAuthInfoSrc->pAuthIdentityData, &pAuthInfoTemp->pAuthIdentityData);
|
|
if (FAILED(hr))
|
|
goto Cleanup;
|
|
}
|
|
hr = S_OK;
|
|
|
|
Cleanup:
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*ppAuthInfoDest = pAuthInfoTemp;
|
|
}
|
|
else if (pAuthInfoTemp)
|
|
{
|
|
SecurityInfo::freeCOAUTHINFO(pAuthInfoTemp);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: EqualAuthInfo
|
|
//
|
|
// Synopsis: Compare two auth info structures and their sub structures.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL
|
|
EqualAuthInfo(
|
|
COAUTHINFO* pAuthInfo,
|
|
COAUTHINFO* pAuthInfoOther)
|
|
{
|
|
if ( pAuthInfo && pAuthInfoOther )
|
|
{
|
|
if ( (pAuthInfo->dwAuthnSvc != pAuthInfoOther->dwAuthnSvc) ||
|
|
(pAuthInfo->dwAuthzSvc != pAuthInfoOther->dwAuthzSvc) ||
|
|
(pAuthInfo->dwAuthnLevel != pAuthInfoOther->dwAuthnLevel) ||
|
|
(pAuthInfo->dwImpersonationLevel != pAuthInfoOther->dwImpersonationLevel) ||
|
|
(pAuthInfo->dwCapabilities != pAuthInfoOther->dwCapabilities) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// only compare pwszServerPrincName's if they're both specified
|
|
if (pAuthInfo->pwszServerPrincName && pAuthInfoOther->pwszServerPrincName)
|
|
{
|
|
if ( lstrcmpW(pAuthInfo->pwszServerPrincName,
|
|
pAuthInfoOther->pwszServerPrincName) != 0 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if one was NULL, both should be NULL for equality
|
|
if (pAuthInfo->pwszServerPrincName != pAuthInfoOther->pwszServerPrincName)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
// we never cache authid, so one of them must be NULL
|
|
ASSERT(!(pAuthInfo->pAuthIdentityData && pAuthInfoOther->pAuthIdentityData));
|
|
if (pAuthInfo->pAuthIdentityData || pAuthInfoOther->pAuthIdentityData)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( pAuthInfo != pAuthInfoOther )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|