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