You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1573 lines
45 KiB
1573 lines
45 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Misc.cxx
|
|
|
|
Abstract:
|
|
|
|
Initalization, Heap, debug, thread manager for OR
|
|
|
|
Author:
|
|
|
|
Mario Goertzel [MarioGo]
|
|
|
|
Revision History:
|
|
|
|
MarioGo 02-11-95 Bits 'n pieces
|
|
|
|
--*/
|
|
|
|
#include <or.hxx>
|
|
#include <mach.hxx>
|
|
#include <excladdr.hxx>
|
|
#include <addrrefresh.hxx>
|
|
|
|
extern "C"
|
|
{
|
|
#define SECURITY_WIN32 // Used by security.h
|
|
#include <security.h>
|
|
}
|
|
|
|
BOOL fListened = FALSE;
|
|
BOOL gfRegisteredAuthInfo = FALSE;
|
|
|
|
extern BOOL gbDynamicIPChangesEnabled;
|
|
extern DWORD gdwTimeoutPeriodForStaleMids;
|
|
|
|
const UINT IP_MAXIMUM_RAW_NAME = 16; // xxx.xxx.xxx.xxx
|
|
const UINT IP_MAXIMUM_PRETTY_NAME = 256; // DNS limit
|
|
|
|
// Contains compressed remote protseqs and network addresses
|
|
// for this process, minus any addresses in the current
|
|
// exclusion list. This is a refcounted object
|
|
CDualStringArray* gpdsaMyBindings = 0;
|
|
|
|
// Contains all compressed remote protseqs and network addresses
|
|
// for this process. Not refcounted. We maintain this one
|
|
// to help when building a new gpdsaMyBindings above.
|
|
DUALSTRINGARRAY* gpdsaFullBindings = 0;
|
|
|
|
// Contains initial DNS name for TCP
|
|
WCHAR gwszInitialDNSName[IP_MAXIMUM_PRETTY_NAME];
|
|
|
|
// Authentication services successfully registered.
|
|
DWORD s_cRpcssSvc = 0;
|
|
SECPKG *s_aRpcssSvc = NULL;
|
|
|
|
// Unique ID used to track which global resolver bindings
|
|
// are currently in use. Note that this starts at one, and
|
|
// everybody else starts theirs at zero. Therefore any
|
|
// updates that originate from PushCurrentBindings should
|
|
// take precedence. This should be looked at\modified only
|
|
// when gpClientLock is taken, in sync with modifying
|
|
// gpdsaMyBindings. In addition, this should be incremented
|
|
// whenever gpdsaMyBindings is updated with new bindings.
|
|
DWORD64 g_dwResolverBindingsID = 1;
|
|
|
|
// Definitions used for asynchronous mid releases
|
|
const DWORD ASYNCMIDRELEASEARGS_SIG = 0xFEDCBA02;
|
|
|
|
typedef struct _ASYNCMIDRELEASEARGS
|
|
{
|
|
DWORD dwAMRASig; // see ASYNCMIDRELEASEARGS_SIG
|
|
HANDLE hTimer;
|
|
CMid* pMidToRelease;
|
|
} ASYNCMIDRELEASEARGS;
|
|
|
|
// Crypt context for generating random #'s
|
|
HCRYPTPROV ghCryptCtx = NULL;
|
|
|
|
ORSTATUS
|
|
StartListeningIfNecessary()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If the process has not successfully listened to remote
|
|
protocols this routine will try do to so.
|
|
|
|
Note:
|
|
|
|
Will not add ncacn_np to the list of supported Network OLE
|
|
protocols because RpcBindingServerFromClient() doesn't
|
|
work on named pipes and is required to unmarshal an in
|
|
interface pointer.
|
|
|
|
Arguments:
|
|
|
|
n/a
|
|
|
|
Return Value:
|
|
|
|
OR_OK - Success.
|
|
|
|
OR_NOMEM - Resource problems.
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS status;
|
|
PWSTR pwstr = gpwstrProtseqs;
|
|
USHORT id;
|
|
CIPAddrs* pIPAddrs = NULL;
|
|
BOOL bPushNewBindings = FALSE;
|
|
|
|
if (fListened == TRUE)
|
|
{
|
|
return(OR_OK);
|
|
}
|
|
|
|
gpClientLock->LockExclusive();
|
|
|
|
if (fListened == TRUE)
|
|
{
|
|
gpClientLock->UnlockExclusive();
|
|
return(OR_OK);
|
|
}
|
|
|
|
OrStringCopy(gwszInitialDNSName, L"");
|
|
|
|
if (pwstr)
|
|
{
|
|
while (*pwstr)
|
|
{
|
|
|
|
// skip leading white space
|
|
while ((*pwstr == L' ') || (*pwstr == L'\t'))
|
|
{
|
|
pwstr++;
|
|
}
|
|
|
|
if (*pwstr)
|
|
{
|
|
id = GetProtseqId(pwstr);
|
|
|
|
// ronans - DCOMHTTP
|
|
// we want to add http to the list of bindings even if listening does not
|
|
// succeed as this machine could still be a client of http without being a server
|
|
if (0 != id)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Trying to listen to [%d]\n",
|
|
id));
|
|
|
|
if (id == ID_DCOMHTTP )
|
|
g_fClientHttp = TRUE;
|
|
|
|
status = UseProtseqIfNecessary(id);
|
|
if (status == RPC_S_OK)
|
|
{
|
|
if (id == ID_TCP)
|
|
{
|
|
gAddrRefreshMgr.SetListenedOnTCP();
|
|
gAddrRefreshMgr.RegisterForAddressChanges();
|
|
|
|
// There may have been processes which connected before
|
|
// we were able to listen on tcp. To make sure they
|
|
// have the correct bindings, we must refresh them now.
|
|
bPushNewBindings = TRUE;
|
|
}
|
|
|
|
// if listening succeeded - no need to special case http as its in the normal
|
|
// list of protocols
|
|
if (id == ID_DCOMHTTP )
|
|
g_fClientHttp = FALSE;
|
|
|
|
fListened = TRUE;
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Listen succeeded on [%d]\n",
|
|
id));
|
|
}
|
|
else
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Listen failed on [%d]\n",
|
|
id));
|
|
}
|
|
}
|
|
|
|
pwstr = OrStringSearch(pwstr, 0) + 1;
|
|
}
|
|
}
|
|
|
|
if ( FALSE == fListened
|
|
&& 0 != gLocalMid)
|
|
{
|
|
// Didn't manage to listen to anything new, no need to
|
|
// recompute all the global arrays.
|
|
|
|
gpClientLock->UnlockExclusive();
|
|
return(OR_OK);
|
|
}
|
|
|
|
// ??? limit to only those protseqs listed in the registry,
|
|
// if the another service used more protseqs they would show up here.
|
|
|
|
RPC_BINDING_VECTOR *pbv;
|
|
PWSTR pwstrT;
|
|
DUALSTRINGARRAY *pdsaT;
|
|
PWSTR *aAddresses;
|
|
USHORT *aProtseqs;
|
|
unsigned short psaLen;
|
|
DWORD i;
|
|
DWORD iProtseq;
|
|
|
|
status = RpcServerInqBindings(&pbv);
|
|
|
|
if (RPC_S_OK == status)
|
|
{
|
|
aAddresses = new PWSTR[pbv->Count];
|
|
aProtseqs = new USHORT[pbv->Count];
|
|
|
|
if ( !aAddresses
|
|
|| !aProtseqs)
|
|
{
|
|
RpcBindingVectorFree(&pbv);
|
|
delete aAddresses; // 0 or allocated.
|
|
delete aProtseqs; // 0 or allocated.
|
|
status = OR_NOMEM;
|
|
}
|
|
}
|
|
else
|
|
status = OR_NOMEM;
|
|
|
|
if (status != OR_OK)
|
|
{
|
|
gpClientLock->UnlockExclusive();
|
|
return(status);
|
|
}
|
|
|
|
// Build array of protseqs id's and addresses we're listening to.
|
|
pwstr = gpwstrProtseqs;
|
|
if (pwstr)
|
|
{
|
|
psaLen = 0;
|
|
iProtseq = 0;
|
|
// start with the list of allowed protocols
|
|
// listed in the registry. For each protocol, in order,
|
|
// check to see if there is an endpoint registered for it.
|
|
|
|
// NOTE: Do not change the order of these loops
|
|
// It is pertinent to correctly order the final
|
|
// string.
|
|
while (*pwstr)
|
|
{
|
|
id = GetProtseqId(pwstr);
|
|
|
|
if (0 != id)
|
|
{
|
|
for (i = 0; i < pbv->Count; i++)
|
|
{
|
|
PWSTR pwstrStringBinding;
|
|
|
|
status = RpcBindingToStringBinding(pbv->BindingH[i], &pwstrStringBinding);
|
|
if (status != RPC_S_OK)
|
|
{
|
|
break;
|
|
}
|
|
ASSERT(pwstrStringBinding);
|
|
|
|
status = RpcStringBindingParse(pwstrStringBinding,
|
|
0,
|
|
&pwstrT,
|
|
&aAddresses[iProtseq],
|
|
0,
|
|
0);
|
|
|
|
RPC_STATUS statusT = RpcStringFree(&pwstrStringBinding);
|
|
ASSERT(statusT == RPC_S_OK && pwstrStringBinding == 0);
|
|
|
|
if (status != RPC_S_OK)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// if the protocol name matches we can use this one
|
|
//
|
|
|
|
if (lstrcmpW(pwstrT, pwstr) == 0)
|
|
{
|
|
aProtseqs[iProtseq] = id;
|
|
|
|
status = RpcStringFree(&pwstrT);
|
|
ASSERT(status == RPC_S_OK && pwstrT == 0);
|
|
|
|
// Disallow datagram protocols till they
|
|
// support SSL and snego.
|
|
if (!IsLocal(aProtseqs[iProtseq]) && aProtseqs[iProtseq] != ID_NP &&
|
|
aProtseqs[iProtseq] != ID_UDP && aProtseqs[iProtseq] != ID_IPX)
|
|
{
|
|
// Only hand out remote non-named pipes protseqs.
|
|
|
|
// Save the dns name RPC gave us; we will need this in future
|
|
// if the IP's change and we need to rebuild the bindings
|
|
//
|
|
// In some cases RPC may give us 127.0.0.1. Don't use that address.
|
|
//
|
|
if (aProtseqs[iProtseq] == ID_TCP &&
|
|
lstrcmpi(aAddresses[iProtseq], L"127.0.0.1"))
|
|
{
|
|
OrStringCopy(gwszInitialDNSName, aAddresses[iProtseq]);
|
|
psaLen += (unsigned short) (1 + OrStringLen(aAddresses[iProtseq]) + 1);
|
|
}
|
|
else if (aProtseqs[iProtseq] != ID_TCP)
|
|
{
|
|
psaLen += (unsigned short) (1 + OrStringLen(aAddresses[iProtseq]) + 1);
|
|
}
|
|
|
|
// compute length w/IP address(es)
|
|
if (aProtseqs[iProtseq] == ID_TCP || aProtseqs[iProtseq] == ID_UDP || ID_DCOMHTTP == aProtseqs[iProtseq])
|
|
{
|
|
if (!pIPAddrs)
|
|
{
|
|
pIPAddrs = gpMachineName->GetIPAddrs();
|
|
}
|
|
|
|
if (pIPAddrs)
|
|
{
|
|
NetworkAddressVector* pNetworkAddrVector = pIPAddrs->_pIPAddresses;
|
|
|
|
ASSERT(pNetworkAddrVector);
|
|
|
|
for (ULONG j=0; j<pNetworkAddrVector->Count; j++)
|
|
{
|
|
// do not include the loopback address in resolver bindings.
|
|
if (lstrcmpW(L"127.0.0.1", pNetworkAddrVector->NetworkAddresses[j]) != 0)
|
|
{
|
|
psaLen += (unsigned short) (1 + OrStringLen(pNetworkAddrVector->NetworkAddresses[j]) + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
iProtseq++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
status = RpcStringFree(&pwstrT);
|
|
ASSERT(status == RPC_S_OK && pwstrT == 0);
|
|
status = RpcStringFree(&aAddresses[iProtseq]);
|
|
ASSERT(status == RPC_S_OK && pwstrT == 0);
|
|
}
|
|
|
|
}
|
|
if (status != RPC_S_OK)
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
pwstr = OrStringSearch(pwstr, 0) + 1;
|
|
}
|
|
}
|
|
|
|
if (status != RPC_S_OK)
|
|
{
|
|
delete aAddresses;
|
|
delete aProtseqs;
|
|
RPC_STATUS status_tmp = RpcBindingVectorFree(&pbv);
|
|
ASSERT(pbv == 0 && status_tmp == RPC_S_OK);
|
|
gpClientLock->UnlockExclusive();
|
|
if (pIPAddrs)
|
|
pIPAddrs->DecRefCount();
|
|
return(status);
|
|
}
|
|
|
|
|
|
// string bindings final null, authn and authz service list and
|
|
// one final nulls
|
|
|
|
if (psaLen == 0)
|
|
{
|
|
// No remote bindings, leave space for an extra NULL.
|
|
psaLen = 1;
|
|
}
|
|
if (s_cRpcssSvc == 0)
|
|
{
|
|
// No authentication services, leave space for an extra NULL.
|
|
psaLen += 1;
|
|
}
|
|
psaLen += (unsigned short)(1 + 3*s_cRpcssSvc + 1);
|
|
|
|
pdsaT = new(psaLen * sizeof(WCHAR)) DUALSTRINGARRAY;
|
|
|
|
if (!pdsaT)
|
|
{
|
|
delete aAddresses;
|
|
delete aProtseqs;
|
|
status = RpcBindingVectorFree(&pbv);
|
|
ASSERT(pbv == 0 && status == RPC_S_OK);
|
|
gpClientLock->UnlockExclusive();
|
|
if (pIPAddrs)
|
|
pIPAddrs->DecRefCount();
|
|
return OR_NOMEM;
|
|
}
|
|
|
|
pdsaT->wNumEntries = psaLen;
|
|
if (s_cRpcssSvc == 0)
|
|
pdsaT->wSecurityOffset = psaLen - 2;
|
|
else
|
|
pdsaT->wSecurityOffset = (unsigned short)(psaLen - 3*s_cRpcssSvc - 1);
|
|
pwstrT = pdsaT->aStringArray;
|
|
|
|
for (i = 0; i < iProtseq; i++)
|
|
{
|
|
// Disallow datagram protocols till they
|
|
// support SSL and snego.
|
|
if (!IsLocal(aProtseqs[i]) && aProtseqs[i] != ID_NP &&
|
|
aProtseqs[i] != ID_UDP && aProtseqs[i] != ID_IPX)
|
|
{
|
|
// Make sure we don't put the localhost ip in here either.
|
|
if ((aProtseqs[i] != ID_TCP)
|
|
||
|
|
((aProtseqs[i] == ID_TCP) && (lstrcmpi(aAddresses[i], L"127.0.0.1"))))
|
|
{
|
|
*pwstrT = aProtseqs[i];
|
|
pwstrT++;
|
|
OrStringCopy(pwstrT, aAddresses[i]);
|
|
pwstrT = OrStringSearch(pwstrT, 0) + 1; // next
|
|
}
|
|
|
|
// add the IP address(es)
|
|
if (aProtseqs[i] == ID_TCP || aProtseqs[i] == ID_UDP || ID_DCOMHTTP == aProtseqs[i])
|
|
{
|
|
if (!pIPAddrs)
|
|
{
|
|
pIPAddrs = gpMachineName->GetIPAddrs();
|
|
}
|
|
|
|
if (pIPAddrs)
|
|
{
|
|
NetworkAddressVector* pNetworkAddrVector = pIPAddrs->_pIPAddresses;
|
|
ASSERT(pNetworkAddrVector);
|
|
|
|
for (ULONG j=0; j<pNetworkAddrVector->Count; j++)
|
|
{
|
|
// do not include the loopback address in resolver bindings.
|
|
if (lstrcmpW(L"127.0.0.1", pNetworkAddrVector->NetworkAddresses[j]) != 0)
|
|
{
|
|
*pwstrT = aProtseqs[i];
|
|
pwstrT++;
|
|
OrStringCopy(pwstrT, pNetworkAddrVector->NetworkAddresses[j]);
|
|
pwstrT = OrStringSearch(pwstrT, 0) + 1; // next
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
status = RpcStringFree(&aAddresses[i]);
|
|
ASSERT(status == RPC_S_OK);
|
|
}
|
|
|
|
if (pdsaT->wSecurityOffset == 2)
|
|
{
|
|
// No remote bindings, put in first null.
|
|
pdsaT->aStringArray[0] = 0;
|
|
pwstrT++;
|
|
}
|
|
|
|
// Zero final terminator
|
|
*pwstrT = 0;
|
|
pwstrT++;
|
|
|
|
// Security authn services
|
|
for (i = 0; i < s_cRpcssSvc; i++)
|
|
{
|
|
// Authn service, Authz service (-1 means none), NULL principal name
|
|
*pwstrT = s_aRpcssSvc[i].wId;
|
|
pwstrT++;
|
|
*pwstrT = -1;
|
|
pwstrT++;
|
|
*pwstrT = 0;
|
|
pwstrT++;
|
|
}
|
|
|
|
// If there are no authentication services, put in an extra NULL.
|
|
if (s_cRpcssSvc == 0)
|
|
{
|
|
*pwstrT = 0;
|
|
pwstrT++;
|
|
}
|
|
|
|
// Final NULL
|
|
*pwstrT = 0;
|
|
|
|
ASSERT(dsaValid(pdsaT));
|
|
|
|
USHORT cRemoteProtseqs = 0;
|
|
|
|
// Convert aProtseqs into remote only array of protseqs and count them.
|
|
for (i = 0; i < iProtseq; i++)
|
|
{
|
|
// Disallow datagram protocols till they
|
|
// support SSL and snego.
|
|
if (!IsLocal(aProtseqs[i]) && aProtseqs[i] != ID_NP &&
|
|
aProtseqs[i] != ID_UDP && aProtseqs[i] != ID_IPX)
|
|
{
|
|
aProtseqs[cRemoteProtseqs] = aProtseqs[i];
|
|
cRemoteProtseqs++;
|
|
}
|
|
}
|
|
|
|
delete aAddresses;
|
|
status = RpcBindingVectorFree(&pbv);
|
|
ASSERT(pbv == 0 && status == RPC_S_OK);
|
|
|
|
if (pIPAddrs) pIPAddrs->DecRefCount();
|
|
pIPAddrs = NULL;
|
|
|
|
gAddrExclusionMgr.InitializeFromRegistry();
|
|
|
|
// Obtain bindings filtered by exclusion list
|
|
HRESULT hr;
|
|
DUALSTRINGARRAY* pdsaFiltered;
|
|
hr = gAddrExclusionMgr.BuildExclusionDSA(pdsaT, &pdsaFiltered);
|
|
if (FAILED(hr))
|
|
{
|
|
delete pdsaT;
|
|
gpClientLock->UnlockExclusive();
|
|
return (OR_NOMEM);
|
|
}
|
|
|
|
// The mid object makes a copy of pdsaFiltered, it doesn't own it
|
|
BOOL fMidInitOkay = FALSE;
|
|
CMid *pMid = new(pdsaFiltered->wNumEntries * sizeof(WCHAR)) CMid(pdsaFiltered, TRUE, gLocalMid, &fMidInitOkay);
|
|
if (pMid && fMidInitOkay)
|
|
{
|
|
CDualStringArray* pdsaWrapper = new CDualStringArray(pdsaFiltered);
|
|
if (pdsaWrapper)
|
|
{
|
|
if (gpdsaFullBindings) delete gpdsaFullBindings;
|
|
gpdsaFullBindings = pdsaT; // the full bindings
|
|
|
|
ASSERT(gpClientLock->HeldExclusive());
|
|
gpMidTable->Add(pMid);
|
|
|
|
aMyProtseqs = aProtseqs;
|
|
cMyProtseqs = cRemoteProtseqs;
|
|
|
|
if (gpdsaMyBindings) gpdsaMyBindings->Release();
|
|
gpdsaMyBindings = pdsaWrapper; // the filtered bindings
|
|
gLocalMid = pMid->Id();
|
|
|
|
// Increment id counter
|
|
g_dwResolverBindingsID++;
|
|
|
|
// Release the lock now, so we don't hold it across PushCurrentBindings.
|
|
gpClientLock->UnlockExclusive();
|
|
|
|
// Push new bindings if so called for. Not fatal if this fails
|
|
if (bPushNewBindings)
|
|
{
|
|
PushCurrentBindings();
|
|
}
|
|
|
|
return OR_OK;
|
|
}
|
|
}
|
|
|
|
// Failed to get memory or mid object failed to init
|
|
if (pMid) pMid->Release();
|
|
delete pdsaT;
|
|
MIDL_user_free(pdsaFiltered);
|
|
delete aProtseqs;
|
|
gpClientLock->UnlockExclusive();
|
|
return(OR_NOMEM);
|
|
}
|
|
|
|
BOOL IsHttpClient()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the global client http flag while holding a lock
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
BOOL retval;
|
|
gpClientLock->LockExclusive();
|
|
|
|
retval = g_fClientHttp;
|
|
|
|
gpClientLock->UnlockExclusive();
|
|
|
|
return retval;
|
|
}
|
|
|
|
void CALLBACK
|
|
AsyncMidReleaseTimerCallback(void* pvParam, BOOLEAN TimerOrWaitFired)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Releases the mid object that was queued to a timer in
|
|
DoAsyncMidRelease below.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
BOOL fResult;
|
|
ASYNCMIDRELEASEARGS* pArgs;
|
|
|
|
ASSERT(TimerOrWaitFired);
|
|
|
|
pArgs = (ASYNCMIDRELEASEARGS*)pvParam;
|
|
ASSERT(pArgs);
|
|
ASSERT(pArgs->dwAMRASig == ASYNCMIDRELEASEARGS_SIG);
|
|
ASSERT(pArgs->hTimer);
|
|
ASSERT(pArgs->pMidToRelease);
|
|
|
|
gpClientLock->LockExclusive();
|
|
|
|
pArgs->pMidToRelease->Release();
|
|
|
|
gpClientLock->UnlockExclusive();
|
|
|
|
// Make a non-blocking call to delete the timer.
|
|
fResult = DeleteTimerQueueTimer(NULL,
|
|
pArgs->hTimer,
|
|
NULL);
|
|
ASSERT(fResult || (GetLastError() == ERROR_IO_PENDING));
|
|
|
|
// Finally, delete the argument structure
|
|
delete pArgs;
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
DoAsyncMidRelease(CMid* pMid, DWORD dwReleaseInMSec)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a timer callback that will call ->Release on the
|
|
supplied mid object in the specified amount of time. If
|
|
the timer creation fails, does an immediate release.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
BOOL bResult;
|
|
HANDLE hNewTimer;
|
|
ASYNCMIDRELEASEARGS* pArgs;
|
|
|
|
ASSERT(pMid);
|
|
ASSERT(gpClientLock->HeldExclusive());
|
|
|
|
pArgs = new ASYNCMIDRELEASEARGS;
|
|
if (!pArgs)
|
|
{
|
|
// Nothing we can do -- mid will be released synchronously
|
|
// when this occurs.
|
|
return;
|
|
}
|
|
|
|
// Initialize the struct
|
|
pArgs->dwAMRASig = ASYNCMIDRELEASEARGS_SIG;
|
|
pArgs->hTimer = NULL;
|
|
pArgs->pMidToRelease = pMid;
|
|
|
|
pMid->Reference();
|
|
|
|
bResult = CreateTimerQueueTimer(&(pArgs->hTimer),
|
|
NULL,
|
|
AsyncMidReleaseTimerCallback,
|
|
pArgs,
|
|
dwReleaseInMSec,
|
|
0,
|
|
0);
|
|
if (!bResult)
|
|
{
|
|
// Timer failed; release struct and mid object immediately to
|
|
// avoid leaking them. Like above, mid is released sync.
|
|
pMid->Release();
|
|
delete pArgs;
|
|
}
|
|
return;
|
|
}
|
|
|
|
RPC_STATUS
|
|
ComputeNewResolverBindings(void)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates the local OR bindings using the current IP addresses, and
|
|
places the results in gpdsaFullBindings. gpdsaFullBindings is then
|
|
passed to the address exclusion mgr object, who creates a "filtered"
|
|
set of bindings, which omits any currently excluded addresses; these
|
|
bindings are saved in gpdsaMyBindings.
|
|
|
|
Caller must be holding gpClientLock.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
RPC_S_OUT_OF_MEMORY
|
|
RPC_S_OUT_OF_RESOURCES
|
|
|
|
--*/
|
|
{
|
|
BOOL fDoneTCP = FALSE;
|
|
BOOL fDoneUDP = FALSE;
|
|
DWORD i;
|
|
CIPAddrs* pIPAddrs;
|
|
DWORD dwNumAddrs;
|
|
|
|
ASSERT(gpdsaMyBindings);
|
|
ASSERT(gpClientLock->HeldExclusive());
|
|
|
|
pIPAddrs = gpMachineName->GetIPAddrs();
|
|
dwNumAddrs = pIPAddrs ? pIPAddrs->_pIPAddresses->Count : 0;
|
|
|
|
// compute size of new dsa (for each IP address, leave space
|
|
// for two copies (one for TCP/IP and one for UDP).
|
|
DWORD psaLen = pIPAddrs ? (pIPAddrs->_pIPAddresses->StringBufferSpace * 2) : 0;
|
|
psaLen += sizeof(DUALSTRINGARRAY) + (gpdsaMyBindings->DSA()->wNumEntries * sizeof(USHORT));
|
|
DWORD dwDNSLen = (DWORD) OrStringLen(gwszInitialDNSName);
|
|
if (dwDNSLen > 0)
|
|
{
|
|
psaLen += dwDNSLen + 2;
|
|
}
|
|
|
|
// Allocate space for the new bindings
|
|
DUALSTRINGARRAY *pdsaT = new(psaLen) DUALSTRINGARRAY;
|
|
if (pdsaT == NULL)
|
|
{
|
|
if (pIPAddrs) pIPAddrs->DecRefCount();
|
|
return RPC_S_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
PWSTR pwstrT = pdsaT->aStringArray;
|
|
PWSTR pwstrSrc = gpdsaFullBindings->aStringArray;
|
|
|
|
// copy in the information. For TCP/IP and UDP, we copy in the
|
|
// new IP addresses. For all others, we leave as is.
|
|
|
|
psaLen = 0;
|
|
while (*pwstrSrc)
|
|
{
|
|
USHORT id = *pwstrSrc; // current tower id
|
|
pwstrSrc++;
|
|
|
|
if (id == NCACN_IP_TCP)
|
|
{
|
|
if (!fDoneTCP)
|
|
{
|
|
// copy in the DNS name, if any, obtained initially from RPC
|
|
// in StartListeningIfNecessary
|
|
if (dwDNSLen > 0)
|
|
{
|
|
*pwstrT = id;
|
|
pwstrT++;
|
|
OrStringCopy(pwstrT, gwszInitialDNSName);
|
|
pwstrT += dwDNSLen + 1;
|
|
psaLen += dwDNSLen + 2;
|
|
}
|
|
|
|
// copy in the new IP addresses
|
|
fDoneTCP = TRUE;
|
|
for (UINT i=0; i<dwNumAddrs; i++)
|
|
{
|
|
*pwstrT = id; // copy in the tower id
|
|
pwstrT++;
|
|
OrStringCopy(pwstrT, pIPAddrs->_pIPAddresses->NetworkAddresses[i]);
|
|
size_t len = OrStringLen(pIPAddrs->_pIPAddresses->NetworkAddresses[i]) + 1;
|
|
pwstrT += len;
|
|
psaLen += (DWORD) len + 1;
|
|
}
|
|
}
|
|
}
|
|
else if (id == NCADG_IP_UDP)
|
|
{
|
|
if (!fDoneUDP)
|
|
{
|
|
// copy in the new IP addresses
|
|
fDoneUDP = TRUE;
|
|
for (UINT i=0; i<dwNumAddrs; i++)
|
|
{
|
|
*pwstrT = id; // copy in the tower id
|
|
pwstrT++;
|
|
OrStringCopy(pwstrT, pIPAddrs->_pIPAddresses->NetworkAddresses[i]);
|
|
size_t len = OrStringLen(pIPAddrs->_pIPAddresses->NetworkAddresses[i]) + 1;
|
|
pwstrT += len;
|
|
psaLen += (DWORD) len + 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// just copy the existing entry unchanged.
|
|
*pwstrT = id; // copy in the tower id
|
|
pwstrT++;
|
|
OrStringCopy(pwstrT, pwstrSrc);
|
|
size_t len = OrStringLen(pwstrSrc) + 1;
|
|
pwstrT += len;
|
|
psaLen += (DWORD) len + 1;
|
|
}
|
|
|
|
// skip to the next towerid entry
|
|
pwstrSrc += OrStringLen(pwstrSrc) + 1;
|
|
}
|
|
|
|
// Zero final terminator
|
|
if (psaLen == 0)
|
|
{
|
|
*((DWORD*) pwstrT) = 0;
|
|
pwstrT += 2;
|
|
psaLen += 2;
|
|
}
|
|
else
|
|
{
|
|
*pwstrT = 0;
|
|
pwstrT++;
|
|
psaLen += 1;
|
|
}
|
|
|
|
// Security authn services
|
|
pdsaT->wSecurityOffset = (unsigned short) psaLen;
|
|
for (i = 0; i < s_cRpcssSvc; i++)
|
|
{
|
|
// Authn service, Authz service (-1 means none), NULL principal name
|
|
*pwstrT = s_aRpcssSvc[i].wId;
|
|
pwstrT++;
|
|
*pwstrT = -1;
|
|
pwstrT++;
|
|
*pwstrT = 0;
|
|
pwstrT++;
|
|
}
|
|
|
|
// If there are no authentication services, put in an extra NULL.
|
|
if (s_cRpcssSvc == 0)
|
|
{
|
|
*pwstrT = 0;
|
|
pwstrT++;
|
|
psaLen += 1;
|
|
}
|
|
|
|
// Final NULL
|
|
psaLen += 3*s_cRpcssSvc + 1;
|
|
*pwstrT = 0;
|
|
|
|
// update the size
|
|
pdsaT->wNumEntries = (unsigned short) psaLen;
|
|
|
|
ASSERT(dsaValid(pdsaT));
|
|
|
|
// Done with ipaddrs
|
|
if (pIPAddrs) pIPAddrs->DecRefCount();
|
|
pIPAddrs = NULL;
|
|
|
|
// Always replace the current "full" bindings, so they
|
|
// are always up-to-date.
|
|
delete gpdsaFullBindings;
|
|
gpdsaFullBindings = pdsaT;
|
|
pdsaT = NULL;
|
|
|
|
HRESULT hr;
|
|
DUALSTRINGARRAY* pdsaFiltered;
|
|
hr = gAddrExclusionMgr.BuildExclusionDSA(gpdsaFullBindings, &pdsaFiltered);
|
|
if (FAILED(hr))
|
|
return RPC_S_OUT_OF_RESOURCES;
|
|
|
|
if (dsaCompare(gpdsaMyBindings->DSA(), pdsaFiltered))
|
|
{
|
|
// the old and new local resolver strings are the same
|
|
// so don't change anything, just throw away the new one.
|
|
MIDL_user_free(pdsaFiltered);
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
// The two are different. First, let's see if an old mid entry
|
|
// for the new bindings is still in the table.
|
|
CMid* pNewLocalMid = NULL;
|
|
CMid* pOldMatchingLocalMid = NULL;
|
|
|
|
pOldMatchingLocalMid = (CMid*)gpMidTable->Lookup(CMidKey(pdsaFiltered));
|
|
if (pOldMatchingLocalMid)
|
|
{
|
|
// Returned mid should be both local and stale
|
|
ASSERT(pOldMatchingLocalMid->IsLocal());
|
|
ASSERT(pOldMatchingLocalMid->IsStale());
|
|
}
|
|
else
|
|
{
|
|
// Not in the table already, create a new mid
|
|
BOOL fMidInitOkay = FALSE;
|
|
|
|
// The mid object makes a copy of pdsaFiltered, it doesn't own it
|
|
pNewLocalMid = new(pdsaFiltered->wNumEntries * sizeof(WCHAR))
|
|
CMid(pdsaFiltered, TRUE, gLocalMid, &fMidInitOkay);
|
|
if (!pNewLocalMid || !fMidInitOkay)
|
|
{
|
|
if (pNewLocalMid)
|
|
{
|
|
delete pNewLocalMid;
|
|
}
|
|
MIDL_user_free(pdsaFiltered);
|
|
return RPC_S_OUT_OF_RESOURCES;
|
|
}
|
|
}
|
|
|
|
// Always need to construct a new gpdsaMyBindings
|
|
CDualStringArray* pdsaWrapper = new CDualStringArray(pdsaFiltered);
|
|
if (pdsaWrapper)
|
|
{
|
|
ASSERT(gpClientLock->HeldExclusive());
|
|
|
|
if (pOldMatchingLocalMid)
|
|
{
|
|
// Mark the mid that's already in the table
|
|
// as no longer stale, and reference it so it
|
|
// stays in the table.
|
|
pOldMatchingLocalMid->Reference();
|
|
pOldMatchingLocalMid->MarkStale(FALSE);
|
|
}
|
|
else
|
|
{
|
|
// Add new local mid to the table; it's "not stale" by default
|
|
ASSERT(pNewLocalMid);
|
|
gpMidTable->Add(pNewLocalMid);
|
|
}
|
|
|
|
// Mark the current mid as stale
|
|
CMid* pCurrentMid = (CMid*)gpMidTable->Lookup(CMidKey(gpdsaMyBindings->DSA()));
|
|
ASSERT(pCurrentMid);
|
|
pCurrentMid->MarkStale(TRUE);
|
|
|
|
// The old local mid object would normally be guaranteed to remain in the table
|
|
// for as long as gpClientLock is held. It is possible that an activation in-flight
|
|
// concurrent with this thread may try to look up the old local mid shortly
|
|
// after this code finishes, and if not found the oxid resolution will proceed
|
|
// as if the mid was a remote machine, not local. This leads to major problems.
|
|
// To get around this somewhat fuzzy window, we addref the old mid (thus keeping
|
|
// it in the table), and ask for a timer callback at a later time when we will
|
|
// be virtually guaranteed that all such in-flight local activations referencing
|
|
// the old mid will have completed.
|
|
DoAsyncMidRelease(pCurrentMid, gdwTimeoutPeriodForStaleMids);
|
|
|
|
// DoAsyncMidRelease takes a reference to be released later; still need
|
|
// to release it here.
|
|
pCurrentMid->Release();
|
|
|
|
// Release old bindings
|
|
gpdsaMyBindings->Release();
|
|
|
|
// Remember new ones
|
|
gpdsaMyBindings = pdsaWrapper;
|
|
|
|
// Increment id counter
|
|
g_dwResolverBindingsID++;
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
// No mem
|
|
if (pNewLocalMid) delete pNewLocalMid;
|
|
|
|
MIDL_user_free(pdsaFiltered);
|
|
|
|
return RPC_S_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
RPC_STATUS
|
|
CopyMyOrBindings(DUALSTRINGARRAY **ppdsaOrBindings, DWORD64* pdwBindingsID)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copies the current OR bindings to return to the
|
|
caller.
|
|
|
|
Parameters:
|
|
|
|
ppdsaOrBindings -- where to put the bindings when done
|
|
pdwBindingsID -- if successful, contains the binding id of the
|
|
returned bindings. Can be NULL if the client
|
|
does not care.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
RPC_S_OUT_OF_MEMORY
|
|
RPC_S_OUT_OF_RESOURCES
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
RPC_STATUS status = RPC_S_OK;
|
|
CDualStringArray* pdsaBindings;
|
|
DWORD64 dwBindingsID;
|
|
|
|
SCMVDATEHEAP();
|
|
|
|
// Take lock only long enough to take a reference on
|
|
// the current bindings
|
|
gpClientLock->LockExclusive();
|
|
|
|
pdsaBindings = gpdsaMyBindings;
|
|
if (pdsaBindings)
|
|
pdsaBindings->AddRef();
|
|
|
|
// Save id now while we're under the lock
|
|
dwBindingsID = g_dwResolverBindingsID;
|
|
|
|
// We call this here in case we failed to register for
|
|
// address change notifications. If it previously succeeded,
|
|
// then this call is a no-op, otherwise it might succeed
|
|
// this time around.
|
|
gAddrRefreshMgr.RegisterForAddressChanges();
|
|
|
|
gpClientLock->UnlockExclusive();
|
|
|
|
ASSERT(pdsaBindings);
|
|
if (!pdsaBindings)
|
|
return RPC_S_OUT_OF_RESOURCES;
|
|
|
|
hr = dsaAllocateAndCopy(ppdsaOrBindings, pdsaBindings->DSA());
|
|
if (SUCCEEDED(hr) && pdwBindingsID)
|
|
{
|
|
*pdwBindingsID = dwBindingsID;
|
|
}
|
|
|
|
pdsaBindings->Release();
|
|
|
|
SCMVDATEHEAP();
|
|
|
|
return ((hr == S_OK) ? RPC_S_OK : RPC_S_OUT_OF_RESOURCES);
|
|
}
|
|
|
|
/*
|
|
// Debugging hack: for when you only want to debug the bindings
|
|
// update stuff with one process instead of every process
|
|
// on the box.
|
|
DWORD GetUpdateablePID()
|
|
{
|
|
HKEY hOle;
|
|
DWORD error;
|
|
DWORD dwValue = -1;
|
|
DWORD dwType;
|
|
DWORD dwBufSize = sizeof(DWORD);
|
|
|
|
error = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
L"SOFTWARE\\Microsoft\\OLE",
|
|
NULL,
|
|
KEY_READ,
|
|
&hOle);
|
|
if (error == ERROR_SUCCESS)
|
|
{
|
|
error = RegQueryValueEx(
|
|
hOle,
|
|
L"UpdateablePID",
|
|
0,
|
|
&dwType,
|
|
(BYTE*)&dwValue,
|
|
&dwBufSize);
|
|
|
|
RegCloseKey(hOle);
|
|
}
|
|
return dwValue;
|
|
}
|
|
*/
|
|
|
|
|
|
void
|
|
GetCurrentBindingsAndID(DWORD64* pdwBindingsID, CDualStringArray** ppDSABindings)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns to the caller the current bindings and their id. *ppDSABindings
|
|
will have a refcount added.
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT(!gpClientLock->HeldExclusive());
|
|
ASSERT(pdwBindingsID && ppDSABindings);
|
|
|
|
// Take reference on gpClientLock long enough to grab a
|
|
// reference on the current bindings
|
|
gpClientLock->LockExclusive();
|
|
|
|
ASSERT(gpdsaMyBindings);
|
|
*ppDSABindings = gpdsaMyBindings;
|
|
(*ppDSABindings)->AddRef();
|
|
*pdwBindingsID = g_dwResolverBindingsID;
|
|
|
|
gpClientLock->UnlockExclusive();
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
PushCurrentBindings()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Propagates the current resolver bindings to all currently
|
|
running processes.
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
{
|
|
ORSTATUS status;
|
|
DWORD64 dwBindingsID;
|
|
CDualStringArray* pDSABindings = NULL;
|
|
|
|
ASSERT(!gpClientLock->HeldExclusive());
|
|
|
|
// If not enabled, don't do it
|
|
if (!gbDynamicIPChangesEnabled)
|
|
return;
|
|
|
|
GetCurrentBindingsAndID(&dwBindingsID, &pDSABindings);
|
|
ASSERT(pDSABindings);
|
|
|
|
// Take a shared lock on the process list, and copy all of
|
|
// the contained processes to a separate list, with an
|
|
// extra refcount added. Then we release the lock, and
|
|
// push the new bindings to each process. New processes
|
|
// that connect after we leave the lock will automatically
|
|
// get the newest bindings.
|
|
gpProcessListLock->LockShared();
|
|
|
|
// Allocate space on the stack to remember each process in
|
|
// the list.
|
|
DWORD i = 0;
|
|
DWORD dwTotalProcesses = 0;
|
|
DWORD dwListSize = gpProcessList->Size();
|
|
|
|
// Check for nothing to do. This can occur early during boot.
|
|
if (dwListSize == 0)
|
|
{
|
|
gpProcessListLock->UnlockShared();
|
|
pDSABindings->Release();
|
|
return;
|
|
}
|
|
|
|
CProcess* pprocess;
|
|
CProcess** ppProcessList =
|
|
(CProcess**)_alloca(sizeof(CProcess*) * dwListSize);
|
|
|
|
// Copy contents of current list
|
|
CBListIterator all_procs(gpProcessList);
|
|
while (pprocess = (CProcess*)all_procs.Next())
|
|
{
|
|
// Only push bindings if process has not been rundown
|
|
if (!pprocess->HasBeenRundown())
|
|
{
|
|
// This is the moral equivalent of ReferenceProcess(pprocess, TRUE),
|
|
// it will prevent the process object from being rundown while
|
|
// we're using it. It will be balanced out below by the
|
|
// ReleaseProcess call.
|
|
pprocess->ClientReference();
|
|
ppProcessList[dwTotalProcesses++] = pprocess;
|
|
}
|
|
}
|
|
|
|
ASSERT(dwTotalProcesses <= dwListSize);
|
|
|
|
gpProcessListLock->UnlockShared();
|
|
|
|
// Now that we're outside the lock, update each process with
|
|
// the new bindings. Note that even if another refresh
|
|
// beats us, the process object tracks the binding id's, so
|
|
// the right thing will happen.
|
|
for (i = 0; i < dwTotalProcesses; i++)
|
|
{
|
|
status = ppProcessList[i]->UpdateResolverBindings(dwBindingsID, pDSABindings->DSA());
|
|
if (status != OR_OK)
|
|
{
|
|
// For use with the debugging hack above
|
|
// if (pprocess->GetPID() != GetUpdateablePID())
|
|
// continue;
|
|
|
|
// Right now I'm considering this a best-case effort; it
|
|
// can expectedly fail in some circumstances; eg, a process
|
|
// initializes COM, does work, then uninit's COM and stops
|
|
// listening on the ole32<->rpcss interfaces.
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: failed to update dynamic resolver "
|
|
"bindings for process pid=%d\n",
|
|
ppProcessList[i]->GetPID()));
|
|
}
|
|
}
|
|
|
|
// Release references on process objects
|
|
for (i = 0; i < dwTotalProcesses; i++)
|
|
{
|
|
ReleaseProcess(ppProcessList[i]);
|
|
}
|
|
|
|
pDSABindings->Release();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
RegisterAuthInfoIfNecessary()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes all COM authentication services. The list is computed in
|
|
ComputeSecurity. Ignore failures. On normal boots the authentication
|
|
services register without error on the first call. During setup the
|
|
authentication services never register but the machine receives no
|
|
remote secure activation requests.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
DWORD Status;
|
|
SECPKG *pSvcList;
|
|
DWORD i;
|
|
DWORD j = 0;
|
|
DWORD k;
|
|
DWORD cClientSvcs;
|
|
SECPKG* aClientSvcs;
|
|
DWORD cServerSvcs;
|
|
USHORT* aServerSvcs;
|
|
|
|
// Doesn't matter if we call RegisterAuthInfo more than once by chance.
|
|
if (gfRegisteredAuthInfo)
|
|
return;
|
|
|
|
// Retrieve client\server services
|
|
if (!GetClientServerSvcs(&cClientSvcs, &aClientSvcs, &cServerSvcs, &aServerSvcs))
|
|
return;
|
|
|
|
if (cServerSvcs == 0)
|
|
{
|
|
CleanupClientServerSvcs(cClientSvcs, aClientSvcs, cServerSvcs, aServerSvcs);
|
|
return;
|
|
}
|
|
|
|
// Allocate an array to hold the list of authentication services that
|
|
// were successfully registered.
|
|
pSvcList = new SECPKG[cServerSvcs];
|
|
if (pSvcList == NULL)
|
|
{
|
|
CleanupClientServerSvcs(cClientSvcs, aClientSvcs, cServerSvcs, aServerSvcs);
|
|
return;
|
|
}
|
|
|
|
ZeroMemory(pSvcList, sizeof(SECPKG) * cServerSvcs);
|
|
|
|
// Loop over the list of authentication services to register.
|
|
for (i = 0; i < cServerSvcs; i++)
|
|
{
|
|
Status = RpcServerRegisterAuthInfo( NULL, aServerSvcs[i], NULL, NULL );
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
pSvcList[j].wId = aServerSvcs[i];
|
|
pSvcList[j].pName = NULL;
|
|
for (k= 0; k < cClientSvcs; k++)
|
|
{
|
|
if (aClientSvcs[k].wId == aServerSvcs[i])
|
|
{
|
|
if (aClientSvcs[k].pName)
|
|
{
|
|
DWORD dwLen = lstrlen(aClientSvcs[k].pName) + 1;
|
|
|
|
pSvcList[j].pName = new WCHAR[dwLen];
|
|
if (!pSvcList[j].pName)
|
|
{
|
|
// No mem; cleanup previously-allocated stuff and return
|
|
for (i = 0; i < cServerSvcs; i++)
|
|
{
|
|
if (pSvcList[i].pName)
|
|
{
|
|
delete pSvcList[i].pName;
|
|
}
|
|
}
|
|
delete pSvcList;
|
|
CleanupClientServerSvcs(cClientSvcs, aClientSvcs, cServerSvcs, aServerSvcs);
|
|
return;
|
|
}
|
|
|
|
lstrcpy(pSvcList[j].pName, aClientSvcs[k].pName);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
ASSERT( pSvcList[j].pName != NULL );
|
|
j++;
|
|
}
|
|
}
|
|
|
|
CleanupClientServerSvcs(cClientSvcs, aClientSvcs, cServerSvcs, aServerSvcs);
|
|
cClientSvcs = 0;
|
|
aClientSvcs = NULL;
|
|
cServerSvcs = 0;
|
|
aServerSvcs = NULL;
|
|
|
|
// If no authentication services were registered.
|
|
if (j == 0)
|
|
return;
|
|
|
|
// Save the new service list if no other thread has.
|
|
gpClientLock->LockExclusive();
|
|
if (!gfRegisteredAuthInfo)
|
|
{
|
|
gfRegisteredAuthInfo = TRUE;
|
|
s_cRpcssSvc = j;
|
|
s_aRpcssSvc = pSvcList;
|
|
pSvcList = NULL;
|
|
}
|
|
gpClientLock->UnlockExclusive();
|
|
|
|
// Free the service list if not saved in a global.
|
|
if (pSvcList)
|
|
{
|
|
for (i = 0; i < cServerSvcs; i++)
|
|
{
|
|
if (pSvcList[i].pName)
|
|
{
|
|
delete pSvcList[i].pName;
|
|
}
|
|
}
|
|
delete pSvcList;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return TRUE if the specified authentication service is in the
|
|
dual string array.
|
|
|
|
--*/
|
|
BOOL ValidAuthnSvc( const DUALSTRINGARRAY *pBinding, WORD wService )
|
|
{
|
|
const WCHAR *pwstrT = &pBinding->aStringArray[pBinding->wSecurityOffset];
|
|
|
|
while (*pwstrT)
|
|
{
|
|
if (*pwstrT == wService)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
pwstrT = OrStringSearch((PWSTR) pwstrT, 0) + 1;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Local ID allocation
|
|
//
|
|
|
|
BOOL
|
|
AllocateId(ID* pID)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates and returns a random 64-bit integer.
|
|
|
|
Arguments:
|
|
|
|
pID - pointer to the caller's 64-bit #
|
|
|
|
Return Value:
|
|
|
|
TRUE -- random # successfully generated
|
|
FALSE -- error occurred
|
|
|
|
--*/
|
|
{
|
|
ASSERT(pID);
|
|
|
|
if (FAILED(gRNG.GenerateRandomNumber(pID, sizeof(*pID))))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Implementation of random # generation wrapper class
|
|
//
|
|
|
|
// single global definition of the above class
|
|
CRandomNumberGenerator gRNG;
|
|
|
|
// Constructor\destructor
|
|
CRandomNumberGenerator::CRandomNumberGenerator() : _rc4SafeCtx(NULL) {}
|
|
|
|
CRandomNumberGenerator::~CRandomNumberGenerator()
|
|
{
|
|
if (_rc4SafeCtx)
|
|
{
|
|
rc4_safe_shutdown(_rc4SafeCtx); // free safe rc4 resources.
|
|
_rc4SafeCtx = NULL;
|
|
}
|
|
}
|
|
|
|
// Initialize - not threadsafe
|
|
BOOL CRandomNumberGenerator::Initialize()
|
|
{
|
|
if (!_rc4SafeCtx)
|
|
rc4_safe_startup(&_rc4SafeCtx);
|
|
|
|
// if this fails to init, we should find out why
|
|
ASSERT(_rc4SafeCtx);
|
|
|
|
return (_rc4SafeCtx) ? TRUE : FALSE;
|
|
}
|
|
|
|
#define RC4_REKEY_PARAM (500000)
|
|
|
|
// GenerateRandomNumber - threadsafe
|
|
HRESULT CRandomNumberGenerator::GenerateRandomNumber(PVOID pBuffer, ULONG ulBufSize)
|
|
{
|
|
unsigned int KeyEntry;
|
|
unsigned int KeyBytesUsed = 0;
|
|
|
|
if (!_rc4SafeCtx)
|
|
{
|
|
ASSERT(!"Should not be called before Initialize\n");
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
rc4_safe_select(_rc4SafeCtx, &KeyEntry, &KeyBytesUsed);
|
|
|
|
if (KeyBytesUsed >= RC4_REKEY_PARAM)
|
|
{
|
|
BYTE newSeed[256];
|
|
|
|
if (!RtlGenRandom(newSeed, sizeof(newSeed)))
|
|
{
|
|
ASSERT(!"RtlGenRandom failure\n");
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
rc4_safe_key(_rc4SafeCtx, KeyEntry, sizeof(newSeed), newSeed);
|
|
}
|
|
|
|
// the rc4_safe fucntion is thread safe
|
|
rc4_safe(_rc4SafeCtx, KeyEntry, ulBufSize, pBuffer);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//
|
|
// Debug helper(s)
|
|
//
|
|
|
|
#if DBG
|
|
|
|
int __cdecl __RPC_FAR ValidateError(
|
|
IN ORSTATUS Status,
|
|
IN ...)
|
|
/*++
|
|
Routine Description
|
|
|
|
Tests that 'Status' is one of an expected set of error codes.
|
|
Used on debug builds as part of the VALIDATE() macro.
|
|
|
|
Example:
|
|
|
|
VALIDATE( (Status,
|
|
OR_BADSET,
|
|
// more error codes here
|
|
OR_OK,
|
|
0) // list must be terminated with 0
|
|
);
|
|
|
|
This function is called with the OrStatus and expected errors codes
|
|
as parameters. If OrStatus is not one of the expected error
|
|
codes and it not zero a message will be printed to the debugger
|
|
and the function will return false. The VALIDATE macro ASSERT's the
|
|
return value.
|
|
|
|
Arguments:
|
|
|
|
Status - Status code in question.
|
|
|
|
... - One or more expected status codes. Terminated with 0 (OR_OK).
|
|
|
|
Return Value:
|
|
|
|
TRUE - Status code is in the list or the status is 0.
|
|
|
|
FALSE - Status code is not in the list.
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS CurrentStatus;
|
|
va_list Marker;
|
|
|
|
if (Status == 0) return(TRUE);
|
|
|
|
va_start(Marker, Status);
|
|
|
|
while (CurrentStatus = va_arg(Marker, RPC_STATUS))
|
|
{
|
|
if (CurrentStatus == Status)
|
|
{
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
va_end(Marker);
|
|
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR Assertion: unexpected failure %lu\n",
|
|
(unsigned long)Status));
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Dummy implementation to keep the linker happy
|
|
//
|
|
HRESULT CStackWalkerCF_CreateInstance (IUnknown *pUnkOuter, REFIID riid, void** ppv)
|
|
{
|
|
ASSERT (!"You shouldn't be calling this");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
#endif
|
|
|