//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1993. // // File: srvreg.hxx // // Contents: Classes used for keeping track of end points for a given // class. // // Classes: SClsSrvHandle // // History: 03-Jan-94 Ricksa Created // 01-Feb-96 BruceMa Support psid checking // //-------------------------------------------------------------------------- #include #pragma hdrstop #include "scm.hxx" #include "port.hxx" #include "srvreg.hxx" #include "or.hxx" #ifdef _CHICAGO_ BOOL NotifyToInitializeRpc(HWND hwnd); DWORD GetOleNotificationWnd(); CStaticPortableMutex CSrvRegList::s_mxsOnlyOne; // mutex semaphore #endif // Mutex to protect multithreaded access to class data CStaticPortableMutex CSrvRegList::s_mxsSyncAccess; BOOL CSrvRegList::s_fForcedScmShutdown = FALSE; //+------------------------------------------------------------------------- // // Member: SSrvRegistration::Free // // Synopsis: Clean up array entry // // Algorithm: Loop through any handle that exists and close the // RPC binding on that handle. // // History: 03-Jan-94 Ricksa Created // //-------------------------------------------------------------------------- void SSrvRegistration::Free(void) { if (_hRpc != NULL) { #ifndef _CHICAGO_ if ( _dwHandleCount == 0 ) { RpcBindingFree(&_hRpc); _hRpc = (RPC_COOKIE) NULL; if (_hRpcAnonymous) { RpcBindingFree(&_hRpcAnonymous); _hRpcAnonymous = 0; } } #else ScmMemFree(_hRpc); _hRpc = (RPC_COOKIE) NULL; #endif } _dwFlags = SRV_REG_INVALID; #ifndef _CHICAGO_ PrivMemFree (_psid); _psid = NULL; PrivMemFree (_pwszWinstaDesktop); _pwszWinstaDesktop = NULL; #endif } //+------------------------------------------------------------------------- // // Member: CSrvRegList::~CSrvRegList // // Synopsis: Clean up a handle object // // Algorithm: Loop through any handle that exists and close the // RPC binding on that handle. // // History: 03-Jan-94 Ricksa Created // // Notes: This should only be used by the update thread so it // doesn't need to be locked // // BillMo: add assertion to ensure we're never removing // registrations that a client may need. // I have added a flag so that we can force the close of // RPC handles during a forced scm shutdown. // //-------------------------------------------------------------------------- CSrvRegList::~CSrvRegList(void) { #ifdef _CHICAGO_ Win4Assert(!InUse()); #else if (!s_fForcedScmShutdown) { Win4Assert(!InUse()); } else // Search for all open RPC handles. if (GetSize() > 0) { // Get pointer to the base of the array SSrvRegistration *pssrvreg = (SSrvRegistration *) GetAt(0); // Loop through array for (int i = 0; i < GetSize(); i++, pssrvreg++) { // Tell RPC we no longer need the handle if (pssrvreg->_hRpc != NULL) { pssrvreg->Free(); } } } #endif } //+------------------------------------------------------------------------- // // Member: CSrvRegList::Insert // // Synopsis: Insert a new registration into the list // // Arguments: [ssrvreg] - an entry for the table // // Returns: 0 - Error occurred and we could not register the handle // ~0 - Handle registered successfully // // Algorithm: Create a handle to an RPC bind. Then search the table for // a place to put the binding. Stick the bind in the first // available spot. // // History: 03-Jan-94 Ricksa Created // //-------------------------------------------------------------------------- DWORD CSrvRegList::Insert( IFSECURITY(PSID psid) WCHAR *pwszWinstaDesktop, #ifdef DCOM PHPROCESS phProcess, OXID oxid, IPID ipid, #else WCHAR *pwszEndpoint, #endif DWORD dwFlags) { // Protect from multiple updates CStaticPortableLock lck(s_mxsSyncAccess); int iNew = 0; DWORD dwReg = 0; // Create an RPC bind SSrvRegistration ssrvregNew; ssrvregNew._dwFlags = (dwFlags == REGCLS_SURROGATE) ? REGCLS_MULTIPLEUSE : dwFlags; ssrvregNew._fSurrogate = (dwFlags == REGCLS_SURROGATE); SSrvRegistration *pssrvreg; #ifndef _CHICAGO_ RPC_STATUS status; // Get the string binding information from the process object. ssrvregNew._hRpc = ((CProcess *)phProcess)->GetBindingHandle(); if (ssrvregNew._hRpc == 0) return 0; status = I_RpcBindingSetAsync(ssrvregNew._hRpc, 0); if (status != RPC_S_OK) return 0; ssrvregNew._oxid = oxid; ssrvregNew._ipid = ipid; status = RpcBindingSetObject( ssrvregNew._hRpc, (GUID *) &ipid ); if (status != ERROR_SUCCESS) { return 0; } ssrvregNew._dwHandleCount = 0; ssrvregNew._hRpcAnonymous = 0; #else // _CHICAGO_ ssrvregNew._hRpc = (WCHAR *)ScmMemAlloc(sizeof(WCHAR) * (lstrlenW(pwszEndpoint)+1)); ssrvregNew._ulWnd = GetOleNotificationWnd(); if (ssrvregNew._hRpc == NULL) { return(0); } lstrcpyW(ssrvregNew._hRpc, pwszEndpoint); #endif // _CHICAGO_ #ifndef _CHICAGO_ ULONG ulLength; NTSTATUS NtStatus; ulLength = RtlLengthSid (psid); ssrvregNew._psid = PrivMemAlloc (ulLength); if (ssrvregNew._psid == NULL) { goto errRet; } NtStatus = RtlCopySid (ulLength, ssrvregNew._psid, psid); Win4Assert (NT_SUCCESS(NtStatus) && "CSrvRegList::Insert"); if (!NT_SUCCESS(NtStatus)) { goto errRet; } #endif // CHICAGO if (pwszWinstaDesktop != NULL) { #ifdef _CHICAGO_ Win4Assert(FALSE); #endif ssrvregNew._pwszWinstaDesktop = (WCHAR *) PrivMemAlloc((lstrlenW(pwszWinstaDesktop) + 1) * sizeof(WCHAR)); if (ssrvregNew._pwszWinstaDesktop == NULL) { goto errRet; } lstrcpyW (ssrvregNew._pwszWinstaDesktop, pwszWinstaDesktop); } else { ssrvregNew._pwszWinstaDesktop = NULL; } // Put bind in our table // Search for first empty bucket that we have if (GetSize() > 0) { pssrvreg = (SSrvRegistration *) GetAt(0); for ( ; iNew < GetSize(); iNew++, pssrvreg++) { if (pssrvreg->_hRpc == 0) { // Found an empty bucket break; } } } if (iNew < GetSize()) { memcpy(pssrvreg, &ssrvregNew, sizeof(ssrvregNew)); } else if (!InsertAt(iNew, &ssrvregNew)) { goto errRet; } CairoleDebugOut((DEB_ITRACE, "CSrvRegList::Insert() -> %08X\n", ssrvregNew._hRpc)); return (DWORD) ssrvregNew._hRpc; errRet: ssrvregNew.Free(); return(0); }; //+------------------------------------------------------------------------- // // Member: CSrvRegList::Delete // // Synopsis: Delete an end point from the list of registered end points // // Arguments: [dwReg] - value used for registration // // Returns: TRUE - LAST registration deleted // FALSE - other registrations still exist // // Algorithm: Convert the registration to the RPC handle and then // loop through the table of registrations to see if the // we can find it. If we do, tell RPC to dump the handle. // // History: 03-Jan-94 Ricksa Created // //-------------------------------------------------------------------------- BOOL CSrvRegList::Delete(RPC_COOKIE hRpc) { BOOL fLast = TRUE; if (hRpc != (RPC_COOKIE)NULL) { // Protect from multiple updates CStaticPortableLock lck(s_mxsSyncAccess); // For Daytona, Registration is actually the RPC handle // For Chicago, Registration is the strings address // Search for matching entry in table SSrvRegistration *pssrvreg = (SSrvRegistration *) GetAt(0); for (int i = 0; i < GetSize(); i++, pssrvreg++) { if (pssrvreg->_hRpc == NULL) { continue; } else if (pssrvreg->_hRpc == hRpc) { pssrvreg->Free(); } else { fLast = FALSE; } } } else { fLast = FALSE; } return fLast; } //+------------------------------------------------------------------------- // // Member: CSrvRegList::GetHandle // // Synopsis: Get a handle from the list of handles for the class // // Arguments: [psid] -- security id of client process // [pwszWinstaDesktop] // [rh] -- binding handle // [fSurrogate] -- flag indicating that we want to find a // surrogate process // // Returns: NULL - could not find a valid registration // ~NULL - handle to a running RPC server. // // Algorithm: Loop through the list searching for the first non-null // entry that we can use as a handle to an object server. // // History: 03-Jan-94 Ricksa Created // //-------------------------------------------------------------------------- BOOL CSrvRegList::GetHandle(IFSECURITY(PSID psid) WCHAR * pwszWinstaDesktop, CPortableRpcHandle &rh, BOOL fSurrogate) { SSrvRegistration *pssrvreg = 0; HWND hWnd; do { #ifdef _CHICAGO_ CStaticPortableLock lckOnlyOne(s_mxsOnlyOne); #endif CairoleDebugOut((DEB_ITRACE, "CSrvRegList::GetHandle(%x) called (%x)\n", this,pssrvreg)); { // begin mutex block hWnd = 0; // bracket for mutex // Protect from multiple updates CStaticPortableLock lck(s_mxsSyncAccess); // Search for first non-empty bucket that we have pssrvreg = (SSrvRegistration *) GetAt(0); for (int i = 0; i < GetSize(); i++, pssrvreg++) { if ( (pssrvreg->_hRpc != NULL) && (pssrvreg->_dwFlags != SRV_REG_INVALID) ) { // if we're looking for a surrogate... if(fSurrogate) { if(!(pssrvreg->_fSurrogate)) { continue; } } #ifndef _CHICAGO_ // // Client's SID param is null when connecting to services // or RunAs servers. In those instances we just take the // first binding handle we find. Only a server running in // the correct security context could register a handle. // // For connecting to "activate as activator" server, both // the SID and, for local activations, the winsta\desktop // must match the server's. // if ( psid != NULL ) { if ( ! RtlEqualSid(pssrvreg->_psid, psid) ) continue; if ( (pwszWinstaDesktop != NULL) && (lstrcmpW(pwszWinstaDesktop, pssrvreg->_pwszWinstaDesktop) != 0) ) continue; } #endif // // On Chicago there are no SIDs nor desktops, so you get // the first handle in the list. // rh.SetRpcCookie( pssrvreg->_hRpc, pssrvreg->_dwFlags == REGCLS_SINGLEUSE, pssrvreg->_ipid ); #ifndef _CHICAGO_ pssrvreg->_dwHandleCount++; #endif // Is this a single use registration? if (pssrvreg->_dwFlags == REGCLS_SINGLEUSE) { CairoleDebugOut((DEB_ITRACE, "CSrvRegList::GetHandle(%x) REGCLS_SINGLEUSE. Nulling handle pssrvreg(%x)\n", this,pssrvreg)); // For the single use class, the call owns the handle now. // So, we clear it and then try to free any other data // associated with the entry. #ifdef _CHICAGO_ if (pssrvreg->_ulWnd == 0) #endif // _CHICAGO_ { pssrvreg->_hRpc = NULL; pssrvreg->Free(); } } #ifdef _CHICAGO_ if(pssrvreg->_ulWnd) { // notify the server once to launch rpc // has to be done outside of the mutex hWnd = (HWND)pssrvreg->_ulWnd; pssrvreg->_ulWnd = 0; CairoleDebugOut((DEB_ITRACE, "CSrvRegList::GetHandle(%x) found with hWnd(%d)\n", this, hWnd)); // fall out of loop and notify the server // outside of the mutex break; } #endif // _CHICAGO_ CairoleDebugOut((DEB_ITRACE, "CSrvRegList::GetHandle(%x) called (%x) done TRUE\n", this,pssrvreg)); return(TRUE); } } } // end mutex block #ifdef _CHICAGO_ if(hWnd) { // notify the server once to launch rpc NotifyToInitializeRpc(hWnd); // find the class again; since we released the // mutex the server might have disappeared } else #endif // _CHICAGO_ break; } while (TRUE); CairoleDebugOut((DEB_ITRACE, "CSrvRegList::GetHandle(%x) called (%x) done FALSE\n", this,pssrvreg)); return(FALSE); } //+------------------------------------------------------------------------- // // Member: CSrvRegList::FindCompatibleSurrogate // // Synopsis: Finds a server for an existing surrogate process // with the desired attributes // // Arguments: [psid] -- security id of client process // [rh] -- binding handle // // Returns: TRUE - it found a server with the desired // attributes -- the rh reference or ppIObjServer // is set to that of the appropriate server // FALSE - no appropriate server // // Algorithm: Ask GetHandle or GetProxy // to search for a surrogate for us with the // desired attributes // // 21-JUN-96 t-adame created // //-------------------------------------------------------------------------- BOOL CSrvRegList::FindCompatibleSurrogate(IFSECURITY(PSID psid) WCHAR* pwszWinstaDesktop, CPortableRpcHandle &rh) { // we set the fSurrogate parameter of GetHandle to TRUE to let // GetHandle know that we need a binding handle for a surrogate // process return GetHandle(IFSECURITY(psid) pwszWinstaDesktop,rh,TRUE); } //+------------------------------------------------------------------------- // // Member: CSrvRegList::InUse // // Synopsis: See whether there is anything current registration // // Returns: TRUE - server is currently registered // FALSE - server is not currently registered // // Algorithm: Loop through the list searching for the first non-null // entry. If we find one return NULL. // // History: 04-Jan-94 Ricksa Created // // Notes: This should only be used by the update thread so it // doesn't need to be locked // //-------------------------------------------------------------------------- BOOL CSrvRegList::InUse(void) { // Protect from multiple updates CStaticPortableLock lck(s_mxsSyncAccess); // Assume there are no current registrations. BOOL fResult = FALSE; // Search for first non-empty bucket that we have SSrvRegistration *pssrvreg = (SSrvRegistration *) GetAt(0); for (int i = 0; i < GetSize(); i++, pssrvreg++) { if (pssrvreg->_hRpc != NULL) { fResult = TRUE; break; } } return fResult; } //+------------------------------------------------------------------------- // // Member: CSrvRegList::VerifyHandle // // Synopsis: Whether this is a valid handle to an object server // // Returns: TRUE - this is still a valid handle to an object server // FALSE - this is no longer a valid handle to an object server // // History: 11-May-94 DonnaLi Created // // Notes: This should only be used to assist the retry logic. // //-------------------------------------------------------------------------- BOOL CSrvRegList::VerifyHandle(RPC_COOKIE hRpc) { if (hRpc == (RPC_COOKIE)NULL) return FALSE; // Protect from multiple updates CStaticPortableLock lck(s_mxsSyncAccess); // Search for first non-empty bucket that we have SSrvRegistration *pssrvreg = (SSrvRegistration *) GetAt(0); for (int i = 0; i < GetSize(); i++, pssrvreg++) { if ( pssrvreg->_hRpc == hRpc #ifndef _CHICAG0_ && pssrvreg->_dwFlags != SRV_REG_INVALID #endif ) { return TRUE; } } return FALSE; } //+------------------------------------------------------------------------- // // Member: CSrvRegList::InvalidateHandle // // Synopsis: Invalidate a handle in the list of handles for the class // Caller of GetHandle concluded it is no longer valid // // Arguments: [hRpc] -- handle to invalidate // // Returns: None // // Algorithm: Loop through the list searching for match on handle or // list is exhausted // // History: 20-Sep-95 MurthyS Created // //-------------------------------------------------------------------------- VOID CSrvRegList::InvalidateHandle( RPC_COOKIE hRpc ) { if (hRpc == (RPC_COOKIE)NULL) { return; } // Protect from multiple updates CStaticPortableLock lck(s_mxsSyncAccess); // Search for first non-empty bucket that we have SSrvRegistration *pssrvreg = (SSrvRegistration *) GetAt(0); for (int i = 0; i < GetSize(); i++, pssrvreg++) { if (pssrvreg->_hRpc == hRpc) { // // At least try to free it. Its remotely possible that another // thread will still have a reference which will cause us to // orphan this handle. // // But we have to NULL it now so that InUse() will work right, // so that the retry logic for launching a server will work right. // This is all very gross and nasty and needs to be redone from // scratch. // // Fix it in NT 5.0. // pssrvreg->Free(); pssrvreg->_hRpc == NULL; return; } } } void CSrvRegList::GetAnonymousHandle( CPortableRpcHandle &rh, handle_t * phRpcAnonymous ) { RPC_STATUS RpcStatus; RPC_SECURITY_QOS Qos; BOOL bMatch; *phRpcAnonymous = 0; bMatch = FALSE; // Protect from multiple updates CStaticPortableLock lck(s_mxsSyncAccess); // Search for first non-empty bucket that we have SSrvRegistration *pssrvreg = (SSrvRegistration *) GetAt(0); for (int i = 0; i < GetSize(); i++, pssrvreg++) { if ( (pssrvreg->_hRpc == rh.GetHandle()) && (pssrvreg->_dwFlags != SRV_REG_INVALID) ) { Win4Assert( pssrvreg->_dwHandleCount ); if ( pssrvreg->_hRpcAnonymous ) { *phRpcAnonymous = pssrvreg->_hRpcAnonymous; return; } bMatch = TRUE; break; } } // // We continue if we don't find the handle in the table if its a single // use handle because it is removed from the table in GetHandle in that // case. // if ( ! bMatch && ! rh.fSingleUse() ) return; IPID ipid; ipid = bMatch ? pssrvreg->_ipid : rh.Ipid(); // // Note that we indicate impersonation level of identify because // anonymous is not supported. However specifing no authentication // in SetAuthInfo will keep the server from impersonating us. // Qos.Version = RPC_C_SECURITY_QOS_VERSION; Qos.Capabilities = RPC_C_QOS_CAPABILITIES_DEFAULT; Qos.ImpersonationType = RPC_C_IMP_LEVEL_IDENTIFY; Qos.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC; RpcStatus = RpcBindingCopy( rh.GetHandle(), phRpcAnonymous ); if ( RpcStatus != RPC_S_OK ) return; RpcStatus = I_RpcBindingSetAsync( *phRpcAnonymous, 0 ); if ( RpcStatus != RPC_S_OK ) goto GetAnonymousHandleExit; RpcStatus = RpcBindingSetObject( *phRpcAnonymous, (GUID *) &ipid ); if ( RpcStatus != RPC_S_OK ) goto GetAnonymousHandleExit; RpcStatus = RpcBindingSetAuthInfoEx( *phRpcAnonymous, NULL, RPC_C_AUTHN_LEVEL_NONE, RPC_C_AUTHN_WINNT, NULL, 0, &Qos ); GetAnonymousHandleExit: if ( (RpcStatus == RPC_S_OK) && bMatch ) pssrvreg->_hRpcAnonymous = *phRpcAnonymous; if ( (RpcStatus != RPC_S_OK) && *phRpcAnonymous ) { RpcBindingFree( phRpcAnonymous ); *phRpcAnonymous = 0; } } #ifndef _CHICAGO_ VOID CSrvRegList::DecHandleCount( RPC_COOKIE hRpc ) { if ( hRpc == (RPC_COOKIE)NULL ) return; // Protect from multiple updates CStaticPortableLock lck(s_mxsSyncAccess); // Search for first non-empty bucket that we have SSrvRegistration *pssrvreg = (SSrvRegistration *) GetAt(0); for (int i = 0; i < GetSize(); i++, pssrvreg++) { if (pssrvreg->_hRpc == hRpc) { if ( pssrvreg->_dwHandleCount > 0 ) pssrvreg->_dwHandleCount--; if ( (pssrvreg->_dwHandleCount == 0) && (pssrvreg->_dwFlags == SRV_REG_INVALID) && (pssrvreg->_hRpc != 0) ) { RpcBindingFree( &pssrvreg->_hRpc ); pssrvreg->_hRpc == NULL; if ( pssrvreg->_hRpcAnonymous ) { RpcBindingFree( &pssrvreg->_hRpcAnonymous ); pssrvreg->_hRpcAnonymous == NULL; } } return; } } } #endif