2025-04-27 07:49:33 -04:00

492 lines
12 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1997.
//
// File:
// scmfuns.cxx
//
// Contents:
//
// A number of functions called mainly by the SCM, including
// methods of the CScmBindingIterator class
//
// History: Created 24 June 96 SatishT
//
//--------------------------------------------------------------------------
#include <or.hxx>
#include <scmfuns.hxx>
#include <actmisc.hxx>
TCCacheList<CScmHandle> ScmHandleList(ScmHandleCacheLimit);
void
ScmProcessAddClassReg(void * hProcess, REFCLSID rclsid, DWORD dwReg)
{
CProtectSharedMemory protector; // locks through rest of lexical scope
ASSERT(hProcess==gpProcess);
((CProcess*)hProcess)->AddClassReg( rclsid, dwReg );
}
void
ScmProcessRemoveClassReg(void * hProcess, REFCLSID rclsid, DWORD dwReg)
{
CProtectSharedMemory protector; // locks through rest of lexical scope
ASSERT(hProcess==gpProcess);
((CProcess*)hProcess)->RemoveClassReg( rclsid, dwReg );
}
void
ScmObjexGetThreadId(LPDWORD pThreadID)
{
CProtectSharedMemory protector; // locks through rest of lexical scope
*pThreadID = (*gpNextThreadID)++;
}
RPC_BINDING_HANDLE
SCMGetBindingHandle(long Id)
{
RPC_BINDING_HANDLE hResult = NULL;
CIdKey Key(Id);
CProcess *pProcess = gpProcessTable->Lookup(Key);
ASSERT(pProcess);
RPC_BINDING_HANDLE hTemp = pProcess->GetBindingHandle();
if (hTemp != NULL)
{
RPC_STATUS status = RpcBindingCopy(hTemp,&hResult);
if (status != RPC_S_OK)
{
return NULL;
}
else
{
return hResult;
}
}
else
{
return NULL;
}
}
void
SCMRemoveClassReg(
long Id,
GUID Clsid,
DWORD Reg
)
{
CIdKey Key(Id);
CProcess *pProcess = gpProcessTable->Lookup(Key);
ASSERT(pProcess);
pProcess->RemoveClassReg(Clsid,Reg);
}
void
SCMAddClassReg(
long Id,
GUID Clsid,
DWORD Reg
)
{
CIdKey Key(Id);
CProcess *pProcess = gpProcessTable->Lookup(Key);
ASSERT(pProcess);
pProcess->AddClassReg(Clsid,Reg);
}
void GetLocalORBindings(
DUALSTRINGARRAY * &pdsaMyBindings
)
{
pdsaMyBindings = gpLocalDSA;
}
void
GetRegisteredProtseqs(
USHORT &cMyProtseqs,
USHORT * &aMyProtseqs
)
{
cMyProtseqs = *gpcRemoteProtseqs;
aMyProtseqs = gpRemoteProtseqIds;
}
//
// CScmBindingIterator methods
//
void
CScmBindingIterator::DeleteFromCache()
{
ASSERT(_pScmHandle != NULL);
CProtectSharedMemory protector; // locks through rest of lexical scope
// This Remove call will only actually remove if the item in the list
// matches the second parameter as a pointer
CScmHandle *pRemHandle = ScmHandleList.Remove(_pwstrServer,_pScmHandle);
_pScmHandle->Release(); // Ordinary release for the ref taken
// in the ctor
if (pRemHandle != NULL) // actually removed it
{
ASSERT(pRemHandle == _pScmHandle);
_pScmHandle->Release(); // this release will destroy the handle eventually
}
}
void
CScmBindingIterator::AddToCache()
{
ORSTATUS status = OR_OK;
CProtectSharedMemory protector; // locks through rest of lexical scope
// The ref count will not drop to zero by doing a remove
// This is because CScmHandles hold an extra self reference
CScmHandle *pHandle = ScmHandleList.Remove(_pwstrServer);
// we do not reuse cached CScmHandles to avoid problems with race conditions
// A handle we are about to destroy or Reset may have been retrieved
// by another thread, and in that case we may do RpcBindingFree
// on a handle it is trying to use
if (pHandle != NULL)
{
pHandle->Release(); // this should delete it, eventually
}
CScmHandle *pRemovedHandle = NULL;
status = ScmHandleList.Insert(_pScmHandle,pRemovedHandle);
ASSERT(status != OR_I_DUPLICATE);
if (status != OR_OK)
{
_pScmHandle->Release(); // this should delete it, eventually
}
if (pRemovedHandle != NULL)
{
// we replaced the least recently used handle pRemovedHandle
pRemovedHandle->Release(); // we don't want to leak this
}
}
CScmBindingIterator::CScmBindingIterator(PWSTR pwstrServer)
: _pwstrServer((PWSTR)NULL)
{
ASSERT(pwstrServer != NULL);
HRESULT hr;
hr = _pwstrServer.Init(pwstrServer);
_ProtseqIndex = -1;
CProtectSharedMemory protector; // locks through rest of lexical scope
_pScmHandle = ScmHandleList.Find(_pwstrServer);
if (NULL != _pScmHandle)
{
_fCached = TRUE;
_pScmHandle->Reference(); // hold a ref so this doesn't go away
}
else
{
_fCached = FALSE;
}
}
CScmBindingIterator::~CScmBindingIterator()
{
CProtectSharedMemory protector; // locks through rest of lexical scope
if (_pScmHandle != NULL)
{
if (!_fCached)
{
// we have a new handle that worked
AddToCache();
}
else
{
// Release the ref we acquired in the ctor
_pScmHandle->Release();
}
}
}
RPC_BINDING_HANDLE
CScmBindingIterator::First(USHORT &wProtseq, HRESULT& hr)
{
hr = S_OK;
if (_fCached)
{
wProtseq = _pScmHandle->GetProtseq();
return _pScmHandle->GetRpcHandle();
}
else
{
return Next(wProtseq, hr);
}
}
RPC_BINDING_HANDLE
CScmBindingIterator::Next(USHORT &wProtseq, HRESULT& hr)
{
hr = S_OK; // be optimistic
if (_fCached)
{
DeleteFromCache();
_fCached = FALSE;
_pScmHandle = NULL;
}
if (_pScmHandle == NULL) // first call to Next()
{
// Create a candidate CScmHandle. This will be Reset to house the
// successively tried RPC_BINDING_HANDLEs and cached if successful
_pScmHandle = new CScmHandle((PWSTR)_pwstrServer,hr);
if ((_pScmHandle == NULL) || FAILED(hr))
{
if (hr == S_OK)
{
hr = E_OUTOFMEMORY;
}
return NULL;
}
}
ASSERT(!_fCached && _pScmHandle != NULL);
LPWSTR pwstrStringBinding = NULL, pwstrProtseq = NULL;
BOOL bUsingHttp = *gpfClientHttp;
while (TRUE)
{
_ProtseqIndex += 1;
if (_ProtseqIndex >= *gpcRemoteProtseqs)
{
// try http as last resort
if (bUsingHttp && (_ProtseqIndex == *gpcRemoteProtseqs)) {
bUsingHttp = FALSE;
wProtseq = ID_DCOMHTTP;
}
else
{
_ProtseqIndex = -1;
if (_pScmHandle != NULL) // this handle failed
{
_pScmHandle->Release(); // release it so it will go away
_pScmHandle = NULL; // let the dtor know we have nothing to cache
}
return NULL;
}
}
else
{
wProtseq = gpRemoteProtseqIds[_ProtseqIndex];
if (IsLocal(wProtseq))
continue;
}
pwstrProtseq = GetProtseq(wProtseq);
if (pwstrProtseq == NULL) continue;
PWSTR pBaseServerName = ::GetBaseServerName((PWSTR)_pwstrServer);
RPC_STATUS status = RpcStringBindingCompose(
NULL,
pwstrProtseq,
pBaseServerName,
GetEndpoint(wProtseq),
NULL,
&pwstrStringBinding
);
RPC_BINDING_HANDLE hRemoteSCM;
status = RpcBindingFromStringBinding(
pwstrStringBinding,
&hRemoteSCM
);
if (status != RPC_S_OK)
{
hr = HRESULT_FROM_WIN32(status);
return NULL;
}
RpcStringFree( &pwstrStringBinding );
pwstrStringBinding = NULL;
// Save the handle in case it works
// we can then cache it in the dtor
_pScmHandle->Reset(wProtseq,hRemoteSCM);
RpcBindingFree(&hRemoteSCM); // this has been copied into _pScmHandle
return _pScmHandle->GetRpcHandle();
}
}
BOOL
CScmBindingIterator::TryUnsecure(RPC_BINDING_HANDLE& hScmHandle)
{
// This can't be happening if the handle is NULL
ASSERT(_pScmHandle != NULL);
if (_UnSecureScmHandle.IsUninitialized())
{
_UnSecureScmHandle = *_pScmHandle; // use assignment operator
}
BOOL fResult = _UnSecureScmHandle.TryUnsecure();
hScmHandle = _UnSecureScmHandle.GetRpcHandle();
return fResult;
}
BOOL
CScmBindingIterator::TryDynamic()
{
// This can't be happening if the handle is cached or NULL
ASSERT(!_fCached && _pScmHandle != NULL);
return _pScmHandle->TryDynamic();
}
RPC_BINDING_HANDLE CScmBindingIterator::SetAuthInfo(COAUTHINFO *pAuthInfo)
{
// This can't be happening if the handle is NULL or pAuthInfo is NULL
ASSERT(_pScmHandle != NULL);
ASSERT(pAuthInfo != NULL);
RPC_STATUS status = RPC_S_OK;
// Assignment does cleanup of previous LHS handle, if any,
// and makes a copy of the RHS handle. The destructor for
// CRpcssHandle will free the handle when the iterator is destroyed
_AuthenticatedHandle = *_pScmHandle;
RPC_BINDING_HANDLE hAuthHandle = _AuthenticatedHandle.GetRpcHandle();
if (status == RPC_S_OK)
{
RPC_SECURITY_QOS Qos;
Qos.Version = RPC_C_SECURITY_QOS_VERSION;
Qos.Capabilities = pAuthInfo->dwCapabilities;
Qos.ImpersonationType = pAuthInfo->dwImpersonationLevel;
Qos.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC;
BOOL fServerPrincNameReset = FALSE;
if (
pAuthInfo->pwszServerPrincName == NULL &&
pAuthInfo->dwAuthnSvc == RPC_C_AUTHN_WINNT
)
{
// The usual hack to avoid calling RpcMgmtInqServerPrincName
pAuthInfo->pwszServerPrincName = L"Default";
fServerPrincNameReset = TRUE;
}
status = RpcBindingSetAuthInfoExW(
hAuthHandle,
pAuthInfo->pwszServerPrincName,
pAuthInfo->dwAuthnLevel,
pAuthInfo->dwAuthnSvc,
pAuthInfo->pAuthIdentityData,
pAuthInfo->dwAuthzSvc,
&Qos );
if (fServerPrincNameReset)
{
pAuthInfo->pwszServerPrincName = NULL;
}
}
if (status == RPC_S_OK)
{
return hAuthHandle;
}
else
{
return NULL;
}
}
//
// This function is called by RPCSS when a user logs off
//
void ClearRPCSSHandles()
{
ASSERT(gfThisIsRPCSS);
CScmHandle *pScmHandle = NULL;
while (pScmHandle = ScmHandleList.Pop())
{
ASSERT(pScmHandle->References() == 1);
pScmHandle->Release(); // release the ref acquired when this was constructed
}
COrBindingIterator::ResolverHandles.RemoveAll();
}
//
// This function wakes RPCSS up for reinitialization of remote protocols
//
BOOL PostWakeupMessageToRpcss()
{
// (Re)initialize ghRpcssWnd from shared memory
ghRpcssWnd = gpGlobalBlock->GetRpcssWindow();
ASSERT(gpProcess != NULL);
if (ghRpcssWnd == NULL)
{
// If ghRpcssWnd == NULL there is a race with multiple threads trying to
// start RPCSS simultaneously -- someone else started RPCSS but it hasn't had
// a chance to initialize the window yet. So let us just act like we launched
// RPCSS by doing nothing and waiting for RPCSS to signal an event
return TRUE;
}
else
{
// Otherwise post the message and send our PID as wParam
return PostMessage(ghRpcssWnd, WM_RPCSS_MSG, gpProcess->GetProcessID(), NULL);
}
}