//+------------------------------------------------------------------------- // // 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; iInterfaces;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;iInterfaces ; 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; }