//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1993. // // File: cls.cxx // // Contents: Methods implementing classes defined in cls.hxx // // Functions: RetryRpc // CClassData::CClassData // CClassData::GetServer // CClassData::GetInProcServerInfo // CClassData::GetSurrogateCmdLine // CClassCacheList::CClassCacheList // CClassCacheList::GetClassData // CClassData::Defined // CClassCacheList::Add // CClassCacheList::SetEndPoint // CClassCacheList::StopServer // // History: 21-Apr-93 Ricksa Created // 31-Dec-93 ErikGav Chicago port // 24-Mar-94 JohannP Delaying rpc initialization on Chicago // 10-Jan-96 BruceMa Added support for per-user registry // //-------------------------------------------------------------------------- #include #pragma hdrstop #ifdef DCOM #include "or.hxx" #endif #include "scm.hxx" #include #include #include #include "port.hxx" #include #include #include "cls.hxx" #include "init.hxx" #ifdef DCOM #include #include "remact.hxx" #endif #ifndef _CHICAGO_ #include #endif extern WCHAR SCMMachineName[]; extern HKEY g_hkAppID; #ifndef _CHICAGO_ extern SC_HANDLE hServiceController; extern HANDLE ghDfs; #endif // Number of times we will try to contact object server in the case of // RPC failures. #define MAX_OBJSRV_RETRIES 2 // Maximum number of times we will let the server tell us we are busy #define MAX_BUSY_RETRIES 3 static WCHAR wszEnableDCOM[] = L"EnableDCOM"; static WCHAR wszPersonalClasses[] = L"PersonalClasses"; static WCHAR wszActivationSecurity[] = L"ActivationSecurity"; static WCHAR wszLaunchPermission[] = L"LaunchPermission"; static WCHAR wszDefaultLaunchPermission[] = L"DefaultLaunchPermission"; static WCHAR wszOleKey[] = L"SOFTWARE\\Microsoft\\OLE"; static WCHAR wszSurrogateName[] = L"dllhost.exe"; WCHAR wszDllSurrogatePathValue[] = L"DllSurrogate"; // string to pass to CSafeLocalServer constructor // for creating a lock for surrogate activations #define wszSurrogateServerAlias L"_SURROGATE_EXE_" WCHAR wszSOFTWAREClassesCLSID[] = L"SOFTWARE\\Classes\\CLSID"; WCHAR wszSOFTWAREClassesAppID[] = L"SOFTWARE\\Classes\\AppID"; #ifndef _CHICAGO_ extern HANDLE g_hForceWakeUpEvent; #endif #define MAX_CLASS_ENTRIES 256 #ifdef DCOM BOOL CkIfRemoteCallAllowed(void); HRESULT GetUserSidHelper(PSID *ppUserSid); HRESULT GetRegistrySecDesc( HKEY hKey, WCHAR *pValue, SECURITY_DESCRIPTOR **pSD ); #endif CScmLock gClassCacheLock(g_hrInit); // Thread model match table. The table's first index is the threading // model of the process and can be either APT_THREADED or // FREE_THREADED. The second index is any one of the types of threading // model's for DLLs. BOOL afThreadModelMatch[2][4] = {{TRUE, FALSE, TRUE, TRUE}, {FALSE, TRUE, FALSE, TRUE}}; #if DBG == 1 char *apszModelStrings[4] = {"Apartment","Free","Single","Both"}; #define ModelStringMask 0x3 #endif #define TRY_LOCAL_ACTIVATION 0 #define DONT_TRY_LOCAL_ACTIVATION 1 #ifdef _CHICAGO_ BOOL StartLocalService(); #endif // _CHICAGO_ #ifdef DCOM //+--------------------------------------------------------------------------- // // Activation // // Main entry point for both local and remote activations. // // Notes concerning CO_E_SERVER_STOPPING : // // It is possible for the SCM to Server calls to fail with // CO_E_SERVER_STOPPING. An example scenario: // Server // - does a CoRegisterClassObject for SINGLE USE resulting // in an entry in the SCM's Class cache and an entry in // OLE's DLL cache // - (acting as its own client) server proceeds to consume // it via CoCreateInstance. In this case the entry in the // DLL cache is found and consumed leaving the SCM's Class // cache in an inconsistent state (yes...a bug, but reality // intrudes). Also, because SINGLE USE was indicated entry // in DLL cache is marked such that it will not be handed // out again. // - if another CoCreateInstance comes along (server itself // or another client) the request will go to the SCM which // in turn will issue RemCoGetActiveClassObject to the // server. Server will return CO_E_SERVER_STOPPING after // failing to find it in the DLL cache. // // To get around this problem we continue and attempt to activate // a new instance of the server. // //---------------------------------------------------------------------------- HRESULT Activation( PACTIVATION_PARAMS pActParams ) { HRESULT hr; RPC_STATUS Status; DWORD n; BOOL ServerStarted = FALSE; BOOL ActivatedRemote = FALSE; BOOL fProcessReference = FALSE; CClassData *pClassData; pActParams->UnsecureActivation = FALSE; pActParams->ORPCthis->flags = ORPCF_LOCAL; pActParams->Localthis->callcat = CALLCAT_SYNCHRONOUS; pActParams->ORPCthat->flags = 0; pActParams->ORPCthat->extensions = NULL; pActParams->ProtseqId = 0; *pActParams->pOxidServer = 0; for ( n = 0; n < pActParams->Interfaces; n++ ) pActParams->pResults[n] = E_FAIL; if ( ! pActParams->RemoteActivation ) { pActParams->pOxidInfo->psa = 0; *pActParams->ppServerORBindings = 0; } if ( ! pActParams->RemoteActivation && ! pActParams->DynamicSecurity ) { // // A local activation passes the "magic" signature from CRpcResolver. // Because ORPC calls cannot have context handles, the signature // is just a pointer the process object. // pActParams->pProcess = ReferenceProcess(pActParams->ProcessSignature, TRUE); if (0 == pActParams->pProcess) { #if DBG DbgPrint("SCM: Unable to unmarshall process signature: %p, %p\n", pActParams, pActParams->ProcessSignature); #endif return(E_ACCESSDENIED); } pActParams->pToken = pActParams->pProcess->GetToken(); fProcessReference = TRUE; } else { // // Remote activations don't have the token signature. // We create tokens that are short lived right now. Do we want // to keep them around forever? // Status = LookupOrCreateToken( pActParams->hRpc, FALSE, &pActParams->pToken ); // // ERROR_ACCESS_DENIED is returned if RpcImpersonateClient fails. // We will take this to mean that the remote activation is coming // in unsecure. In this case we have no Token object and must do // permission checks manually. We set the winsta/desktop to an // empty string to distinguish this case during some ROT lookups. // Yet another wonderful piece of logic in this grotesque code. // // Unsecure remote clients can only connect to services or RunAs // servers. // if ( Status == ERROR_ACCESS_DENIED ) { pActParams->pToken = 0; pActParams->pwszWinstaDesktop = L""; pActParams->UnsecureActivation = TRUE; Status = RPC_S_OK; } if ( Status != RPC_S_OK ) return HRESULT_FROM_WIN32(Status); } Win4Assert( pActParams->pToken || pActParams->UnsecureActivation ); // If per-user registry is turned on we'll need our sid. // If we already have seen this user then we've cached a basic // sid for him. Otherwise, the initial sid we get becomes his // standard sid. By using a common sid for a user we can compare // with == rather than RtlEqualSid // // NT 5.0 // if (gpClassCache->GetPersonalClasses()) // { // PSID pUserSid = gpClassCache->GetHkeyPsid(pActParams->pToken->GetSid()); // } hr = gpClassCache->GetClassData( *pActParams->Clsid, &pClassData, FALSE, FALSE ); if ( pActParams->pwszServer ) { WCHAR * pwszServerName; pwszServerName = pActParams->pwszServer; if ( pwszServerName[0] == L'\\' && pwszServerName[1] == L'\\' ) pwszServerName += 2; // // An explicit server machine name param equal to the machine we're // on means the client doesn't want the activation to be remoted. // An explicit server name other than our name implies CLSTXT_REMOTE_SERVER. // if ( lstrcmpiW(pwszServerName, SCMMachineName) == 0 ) pActParams->ClsContext &= ~CLSCTX_REMOTE_SERVER; else pActParams->ClsContext |= CLSCTX_REMOTE_SERVER; } else { // // CLSCTX_REMOTE_SERVER is implied if either ActivateAtStorage or // RemoteServerName is present on the CLSID. // if ( ! pActParams->RemoteActivation && pClassData && (pClassData->HasActivateAtStorage() || pClassData->HasRemoteServerName()) ) { pActParams->ClsContext |= CLSCTX_REMOTE_SERVER; } } if ( FAILED(hr) ) { // // If we don't find an entry for the CLSID then we must still try // a remote activation if a server name was given. // // If there is no server name, we try an ActivateAtStorage. If the // path is local this will fail, otherwise we'll try to activate a // server on the machine where the path leads us. // // CLSCTX_REMOTE_SERVER is implied if a remote server name is given, // but is not implied for a CLSID which is not present in the client's // registry. // if ( (hr == REGDB_E_CLASSNOTREG) && ! pActParams->RemoteActivation ) { // // Create a skeleton class data object so we can // call its methods. // CClassData ClassData( *pActParams->Clsid, hr ); BOOL Status; if ( pActParams->pwszServer ) { Status = ClassData.ActivateRemote( &hr, pActParams ); if ( Status == TRY_LOCAL_ACTIVATION ) hr = REGDB_E_CLASSNOTREG; } else if ( pActParams->ClsContext & CLSCTX_REMOTE_SERVER ) { ClassData.SetActivateAtStorage(); Status = ClassData.ActivateAtStorage( &hr, pActParams ); if ( Status == TRY_LOCAL_ACTIVATION ) hr = REGDB_E_CLASSNOTREG; } if ( hr == S_OK ) hr = ResolveORInfo( pActParams, TRUE ); } goto ActivationExit; } for (;;) { error_status_t rpcstat; int BusyRetries; CPortableRpcHandle rh; handle_t hRpcAnonymous; hRpcAnonymous = 0; BOOL fSurrogate = FALSE; // // Start local server or service, or forward activation call // to remote machine. // hr = pClassData->GetServer( pActParams, rh, ServerStarted, ActivatedRemote, fSurrogate); // // We're done and do not make a call to the server if the server launch // failed, we found the object in the ROT, or we tried to make a remote // activation. // if ( FAILED(hr) || pActParams->FoundInROT || ActivatedRemote ) break; BusyRetries = 0; // // Impersonate client while making the call to the server so that the // server can not impersonate local system and can identify the client // if needed. The SCM-Server binding handle is created with // identification level security. // During unsecure activations we use an rpc handle created at // impersonation level of none. // if ( ! pActParams->UnsecureActivation ) { RpcImpersonateClient((RPC_BINDING_HANDLE)0); } else { pClassData->GetAnonymousHandle( rh, &hRpcAnonymous ); if ( ! hRpcAnonymous ) { pClassData->DecHandleCount(rh); hr = E_OUTOFMEMORY; break; } } // // Keeping server busy retry logic for now, but do we really care? // do { switch (pActParams->MsgType) { case GETCLASSOBJECTEX: hr = ObjectServerGetClassObject( hRpcAnonymous ? hRpcAnonymous : rh.GetHandle(), pActParams->ORPCthis, pActParams->Localthis, pActParams->ORPCthat, pActParams->Clsid, pActParams->pIIDs, fSurrogate, (MInterfacePointer **)pActParams->ppIFD, &rpcstat ); break; case CREATEINSTANCEEX: hr = ObjectServerCreateInstance( hRpcAnonymous ? hRpcAnonymous : rh.GetHandle(), pActParams->ORPCthis, pActParams->Localthis, pActParams->ORPCthat, pActParams->Clsid, pActParams->Interfaces, pActParams->pIIDs, (MInterfacePointer **)pActParams->ppIFD, pActParams->pResults, &rpcstat ); break; case GETPERSISTENTEX: hr = ObjectServerGetInstance( hRpcAnonymous ? hRpcAnonymous : rh.GetHandle(), pActParams->ORPCthis, pActParams->Localthis, pActParams->ORPCthat, pActParams->Clsid, pActParams->Mode, pActParams->pwszPath, (MInterfacePointer *)pActParams->pIFDStorage, pActParams->Interfaces, pActParams->pIIDs, (MInterfacePointer *)pActParams->pIFDROT, (MInterfacePointer **)pActParams->ppIFD, pActParams->pResults, &rpcstat ); break; } } while ( (rpcstat == RPC_S_SERVER_TOO_BUSY) && (BusyRetries++ < MAX_BUSY_RETRIES) ); if ( ! pActParams->UnsecureActivation ) RpcRevertToSelf(); // // This frees the RPC binding handle for a single use // registered class. Its already been removed from the // class registration list. // rh.FreeSingleUseBinding(); // // This releases our reference to this registered handle so that it // can be freed if and when it is revoked or invalidated. // pClassData->DecHandleCount(rh); // // We get a non-zero rpcstat if there was a communication problem // with the server. We get CO_E_SERVER_STOPPING if a server // consumes its own single use registration (see comment in function // header), or was in the process of revoking its registration when // we called. // // We'll try the activation again if we get CO_E_SERVER_STOPPING and // used a previously registered handle. If we launched a new server // then something is hosed and we return. // if ( (rpcstat != RPC_S_OK) || (hr == CO_E_SERVER_STOPPING) ) { // // We didn't like that handle, so we blow it away. // There is a bug here because it's possible the handle will not // get freed. Its not a huge deal because this will be very rare. // Fix it in NT 5.0. // pClassData->InvalidateHandle(rh); if ( ServerStarted && (rpcstat != RPC_S_OK) ) { // TODO: log message about rpc error talking to server hr = HRESULT_FROM_WIN32(rpcstat); break; } // // Re-read the class information if there are no more registered // binding handles left. // if ( ! pClassData->InUse() ) { gClassCacheLock.WriteLock(); pClassData->Release(); gClassCacheLock.WriteUnlock(); pClassData = 0; hr = gpClassCache->GetClassData( *pActParams->Clsid, &pClassData, FALSE, FALSE ); if ( FAILED(hr) ) goto ActivationExit; } continue; } // Success - return the result to the client. break; } if ( pClassData != NULL ) { // // We need to take the write lock since the destruction // of CClassData will affect the ref counted objects // (such as CSafeLocalServer.) // gClassCacheLock.WriteLock(); pClassData->Release(); gClassCacheLock.WriteUnlock(); } if ( SUCCEEDED(hr) ) hr = ResolveORInfo( pActParams, ActivatedRemote ); ActivationExit: if ( fProcessReference ) { ReleaseProcess( pActParams->pProcess ); } else { if ( pActParams->pToken != 0 ) pActParams->pToken->Release(); } return hr; } HRESULT ResolveORInfo( PACTIVATION_PARAMS pActParams, BOOL ActivatedRemote ) { MInterfacePointer * pIFD; OBJREF * pObjRef; STDOBJREF * pStdObjRef; DUALSTRINGARRAY * pORBindings; DWORD DataSize; RPC_STATUS sc; DWORD n; // // This routine probes the interface data returned from the server's // OLE during a successfull activation, but we're still going to // protect ourself from bogus data. // pIFD = 0; for ( n = 0; n < pActParams->Interfaces; n++ ) { pIFD = pActParams->ppIFD[n]; if ( pIFD ) break; } Win4Assert( pIFD ); if ( pIFD->ulCntData < 2*sizeof(ULONG) ) { Win4Assert( !"Bad interface data returned from server" ); return S_OK; } pObjRef = (OBJREF *)pIFD->abData; if ( (pObjRef->signature != OBJREF_SIGNATURE) || (pObjRef->flags & ~(OBJREF_STANDARD | OBJREF_HANDLER | OBJREF_CUSTOM)) || (pObjRef->flags == 0) ) { Win4Assert( !"Bad interface data returned from server" ); return S_OK; } // No OR info sent back for custom marshalled interfaces. if ( pObjRef->flags == OBJREF_CUSTOM ) return S_OK; DataSize = 2*sizeof(ULONG) + sizeof(GUID); pStdObjRef = (STDOBJREF *)(pIFD->abData + DataSize); DataSize += sizeof(STDOBJREF); if ( pObjRef->flags == OBJREF_HANDLER ) DataSize += sizeof(CLSID); pORBindings = (DUALSTRINGARRAY *)(pIFD->abData + DataSize); DataSize += 2 * sizeof(USHORT); if ( pIFD->ulCntData < DataSize ) { Win4Assert( !"Bad interface data returned from server" ); return S_OK; } // If we activated the server on this machine, we need the OXID of the server. if ( ! ActivatedRemote ) *pActParams->pOxidServer = *((OXID UNALIGNED *)&pStdObjRef->oxid); // // If we're servicing a remote activation, all we need is the server's OXID. // The client will call ResolveClientOXID from its ResolveORInfo. // if ( pActParams->RemoteActivation ) return S_OK; DataSize += pORBindings->wNumEntries * sizeof(USHORT); if ( (pIFD->ulCntData < DataSize) || (pORBindings->wNumEntries != 0 && (pORBindings->wSecurityOffset >= pORBindings->wNumEntries)) ) { Win4Assert( !"Bad interface data returned from server" ); return S_OK; } // // If empty OR bindings were supplied then the server and client are // both local to this machine, so use the local OR bindings. // if (pORBindings->wNumEntries == 0) { pORBindings = pdsaMyBindings; } // // This was a local activation so use our string bindings for the OR // binding string. // *pActParams->ppServerORBindings = (DUALSTRINGARRAY *) MIDL_user_allocate( sizeof(DUALSTRINGARRAY) + pORBindings->wNumEntries*sizeof(USHORT) ); if ( ! *pActParams->ppServerORBindings ) return E_OUTOFMEMORY; dsaCopy( *pActParams->ppServerORBindings, pORBindings ); // // If we did a remote activation then we already have the server's OXID and // OR string bindings from the RemoteActivation call and pieces of the OXID // info have been filled in. // // Could we optimize this at all for the local case? sc = ResolveClientOXID( pActParams->hRpc, (PVOID)pActParams->pProcess, pActParams->pOxidServer, *pActParams->ppServerORBindings, pActParams->Apartment, pActParams->ProtseqId, pActParams->pOxidInfo, pActParams->pLocalMidOfRemote ); return HRESULT_FROM_WIN32(sc); } #endif // DCOM #ifdef _CHICAGO_ //+--------------------------------------------------------------------------- // // Method: ScmStackSwitch // // Synopsis: calls CClassCacheList::SSProcessScmMessage // // Arguments: [pClsCacheList] -- // [pbd] -- // [pgcd] -- // // Returns: // // History: 4-19-95 JohannP (Johann Posch) Created // // Notes: // //---------------------------------------------------------------------------- HRESULT ScmStackSwitch(CClassCacheList *pClsCacheList, CBaseData *pbd, CGetCreateData *pgcd) { HRESULT hres; StackDebugOut((DEB_STCKSWTCH, "ScmStackSwitch 32->16 : pClsCacheList(%x), pbd(%x), pgcd(%x)\n", pClsCacheList, pbd, pgcd)); hres = pClsCacheList->SSProcessScmMessage(pbd, pgcd); StackDebugOut((DEB_STCKSWTCH, "ScmStackSwitch 32<-16 back; hres:%x\n", hres)); return hres; } //+--------------------------------------------------------------------------- // // Method: CClassCacheList::ProcessScmMessage // // Synopsis: Switched to the 16 bit stack and calls SSProcessScmMessage // via ScmStackSwitch // // Arguments: [pbd] -- // [pgcd] -- // // Returns: // // History: 4-19-95 JohannP (Johann Posch) Created // // Notes: // //---------------------------------------------------------------------------- HRESULT CClassCacheList::ProcessScmMessage(CBaseData *pbd, CGetCreateData *pgcd) { HRESULT hres; CairoleDebugOut((DEB_STCKSWTCH, "In ProcessScmMessage this(%x), pbd(%x), pgcd(%x)\n", this, pbd, pgcd)); if (SSONBIGSTACK()) { hres = SSCall(12, SSF_SmallStack, (LPVOID)ScmStackSwitch, (DWORD)this, (DWORD) pbd, (DWORD) pgcd); } else hres = SSProcessScmMessage(pbd, pgcd); CairoleDebugOut((DEB_STCKSWTCH, "Out ProcessScmMessage this(%x), pbd(%x), pgcd(%x) =>hres(%x)\n", this, pbd, pgcd, hres)); return hres; } //+------------------------------------------------------------------- // // Member: ProcessScmMessage // // Synopsis: Generic routine to process activation related messages // to SCM. // // Arguments: [pbd] -- base message data for all message types // [pcgd] -- extended data for (Get/Create)PersistentObj // // Notes: // //-------------------------------------------------------------------- HRESULT CClassCacheList::SSProcessScmMessage(CBaseData *pbd, CGetCreateData *pgcd) { // Result from trying to get server information including // starting the server if necessary. This used for the // return code when the call succeeds. HRESULT hr; int cRetries = 0; // Usually result of contacting object server. This result // is always returned when the call has failed. HRESULT hr2 = S_OK; // Whether we actually did start a server. This controls // whether we actually retry. The basic idea here is that // if we have started a server and it crashes, then we don't // need to retry. However, if we haven't started the server // and we find the RPC handle invalid, then we start the server // again. BOOL ServerStarted = FALSE; BOOL ActivatedRemote; CClassData *pccd; // Retain original value of *pbd->_pdwDllType here. Calling GetServer will // result in the value getting updated. Since we may end up calling // GetServer multiple times, we initialize *pbd->_pdwDllType before each // call with the saved value. DWORD dwsavedDllType = *pbd->_pdwDllType; #ifdef DCOM // Check whether the registry has changed if (WaitForSingleObject(_hRegEvent, 0) == WAIT_OBJECT_0) { ReadRemoteActivationKeys(); // Re-arm the event int err; if ((err = RegNotifyChangeKeyValue(HKEY_LOCAL_MACHINE, TRUE, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES | REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY, _hRegEvent, TRUE)) != ERROR_SUCCESS) { return HRESULT_FROM_WIN32(err); } } #endif hr = GetClassData ( pbd->_guidForClass, &pccd, FALSE /* don't leave locked */ ); #ifdef DCOM if ( pbd->_pwszServer ) { WCHAR * pwszServerName; pwszServerName = pbd->_pwszServer; if ( pwszServerName[0] == L'\\' && pwszServerName[1] == L'\\' ) pwszServerName += 2; // // An explicit server machine name param equal to the machine we're // on means the client doesn't want the activation to be remoted. // An explicit server name other than our name implies CLSTXT_REMOTE_SERVER. // if ( lstrcmpiW(pwszServerName, SCMMachineName) == 0 ) pbd->_dwContext &= ~CLSCTX_REMOTE_SERVER; else pbd->_dwContext |= CLSCTX_REMOTE_SERVER; } else { // // CLSCTX_REMOTE_SERVER is implied if either ActivateAtStorage or // RemoteServerName is present on the CLSID. // if ( ! pbd->_fRemoteActivation && pccd && (pccd->HasActivateAtStorage() || pccd->HasRemoteServerName()) ) { pbd->_dwContext |= CLSCTX_REMOTE_SERVER; } } if ( FAILED(hr) ) { // // If we don't find an entry for the CLSID then we must still try // a remote activation if a server name was given. // // If there is no server name, we try an ActivateAtStorage. If the // path is local this will fail, otherwise we'll try to activate a // server on the machine where the path leads us. // // CLSCTX_REMOTE_SERVER is implied if a remote server name is given, // but is not implied for a CLSID which is not present in the client's // registry. // if ( (hr == REGDB_E_CLASSNOTREG) && ! pbd->_fRemoteActivation ) { // // Create a skeleton class data object so we can // call its methods. // CClassData ClassData( pbd->_guidForClass, hr ); BOOL Status; if ( pbd->_pwszServer ) { Status = ClassData.ActivateRemote( &hr, pbd, pgcd ); if ( Status == TRY_LOCAL_ACTIVATION ) hr = REGDB_E_CLASSNOTREG; return hr; } if ( pbd->_dwContext & CLSCTX_REMOTE_SERVER ) { ClassData.SetActivateAtStorage(); Status = ClassData.ActivateAtStorage( &hr, pbd, pgcd ); if ( Status == TRY_LOCAL_ACTIVATION ) hr = REGDB_E_CLASSNOTREG; } } return hr; } #else if ( FAILED(hr) ) { return hr; } #endif // DCOM #ifdef DCOM // Make up the ORPC headers. ORPCTHIS orpcthis; LOCALTHIS localthis; ORPCTHAT orpcthat; orpcthis.version.MajorVersion = COM_MAJOR_VERSION; orpcthis.version.MinorVersion = COM_MINOR_VERSION; orpcthis.flags = ORPCF_LOCAL; orpcthis.reserved1 = 0; orpcthis.cid = *pbd->_pguidThreadId; orpcthis.extensions = NULL; if (pgcd != NULL) localthis.dwClientThread = pgcd->_dwTIDCaller; else localthis.dwClientThread = GetCurrentThreadId(); if ( pbd->_ORPCthis == 0 ) { // // Setup the ORPC stuff for down level activation calls. // // Initializing on the stack is ok since only the SCM RPC entry // points call us and they don't ever use pbd. // pbd->_ORPCthis = &orpcthis; pbd->_Localthis = &localthis; pbd->_ORPCthat = &orpcthat; } #endif do { cRetries++; CPortableRpcHandle rh; *pbd->_pdwDllType = dwsavedDllType; // restore in case not first time // // Get server DLL info, start local server, or forward activation call // to remote machine. // hr = pccd->GetServer(pbd, pgcd, rh, ServerStarted, ActivatedRemote); // If we did a remote activation then, success or not, we're done. if ( ActivatedRemote ) break; if (rh.GetHandle() == NULL) { // If we don't have an RPC handle then we don't need to // do any communication so we are done. break; } if (ServerStarted) { // We will break this loop if communication fails with // the server. cRetries = MAX_OBJSRV_RETRIES; } // plsrv will be NULL in the event of an error so it will only be // set if there is indeed an object server that we need to contact. error_status_t rpcstat = RPC_S_OK; int cBusyRetries = 0; do { _try { switch (pbd->_scmmsg) { #ifndef _CHICAGO_ case GETCLASSOBJECTEX: localthis.callcat = CALLCAT_INPUTSYNC; hr2=ObjectServerGetClassObject(rh.GetHandle(), &orpcthis, &localthis, &orpcthat, &pbd->_guidForClass, (MInterfacePointer **)pbd->_ppIFD); break; case CREATEINSTANCEEX: localthis.callcat = CALLCAT_SYNCHRONOUS; hr2 = ObjectServerCreateInstance( rh.GetHandle(), &orpcthis, &localthis, &orpcthat, NULL, &pbd->_guidForClass, pgcd->_dwInterfaces, pgcd->_pIIDs, pgcd->_pclsidHandler, pgcd->_pIFPClientSiteHandler, (MInterfacePointer **)pgcd->_ppIFDunk, pgcd->_pResults, pgcd->_ppIFPServerHandler ); break; case GETPERSISTENTEX: localthis.callcat = CALLCAT_SYNCHRONOUS; hr2 = ObjectServerGetInstance( rh.GetHandle(), &orpcthis, &localthis, &orpcthat, NULL, &pbd->_guidForClass, pgcd->_grfMode, pgcd->_pwszPath, (MInterfacePointer *)pgcd->_pIFDstg, pgcd->_dwInterfaces, (MInterfacePointer *)pgcd->_pifdFromROT, pgcd->_pIIDs, (MInterfacePointer **)pgcd->_ppIFDunk, pgcd->_pResults ); break; #else case GETCLASSOBJECT: hr2=REMCOGETACTIVECLASSOBJECT(rh.GetHandle(), pbd->_pguidThreadId, &pbd->_guidForClass, pbd->_ppIFD, &rpcstat); break; case GETPERSISTENTOBJ: hr2=REMCOACTIVATEOBJECT(rh.GetHandle(), pgcd->_pwszProtseq, pbd->_pguidThreadId, &pbd->_guidForClass, pgcd->_grfMode, pgcd->_pwszPath, pgcd->_pIFDstg, pgcd->_dwTIDCaller, pgcd->_pdwTIDCallee, pgcd->_ppIFDunk, pgcd->_pifdFromROT, &rpcstat); break; case CREATEPERSISTENTOBJ: hr2=REMCOCREATEOBJECT(rh.GetHandle(), pgcd->_pwszProtseq, pbd->_pguidThreadId, &pbd->_guidForClass, pgcd->_grfMode, pgcd->_pwszPath, pgcd->_pIFDstg, pgcd->_pwszNewName, pgcd->_dwTIDCaller, pgcd->_pdwTIDCallee, pgcd->_ppIFDunk, &rpcstat); break; #endif // _CHICAGO_ } // // Treat CO_E_SERVER_STOPPING as a special case. It is // possible for something like RemCoGetActiveClassObject to // fail with this error. An example scenario: // Server // - does a CoRegisterClassObject for SINGLE USE resulting // in an entry in the SCM's Class cache and an entry in // OLE's DLL cache // - (acting as its own client) server proceeds to consume // it via CoCreateInstance. In this case the entry in the // DLL cache is found and consumed leaving the SCM's Class // cache in an inconsistent state (yes...a bug, but reality // intrudes). Also, because SINGLE USE was indicated entry // in DLL cache is marked such that it will not be handed // out again. // - if another CoCreateInstance comes along (server itself // or another client) the request will go to the SCM which // in turn will issue RemCoGetActiveClassObject to the // server. Server will return CO_E_SERVER_STOPPING after // failing to find it in the DLL cache. // // To get around this problem we break out of the inner loop // and make it go through the outer do...while which should // result in the activation of a new instance of the server. // // Note that in the above case the SCM's class cache entry has // already been invalidated by the earlier call to GetServer // (because of SINGLE USE). The invalidation code is here for // other cases of CO_E_SERVER_STOPPING. // if (hr2 == CO_E_SERVER_STOPPING) { if (!rh.fSingleUse()) { pccd->InvalidateHandle(rh); } break; } } _except(EXCEPTION_EXECUTE_HANDLER) { rpcstat = GetExceptionCode(); } } while(RetryRpc(cBusyRetries, rpcstat, hr2)); rh.FreeSingleUseBinding(); #ifndef _CHICAGO_ pccd->DecHandleCount(rh); #endif // Call to object server to complete the operation if (FAILED(hr2)) { // We will assume that if these errors occur that we // should try to start the server because it died // in some awful way. if (hr2 == CO_E_OBJSRV_RPC_FAILURE) { BOOL fReload = FALSE; if (!rh.fSingleUse()) { CScmLockForWrite scmlckwr(gClassCacheLock); // TRUE means the last registration was deleted fReload = pccd->StopServer(rh); if (fReload) { // This will release if we're the only thread. pccd->Release(this); pccd = NULL; } } if (fReload) { hr = GetClassData ( pbd->_guidForClass, &pccd, FALSE /* don't leave locked */ ); if ( FAILED(hr) ) { break; } } continue; } else if (hr2 == CO_E_SERVER_STOPPING) { // This is really an RPC problem with the server // If we started the server and we land up here. // If we got a version of the server that is stopping // we will try to restart it. hr2 = CO_E_OBJSRV_RPC_FAILURE; continue; } else if (!rh.fSingleUse()) { Sleep (1000); if (!pccd->VerifyHandle(rh)) continue; } // All return results from the above RPC will made 1 to 1 // to error returns from the SCM interface so that we don't // have to remap error codes with a wasteful switch statement. // BUGBUG: Make sure above is true. break; } // Success - return the result to the client break; } while(cRetries < MAX_OBJSRV_RETRIES); { CScmLockForWrite scmlckwr(gClassCacheLock); // // NOTE! We need to take the write lock since the destruction // of CClassData will affect the ref counted objects // (such as CSafeLocalServer.) // if (pccd != NULL) pccd->Release(this); } return SUCCEEDED(hr2) ? hr : hr2; } #endif // _CHICAGO_ //+------------------------------------------------------------------------- // // Function: ThreadModelMatch // // Synopsis: Determines whether caller and DLL thread models match // // Arguments: [dwCallerThreadModel] - Caller thread model // [dwDllThreadModel] - DLL thread model // // Returns: TRUE - DLL can be loaded caller // FALSE - DLL cannot be loaded into caller. // // Algorithm: If the caller's thread model is apartment, then check // whether the DLL is one of apartment, single threaded or // both threaded. If it is, then return TRUE. Otherwise, // for free threading return TRUE if the DLL model is either // both or free threaded. If neither of the above is TRUE // then return FALSE. // // History: 10-Nov-94 Ricksa Created // //-------------------------------------------------------------------------- BOOL ThreadModelMatch( DWORD dwCallerThreadModel, DWORD dwDllThreadModel, DWORD dwContext) { BOOL fResult = afThreadModelMatch[dwCallerThreadModel] [dwDllThreadModel]; if (dwContext & CLSCTX_PS_DLL) { fResult = TRUE; } return fResult; } //+------------------------------------------------------------------------- // // Function: RetryRpc // // Synopsis: Determines whether a request to a object server s/b retried. // // Arguments: [cRetries] - how many retries have occurred. // [rpcstat] - RPC status from call. // [sc] - Error return from the API. // // Returns: TRUE - retry call to server // FALSE - do not retry the RPC call. // // Algorithm: We default the retry to FALSE. Then we check the status // returned by RPC. If it is because the server is too // busy and we have not exceeded our maximum number of // retries, we set the retry flag to TRUE. Otherwise, // we give up. If the error we have gotten back is from // the object server, then if it is a message that // the server is stopping, we sleep but do not set // the error flag. This because we don't want to retry // the RPC because the server is going away. But we // want to make sure that it is gone and then we will // restart the service. // // History: 21-Apr-93 Ricksa Created // //-------------------------------------------------------------------------- BOOL RetryRpc( int& cRetries, error_status_t rpcstat, HRESULT& hr) { // Assume we do not want to loop BOOL fResult = FALSE; if (rpcstat != 0 || hr == RPC_E_SERVER_DIED_DNE) { hr = CO_E_OBJSRV_RPC_FAILURE; if (rpcstat == RPC_S_SERVER_TOO_BUSY) { if (cRetries++ != MAX_BUSY_RETRIES) { // We haven't exceeed our max so sleep and retry Sleep(500); fResult = TRUE; } } } return fResult; } //+------------------------------------------------------------------------- // // Member: CClassData::CClassData // // Synopsis: Create service information object in the cache by // starting from scratch or by merging. // // Arguments: [ccd] - class ID for the object // [ppwszLocalSrv] - name of the local server exe // [fActivateAtStorage] - whether we activate the object at bits // [cEntries] - number of entries in the skip list. // [SixteenBitFlags] - Which of the above are 16-bit // [hr] - On error, set to error code. On success, left // unchanged. // // History: 21-Apr-93 Ricksa Created // // Notes: // There are two types of classes we must deal with. 16bit and 32bit. // To the SCM, the only real difference is which registry key the // class was found under. SixteenBitFlags indicates which registry // key was found to be 16-bit, and which was 32-bit. //-------------------------------------------------------------------------- CClassData::CClassData( const CClassID& ccd, WCHAR *pwszAppID, const WCHAR *pwszLocalSrv, WCHAR *pwszRemoteServerName, BOOL fHasService, WCHAR *pwszServiceArgs, WCHAR *pwszRunAsUserName, WCHAR *pwszRunAsDomainName, #ifdef DCOM // NT 5.0 PSID pUserSid, #endif // DCOM const BOOL ActivateAtStorage, const BOOL RemoteServerName, const SECURITY_DESCRIPTOR * pSD, const BOOL f16Bit, HRESULT &hr) // NT 5.0 : CClsidSid(ccd, pUserSid), : CClassID(ccd), _pwszAppID( pwszAppID ), _slocalsrv(pwszLocalSrv, hr), _pwszRemoteServer(pwszRemoteServerName), _fHasService(fHasService), _pwszServiceArgs(pwszServiceArgs), _pwszRunAsDomainName(pwszRunAsDomainName), _pwszRunAsUserName(pwszRunAsUserName), _pwszSurrogateCmdLine(NULL), _fActivateAtStorage(ActivateAtStorage), _fRemoteServerName(RemoteServerName), _pSD(pSD), #ifndef _CHICAGO_ _hClassStart(NULL), #endif _fLocalServer16(f16Bit), _pssrvreg(NULL), _ulRefs(1) { // // A service name is allocated by CAppIDData class and ownership is passed // to CClassData. CLocalServer allocates and copies the string, so free // the one passed in. // if ( _fHasService ) ScmMemFree( (void *)pwszLocalSrv ); if (FAILED(hr)) return; // rpc handle list _pssrvreg = new CSrvRegList(); // even if this succeeds, the srvreglist may fail when used. if (_pssrvreg == NULL) { CairoleDebugOut ((DEB_ERROR, "Fail to allocate memory in CClassData\n")); hr = E_OUTOFMEMORY; return; } #ifndef _CHICAGO_ // // Create our class start notification - note this can be automatic reset // because we only allow one thread to wait on this event because we will // own the server object lock when we are waiting on this event. // NT uses events created now and kept around (not named.) // (x86 Windows creates/destroys event when needed.) // _hClassStart = CreateEvent(NULL, TRUE, FALSE, NULL); if (_hClassStart == NULL) { CairoleDebugOut((DEB_ERROR, "CClassData::CClassData Event Create Failed %lx\n", GetLastError())); delete _pssrvreg; _pssrvreg = NULL; hr = E_OUTOFMEMORY; return; } #endif Win4Assert(_pssrvreg != NULL); // Make sure we set the error code correctly on success hr = NOERROR; } CClassData::CClassData( const CClassID& ccd, HRESULT &hr) // NT 5.0 : CClsidSid(ccd, NULL), : CClassID(ccd), _pwszAppID(NULL), _slocalsrv(NULL, hr), _pwszRemoteServer(NULL), _fHasService(FALSE), _pwszServiceArgs(NULL), _pwszRunAsDomainName(NULL), _pwszRunAsUserName(NULL), _pwszSurrogateCmdLine(NULL), _fActivateAtStorage(FALSE), _fRemoteServerName(FALSE), _pSD(NULL), #ifndef _CHICAGO_ _hClassStart(NULL), #endif _fLocalServer16(FALSE), _pssrvreg(NULL), _ulRefs(1) { } //+------------------------------------------------------------------------- // // Member: CClassData::Release // // Synopsis: Decrement ref count and delete from list if no server regs. // // Arguments: [pccl] - list to delete from // // History: 08-Dec-94 BillMo Created // //-------------------------------------------------------------------------- VOID CClassData::Release() { Win4Assert(_ulRefs != 0); _ulRefs --; if (_ulRefs == 0) { if (!InUse()) { // NT 5.0 Verify(NULL != gpClassCache->Remove((CClsidSid*)this)); Verify(NULL != gpClassCache->Remove((CClassID*)this)); delete this; } } } void LogRegisterTimeout( CLSID & clsid, ACTIVATION_PARAMS * pActParams ) { // %1 is the clsid HANDLE LogHandle; LPTSTR Strings[1]; // array of message strings. WCHAR wszClsid[GUIDSTR_MAX]; // Get the clsid wStringFromGUID2(clsid, wszClsid, sizeof(wszClsid)); Strings[0] = wszClsid; // Get the log handle, then report then event. LogHandle = RegisterEventSource( NULL, SCM_EVENT_SOURCE ); if ( LogHandle ) { ReportEvent( LogHandle, EVENTLOG_ERROR_TYPE, 0, // event category EVENT_RPCSS_SERVER_START_TIMEOUT, pActParams->pToken ? pActParams->pToken->GetSid() : NULL, // SID 1, // 1 strings passed 0, // 0 bytes of binary (LPCTSTR *)Strings, // array of strings NULL ); // no raw data // clean up the event log handle DeregisterEventSource(LogHandle); } } //+------------------------------------------------------------------------- // // Member: CClassData::GetServer // // Synopsis: Get the server information for a service // // Arguments: [dwContext] - context requested by client // [ppwszDll ] - where to put ptr to DLL name // [rh] - RPC handle to use for talking to object server // [ServerStarted] - whether we actually started the server. // // Returns: REGDB_E_CLASSNOTREG - Information for context not in DB // // Algorithm: First we check whether it is appropriate to run with // an inprocess server. If so we return that. If a handler // is appropriate for the context, we return that. Otherwise, // we return a server object and a handler if the object // requires it. // // We also allow for the case where the client wants // to load a 16-bit inproc server. This is used by the VDM // to load proxy/stub code for 16/32 interoperability. // // History: 21-Apr-93 Ricksa Created // //-------------------------------------------------------------------------- #ifndef _CHICAGO_ HRESULT CClassData::GetServer( ACTIVATION_PARAMS * pActParams, CPortableRpcHandle &rh, BOOL& ServerStarted, BOOL& ActivatedRemote, BOOL& fSurrogate) { #if DBG WCHAR wszClass[40]; CairoleDebugOut((DEB_ITRACE, "CClassData::GetServer(%x) for class %ws\n", this, FormatGuid(*pActParams->Clsid, wszClass))); #endif HRESULT hr; BOOL bStatus; WCHAR * pwszSurrogateCmdLine = NULL; ActivatedRemote = FALSE; // // If an explicit server name was specified in the remote activation // call, then we don't check local class info. Note that if the server // name is equal to the current machine name, the CLSCTX_REMOTE_SERVER // bit gets masked out in Activation. // if ( pActParams->pwszServer && (pActParams->ClsContext & CLSCTX_REMOTE_SERVER) ) goto GET_SERVER_TRY_REMOTE; // // Next we check if this CLSID has ActivateAtStorage, and do a remote // activation if the path is remote, otherwise we'll continue along. // if ( ActivateAtStorage( &hr, pActParams ) == DONT_TRY_LOCAL_ACTIVATION ) { ActivatedRemote = TRUE; return hr; } if ( pActParams->pwszPath != NULL ) { // // We use FoundInROT to indicate that we got an object from the // ROT. We need to do this because there is no guarantee that the // ROT entry will not be bad by the time we get back to the caller // and therefore, the caller will want to retry in this case. // hr = GetObjectFromRot( pActParams->pToken, pActParams->pwszWinstaDesktop, pActParams->pwszPath, (InterfaceData **)&pActParams->pIFDROT ); if ( hr == S_OK ) { // // Is this a call from a local client and is a single // interface being requested? If so, we can return now. // Otherwise we call the server because we either need more // interfaces and choose to get them now, or because we are // servicing a remote activation and want to get a normal // marshalled interface pointer rather than the table marshalled // interface pointer sitting in the ROT. // if ( ! pActParams->RemoteActivation && (pActParams->Interfaces == 1) ) { // Return the marshaled interface from the ROT to the caller. *pActParams->ppIFD = pActParams->pIFDROT; pActParams->pResults[0] = S_OK; // So we remember not to clean up the buffer pActParams->pIFDROT = NULL; // Let caller know that we got this from the ROT so // if it doesn't work they should try again. pActParams->FoundInROT = TRUE; // We got what we came for from the ROT so we can exit. CairoleDebugOut((DEB_TRACE, "Found object in the ROT\n")); return S_OK; } // // Because a tabled marshaled interface is not enought // to get unmarshaled locally, we need to send it back // to the object server to get something we can pass back // to another machine. So we continue on here... // } } if ( pActParams->ClsContext & CLSCTX_LOCAL_SERVER ) { PSID psid = NULL; // // We need to compare SIDs unless the server is configured as // LocalService or RunAs. // if( !_fHasService && !_pwszRunAsUserName ) { // // Unsecure activation can only connect to services or RunAs // servers. // if ( pActParams->UnsecureActivation ) return E_ACCESSDENIED; psid = pActParams->pToken->GetSid(); } // Check if the server is already registered. bStatus = _pssrvreg->GetHandle( IFSECURITY(psid) pActParams->pwszWinstaDesktop, rh, FALSE); // // If we didn't find a handle or IObjServer for an already running server and // a local server is not in the registry for this class, we check // for an inproc server to start in surrogate if(!bStatus && !(_slocalsrv.Defined())) { fSurrogate = SUCCEEDED(GetInProcServerInfo(&pwszSurrogateCmdLine)); } // if there's no existing server running if ( ! bStatus && (_slocalsrv.Defined() || fSurrogate) ) { Win4Assert(!(_slocalsrv.Defined() && fSurrogate)); CSafeLocalServer * pslocalsrv = (fSurrogate && !_fHasService) ? gpClassCache->GetSurrogateLocalServer() : &_slocalsrv; // // CPortableServerLock is to prevent multiple process creates for // the same server. // CPortableServerEvent is signaled when the class we're activating // for gets registered. // CPortableServerLock sl(*pslocalsrv); CPortableServerEvent se(this); HANDLE hProcess = 0; SC_HANDLE hService = 0; SERVICE_STATUS ServiceStatus; DWORD dwWaitResult; BOOL ServiceFailed = FALSE; HRESULT hr = S_OK; error_status_t rpcstat; if ( sl.Error() != ERROR_SUCCESS ) { hr = sl.Error(); goto EXIT_START_SERVER; } if ( se.Create() != ERROR_SUCCESS ) { hr = se.Create(); goto EXIT_START_SERVER; } // if this is a new surrogate, get its command line and save it if(pwszSurrogateCmdLine) { _pwszSurrogateCmdLine = pwszSurrogateCmdLine; pwszSurrogateCmdLine = NULL; } // // We check the class registrations again after taking the server // lock. This is stupid, but I need to redo that brain dead // CPortableServerLock crap before changing this. // bStatus = _pssrvreg->GetHandle( IFSECURITY(psid) pActParams->pwszWinstaDesktop, rh, FALSE); if ( bStatus ) { goto EXIT_START_SERVER; } // Check that we have access rights to the server // irrespective of whether this is a local or remote // call if (FAILED(CkIfCallAllowed(pActParams))) { hr = E_ACCESSDENIED; goto EXIT_START_SERVER; } // Local services take precedence over local servers. if (_fHasService) { bStatus = (*pslocalsrv)->StartService(_guid, pActParams->pToken, _pwszServiceArgs, &hService); } else { CClassData* pccdSrgt = NULL; if(fSurrogate && gpClassCache->FindCompatibleSurrogate( IFSECURITY(psid) &pccdSrgt, pActParams->pwszWinstaDesktop, _pwszAppID, rh)) { HRESULT hrSurrogateLaunched; bStatus = FALSE; if ( ! pActParams->UnsecureActivation ) RpcImpersonateClient((RPC_BINDING_HANDLE)0); hrSurrogateLaunched = ObjectServerLoadDll( rh.GetHandle(), pActParams->ORPCthis, pActParams->Localthis, pActParams->ORPCthat, &_guid, &rpcstat); if ( ! pActParams->UnsecureActivation ) RpcRevertToSelf(); pccdSrgt->DecHandleCount(rh); if ( FAILED(hrSurrogateLaunched) || (rpcstat != RPC_S_OK) ) { if ( (hrSurrogateLaunched == CO_E_SERVER_STOPPING) || (rpcstat != RPC_S_OK) ) { InvalidateHandle(rh); } else { hr = hrSurrogateLaunched; goto EXIT_START_SERVER; } } else { bStatus = TRUE; goto EXIT_START_SERVER; } } if ( ! bStatus ) { bStatus = (*pslocalsrv)->StartServer( _guid, _pwszAppID, pActParams->pToken, pActParams->pwszWinstaDesktop, &hProcess, _pwszRunAsDomainName, _pwszRunAsUserName, _pwszSurrogateCmdLine, fSurrogate); } } if ( ! bStatus ) { CairoleDebugOut((DEB_ERROR, "Start Server/Service failed in GetServer," "returning CO_E_SERVER_EXEC_FAILURE\n")); hr = CO_E_SERVER_EXEC_FAILURE; goto EXIT_START_SERVER; } // Let caller know that we did start a server ServerStarted = TRUE; for (;;) { dwWaitResult = WaitForSingleObject( se.GetHandle(), MAX_CLASS_START_WAIT ); if ( dwWaitResult == WAIT_OBJECT_0 ) break; if ( ! _fHasService ) { TerminateProcess(hProcess, 0); break; } bStatus = ControlService( hService, SERVICE_CONTROL_INTERROGATE, &ServiceStatus ); if ( ! bStatus ) { ServiceFailed = TRUE; break; } switch ( ServiceStatus.dwCurrentState ) { case SERVICE_STOPPED : case SERVICE_STOP_PENDING : break; case SERVICE_START_PENDING : case SERVICE_CONTINUE_PENDING : continue; case SERVICE_RUNNING : case SERVICE_PAUSE_PENDING : case SERVICE_PAUSED : ServiceFailed = TRUE; break; } break; } if ( ServiceFailed ) { (void) ControlService( hService, SERVICE_CONTROL_STOP, &ServiceStatus ); } if ( hProcess ) CloseHandle( hProcess ); if ( hService ) CloseServiceHandle( hService ); // Server is started so we can now get a handle. bStatus = _pssrvreg->GetHandle( IFSECURITY(psid) pActParams->pwszWinstaDesktop, rh, FALSE); if ( ! bStatus ) { // TODO: log message that server did not register w/in timeout LogRegisterTimeout( _guid, pActParams ); CairoleDebugOut((DEB_ERROR, "GetServer returning CO_E_SERVER_EXEC_FAILURE\n")); hr = CO_E_SERVER_EXEC_FAILURE; goto EXIT_START_SERVER; } EXIT_START_SERVER: if(pwszSurrogateCmdLine) { ScmMemFree(pwszSurrogateCmdLine); } if(FAILED(hr) && !bStatus) { return hr; } } // // If we got a handle from either an already running server or a // newly launched server we're done. If we tried to launch a new // server and it failed we will return above. // if ( bStatus ) return S_OK; Win4Assert( ! _slocalsrv.Defined() ); // // If we didn't find a handle to an already registered server and // a local server is not in the registry for this class, we check // for a remote server name in the registry below. // } GET_SERVER_TRY_REMOTE: if ( pActParams->ClsContext & CLSCTX_REMOTE_SERVER ) { // // If this returns TRY_LOCAL_ACTIVATION it means no server name is // specified in the registry and no server name was given in the // activation call. So we return an error. // // If CLSCTX_REMOTE_SERVER was the only class context we'll return a // more specific error. // if ( ActivateRemote( &hr, pActParams ) == TRY_LOCAL_ACTIVATION ) { hr = (pActParams->ClsContext == CLSCTX_REMOTE_SERVER) ? CO_E_CANT_REMOTE : REGDB_E_CLASSNOTREG; } // // We always set this to TRUE at this point, success or not, to // indicate we tried a remote activation. This will tell // ProcessScmMessage to return and not make a SCM-OLE call. // ActivatedRemote = TRUE; return hr; } return REGDB_E_CLASSNOTREG; } #endif //+------------------------------------------------------------------- // // Member: CClassData::ActivateAtStorage // // Synopsis: Forward remote activation request, if necessary. // // Arguments: [phr] -- set if atbits processing was done // // Returns: FALSE if it's a local activation // //-------------------------------------------------------------------- BOOL CClassData::ActivateAtStorage( HRESULT *phr, ACTIVATION_PARAMS * pActParams ) { #ifdef DCOM RPC_STATUS RpcStatus; HRESULT hr; WCHAR wszMachineName[MAX_COMPUTERNAME_LENGTH+1]; WCHAR wszPathForServer[MAX_PATH+1]; WCHAR * pwszPathForServer; switch (pActParams->MsgType) { case GETCLASSOBJECTEX : case CREATEINSTANCEEX : // // The ActivateAtStorage key is ignored for CoGetClassObject and // CoCreateInstanceEx. // return TRY_LOCAL_ACTIVATION; break; case GETPERSISTENTEX : if ( ! HasActivateAtStorage() ) return TRY_LOCAL_ACTIVATION; break; } // // CLSCTX_REMOTE_SERVER must be set or a remote activation is not // possible. // if ( ! (pActParams->ClsContext & CLSCTX_REMOTE_SERVER) ) { return TRY_LOCAL_ACTIVATION; } // // This is for DFS support. If the file hasn't been opened yet, we must // open it before trying to resolve the DFS pathname in GetMachineName. // This is just how DFS works. FindFirstFile results in the fewest number // of network packets. // if ( ! pActParams->FileWasOpened ) { HANDLE hFile; WIN32_FIND_DATA Data; RpcImpersonateClient(NULL); hFile = FindFirstFile( pActParams->pwszPath, &Data ); if ( hFile != INVALID_HANDLE_VALUE ) (void) FindClose( hFile ); RpcRevertToSelf(); if ( INVALID_HANDLE_VALUE == hFile ) { *phr = CO_E_BAD_PATH; return DONT_TRY_LOCAL_ACTIVATION; } } hr = GetMachineName( pActParams->pwszPath, wszMachineName, TRUE ); if ( hr == S_FALSE ) { // We couldn't get the machine name, path must be local. return TRY_LOCAL_ACTIVATION; } else if ( hr != S_OK ) { // We got an error while trying to find the UNC machine name. *phr = hr; return DONT_TRY_LOCAL_ACTIVATION; } // // Make sure the server's name is not our machine name. Return status // to indicate a local activation should be tried. // if ( lstrcmpiW(wszMachineName,SCMMachineName) == 0 ) return TRY_LOCAL_ACTIVATION; hr = GetPathForServer( pActParams->pwszPath, wszPathForServer, &pwszPathForServer ); if ( hr != S_OK ) { *phr = hr; return DONT_TRY_LOCAL_ACTIVATION; } *phr = RemoteActivationCall( pActParams, wszMachineName, pwszPathForServer ); return DONT_TRY_LOCAL_ACTIVATION; #else return TRY_LOCAL_ACTIVATION; #endif // DCOM } //+------------------------------------------------------------------- // // Member: CClassData::ActivateRemote // // Synopsis: Forward remote activation request, if necessary. // // Arguments: [phr] -- set if atbits processing was done // // Returns: FALSE if it's a local activation // //-------------------------------------------------------------------- BOOL CClassData::ActivateRemote( HRESULT *phr, ACTIVATION_PARAMS * pActParams ) { #ifdef DCOM handle_t hRemoteSCMHandle; RPC_STATUS RpcStatus; HRESULT hr; WCHAR * pwszServerName; WCHAR wszPathForServer[MAX_PATH+1]; WCHAR * pwszPathForServer; BOOL ActivateRemote; *phr = S_OK; ActivateRemote = HasRemoteServerName() || pActParams->pwszServer; switch (pActParams->MsgType) { case GETCLASSOBJECTEX : case CREATEINSTANCEEX : if ( ! ActivateRemote ) return TRY_LOCAL_ACTIVATION; break; case GETPERSISTENTEX : if ( ! ActivateRemote ) return TRY_LOCAL_ACTIVATION; break; } // // CLSCTX_REMOTE_SERVER must be set or a remote activation is not // possible. // if ( ! (pActParams->ClsContext & CLSCTX_REMOTE_SERVER) ) { return TRY_LOCAL_ACTIVATION; } pwszServerName = pActParams->pwszServer; if ( ! pwszServerName ) pwszServerName = GetRemoteServerName(); if ( pwszServerName[0] == L'\\' && pwszServerName[1] == L'\\' ) pwszServerName += 2; if ( pwszServerName[0] == L'\0' ) { *phr = CO_E_BAD_SERVER_NAME; return DONT_TRY_LOCAL_ACTIVATION; } // // Make sure the server's name is not our machine name. // // Return TRUE to indicate that no local activation should be tried in // these cases. // if ( lstrcmpiW(pwszServerName,SCMMachineName) == 0 ) { return TRY_LOCAL_ACTIVATION; } // // Note that pActParams->pwszPath can be NULL for GetClassObject or // CreateInstance remoted calls. // if ( pActParams->pwszPath ) { hr = GetPathForServer( pActParams->pwszPath, wszPathForServer, &pwszPathForServer ); if ( hr != S_OK ) { *phr = hr; return DONT_TRY_LOCAL_ACTIVATION; } } else { pwszPathForServer = 0; } *phr = RemoteActivationCall( pActParams, pwszServerName, pwszPathForServer ); return DONT_TRY_LOCAL_ACTIVATION; #else return TRY_LOCAL_ACTIVATION; #endif // DCOM } //+------------------------------------------------------------------- // // Member: CClassData::GetSurrogateCmdLine // // Parameters: ppwszCmdLine (OUT) - a pointer to a string that will // be allocated by this function to hold the command line // Thus, *ppwszCmdLine should be freed by the caller when // it is no longer needed // // Synopsis: Builds a command line for the surrogate // Using the CLSID and Inproc Server path // // Returns: E_FAIL if the command line couldn't be built, // S_OK if it could // //-------------------------------------------------------------------- HRESULT CClassData::GetSurrogateCmdLine( WCHAR* wszSurrogatePath, WCHAR** ppwszCmdLine) { HRESULT hr = E_FAIL; DWORD AllocSize; Win4Assert(!(*ppwszCmdLine)); WCHAR wszClsid[GUIDSTR_MAX + 1]; // holds string representation of CLSID // get the string representation of the CLSID if(FAILED(wStringFromGUID2(_guid,wszClsid,GUIDSTR_MAX))) { return E_FAIL; } // allocate space for the command line, which will be represented as // // if wszSurrogatePath is NULL // (no surrogate was specified in the registry), // we'll use the default surrogate path, // which we must construct from the system directory path and the // wszSurrogateName if ( wszSurrogatePath[0] ) AllocSize = lstrlenW(wszSurrogatePath); else AllocSize = MAX_PATH + 1 + lstrlenW(wszSurrogateName); AllocSize += 1 + GUIDSTR_MAX; AllocSize *= sizeof(WCHAR); *ppwszCmdLine = (WCHAR *) PrivMemAlloc( AllocSize ); if(!*ppwszCmdLine) { hr = E_OUTOFMEMORY; goto cleanup_and_exit; } // if a surrogate path was not specified in the registry, // use the default if(wszSurrogatePath[0] == L'\0') { #ifndef _CHICAGO_ if(!GetSystemDirectoryW(*ppwszCmdLine,MAX_PATH + 1)) { goto cleanup_and_exit; } #else // !_CHICAGO_ char szCmdLine[MAX_PATH + 1]; if(!GetSystemDirectory(szCmdLine,MAX_PATH + 1)) { goto cleanup_and_exit; } if(!(MultiByteToWideChar(CP_ACP, 0, szCmdLine, lstrlenA(szCmdLine) + 1, *ppwszCmdLine, MAX_PATH))) { goto cleanup_and_exit; } Win4Assert(lstrlenA(szCmdLine) == lstrlenW(*ppwszCmdLine)); #endif // !_CHICAGO_ lstrcatW(*ppwszCmdLine,L"\\"); lstrcatW(*ppwszCmdLine,wszSurrogateName); } else // use the path specified in the registry { lstrcpyW(*ppwszCmdLine,wszSurrogatePath); } lstrcatW(*ppwszCmdLine,L" "); lstrcatW(*ppwszCmdLine,wszClsid); hr = S_OK; cleanup_and_exit: return hr; } WCHAR * CClassData::GetRemoteServerName() { if ( ! HasRemoteServerName() ) return NULL; return _pwszRemoteServer; } //+------------------------------------------------------------------- // // Member: CClassData::GetInProcServerinfo // // Synopsis: finds either the pathname of an unlaunched surrogate // executable for loading an inproc server, or finds // a binding handle to an existing surrogate process // // Returns: REGDB_E_CLASSNOTREG if there is no DllSurrogate key // in the registry // S_OK if the DllSurrogate key exists in the registry // and/or it finds an existing surrogate process that // has the appropriate appid and security id. // E_FAIL for any other errors // //-------------------------------------------------------------------- HRESULT CClassData::GetInProcServerInfo(WCHAR** ppwszSurrogateCmdLine) { HKEY hkThisClsID = NULL; HKEY hkThisAppID = NULL; WCHAR wszDllSurrogatePath[MAX_PATH + 1]; ULONG ulSize = sizeof(DWORD); LONG lSize = MAX_PATH + 1; DWORD dwValType; DWORD dwSurrogateRegister; LONG lerr; BOOL fDllSurrogateValueExists; HRESULT hr = E_FAIL; if(_pwszSurrogateCmdLine) { return S_OK; } // open the correct APPID key if(RegOpenKeyEx(g_hkAppID,_pwszAppID, NULL, KEY_READ, &hkThisAppID) != ERROR_SUCCESS) { goto cleanup_and_exit; } lSize = sizeof(wszDllSurrogatePath); // read the path for the surrogate lerr = QueryStripRegNamedValue(hkThisAppID, wszDllSurrogatePathValue, wszDllSurrogatePath, &lSize, &fDllSurrogateValueExists); // QueryStripRegNamedValue returns ERROR_FILE_NOT_FOUND if a value // does not exist or if the value is the empty string, so since we // want to distinguish between those two cases, we need to test // to see if the DllSurrogate key exists if we get ERROR_FILE_NOT_FOUND if ((lerr == ERROR_FILE_NOT_FOUND) && !fDllSurrogateValueExists) { // we don't launch surrogates unless the DllSurrogate value // exists in the registry hr = REGDB_E_CLASSNOTREG; goto cleanup_and_exit; } Win4Assert(fDllSurrogateValueExists); // close the registry key as soon as possible RegCloseKey(hkThisAppID); hkThisAppID = NULL; if(FAILED(hr = GetSurrogateCmdLine(wszDllSurrogatePath, ppwszSurrogateCmdLine))) { goto cleanup_and_exit; } hr = S_OK; cleanup_and_exit: if(hkThisAppID) { RegCloseKey(hkThisAppID); } return hr; } //+------------------------------------------------------------------------- // // Member: CClassData::Defined // // Synopsis: check whether this class entry is properly initialized // // Returns: TRUE - this class entry is now properly initialized // FALSE - this class entry is not properly initialized // // History: 30-Aug-94 DonnaLi Created // //-------------------------------------------------------------------------- BOOL CClassData::Defined (void) { // _pssrvreg is the last to get initialized // if it is initialized, everything else is initialized as well if (_pssrvreg == NULL || !_pssrvreg->CreatedOk()) { CairoleDebugOut ((DEB_ERROR, "Fail to allocate memory in CClassData\n")); return FALSE; } return TRUE; } //+------------------------------------------------------------------------- // // Member: CClassData::InvalidateHandle // // Synopsis: Invalidate a handle in the list of handles for the class // Caller of GetServer concluded it is no longer valid // // Arguments: [rh] -- 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 CClassData::InvalidateHandle( CPortableRpcHandle &rh ) { #ifndef _CHICAGO_ rh.InvalidateHandle(_pssrvreg); #else // BUGBUG: (KevinRo) What is supposed to happen here? Win4Assert("Unimplemented Function"); #endif } void CClassData::GetAnonymousHandle( CPortableRpcHandle &rh, handle_t * phRpcAnonymous ) { _pssrvreg->GetAnonymousHandle( rh, phRpcAnonymous ); } #ifndef _CHICAGO_ void CClassData::DecHandleCount( CPortableRpcHandle &rh ) { _pssrvreg->DecHandleCount(rh.GetHandle()); } #endif const ULONG MAX_SERVICE_ARGS = 16; //+------------------------------------------------------------------------- // // Member: CLocalServer::StartService // // Synopsis: Start a specified system service // // Arguments: HANDLE * // // Returns: BOOL // // History: 08-Nov-95 BruceMa Created // //-------------------------------------------------------------------------- #ifndef _CHICAGO_ BOOL CLocalServer::StartService( CLSID & clsid, CToken * pClientToken, WCHAR *pwszRegServiceArgs, SC_HANDLE *phService) { WCHAR *pwszServiceArgs = NULL; ULONG cArgs = 0; WCHAR *apwszArgs[MAX_SERVICE_ARGS]; BOOL Status; *phService = OpenService( hServiceController, _pwszPath, GENERIC_EXECUTE | GENERIC_READ ); if ( ! *phService ) { CairoleDebugOut((DEB_ERROR, "OpenService %ws failed, error = %#x\n",_pwszPath,GetLastError()) ); return FALSE; } // Formulate the arguments (if any) if (pwszRegServiceArgs) { UINT k = 0; // Make a copy of the service arguments pwszServiceArgs = (WCHAR *) PrivMemAlloc( (lstrlenW(pwszRegServiceArgs) + 1) * sizeof(WCHAR)); if (pwszServiceArgs == NULL) { CloseServiceHandle(*phService); *phService = 0; return FALSE; } lstrcpyW(pwszServiceArgs, pwszRegServiceArgs); // Scan the arguments do { // Scan to the next non-whitespace character while(pwszServiceArgs[k] && (pwszServiceArgs[k] == L' ' || pwszServiceArgs[k] == L'\t')) { k++; } // Store the next argument if (pwszServiceArgs[k]) { apwszArgs[cArgs++] = &pwszServiceArgs[k]; } // Scan to the next whitespace char while(pwszServiceArgs[k] && pwszServiceArgs[k] != L' ' && pwszServiceArgs[k] != L'\t') { k++; } // Null terminate the previous argument if (pwszServiceArgs[k]) { pwszServiceArgs[k++] = L'\0'; } } while(pwszServiceArgs[k]); } Status = ::StartService( *phService, cArgs, cArgs > 0 ? (LPCTSTR *) apwszArgs : NULL); PrivMemFree(pwszServiceArgs); if ( Status ) return TRUE; CairoleDebugOut((DEB_ERROR, "StartService %ws failed, error = %#x\n",_pwszPath,GetLastError())); CloseServiceHandle(*phService); *phService = 0; // %1 is the error number // %2 is the service name // %3 is the serviceargs // %4 is the clsid HANDLE LogHandle; LPTSTR Strings[4]; // array of message strings. WCHAR wszClsid[GUIDSTR_MAX]; WCHAR wszErrnum[20]; DWORD err = GetLastError(); // Save the error number wsprintf(wszErrnum, L"%lu",err ); Strings[0] = wszErrnum; Strings[1] = _pwszPath; Strings[2] = pwszRegServiceArgs; // Get the clsid wStringFromGUID2(clsid, wszClsid, sizeof(wszClsid)); Strings[3] = wszClsid; // Get the log handle, then report then event. LogHandle = RegisterEventSource( NULL, SCM_EVENT_SOURCE ); if ( LogHandle ) { ReportEvent( LogHandle, EVENTLOG_ERROR_TYPE, 0, // event category EVENT_RPCSS_START_SERVICE_FAILURE, pClientToken ? pClientToken->GetSid() : NULL, // SID 4, // 4 strings passed 0, // 0 bytes of binary (LPCTSTR *)Strings, // array of strings NULL ); // no raw data // clean up the event log handle DeregisterEventSource(LogHandle); } return FALSE; } #endif // !_CHICAGO_ #ifdef DCOM // the constant generic mapping structure GENERIC_MAPPING sGenericMapping = { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL}; //+------------------------------------------------------------------------- // // Function: CheckForAccess // // Synopsis: Checks whether the passed in token is allowed by the // passed in Security Descriptor. // // Arguments: // // Returns: HRESULT // // History: 29-Mar-96 GregJen Created // //-------------------------------------------------------------------------- HRESULT CheckForAccess( CToken * pToken, const SECURITY_DESCRIPTOR * pSD ) { // if we have an empty SD, deny everyone if ( !pSD ) return E_ACCESSDENIED; // // pToken is NULL during an unsecure activation, in which case we check // if EVERYONE is granted access in the security descriptor. // if ( pToken ) { HANDLE hToken = pToken->GetToken(); BOOL fAccess = FALSE; BOOL fSuccess = FALSE; DWORD dwGrantedAccess; PRIVILEGE_SET sPrivilegeSet; DWORD dwSetLen = sizeof( sPrivilegeSet ); sPrivilegeSet.PrivilegeCount = 1; sPrivilegeSet.Control = 0; fSuccess = AccessCheck( (PSECURITY_DESCRIPTOR) pSD, hToken, COM_RIGHTS_EXECUTE, &sGenericMapping, &sPrivilegeSet, &dwSetLen, &dwGrantedAccess, &fAccess ); if ( fSuccess && fAccess ) { return S_OK; } if ( !fSuccess ) { CairoleDebugOut((DEB_ERROR, "Bad Security Descriptor 0x%08x, Access Check returned 0x%x\n", pSD, GetLastError() )); } return E_ACCESSDENIED; } else { BOOL bStatus; BOOL bDaclPresent; BOOL bDaclDefaulted; DWORD Index; HRESULT hr; PACL pDacl; PACCESS_ALLOWED_ACE pAllowAce; SID SidEveryone = { SID_REVISION, 1, SECURITY_WORLD_SID_AUTHORITY, 0 }; bStatus = GetSecurityDescriptorDacl( (void *)pSD, &bDaclPresent, &pDacl, &bDaclDefaulted ); if ( ! bStatus ) return E_ACCESSDENIED; if ( ! pDacl ) return S_OK; hr = E_ACCESSDENIED; for ( Index = 0; Index < pDacl->AceCount; Index++ ) { bStatus = GetAce( pDacl, Index, (void **) &pAllowAce ); if ( ! bStatus ) break; if ( pAllowAce->Header.AceType != ACCESS_ALLOWED_ACE_TYPE ) continue; if ( ! (pAllowAce->Mask & COM_RIGHTS_EXECUTE) ) continue; if ( EqualSid( (PSID)(&pAllowAce->SidStart), &SidEveryone ) ) { hr = S_OK; break; } } LocalFree( pDacl ); return hr; } } //+------------------------------------------------------------------------- // // Member: CClassData::CkIfCallAllowed // // Synopsis: Checks the global key EnableDCOM and performs // different logic depending on its value. Then check the // ACL's on various registry keys. If this is a local call // from the ingteractive user and none of these keys are // present, then return success. This is to support legacy // situations. // // Arguments: // // Returns: HRESULT // // History: 30-Oct-95 BruceMa Created // //-------------------------------------------------------------------------- HRESULT CClassData::CkIfCallAllowed( ACTIVATION_PARAMS * pActParams ) { HRESULT hr; PSID psid; DWORD err; DWORD dwEventID; // If the call is remote and EnableDCOM disallows remotes, // then fail if ( pActParams->RemoteActivation && gpClassCache->GetEnableDCOM() == REMOTEACCESSBY_NOBODY ) { return E_ACCESSDENIED; } // Unsecure activations have no Token object. psid = pActParams->pToken ? pActParams->pToken->GetSid() : NULL; #if DBG==1 WCHAR wszUser[MAX_PATH]; ULONG cchSize = MAX_PATH; if ( ! pActParams->UnsecureActivation ) { RpcImpersonateClient((RPC_BINDING_HANDLE)0); GetUserName(wszUser, &cchSize); RpcRevertToSelf(); } else { lstrcpyW(wszUser, L"Anonymous"); } CairoleDebugOut((DEB_TRACE, "rpcss: CkIfCallAllowed on %ws\n", wszUser)); #endif // DBG // Assume failure hr = E_ACCESSDENIED; // If there is a local LaunchPermission key, attempt to access it if (_pSD) { dwEventID = EVENT_RPCSS_LAUNCH_ACCESS_DENIED; hr = CheckForAccess( pActParams->pToken, _pSD ); } else { dwEventID = EVENT_RPCSS_DEFAULT_LAUNCH_ACCESS_DENIED; hr = CheckForAccess( pActParams->pToken, gpClassCache->GetDefaultLaunchSD() ); } // report access denied to event logger if ( FAILED( hr ) ) { #ifndef _CHICAGO_ // for this message, %1 is the clsid HANDLE LogHandle; LPTSTR Strings[1]; // array of message strings. WCHAR wszClsid[GUIDSTR_MAX]; // Get the clsid wStringFromGUID2(*(pActParams->Clsid), wszClsid, sizeof(wszClsid)); Strings[0] = wszClsid; // Get the log handle, then report then event. LogHandle = RegisterEventSource( NULL, SCM_EVENT_SOURCE ); if ( LogHandle ) { ReportEvent( LogHandle, EVENTLOG_ERROR_TYPE, 0, // event category dwEventID, psid, // SID 1, // 1 strings passed 0, // 0 bytes of binary (LPCTSTR *)Strings, // array of strings NULL ); // no raw data // clean up the event log handle DeregisterEventSource(LogHandle); } #endif // _CHICAGO_ } return hr; } #endif // NT 5.0 /*** //+------------------------------------------------------------------------- // // Member: CClassCacheList::SkipListCompareClassIDsPlusSids // // Synopsis: Compares class ids plus user sid's. // //-------------------------------------------------------------------------- int SkipListCompareClassIDsPlusSids(void * pkey1, void * pkey2) { return((CClsidSid*)pkey1)->Compare(*(const CClsidSid*)pkey2); } ***/ //+------------------------------------------------------------------------- // // Member: CClassCacheList::SkipListCompareClassIDs // // Synopsis: Compares class ids. // //-------------------------------------------------------------------------- int SkipListCompareClassIDs(void * pkey1, void * pkey2) { return((CClassID*)pkey1)->Compare(*(const CClassID*)pkey2); } //+------------------------------------------------------------------------- // // Member: CClassCacheList::SkipListDeleteClassData // // Synopsis: Deletes class data for ~CSkipList // //-------------------------------------------------------------------------- void SkipListDeleteClassData(void *pdata) { ((CClassData*)pdata)->DeleteThis(); // BUGBUG: there may be other threads around. } //+------------------------------------------------------------------------- // // Member: CClassCacheList::CClassCacheList // // Synopsis: Creates a class cache for service. // // History: 21-Apr-93 Ricksa Created // 1-Nov-94 BillMo pass in an object responsible for // comparisions. Depends on base address // of DLL being same in all processes. // //-------------------------------------------------------------------------- CClassCacheList::CClassCacheList(HRESULT &hr) : CSkipList( // NT 5.0 //(LPFNCOMPARE)(SkipListCompareClassIDsPlusSids), //OFFSETBETWEEN(CClassData, CClsidSid), (LPFNCOMPARE)(SkipListCompareClassIDs), (LPFNDELETE)(SkipListDeleteClassData), OFFSETBETWEEN(CClassData, CClassID), SKIPLIST_SHARED, &_ccidMax, // NOTE! even though the base CSkipList is // constructed first, we can pass in // the key because it is a pointer (void*) MAX_CLASS_ENTRIES, hr), _pDefaultLaunchSD( NULL ), _ccidMax((BYTE)0xff) // In x86 Windows : in shared memory // In NT : in private memory #ifdef DCOM , _aSidHkey(sizeof(SSidHkey)) #endif // DCOM ,_slsSurrogate(wszSurrogateServerAlias,hr) { hr = S_OK; // Fetch the values of // \\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\OLE.EnableDCOM // \\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\OLE.PersonalClasses // We only need to do this once unless the registry changes ReadRemoteActivationKeys(); #ifdef DCOM // Create the event so we know when the registry changes SECURITY_ATTRIBUTES secattr; secattr.nLength = sizeof(secattr); secattr.bInheritHandle = FALSE; CWorldSecurityDescriptor secd; secattr.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR) secd; _hRegEvent = CreateEvent(&secattr, FALSE, FALSE, L"RegEvent"); if (_hRegEvent) { // Set up the event to catch registry changes to HKEY_LOCAL_MACHINE int err; if ((err = RegNotifyChangeKeyValue(HKEY_LOCAL_MACHINE, TRUE, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES | REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY, _hRegEvent, TRUE)) != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(err); } } else { hr = HRESULT_FROM_WIN32(GetLastError()); } #endif // DCOM } //+------------------------------------------------------------------------- // // Member: CClassCacheList::GetClassData // // Synopsis: Gets a CClassData *. Loads if necessary from registry. // // Arguments: [guidForClass] - guid that identifies the class needed // [ppccd] - a new CClassData object that can be used // to perform scm operations. // [fLeaveLocked] - if TRUE, then when routine returns // gClassCacheLock will still be acquired. // otherwise it will not be locked. // // Returns: S_OK // REGDB_E_CLASSNOTREG - Information for context not in DB // // Algorithm: Acquire mutex protecting class cache list. // Determine whether data needs to be loaded from registry. // If data does not need to be loaded, copy it to [pcd] // and return, releasing mutex. // Release mutex // If data needs to be loaded, load it from registry. // Reacquire mutex // Determine whether data still needs to be loaded. // If data still needs to be loaded, put in data just loaded. // Release mutex. // // History: 11-Nov-94 BillMo Created // 09-Jan-96 BruceMa Added per-user registry support // //-------------------------------------------------------------------------- HRESULT CClassCacheList::GetClassData( const GUID& guidForClass, CClassData **ppccd, BOOL CheckTreatAs, BOOL CheckAutoConvert ) { HRESULT hr = S_OK; LONG err; CClassID ccid(guidForClass); CClassData *pccdInCache; CClassRegistryReader *pcrr = NULL; BOOL fLocked; *ppccd = NULL; // // claim mutex to see if we need to reload // // // first: simply lookup // later: lookup after having loaded // reg info to see if we need to insert. // gClassCacheLock.WriteLock(); // simple mutex fLocked = TRUE; // First look for a cached common registration pccdInCache = Search(ccid #ifdef NT50 , NULL #endif // NT50 ); // NT 5.0 // If no common, then look for a cached per-user registration // if (pccdInCache == NULL && g_pcllClassCache->GetPersonalClasses()) // { // pccdInCache = Search(ccid, pUserSid); // } if (pccdInCache != NULL) { (*ppccd = pccdInCache)->AddRef(); hr = S_OK; goto cleanup_and_exit; } gClassCacheLock.WriteUnlock(); fLocked = FALSE; // // Read in fresh registry data. // Create object to read the registry in the heap because // it maybe too big for Chicago's stacks. // pcrr = new CClassRegistryReader; if (pcrr == NULL) { hr = E_OUTOFMEMORY; goto cleanup_and_exit; } // // read the registry into the object // err = pcrr->ReadSingleClass(guidForClass, CheckTreatAs, CheckAutoConvert); if (NotFoundError(err)) { hr = REGDB_E_CLASSNOTREG; goto cleanup_and_exit; } if (err != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(err); goto cleanup_and_exit; } // now search again gClassCacheLock.WriteLock(); // simple mutex fLocked = TRUE; // First look for a cached common registration pccdInCache = Search(ccid #ifdef NT50 , NULL #endif // NT50 ); // NT 5.0 // If no common, then look for a cached per-user registration // if (pccdInCache == NULL && g_pcllClassCache->GetPersonalClasses()) // { // pccdInCache = Search(ccid, pUserSid); // } if (pccdInCache != NULL) { (*ppccd = pccdInCache)->AddRef(); hr = S_OK; goto cleanup_and_exit; } // // create a new CClassData from registry info // and insert // *ppccd = pcrr->NewClassData(hr #ifdef DCOM // NT5.0 , pUserSid #endif DCOM ); if (*ppccd == NULL || FAILED(hr) || Insert(*ppccd) == NULL) // the lock is still held { if (SUCCEEDED(hr)) hr = E_OUTOFMEMORY; } cleanup_and_exit: if (FAILED(hr)) { if (*ppccd != NULL) { if (!fLocked) { gClassCacheLock.WriteLock(); fLocked = TRUE; } (*ppccd)->DeleteThis(); // this should always delete since the insertion failed. *ppccd = NULL; } } if ( pcrr ) { delete pcrr; // deliberately here so lock is not acquired. } // whilst closing registry if ( fLocked ) gClassCacheLock.WriteUnlock(); return(hr); } //+------------------------------------------------------------------------- // // Member: CClassCacheList::SetEndPoint // // Synopsis: Set the endpoint of a server // // Arguments: [clsid] - class id that is started // [pwszEndPoint] - end point for the server // [dwFlags] - type of service object // // Returns: HRESULT // // Algorithm: Search cache of services for the class. If there is // no such class we return an error otherwise we update // the cache with the endpoint to the server. // // History: 21-Apr-93 Ricksa Created // // Notes: This is called by object servers to notify the SCM // that the object server is active for a class. // //-------------------------------------------------------------------------- HRESULT CClassCacheList::SetEndPoints( #ifndef _CHICAGO_ PHPROCESS phProcess, #endif IFSECURITY(PSID psid) WCHAR *pwszWinstaDesktop, RegInput * pRegInput, RegOutput * pRegOutput ) { HRESULT hr; CClassData *apccd[8]; CClassData **ppccd; CClassData *pccd; RegInputEntry *preginent; RegOutputEnt *pregoutent; DWORD Entries; DWORD n; Entries = pRegInput->dwSize; preginent = pRegInput->rginent; pregoutent = pRegOutput->regoutent; if ( Entries > 8 ) { ppccd = (CClassData **) PrivMemAlloc( Entries * sizeof(CClassData *) ); if ( ! ppccd ) return E_OUTOFMEMORY; } else ppccd = apccd; memset( ppccd, 0, Entries * sizeof(CClassData *) ); // // Keep mutex for duration of this routine since CClassData entries // can be deleted in this routine and this messes with our stupid // skip list. // gClassCacheLock.WriteLock(); // // First loop, we add all of the registrations to the class // table. // for ( n = 0; n < Entries; n++, pregoutent++, preginent++ ) { hr = GetClassCache().GetClassData( preginent->clsid, &pccd, FALSE, FALSE ); if (pccd == NULL) { CClassID ccid(preginent->clsid); // Reset the error. hr = NOERROR; // Create a class entry with no reqistry information pccd = new CClassData(ccid, NULL, // No AppID NULL, // No local server NULL, // No remote server NULL, // No service NULL, // No service parameters NULL, // No RunAs domain NULL, // No RunAs user // NT 5.0 : SID NULL, FALSE, // Cannot be activate at bits FALSE, // Cannot be activate remote FALSE, // No AllowRemoteActivation key present FALSE, // Not 16 bits hr); if (pccd == NULL) { hr = E_OUTOFMEMORY; } else if (SUCCEEDED(hr) && (Insert(pccd) == NULL)) { hr = E_OUTOFMEMORY; } if (FAILED(hr)) { if (pccd != NULL) { pccd->DeleteThis(); pccd = NULL; } break; } } ppccd[n] = pccd; #ifndef _CHICAGO_ // // Check that the caller is allowed to register for this CLSID. // if ( (pccd->_pwszRunAsUserName || pccd->_fHasService) ) { PSID pRequiredSid = NULL; WCHAR * pLocalService = NULL; BOOL Status; if ( pccd->_slocalsrv.Defined() ) { pRequiredSid = pccd->_slocalsrv->GetRunAsSid(); if ( pccd->_fHasService ) pLocalService = pccd->_slocalsrv->pwszPath(); } Status = CertifyServer( pccd->_pwszAppID, pccd->_pwszRunAsDomainName, pccd->_pwszRunAsUserName, pLocalService, pRequiredSid, psid ); if ( ! Status ) { hr = CO_E_WRONG_SERVER_IDENTITY; break; } } #endif pregoutent->dwReg = pccd->SetEndPoint( IFSECURITY(psid) pwszWinstaDesktop, #ifndef _CHICAGO_ phProcess, preginent->oxid, preginent->ipid, #else preginent->pwszEndPoint, #endif preginent->dwFlags); if ( pregoutent->dwReg ) { hr = S_OK; pregoutent->dwAtStorage = pccd->HasActivateAtStorage(); } else { hr = E_OUTOFMEMORY; break; } } // for // // If we encountered any errors then we remove any entries which were // successfully added, release any references to class data classes and // return an error. // if ( hr != S_OK ) { preginent = pRegInput->rginent; pregoutent = pRegOutput->regoutent; for ( n = 0; n < Entries; n++, pregoutent++, preginent++ ) { if ( ppccd[n] ) { if ( pregoutent->dwReg ) { CPortableRpcHandle rh( pregoutent->dwReg ); ppccd[n]->InvalidateHandle( rh ); } ppccd[n]->Release(); } } memset( pRegOutput->regoutent, 0, pRegOutput->dwSize * sizeof(RegOutputEnt) ); } // // On success, we now signal all of the class table events and release // our class data references. // if ( hr == S_OK ) { for ( n = 0; n < Entries; n++ ) { Win4Assert( ppccd[n] ); { CPortableServerEvent se(ppccd[n]); if (se.Open() == ERROR_SUCCESS) SetEvent(se.GetHandle()); ppccd[n]->Release(); } } } gClassCacheLock.WriteUnlock(); if ( hr == S_OK ) { preginent = pRegInput->rginent; pregoutent = pRegOutput->regoutent; for ( n = 0; n < Entries; n++, pregoutent++, preginent++ ) ((CProcess *)phProcess)->AddClassReg( preginent->clsid, pregoutent->dwReg ); } return hr; } //+------------------------------------------------------------------------- // // Member: CClassCacheList::StopServer // // Synopsis: Set a local server object to stopped. // // Arguments: [clsid] - class id to mark as stopped // [dwReg] - registration id returned by SCM // // Algorithm: Search cache for class and mark class as stopped. // // History: 21-Apr-93 Ricksa Created // //-------------------------------------------------------------------------- void CClassCacheList::StopServer( REFCLSID clsid, IFSECURITY(PSID psid) DWORD dwReg) { HRESULT hr; // While we are reading lock out any updates CScmLockForWrite scmlckwr(gClassCacheLock); // Get the server for the object - we don't know whether the server was // launched via a user's private registration or not, so we search // both ways CClassID ccid(clsid); CClassData *pccd = Search(ccid); // NT 5.0 // CClassData *pccd = Search(ccid, NULL); // if (pccd == NULL) // pccd = Search(ccid, psid); // Ignore the error since the caller couldn't have registered // with us in the first place. if (pccd != NULL) { CPortableRpcHandle rh(dwReg); pccd->AddRef(); pccd->StopServer(rh); // TRUE means the last registration pccd->Release(); // delete will occur when last // thread exits. } } //+------------------------------------------------------------------------- // // Member: CClassCacheList::ReadRemoteActivationKeys // // Synopsis: Reads the registry named values // // "\HKEY_LOCAL_MACHINE\SYSTEM\Software\Microsoft\OLE.EnableDCOM" // "\HKEY_LOCAL_MACHINE\SYSTEM\Software\Microsoft\OLE.PersonalClasses" // // History: 25-Oct-95 BruceMa Created // //-------------------------------------------------------------------------- void CClassCacheList::ReadRemoteActivationKeys(void) { DWORD err; HKEY hOle; DWORD dwType; DWORD dwPolicy; DWORD dwSize; WCHAR wszYN[16]; // Set the default // BUGBUG: When the named value EnableDCOM is created in // the setup hives, then this default might need to be changed _tagRAType = REMOTEACCESSBY_NOBODY; _fPersonalClasses = FALSE; // Read the EnableDCOM value (if present) from the registry if ((err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, wszOleKey, NULL, KEY_READ, &hOle)) == ERROR_SUCCESS) { // Initialize dwSize = 16; // "EnableDCOM" if ((err = RegQueryValueEx(hOle, wszEnableDCOM, NULL, &dwType, (BYTE *) wszYN, &dwSize)) == ERROR_SUCCESS) { if (dwType == REG_SZ && (wszYN[0] == L'y' || wszYN[0] == L'Y')) { _tagRAType = (EnableDCOM) REMOTEACCESSBY_KEY; } } if ( _pDefaultLaunchSD ) { ScmMemFree( _pDefaultLaunchSD ); _pDefaultLaunchSD = NULL; } GetRegistrySecDesc( hOle, wszDefaultLaunchPermission, &_pDefaultLaunchSD); // "PersonalClasses" // NT 5.0 /**** DWORD dwSize = 16; if ((err = RegQueryValueEx(hOle, wszPersonalClasses, NULL, &dwType, (BYTE *) &wszYN, &dwSize)) == ERROR_SUCCESS) { if (dwType == REG_SZ && (wszYN[0] == L'y' || wszYN[0] == L'Y')) { _fPersonalClasses = TRUE; } } ***/ RegCloseKey(hOle); } } //+------------------------------------------------------------------------- // // Member: CClassCacheList::Flush // // Synopsis: flushes the cache before process exit. // // History: 07-Apr-94 Rickhi Created // // Notes: We only want to spend time flushing the cache on the debug // build so that we can find any other potential memory leaks. // //-------------------------------------------------------------------------- #if DBG==1 && !defined(_CHICAGO_) void CClassCacheList::Flush(void) { CClassData *pccd; CSkipListEnum sle; // Loop getting any server handles and while (pccd = (CClassData *) CSkipList::First(&sle)) { // NT 5.0 void *pv = Remove((CClsidSid*)pccd); void *pv = Remove((CClassID*)pccd); Win4Assert(pv == pccd); pccd->DeleteThis(); } } #endif // NT 5.0 /***** #ifdef DCOM //+------------------------------------------------------------------------- // // Member: CClassCacheList::GetHkey // // Synopsis: Given a PSID, return the associated HKEY // // Arguments: PSID // // Returns: HKEY or NULL // // History: 10-Jan-96 BruceMa Created // //-------------------------------------------------------------------------- HKEY CClassCacheList::GetHkey(PSID pUserSid) { int cSize = _aSidHkey.GetSize(); PSSidHkey pSidHkey; for (int k = 0; k < cSize; k++) { pSidHkey = (PSSidHkey) _aSidHkey.GetAt(k); if (RtlEqualSid(pUserSid, pSidHkey->pUserSid)) { return pSidHkey->hKey; } } return NULL; } //+------------------------------------------------------------------------- // // Member: CClassCacheList::SetHkey // // Synopsis: Given a PSID, store its associated HKEY // // Arguments: PSID // HKEY // // Returns: TRUE if success // FALSE if failure // // History: 10-Jan-96 BruceMa Created // //-------------------------------------------------------------------------- BOOL CClassCacheList::SetHkey(PSID pUserSid, HKEY hKey) { int cSize = _aSidHkey.GetSize(); SSidHkey sSidHkey; sSidHkey.pUserSid = pUserSid; sSidHkey.hKey = hKey; return _aSidHkey.InsertAt(cSize, (void *) &sSidHkey, 1); } //+------------------------------------------------------------------------- // // Member: CClassCacheList::GetHkeyPsid // // Synopsis: Given a PSID, get its standard psid // // Arguments: PSID // // Returns: PSID if successful // NULL otherwise // // History: 26-Jan-96 BruceMa Created // //-------------------------------------------------------------------------- PSID CClassCacheList::GetHkeyPsid(PSID pUserSid) { int cSize = _aSidHkey.GetSize(); PSSidHkey pSidHkey; for (int k = 0; k < cSize; k++) { pSidHkey = (PSSidHkey) _aSidHkey.GetAt(k); if (RtlEqualSid(pUserSid, pSidHkey->pUserSid)) { return pSidHkey->pUserSid; } } return NULL; } //+------------------------------------------------------------------------- // // Member: CClassCacheList::FlushSidHkey // // Synopsis: Flush the // // Arguments: PSID // HKEY // // Returns: S_OK // E_OUTOFMEMORY // // Note: This releases the user SID's. // // History: 10-Jan-96 BruceMa Created // //-------------------------------------------------------------------------- void CClassCacheList::FlushSidHkey(void) { int cSize = _aSidHkey.GetSize(); PSSidHkey pSidHkey; // Release the user SID's for (int k = 0; k < cSize; k++) { pSidHkey = (PSSidHkey) _aSidHkey.GetAt(k); DeleteUserSid(pSidHkey->pUserSid); } // Flush the table _aSidHkey.SetSize(0); } #endif ***/ #ifdef DCOM // // Called from CProcess::Rundown() in OR. // void SCMRemoveRegistration( GUID Clsid, PSID pSid, DWORD Reg ) { gpClassCache->StopServer( Clsid, pSid, Reg ); } HRESULT GetMachineName( WCHAR * pwszPath, WCHAR wszMachineName[MAX_COMPUTERNAME_LENGTH+1], BOOL bDoDfsConversion ) { WCHAR * pwszServerName; BYTE Buffer[sizeof(REMOTE_NAME_INFO)+MAX_PATH*sizeof(WCHAR)]; DWORD BufferSize = sizeof(Buffer); WCHAR Drive[4]; DWORD Status; // // Extract the server name from the file's path name. // if ( pwszPath[0] != L'\\' || pwszPath[1] != L'\\' ) { lstrcpynW( Drive, pwszPath, 3 ); Drive[3] = 0; if ( GetDriveType( Drive ) != DRIVE_REMOTE ) return S_FALSE; if ( RpcImpersonateClient((RPC_BINDING_HANDLE)0) != ERROR_SUCCESS ) return CO_E_SCM_RPC_FAILURE; Status = ScmWNetGetUniversalName( pwszPath, UNIVERSAL_NAME_INFO_LEVEL, Buffer, &BufferSize ); RpcRevertToSelf(); if ( Status != NO_ERROR ) { return CO_E_BAD_PATH; } pwszPath = ((UNIVERSAL_NAME_INFO *)Buffer)->lpUniversalName; if ( ! pwszPath || pwszPath[0] != L'\\' || pwszPath[1] != L'\\' ) { // Must be a local path. return S_FALSE; } } WCHAR wszDfsPath[MAX_PATH]; if ( bDoDfsConversion ) { WCHAR * pwszDfsPath; DWORD DfsPathLen; pwszDfsPath = wszDfsPath; DfsPathLen = sizeof(wszDfsPath); for (;;) { Status = DfsFsctl( ghDfs, FSCTL_DFS_GET_SERVER_NAME, (PVOID) &pwszPath[1], lstrlenW(&pwszPath[1]) * sizeof(WCHAR), (PVOID) pwszDfsPath, &DfsPathLen ); if ( Status == STATUS_BUFFER_OVERFLOW ) { Win4Assert( pwszDfsPath == wszDfsPath ); pwszDfsPath = (WCHAR *) PrivMemAlloc( DfsPathLen ); if ( ! pwszDfsPath ) return E_OUTOFMEMORY; continue; } break; } if ( Status == STATUS_SUCCESS ) pwszPath = pwszDfsPath; else bDoDfsConversion = FALSE; } // Skip the "\\". pwszPath += 2; pwszServerName = wszMachineName; while ( *pwszPath != L'\\' ) *pwszServerName++ = *pwszPath++; *pwszServerName = 0; if ( bDoDfsConversion && (pwszPath != wszDfsPath) ) PrivMemFree( pwszPath ); return S_OK; } HRESULT GetPathForServer( WCHAR * pwszPath, WCHAR wszPathForServer[MAX_PATH+1], WCHAR ** ppwszPathForServer ) { BYTE Buffer[sizeof(REMOTE_NAME_INFO)+MAX_PATH*sizeof(WCHAR)]; WCHAR Drive[4]; DWORD BufferSize = sizeof(Buffer); DWORD PathLength; DWORD Status; *ppwszPathForServer = 0; if ( pwszPath && (lstrlenW(pwszPath) >= 3) && (pwszPath[1] == L':') && (pwszPath[2] == L'\\') ) { lstrcpynW( Drive, pwszPath, 3 ); Drive[3] = 0; switch ( GetDriveType( Drive ) ) { case 0 : // Drive type can not be determined case 1 : // The root directory does not exist case DRIVE_CDROM : case DRIVE_RAMDISK : case DRIVE_REMOVABLE : // // We can't convert these to file names that the server will be // able to access. // return CO_E_BAD_PATH; case DRIVE_FIXED : wszPathForServer[0] = wszPathForServer[1] = L'\\'; lstrcpyW( &wszPathForServer[2], SCMMachineName ); PathLength = lstrlenW( wszPathForServer ); wszPathForServer[PathLength] = L'\\'; wszPathForServer[PathLength+1] = pwszPath[0]; wszPathForServer[PathLength+2] = L'$'; wszPathForServer[PathLength+3] = L'\\'; lstrcpyW( &wszPathForServer[PathLength+4], &pwszPath[3] ); *ppwszPathForServer = wszPathForServer; break; case DRIVE_REMOTE : if ( RpcImpersonateClient((RPC_BINDING_HANDLE)0) != ERROR_SUCCESS ) return CO_E_SCM_RPC_FAILURE; Status = ScmWNetGetUniversalName( pwszPath, UNIVERSAL_NAME_INFO_LEVEL, Buffer, &BufferSize ); RpcRevertToSelf(); if ( Status != NO_ERROR ) { return CO_E_BAD_PATH; } Win4Assert( ((UNIVERSAL_NAME_INFO *)Buffer)->lpUniversalName ); lstrcpyW( wszPathForServer, ((UNIVERSAL_NAME_INFO *)Buffer)->lpUniversalName ); *ppwszPathForServer = wszPathForServer; // BUGBUG : We're probably screwed on Banyan Vines networks. Win4Assert( wszPathForServer[0] == L'\\' && wszPathForServer[1] == L'\\' ); break; } } else { *ppwszPathForServer = pwszPath; } return S_OK; } #endif // DCOM void CheckLocalCall( handle_t hRpc ) { #ifndef _CHICAGO_ UINT Type; if ( (I_RpcBindingInqTransportType( hRpc, &Type) != RPC_S_OK) || ((Type != TRANSPORT_TYPE_LPC) && (Type != TRANSPORT_TYPE_WMSG)) ) RpcRaiseException( ERROR_ACCESS_DENIED ); #endif // // Null method on Chicago unless we support incomming remote activation // calls some day. // } #ifndef _CHICAGO_ BOOL CertifyServer( WCHAR *pwszAppId, WCHAR *pwszRunAsDomainName, WCHAR *pwszRunAsUserName, WCHAR *pwszLocalService, PSID pExpectedSid, PSID pServerSid ) { PSID pRequiredSid = NULL; HANDLE hToken = 0; BOOL CloseToken = FALSE; BOOL Status; if ( pwszLocalService ) { SERVICE_STATUS ServiceStatus; SC_HANDLE hService; hService = OpenService( hServiceController, pwszLocalService, GENERIC_READ ); if ( ! hService ) return FALSE; Status = QueryServiceStatus( hService, &ServiceStatus ); if ( Status ) { if ( (ServiceStatus.dwCurrentState == SERVICE_STOPPED) || (ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING) ) Status = FALSE; } CloseServiceHandle(hService); return Status; } if ( ! pExpectedSid ) { if ( lstrcmpiW( pwszRunAsUserName, L"Interactive User" ) == 0 ) hToken = GetShellProcessToken(); else { hToken = GetRunAsToken( pwszAppId, pwszRunAsDomainName, pwszRunAsUserName ); CloseToken = TRUE; } if ( hToken ) pExpectedSid = GetUserSid(hToken); } Status = pExpectedSid && RtlEqualSid( pServerSid, pExpectedSid ); if ( hToken && pExpectedSid ) DeleteUserSid( pExpectedSid ); if ( hToken && CloseToken ) NtClose( hToken ); return Status; } #endif //+------------------------------------------------------------------------- // // Member: CClassCacheList::FindCompatibleSurrogate // // // Synopsis: Iterate through the CClassCacheList and to find // a surrogate process that has the requested appid and // security id. // // Arguments: psid (in) -- the desired security id // pwszAppID (in) -- the appid we're looking for // rh (in out) -- if we find an compatible surrogate process, // we'll set this to that surrogate's binding handle. // // Returns: TRUE // FALSE // // History: 21-Jun-96 t-adame created // //-------------------------------------------------------------------------- BOOL CClassCacheList::FindCompatibleSurrogate(IFSECURITY(PSID psid) CClassData** ppccdSrgt, WCHAR* pwszWinstaDesktop, WCHAR* pwszAppID, CPortableRpcHandle &rh) { BOOL fSuccess = FALSE; CSkipListEnum psle = NULL; CClassData* pCurrent; Win4Assert(pwszAppID); gClassCacheLock.WriteLock(); // iterate through the list pCurrent = (CClassData*)First(&psle); while(pCurrent) { CSrvRegList* psrvlist = NULL; if(psrvlist = pCurrent->_pssrvreg) { // for each ClassCache entry, see if its AppID is // the same as the one we're looking for, then // check its server registration list for a surrogate // with the appropriate security identity if(pCurrent->_pwszAppID && (lstrcmpiW(pwszAppID,pCurrent->_pwszAppID) == 0) && psrvlist->FindCompatibleSurrogate( IFSECURITY(psid) pwszWinstaDesktop, rh)) { *ppccdSrgt = pCurrent; fSuccess = TRUE; break; } } pCurrent = (CClassData*) Next(&psle); } gClassCacheLock.WriteUnlock(); return fSuccess; }