|
|
//+-------------------------------------------------------------------------
//
// 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; Win4Assert( 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; Win4Assert(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(); Win4Assert(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; }
//+---------------------------------------------------------------------------
//
// 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; USHORT AuthnSvc; USHORT ProtseqId; 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;
pMachineBinding = LookupBinding( AuthnSvc, pActParams->pAuthInfo );
if ( pMachineBinding ) { AuthnSvc = pMachineBinding->_AuthnSvc; Status = CallRemoteMachine( pMachineBinding->_hBinding, pMachineBinding->_ProtseqId, pActParams, pwszPathForServer, _pwszMachine, &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) { Status = PickAuthnAndActivate( pActParams, pwszPathForServer, &hBinding, AuthnSvc, 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;
RpcBindingFree( &(ping.Info(ProtseqIndex)->hRpc));
if (Status != RPC_S_OK) { RpcBindingFree(&tmpBinding); break; }
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 ); gpRemoteMachineLock->UnlockExclusive(); }
ping.Reset();
}
// Try auth info with the new binding.
if (hBinding != NULL) { Status = PickAuthnAndActivate( pActParams, pwszPathForServer, &hBinding, RPC_C_AUTHN_NONE, 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::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 USHORT ProtseqId, OUT HRESULT * phr ) { RPC_SECURITY_QOS Qos; DWORD i; void *pAuthId = NULL; RPC_STATUS Status; CMachineBinding *pMachineBinding; BOOL fTry; CDualStringArray *pdsa = NULL; COAUTHIDENTITY *pAuthIdentityFinalCopy = 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; 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; // If using RPC_C_QOS_IDENTITY_DYNAMIC, we need to make a saveable copy of the
// client's authidentity struct.
if (Qos.IdentityTracking == RPC_C_QOS_IDENTITY_DYNAMIC && pActParams->pAuthInfo->pAuthIdentityData) { hr = CopyAuthIdentity(pActParams->pAuthInfo->pAuthIdentityData, &pAuthIdentityFinalCopy); } if (SUCCEEDED(hr)) { Status = RpcBindingSetAuthInfoExW( *pBinding, pActParams->pAuthInfo->pwszServerPrincName, pActParams->pAuthInfo->dwAuthnLevel, pActParams->pAuthInfo->dwAuthnSvc, (Qos.IdentityTracking == RPC_C_QOS_IDENTITY_DYNAMIC) ? pAuthIdentityFinalCopy : pActParams->pAuthInfo->pAuthIdentityData, pActParams->pAuthInfo->dwAuthzSvc, &Qos ); // Try the activation.
if (Status == RPC_S_OK) { Status = CallRemoteMachine( *pBinding, ProtseqId, pActParams, pwszPathForServer, _pwszMachine, phr ); if (Status != RPC_S_OK) { // we won't be caching this handle, so free the COAUTHIDENTITY copy
FreeAuthIdentity(pAuthIdentityFinalCopy); pAuthIdentityFinalCopy = NULL; } } } else { // couldn't get enough memory to copy the user's COAUTHIDENTITY
*phr = E_OUTOFMEMORY; Status = RPC_S_OUT_OF_RESOURCES; } } // 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; // 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); pAuthIdentityFinalCopy = (COAUTHIDENTITY*) ComputeSvcList( pdsa->DSA() ); if (pAuthIdentityFinalCopy) { // if using snego, we need to know what sec pkg is eventually negotiated:
if (gpCRpcSecurityCallbackMgr->RegisterForRpcAuthSvcCallBack(*pBinding)) bSetSecurityCallBack = TRUE; } else { // Using snego, but ComputeSvcList returned NULL (out of memory)
*phr = E_OUTOFMEMORY; Status = RPC_S_OUT_OF_RESOURCES; } } if (Status == RPC_S_OK) { Status = RpcBindingSetAuthInfoEx( *pBinding, _pwszScmSPN, RPC_C_AUTHN_LEVEL_CONNECT, s_aRpcssSvc[i].wId, pAuthIdentityFinalCopy, 0, &Qos ); } if (Status != RPC_S_OK) { if (bSetSecurityCallBack) gpCRpcSecurityCallbackMgr->GetAuthSvcAndTurnOffCallback(*pBinding, NULL); // Free our authident copy if we have one
FreeAuthIdentity(pAuthIdentityFinalCopy); pAuthIdentityFinalCopy = NULL; } // Try the activation.
if (Status == RPC_S_OK) { Status = CallRemoteMachine( *pBinding, ProtseqId, pActParams, pwszPathForServer, _pwszMachine, phr ); if (Status != RPC_S_OK) { // call didn't work; we definitely won't be caching this handle,
// so free the authidentity copy
FreeAuthIdentity(pAuthIdentityFinalCopy); pAuthIdentityFinalCopy = NULL; } 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->GetAuthSvcAndTurnOffCallback(*pBinding, &usAuthSvcFromCallback)) { // something went wrong. In this case we don't trust what the callback
// told us. Fall back on the original behavior
bSetSecurityCallBack = FALSE; } } else { // the call did not go through
if (bSetSecurityCallBack) { // cancel the callback
gpCRpcSecurityCallbackMgr->GetAuthSvcAndTurnOffCallback(*pBinding, NULL); 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, NULL ); if ( pMachineBinding ) { Status = CallRemoteMachine( pMachineBinding->_hBinding, pMachineBinding->_ProtseqId, pActParams, pwszPathForServer, _pwszMachine, 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, &Qos ); if (Status == RPC_S_OK) { AuthnSvc = RPC_C_AUTHN_NONE; Status = CallRemoteMachine( *pBinding, ProtseqId, pActParams, pwszPathForServer, _pwszMachine, 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->pAuthInfo, pAuthIdentityFinalCopy); *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 COAUTHINFO * pAuthInfo OPTIONAL ) { CMachineBinding * pMachineBinding;
gpRemoteMachineLock->LockShared();
for ( pMachineBinding = (CMachineBinding *) _BindingList.First(); pMachineBinding; pMachineBinding = (CMachineBinding *) pMachineBinding->Next() ) { if ( pMachineBinding->Equal( AuthnSvc, 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 COAUTHINFO * pAuthInfo OPTIONAL, IN COAUTHIDENTITY* pAuthIdentityFinal ) { CMachineBinding * pMachineBinding; CMachineBinding * pExistingBinding; COAUTHINFO * pAuthInfoCopy;
pAuthInfoCopy = 0; pMachineBinding = 0;
if ( ! pAuthInfo || (CopyAuthInfo( pAuthInfo, &pAuthInfoCopy ) == S_OK) ) { pMachineBinding = new CMachineBinding( hBinding, ProtseqId, AuthnSvc, pAuthInfoCopy, pAuthIdentityFinal); }
if ( ! pMachineBinding ) { FreeAuthIdentity(pAuthIdentityFinal); RpcBindingFree( &hBinding ); return; }
gpRemoteMachineLock->LockExclusive();
for ( pExistingBinding = (CMachineBinding *) _BindingList.First(); pExistingBinding; pExistingBinding = (CMachineBinding *) pExistingBinding->Next() ) { if ( pExistingBinding->Equal( AuthnSvc, pAuthInfoCopy ) ) break; }
if ( ! pExistingBinding ) _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 COAUTHINFO * pAuthInfo OPTIONAL, IN COAUTHIDENTITY* pAuthIdentityFinal ) { _hBinding = hBinding; _ProtseqId = ProtseqId; _AuthnSvc = AuthnSvc; _pAuthInfo = pAuthInfo; _pAuthIdentity = pAuthIdentityFinal; // the only reason to hold on to this
// is because in some situations it
// must not be released until the binding
// handle is gone
}
//+---------------------------------------------------------------------------
//
// Function: CMachineBinding::~CMachineBinding
//
// Synopsis: Destructor
//
//----------------------------------------------------------------------------
CMachineBinding::~CMachineBinding() { if ( _hBinding ) RpcBindingFree( &_hBinding );
if ( _pAuthInfo ) { PrivMemFree( _pAuthInfo->pwszServerPrincName );
if ( _pAuthInfo->pAuthIdentityData ) { FreeAuthIdentity(_pAuthInfo->pAuthIdentityData); }
PrivMemFree( _pAuthInfo ); }
if (_pAuthIdentity) FreeAuthIdentity(_pAuthIdentity); }
//+---------------------------------------------------------------------------
//
// 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 COAUTHINFO * pAuthInfo OPTIONAL ) { return( (AuthnSvc == _AuthnSvc || (AuthnSvc == AUTHN_ANY && _AuthnSvc != RPC_C_AUTHN_NONE)) && EqualAuthInfo( pAuthInfo, _pAuthInfo ) ); }
//+---------------------------------------------------------------------------
//
// 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, HRESULT * phr ) { RPC_STATUS Status=RPC_S_OK;
// jsimmons 2/15/00 -- we explicitly don't cache the COM version of the target server; if
// we did this would allow us on subsequent calls to know in advance which RPC interface
// to use while calling. However, PM decision was to leave the current behavior alone.
// First try new interface
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
MIDL_user_free(pIFDIn);
if ((Status == RPC_S_OK) || (!((Status == RPC_S_UNKNOWN_IF) || (Status == RPC_S_INTERFACE_NOT_FOUND) || (Status == RPC_S_NO_INTERFACES) || (Status == RPC_E_NOT_REGISTERED)))) {
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; }
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); Win4Assert(pReply!=NULL);
*pActParams->pOxidServer = pReply->Oxid;
//We will not have protocol bindings for custom marshalled objrefs
if ((pReply->pdsaOxidBindings) && (pReply->pdsaOxidBindings->wNumEntries)) { Win4Assert(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; }
if ( pActParams->fComplusOnly ) { *phr = HRESULT_FROM_WIN32(Status); return 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 is 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)) { #if 0
*phr = *pActParams->pResults; #else
*pActParams->pResults = *phr; #endif
}
//
// 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;
pActParams->ProtseqId = ProtseqId;
ASSERT(pActParams->pActPropsIn != NULL); *phr = pActParams->pActPropsIn->GetReturnActivationProperties((ActivationPropertiesOut **) &pActParams->pActPropsOut);
*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 ) { ULONG ulBufSize; COAUTHIDENTITY* pAuthIdentTemp; ULONG ulUserLen; ULONG ulDomainLen; ULONG ulPwdLen;
*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!"); return E_UNEXPECTED; }
if (pAuthIdentSrc->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) { // Copy Unicode version of the struct
WCHAR* pEnd;
// Calculate size of the individual strings, with terminating nulls
ulUserLen = pAuthIdentSrc->User ? lstrlenW(pAuthIdentSrc->User) + 1 : 0; ulDomainLen = pAuthIdentSrc->Domain ? lstrlenW(pAuthIdentSrc->Domain) + 1 : 0; ulPwdLen = pAuthIdentSrc->Password ? lstrlenW(pAuthIdentSrc->Password) + 1 : 0;
// Calculate size of the entire buffer, in bytes
ulBufSize = sizeof(COAUTHIDENTITY) + ((ulUserLen + ulDomainLen + ulPwdLen) * sizeof(WCHAR)); // Allocate one buffer to hold struct+strings
pAuthIdentTemp = (COAUTHIDENTITY*) PrivMemAlloc(ulBufSize); if (!pAuthIdentTemp) return E_OUTOFMEMORY;
// Copy string lengths. These exclude the null.
pAuthIdentTemp->UserLength = (ulUserLen != 0) ? ulUserLen - 1 : 0; pAuthIdentTemp->DomainLength = (ulDomainLen != 0) ? ulDomainLen - 1 : 0; pAuthIdentTemp->PasswordLength = (ulPwdLen != 0) ? ulPwdLen - 1 : 0;
// Copy flags
pAuthIdentTemp->Flags = pAuthIdentSrc->Flags;
// Copy strings
pEnd = (WCHAR*)(pAuthIdentTemp + 1); // point to buffer just past end of struct
if (pAuthIdentSrc->User) { pAuthIdentTemp->User = pEnd; lstrcpyW(pEnd, pAuthIdentSrc->User); pEnd += ulUserLen; } else pAuthIdentTemp->User = NULL;
if (pAuthIdentSrc->Domain) { pAuthIdentTemp->Domain = pEnd; lstrcpyW(pEnd, pAuthIdentSrc->Domain); pEnd += ulDomainLen; } else pAuthIdentTemp->Domain = NULL;
if (pAuthIdentSrc->Password) { pAuthIdentTemp->Password = pEnd; lstrcpyW(pEnd, pAuthIdentSrc->Password); pEnd += ulPwdLen; } else pAuthIdentTemp->Password = NULL; } else if (pAuthIdentSrc->Flags & SEC_WINNT_AUTH_IDENTITY_ANSI ) { // Copy ANSI version of the struct
CHAR* pEnd;
// Calculate size of the individual strings, with terminating nulls
ulUserLen = pAuthIdentSrc->User ? lstrlenA((LPCSTR)pAuthIdentSrc->User) + 1 : 0; ulDomainLen = pAuthIdentSrc->Domain ? lstrlenA((LPCSTR)pAuthIdentSrc->Domain) + 1 : 0; ulPwdLen = pAuthIdentSrc->Password ? lstrlenA((LPCSTR)pAuthIdentSrc->Password) + 1 : 0;
// Calculate size of the entire buffer, in bytes
ulBufSize = sizeof(COAUTHIDENTITY) + ((ulUserLen + ulDomainLen + ulPwdLen) * sizeof(CHAR)); // Allocate one buffer to hold struct+strings
pAuthIdentTemp = (COAUTHIDENTITY*) PrivMemAlloc(ulBufSize); if (!pAuthIdentTemp) return E_OUTOFMEMORY;
// Copy string lengths. These exclude the null.
pAuthIdentTemp->UserLength = (ulUserLen != 0) ? ulUserLen - 1 : 0; pAuthIdentTemp->DomainLength = (ulDomainLen != 0) ? ulDomainLen - 1 : 0; pAuthIdentTemp->PasswordLength = (ulPwdLen != 0) ? ulPwdLen - 1 : 0;
// Copy flags
pAuthIdentTemp->Flags = pAuthIdentSrc->Flags;
// Copy strings
pEnd = (CHAR*)(pAuthIdentTemp + 1); // point to buffer just past end of struct
if (pAuthIdentSrc->User) { pAuthIdentTemp->User = (USHORT*)pEnd; lstrcpyA(pEnd, (LPCSTR)pAuthIdentSrc->User); pEnd += ulUserLen; } else pAuthIdentTemp->User = NULL;
if (pAuthIdentSrc->Domain) { pAuthIdentTemp->Domain = (USHORT*)pEnd; lstrcpyA(pEnd, (LPCSTR)pAuthIdentSrc->Domain); pEnd += ulDomainLen; } else pAuthIdentTemp->Domain = NULL;
if (pAuthIdentSrc->Password) { pAuthIdentTemp->Password = (USHORT*)pEnd; lstrcpyA(pEnd, (LPCSTR)pAuthIdentSrc->Password); pEnd += ulPwdLen; } else pAuthIdentTemp->Password = NULL; } else { // The user didn't specify either string bit? How did we get here?
ASSERT(0 && "String type flag was not set!"); return E_UNEXPECTED; }
*ppAuthIdentDest = pAuthIdentTemp;
return S_OK; }
//+---------------------------------------------------------------------------
//
// Function: FreeAuthInfo
//
// Synopsis: Free an auth info structure and all its substructures.
//
//----------------------------------------------------------------------------
void FreeAuthInfo(COAUTHINFO *pAuthInfo) { if (pAuthInfo) { if (pAuthInfo->pwszServerPrincName) PrivMemFree(pAuthInfo->pwszServerPrincName);
if (pAuthInfo->pAuthIdentityData) FreeAuthIdentity(pAuthInfo->pAuthIdentityData);
PrivMemFree(pAuthInfo); } }
//+---------------------------------------------------------------------------
//
// Function: FreeAuthIdentity
//
// Synopsis: Free an auth identity structure and all its sub strings.
//
// Note: because of the way the various activation paths work, we may be
// freeing one of two different structs here: either a COAUTHIDENTITY
// or a SEC_WINNT_AUTH_IDENTITY_EXW. COAUTHIDENTITY's are created\copied
// in CopyAuthIdentity above; SEC_WINNT_AUTH_IDENTITY_EXW's are created in
// ComputeSvcList. In both cases they are constructed as one contiguous
// buffer, so we don't have to care about the details here; just free it.
//
//----------------------------------------------------------------------------
void FreeAuthIdentity(void* pAuthIdentity) { if (pAuthIdentity) { PrivMemFree(pAuthIdentity); } }
//+---------------------------------------------------------------------------
//
// 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; *ppAuthInfoDest = NULL;
if (pAuthInfoSrc == NULL) { *ppAuthInfoDest = NULL; return S_OK; } if ( !(*ppAuthInfoDest = (COAUTHINFO*)PrivMemAlloc(sizeof(COAUTHINFO))) ) { goto COPY_AUTHINFO_EXIT; }
// only alloc space for pwszServerPrincName if its non-null
if (pAuthInfoSrc->pwszServerPrincName) { if ( !((*ppAuthInfoDest)->pwszServerPrincName = (LPWSTR)PrivMemAlloc((lstrlenW(pAuthInfoSrc->pwszServerPrincName) + 1) * sizeof(WCHAR))) ) { goto COPY_AUTHINFO_EXIT; } } else { (*ppAuthInfoDest)->pwszServerPrincName = NULL; }
// copy the AuthIdentity if its non-null
if (pAuthInfoSrc->pAuthIdentityData) { if ( FAILED(CopyAuthIdentity(pAuthInfoSrc->pAuthIdentityData, &((*ppAuthInfoDest)->pAuthIdentityData))) ) { goto COPY_AUTHINFO_EXIT; } } else { (*ppAuthInfoDest)->pAuthIdentityData = NULL; }
(*ppAuthInfoDest)->dwAuthnSvc = pAuthInfoSrc->dwAuthnSvc; (*ppAuthInfoDest)->dwAuthzSvc = pAuthInfoSrc->dwAuthzSvc; (*ppAuthInfoDest)->dwAuthnLevel = pAuthInfoSrc->dwAuthnLevel; (*ppAuthInfoDest)->dwImpersonationLevel = pAuthInfoSrc->dwImpersonationLevel; (*ppAuthInfoDest)->dwCapabilities = pAuthInfoSrc->dwCapabilities;
if (pAuthInfoSrc->pwszServerPrincName) { lstrcpyW((*ppAuthInfoDest)->pwszServerPrincName,pAuthInfoSrc->pwszServerPrincName); }
return S_OK;
COPY_AUTHINFO_EXIT:
if ( *ppAuthInfoDest ) { if ( (*ppAuthInfoDest)->pwszServerPrincName ) { PrivMemFree( (*ppAuthInfoDest)->pwszServerPrincName ); } PrivMemFree( *ppAuthInfoDest ); }
return hr; }
//+---------------------------------------------------------------------------
//
// Function: EqualAuthIdentity
//
// Synopsis: Compare two auth identity structures by member.
//
//----------------------------------------------------------------------------
BOOL EqualAuthIdentity( COAUTHIDENTITY* pAuthIdent, COAUTHIDENTITY* pAuthIdentOther ) { if ( pAuthIdent->Flags != pAuthIdentOther->Flags ) { return FALSE; }
ULONG cch;
if ( pAuthIdent->User && pAuthIdentOther->User ) { if ( (cch = pAuthIdent->UserLength) != pAuthIdentOther->UserLength ) { return FALSE; }
if ( memcmp(pAuthIdent->User,pAuthIdentOther->User,(cch+1) * sizeof(WCHAR)) != 0 ) { return FALSE; } } else if ( pAuthIdent->User || pAuthIdentOther->User ) return FALSE;
if ( pAuthIdent->Domain && pAuthIdentOther->Domain ) { if ( (cch = pAuthIdent->DomainLength) != pAuthIdentOther->DomainLength ) { return FALSE; }
if ( memcmp(pAuthIdent->Domain,pAuthIdentOther->Domain,(cch+1) * sizeof(WCHAR)) != 0 ) { return FALSE; } } else if ( pAuthIdent->Domain || pAuthIdentOther->Domain ) return FALSE;
if ( pAuthIdent->Password && pAuthIdentOther->Password ) { if ( (cch = pAuthIdent->PasswordLength) != pAuthIdentOther->PasswordLength ) { return FALSE; }
if ( memcmp(pAuthIdent->Password,pAuthIdentOther->Password,(cch+1) * sizeof(WCHAR)) != 0 ) { return FALSE; } } else if ( pAuthIdent->Password || pAuthIdentOther->Password ) return FALSE;
return TRUE; }
//+---------------------------------------------------------------------------
//
// 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; } }
if (pAuthInfo->pAuthIdentityData && pAuthInfoOther->pAuthIdentityData) { if (!(EqualAuthIdentity(pAuthInfo->pAuthIdentityData, pAuthInfoOther->pAuthIdentityData)) ) { return FALSE; } } else { // if either authident was NULL, they should both be NULL for equality
if (pAuthInfo->pAuthIdentityData != pAuthInfoOther->pAuthIdentityData) { return FALSE; } } } else { if ( pAuthInfo != pAuthInfoOther ) { return FALSE; } }
return TRUE; }
|