Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1572 lines
43 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;
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.ListenedOnTCP();
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;
}
}
// Initialize machine name object now that winsock should be started
status = gpMachineName->Initialize();
if (status != 0)
{
gpClientLock->UnlockExclusive();
return status;
}
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.
psaLen += 1 + OrStringLen(aAddresses[iProtseq]) + 1;
// 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
if (aProtseqs[iProtseq] == ID_TCP)
{
OrStringCopy(gwszInitialDNSName, aAddresses[iProtseq]);
}
// compute length w/IP address(es)
if (aProtseqs[iProtseq] == ID_TCP || aProtseqs[iProtseq] == ID_UDP)
{
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 += 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)
{
*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)
{
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
CMid *pMid = new(pdsaFiltered->wNumEntries * sizeof(WCHAR)) CMid(pdsaFiltered, TRUE, gLocalMid);
if (pMid)
{
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
if (pMid) delete pMid;
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);
// 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,
WT_EXECUTEINTIMERTHREAD);
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 max IP name size * 2 (one for TCP/IP and one for UDP).
DWORD psaLen = (dwNumAddrs * IP_MAXIMUM_RAW_NAME * sizeof(RPC_CHAR) * 2);
psaLen += sizeof(DUALSTRINGARRAY) + (gpdsaMyBindings->DSA()->wNumEntries * sizeof(USHORT));
DWORD dwDNSLen = 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]);
int len = OrStringLen(pIPAddrs->_pIPAddresses->NetworkAddresses[i]) + 1;
pwstrT += len;
psaLen += 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]);
int len = OrStringLen(pIPAddrs->_pIPAddresses->NetworkAddresses[i]) + 1;
pwstrT += len;
psaLen += len + 1;
}
}
}
else
{
// just copy the existing entry unchanged.
*pwstrT = id; // copy in the tower id
pwstrT++;
OrStringCopy(pwstrT, pwstrSrc);
int len = OrStringLen(pwstrSrc) + 1;
pwstrT += len;
psaLen += 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
// The mid object makes a copy of pdsaFiltered, it doesn't own it
pNewLocalMid = new(pdsaFiltered->wNumEntries * sizeof(WCHAR))
CMid(pdsaFiltered, TRUE, gLocalMid);
if (!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 bindins
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 = gpProcessList->Size();
// Check for nothing to do. This can occur early during boot.
if (dwTotalProcesses == 0)
{
gpProcessListLock->UnlockShared();
pDSABindings->Release();
return;
}
CProcess* pprocess;
CProcess** ppProcessList =
(CProcess**)_alloca(sizeof(CProcess*) * dwTotalProcesses);
// Copy contents of current list
CBListIterator all_procs(gpProcessList);
while (pprocess = (CProcess*)all_procs.Next())
{
ppProcessList[i++] = pprocess;
pprocess->Reference();
}
ASSERT(i == dwTotalProcesses);
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
gpServerLock->LockExclusive();
for (i = 0; i < dwTotalProcesses; i++)
{
ppProcessList[i]->Release();
}
gpServerLock->UnlockExclusive();
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;
}
void *ComputeSvcList( const DUALSTRINGARRAY *pBinding )
/*++
Routine Description:
Allocates and returns an initialized auth identity structure that
contains a list of authentication services for snego.
--*/
{
SEC_WINNT_AUTH_IDENTITY_EXW *pAuthId;
DWORD cbAuthId;
DWORD i;
WCHAR *pEnd;
// Compute the size of the authentication service name strings.
cbAuthId = sizeof(*pAuthId);
for (i = 0; i < s_cRpcssSvc; i++)
if (s_aRpcssSvc[i].wId != RPC_C_AUTHN_GSS_NEGOTIATE &&
(pBinding == NULL ||
ValidAuthnSvc( pBinding, s_aRpcssSvc[i].wId )))
cbAuthId += (lstrlenW( s_aRpcssSvc[i].pName ) + 1)*
sizeof(WCHAR);
// Allocate the authentication identity structure.
pAuthId = (SEC_WINNT_AUTH_IDENTITY_EXW *)PrivMemAlloc(cbAuthId);
if (pAuthId == NULL)
return NULL;
// Initialize it.
pEnd = (WCHAR *) (pAuthId+1);
pAuthId->Version = SEC_WINNT_AUTH_IDENTITY_VERSION;
pAuthId->Length = sizeof(*pAuthId);
pAuthId->User = NULL;
pAuthId->UserLength = 0;
pAuthId->Domain = NULL;
pAuthId->DomainLength = 0;
pAuthId->Password = NULL;
pAuthId->PasswordLength = 0;
pAuthId->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
pAuthId->PackageList = pEnd;
pAuthId->PackageListLength = (cbAuthId - sizeof(*pAuthId)) /
sizeof(WCHAR);
// Copy in the authentication service name strings.
for (i = 0; i < s_cRpcssSvc; i++)
if (s_aRpcssSvc[i].wId != RPC_C_AUTHN_GSS_NEGOTIATE &&
(pBinding == NULL ||
ValidAuthnSvc( pBinding, s_aRpcssSvc[i].wId )))
{
lstrcpyW( pEnd, s_aRpcssSvc[i].pName );
pEnd += lstrlenW( pEnd );
pEnd[0] = L',';
pEnd += 1;
}
pEnd -= 1;
pEnd[0] = 0;
return pAuthId;
}
/*++
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
//
ID
AllocateId(
IN LONG cRange
)
/*++
Routine Description:
Allocates a unique local ID.
This id is 64bits. The low 32 bits are a sequence number which
is incremented with each call. The high 32bits are seconds
since 1980. The ID of 0 is not used.
Limitations:
No more then 2^32 IDs can be generated in a given second without a duplicate.
When the time stamp overflows, once every >126 years, the sequence numbers
are likely to be generated in such a way as to collide with those from 126
years ago.
There is no prevision in the code to deal with overflow or duplications.
Arguments:
cRange - Number to allocate in sequence, default is 1.
Return Value:
A 64bit id.
--*/
{
static LONG sequence = 1;
FILETIME ft;
LARGE_INTEGER id;
DWORD seconds;
BOOL fSuccess;
ASSERT(cRange > 0 && cRange < 11);
GetSystemTimeAsFileTime(&ft);
fSuccess = RtlTimeToSecondsSince1980((PLARGE_INTEGER)&ft,
&seconds);
ASSERT(fSuccess); // Only fails when time is <1980 or >2115
do
{
id.HighPart = seconds;
id.LowPart = InterlockedExchangeAdd(&sequence, cRange);
}
while (id.QuadPart == 0 );
return(id.QuadPart);
}
//
// 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);
}
#endif