mirror of https://github.com/tongzx/nt5src
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.
3039 lines
90 KiB
3039 lines
90 KiB
/*++
|
|
|
|
Copyright (c) 1995-1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Manager.cxx
|
|
|
|
Abstract:
|
|
|
|
Stub/OR interface
|
|
|
|
Author:
|
|
|
|
Mario Goertzel [mariogo] Feb-02-1995
|
|
|
|
Revision Hist:
|
|
|
|
MarioGo 02-10-95 Bits 'n pieces
|
|
MarioGo 01-31-96 New local and remote interfaces
|
|
TarunA 10-12-98 OXIDs are referenced on OID basis (Client side)
|
|
TarunA 10-31-98 Added PID of the process connecting
|
|
|
|
a-sergiv 07-09-99 Impersonate for the entire duration of
|
|
ResolveClientOXID for ncacn_http protocol
|
|
(to fix a bad COM Internet Services bug)
|
|
--*/
|
|
|
|
#include <or.hxx>
|
|
extern "C"
|
|
{
|
|
#include <inc.hxx>
|
|
}
|
|
|
|
// These variables hold the list of channel hooks registered for the machine.
|
|
// They are updated as the registry changes.
|
|
LONG s_cChannelHook = 0;
|
|
GUID *s_aChannelHook = NULL;
|
|
HANDLE s_hChannelHook = NULL;
|
|
|
|
// Definition of single instance of this class:
|
|
CRpcSecurityCallbackManager* gpCRpcSecurityCallbackMgr;
|
|
CPingSetQuotaManager* gpPingSetQuotaManager;
|
|
|
|
//
|
|
// Helper routines
|
|
//
|
|
|
|
extern void CheckLocalCall(IN handle_t hRpc);
|
|
|
|
extern BOOL gbDynamicIPChangesEnabled;
|
|
|
|
void
|
|
CheckLocalSecurity(
|
|
IN handle_t hClient,
|
|
IN CProcess *pProcess
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks that a client is correctly calling one of the local
|
|
(lclor.idl) methods.
|
|
|
|
Arguments:
|
|
|
|
hClient - Rpc binding handle (SCALL) of the call in progress. If NULL,
|
|
then the call is being made internally and is okay.
|
|
|
|
pProcess - Context handle passed in by the client. Must not be zero.
|
|
|
|
Return Value:
|
|
|
|
Raises OR_NOACCESS if not okay.
|
|
|
|
--*/
|
|
|
|
{
|
|
UINT type;
|
|
|
|
if ( (0 != hClient)
|
|
&& ( (I_RpcBindingInqTransportType(hClient, &type) != RPC_S_OK)
|
|
|| (type != TRANSPORT_TYPE_LPC)
|
|
|| (0 == pProcess) ) )
|
|
{
|
|
RpcRaiseException(OR_NOACCESS);
|
|
}
|
|
|
|
// pProcess is not needed here. On LRPC the RPC runtime
|
|
// prevents a different local clients from using a context handle
|
|
// of another client.
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Update the channel hook list if it has changed in the registry.
|
|
//
|
|
|
|
void UpdateChannelHooks( LONG *pcChannelHook, GUID **paChannelHook )
|
|
{
|
|
BOOL fSuccess;
|
|
BOOL fUpdate = FALSE;
|
|
DWORD result;
|
|
HKEY hKey;
|
|
DWORD lType;
|
|
DWORD lDataSize;
|
|
GUID *aChannelHook;
|
|
LONG cChannelHook;
|
|
DWORD i;
|
|
DWORD lExtent;
|
|
WCHAR wExtent[39];
|
|
DWORD j;
|
|
|
|
// Lock
|
|
gpClientLock->LockExclusive();
|
|
|
|
// If the handle hasn't been created, create it.
|
|
if (s_hChannelHook == NULL)
|
|
{
|
|
// Nothing can be done if the event can't be created.
|
|
s_hChannelHook = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
fUpdate = TRUE;
|
|
}
|
|
|
|
// If the handle has been created, see if it has been signalled.
|
|
else
|
|
{
|
|
result = WaitForSingleObject(s_hChannelHook, 0);
|
|
fUpdate = result == WAIT_OBJECT_0;
|
|
}
|
|
|
|
// Reread the registry if necessary.
|
|
if (fUpdate)
|
|
{
|
|
|
|
// Register for changes.
|
|
RegNotifyChangeKeyValue( s_hOle, TRUE,
|
|
REG_NOTIFY_CHANGE_NAME |
|
|
REG_NOTIFY_CHANGE_ATTRIBUTES |
|
|
REG_NOTIFY_CHANGE_LAST_SET |
|
|
REG_NOTIFY_CHANGE_SECURITY,
|
|
s_hChannelHook, TRUE );
|
|
|
|
// Open the channel hook key.
|
|
result = RegOpenKeyEx( s_hOle, L"ChannelHook",
|
|
NULL, KEY_QUERY_VALUE, &hKey );
|
|
if (result == ERROR_SUCCESS)
|
|
{
|
|
// Find out how many values exist.
|
|
cChannelHook = 0;
|
|
RegQueryInfoKey( hKey, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
(DWORD *) &cChannelHook, NULL, NULL, NULL, NULL );
|
|
|
|
// If there are no channel hooks, throw away the old data.
|
|
if (cChannelHook == 0)
|
|
{
|
|
delete s_aChannelHook;
|
|
s_aChannelHook = NULL;
|
|
s_cChannelHook = 0;
|
|
aChannelHook = NULL;
|
|
}
|
|
|
|
// Reuse the existing array.
|
|
else if (cChannelHook <= s_cChannelHook)
|
|
aChannelHook = s_aChannelHook;
|
|
|
|
// Allocate memory for them.
|
|
else
|
|
aChannelHook = new GUID[cChannelHook];
|
|
|
|
// If there is not enough memory, don't make changes.
|
|
if (aChannelHook != NULL)
|
|
{
|
|
|
|
// Enumerate over the channel hook ids.
|
|
j = 0;
|
|
for (i = 0; i < (DWORD)cChannelHook; i++)
|
|
{
|
|
|
|
// Get the next key.
|
|
lExtent = sizeof(wExtent) / sizeof(WCHAR);
|
|
result = RegEnumValueW( hKey, i, wExtent, &lExtent, NULL,
|
|
&lType, NULL, NULL );
|
|
|
|
// Convert it to a GUID. Note that lExtent is set to
|
|
// the length in characters not bytes despite what
|
|
// the documentation says.
|
|
if (result == ERROR_SUCCESS && lExtent == 38 &&
|
|
lType == REG_SZ &&
|
|
GUIDFromString( wExtent, &aChannelHook[j] ))
|
|
j += 1;
|
|
}
|
|
|
|
// Save the new channel hook array.
|
|
if (aChannelHook != s_aChannelHook)
|
|
delete s_aChannelHook;
|
|
s_aChannelHook = aChannelHook;
|
|
s_cChannelHook = j;
|
|
}
|
|
|
|
// Close the registry key.
|
|
RegCloseKey( hKey );
|
|
}
|
|
|
|
// There are no channel hooks. Throw away the old data.
|
|
else
|
|
{
|
|
delete s_aChannelHook;
|
|
s_aChannelHook = NULL;
|
|
s_cChannelHook = 0;
|
|
}
|
|
}
|
|
|
|
// Return the current channel hook list.
|
|
*paChannelHook = (GUID *) MIDL_user_allocate( s_cChannelHook * sizeof(GUID) );
|
|
if (*paChannelHook != NULL)
|
|
{
|
|
*pcChannelHook = s_cChannelHook;
|
|
memcpy( *paChannelHook, s_aChannelHook, s_cChannelHook * sizeof(GUID) );
|
|
}
|
|
else
|
|
*pcChannelHook = 0;
|
|
|
|
// Unlock
|
|
gpClientLock->UnlockExclusive();
|
|
}
|
|
|
|
//
|
|
// Manager (server-side) calls to the local OR interface. lclor.idl
|
|
//
|
|
|
|
error_status_t
|
|
_Connect(
|
|
IN handle_t hClient,
|
|
IN WCHAR *pwszWinstaDesktop,
|
|
IN DWORD procID,
|
|
IN DWORD dwFlags,
|
|
OUT PHPROCESS *phProcess,
|
|
OUT DWORD *pTimeoutInSeconds,
|
|
OUT DUALSTRINGARRAY **ppdsaOrBindings,
|
|
OUT MID *pLocalMid,
|
|
IN LONG cIdsToReserve,
|
|
OUT ID *pidReservedBase,
|
|
OUT DWORD *pfConnectFlags,
|
|
OUT WCHAR **pLegacySecurity,
|
|
OUT DWORD *pAuthnLevel,
|
|
OUT DWORD *pImpLevel,
|
|
OUT DWORD *pcServerSvc,
|
|
OUT USHORT **aServerSvc,
|
|
OUT DWORD *pcClientSvc,
|
|
OUT SECPKG **aClientSvc,
|
|
OUT LONG *pcChannelHook,
|
|
OUT GUID **paChannelHook,
|
|
OUT DWORD *pThreadID,
|
|
OUT DWORD *pScmProcessID,
|
|
OUT ULONG64 *pSignature,
|
|
OUT GUID *pguidRPCSSProcessIdentifier
|
|
)
|
|
{
|
|
ORSTATUS status;
|
|
CProcess *pProcess;
|
|
CToken *pToken;
|
|
BOOL fRet;
|
|
|
|
// Ensure this is a local client calling (raises an exception
|
|
// if this is not the case):
|
|
CheckLocalCall(hClient);
|
|
|
|
// Parameter validation
|
|
if (!pwszWinstaDesktop || !phProcess || !pTimeoutInSeconds ||
|
|
!ppdsaOrBindings || !pLocalMid || !pidReservedBase ||
|
|
!pfConnectFlags || !pLegacySecurity || !pAuthnLevel ||
|
|
!pImpLevel || !pcServerSvc || !aServerSvc || !pcClientSvc ||
|
|
!aClientSvc ||!pcChannelHook || !paChannelHook || !pThreadID ||
|
|
!pScmProcessID || !pSignature)
|
|
{
|
|
return OR_BADPARAM;
|
|
}
|
|
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_INFO_LEVEL,
|
|
"OR: Client connected\n"));
|
|
|
|
*pfConnectFlags = 0;
|
|
|
|
// Fill in security parameters.
|
|
if (s_fEnableDCOM == FALSE) *pfConnectFlags |= CONNECT_DISABLEDCOM;
|
|
if (s_fCatchServerExceptions) *pfConnectFlags |= CONNECT_CATCH_SERVER_EXCEPTIONS;
|
|
if (s_fBreakOnSilencedServerExceptions) *pfConnectFlags |= CONNECT_BREAK_ON_SILENCED_SERVER_EXCEPTIONS;
|
|
if (s_fMutualAuth) *pfConnectFlags |= CONNECT_MUTUALAUTH;
|
|
if (s_fSecureRefs) *pfConnectFlags |= CONNECT_SECUREREF;
|
|
|
|
*pAuthnLevel = s_lAuthnLevel;
|
|
*pImpLevel = s_lImpLevel;
|
|
|
|
// Get legacy security settings
|
|
fRet = GetLegacySecurity(pLegacySecurity);
|
|
if (!fRet)
|
|
{
|
|
return OR_NOMEM;
|
|
}
|
|
|
|
// Get client\server svcs
|
|
fRet = GetClientServerSvcs(pcClientSvc, aClientSvc, pcServerSvc, aServerSvc);
|
|
if (!fRet)
|
|
{
|
|
return OR_NOMEM;
|
|
}
|
|
|
|
// Fill in channel hooks.
|
|
UpdateChannelHooks( pcChannelHook, paChannelHook );
|
|
|
|
*pSignature = 0;
|
|
|
|
// This fails during setup but RPCSS can function anyway.
|
|
RegisterAuthInfoIfNecessary();
|
|
|
|
status = StartListeningIfNecessary();
|
|
|
|
if (status != OR_OK)
|
|
{
|
|
return(status);
|
|
}
|
|
|
|
// Do client specific stuff
|
|
status = CopyMyOrBindings(ppdsaOrBindings, NULL);
|
|
if (status != OR_OK)
|
|
{
|
|
return(status);
|
|
}
|
|
|
|
status = LookupOrCreateToken(hClient, TRUE, &pToken); // Will check security
|
|
if (status != OR_OK)
|
|
{
|
|
MIDL_user_free(*ppdsaOrBindings);
|
|
*ppdsaOrBindings = 0;
|
|
return(status);
|
|
}
|
|
|
|
gpClientLock->LockShared();
|
|
|
|
pProcess = new CProcess(pToken, pwszWinstaDesktop, procID, dwFlags, status);
|
|
if (pProcess && status == OR_OK)
|
|
{
|
|
*phProcess = (void *)pProcess;
|
|
}
|
|
else
|
|
{
|
|
if (pProcess)
|
|
{
|
|
gpClientLock->UnlockShared(); // Rundown takes an exclusive
|
|
ReleaseProcess(pProcess);
|
|
gpClientLock->LockShared(); // take it back
|
|
}
|
|
else
|
|
{
|
|
status = OR_NOMEM;
|
|
}
|
|
}
|
|
|
|
if (status != OR_OK)
|
|
{
|
|
gpClientLock->ConvertToExclusive();
|
|
MIDL_user_free(*ppdsaOrBindings);
|
|
*ppdsaOrBindings = 0;
|
|
*phProcess = 0;
|
|
if ( pToken )
|
|
pToken->Release();
|
|
*pSignature = 0;
|
|
gpClientLock->UnlockExclusive();
|
|
return(OR_NOMEM);
|
|
}
|
|
|
|
*pSignature = (ULONG64) pProcess;
|
|
|
|
*pTimeoutInSeconds = BaseTimeoutInterval;
|
|
*pLocalMid = gLocalMid;
|
|
|
|
ASSERT( (*phProcess == 0 && *ppdsaOrBindings == 0) || status == OR_OK);
|
|
|
|
_AllocateReservedIds(0,
|
|
cIdsToReserve,
|
|
pidReservedBase);
|
|
|
|
*pScmProcessID = GetCurrentProcessId();
|
|
*pThreadID = InterlockedExchangeAdd((long *)&gNextThreadID,1);
|
|
*pguidRPCSSProcessIdentifier = *(pProcess->GetGuidProcessIdentifier());
|
|
|
|
gpClientLock->UnlockShared();
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
error_status_t
|
|
_AllocateReservedIds(
|
|
IN handle_t hClient,
|
|
IN LONG cIdsToReserve,
|
|
OUT ID *pidReservedBase
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
// Called by local clients to reserve a range of IDs which will
|
|
// not conflict with any other local IDs.
|
|
|
|
Arguments:
|
|
|
|
hClient - 0 or the connection of the client.
|
|
|
|
cIdsToReserve - Number of IDs to reserve.
|
|
|
|
pidReservedBase - Starting value of the reserved IDs. The
|
|
lower DWORD of this can be increatmented to generate
|
|
cIdsToReserve unique IDs.
|
|
|
|
Return Value:
|
|
|
|
OR_OK
|
|
|
|
--*/
|
|
{
|
|
UINT type;
|
|
|
|
// Ensure this is a local client calling (raises an exception
|
|
// if this is not the case):
|
|
CheckLocalCall(hClient);
|
|
|
|
// Parameter validation
|
|
if (!pidReservedBase)
|
|
return OR_BADPARAM;
|
|
|
|
if (cIdsToReserve > 10 || cIdsToReserve < 0)
|
|
{
|
|
cIdsToReserve = 10;
|
|
}
|
|
|
|
*pidReservedBase = AllocateId(cIdsToReserve);
|
|
return(OR_OK);
|
|
}
|
|
|
|
|
|
|
|
RPC_STATUS
|
|
NegotiateDCOMVersion(
|
|
IN OUT COMVERSION *pVersion
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
// Called when we receive a COMVERSION from a remote machine
|
|
// to determine which DCOM protocol level to talk.
|
|
|
|
Arguments:
|
|
|
|
pVersion - version of the remote machine. Modified if necessary
|
|
by this routine to be the lower of the two versions.
|
|
|
|
Return Value:
|
|
|
|
OR_OK
|
|
|
|
--*/
|
|
{
|
|
// Parameter validation
|
|
if (!pVersion)
|
|
return OR_BADPARAM;
|
|
|
|
if (pVersion->MajorVersion == COM_MAJOR_VERSION)
|
|
{
|
|
if (pVersion->MinorVersion > COM_MINOR_VERSION)
|
|
{
|
|
// since the client has a lower minor version number,
|
|
// use the lower of the two.
|
|
pVersion->MinorVersion = COM_MINOR_VERSION;
|
|
}
|
|
|
|
return OR_OK;
|
|
}
|
|
return RPC_E_VERSION_MISMATCH;
|
|
}
|
|
|
|
|
|
|
|
error_status_t
|
|
_ClientResolveOXID(
|
|
IN handle_t hClient,
|
|
IN PHPROCESS phProcess,
|
|
IN OXID *poxidServer,
|
|
IN DUALSTRINGARRAY *pdsaServerBindings,
|
|
IN LONG fApartment,
|
|
OUT OXID_INFO *poxidInfo,
|
|
OUT MID *pDestinationMid,
|
|
OUT USHORT *pusAuthnSvc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Discovers the OXID_INFO for an oxid. Will find local
|
|
OXIDs without any help. It needs OR bindings in order
|
|
to discover remote OXIDs.
|
|
|
|
Arguments:
|
|
|
|
phProcess - The context handle of the process.
|
|
|
|
poxidServer - The OXID (a uuid) to resolve.
|
|
|
|
pdsaServerBindings - Compressed string bindings to
|
|
the OR on the server's machine.
|
|
|
|
fApartment - non-zero if the client is aparment model.
|
|
REVIEW: What to do with mixed model clients?
|
|
What to do when auto registering an OID?
|
|
|
|
|
|
poxidInfo - If successful this will contain information about the oxid and
|
|
an expanded string binding to the server oxid's process.
|
|
|
|
|
|
pulAuthnSvc - if successful this will contain the exact id (ie, will not be snego)
|
|
of the authn svc used to talk to the server.
|
|
|
|
Return Value:
|
|
|
|
OR_NOMEM - Common.
|
|
|
|
OR_BADOXID - Unable to resolve it.
|
|
|
|
OR_OK - Success.
|
|
|
|
--*/
|
|
{
|
|
// REVIEW: no security check here. OXID info
|
|
// is not private and you can allocate memory in
|
|
// your process, too. If we needed to store some
|
|
// info in the client process then a security
|
|
// is needed
|
|
|
|
// Parameter validation done below in ResolveClientOXID
|
|
|
|
return ResolveClientOXID( hClient,
|
|
phProcess,
|
|
poxidServer,
|
|
pdsaServerBindings,
|
|
fApartment,
|
|
0,
|
|
NULL,
|
|
poxidInfo,
|
|
pDestinationMid,
|
|
FALSE,
|
|
RPC_C_AUTHN_NONE,
|
|
NULL,
|
|
pusAuthnSvc);
|
|
}
|
|
|
|
error_status_t
|
|
ResolveClientOXID(
|
|
handle_t hClient,
|
|
PHPROCESS phProcess,
|
|
OXID *poxidServer,
|
|
DUALSTRINGARRAY *pdsaServerBindings,
|
|
LONG fApartment,
|
|
USHORT wProtseqId,
|
|
WCHAR *pMachineName,
|
|
OXID_INFO *poxidInfo,
|
|
MID *pDestinationMid,
|
|
BOOL fUnsecure,
|
|
USHORT wAuthnSvc,
|
|
BOOL* pIsLocalOxid,
|
|
USHORT* pusAuthnSvc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Discovers the OXID_INFO for an oxid. Will find local
|
|
OXIDs without any help. It needs OR bindings in order
|
|
to discover remote OXIDs.
|
|
|
|
Arguments:
|
|
|
|
phProcess - The context handle of the process.
|
|
Since this is called from SCM directly this function
|
|
CAN BE called on the same process by more then one
|
|
thread at a time.
|
|
|
|
poxidServer - The OXID (a uuid) to resolve.
|
|
|
|
pdsaServerBindings - Compressed string bindings to
|
|
the OR on the server's machine.
|
|
|
|
fApartment - non-zero if the client is aparment model.
|
|
REVIEW: What to do with mixed model clients?
|
|
What to do when auto registering an OID?
|
|
|
|
|
|
poxidInfo - If successful this will contain information about the oxid and
|
|
an expanded string binding to the server oxid's process.
|
|
|
|
fUnsecure - if TRUE, activation was done unsecurely so set
|
|
MID appropriately.
|
|
|
|
wAuthnSvc - Hint of authentication service that might work or
|
|
RPC_C_AUTHN_NONE if no hint.
|
|
|
|
pusAuthnSvc - if successful this will contain the exact id (ie, will not be snego)
|
|
of the authn svc used to talk to the server.
|
|
|
|
Return Value:
|
|
|
|
OR_NOMEM - Common.
|
|
|
|
OR_BADOXID - Unable to resolve it.
|
|
|
|
OR_OK - Success.
|
|
|
|
--*/
|
|
{
|
|
CProcess *pProcess;
|
|
CClientOxid *pOxid = NULL;
|
|
CServerOxid *pServerOxid;
|
|
CMid *pMid;
|
|
ORSTATUS status = OR_OK;
|
|
BOOL fReference;
|
|
BOOL fServerApartment = FALSE;
|
|
BOOL fResolved = FALSE;
|
|
BOOL fLazyReleaseNewCopyOfpOxid = FALSE;
|
|
DWORD i = 0;
|
|
WCHAR *pPrincipal = NULL;
|
|
WCHAR *pMachineNameFromBindings = NULL;
|
|
BOOL fImpersonating = FALSE;
|
|
|
|
// Parameter validation
|
|
if (!poxidServer || !pdsaServerBindings || !poxidInfo ||
|
|
!pDestinationMid || !pusAuthnSvc)
|
|
{
|
|
return OR_BADPARAM;
|
|
}
|
|
|
|
pProcess = ReferenceProcess(phProcess);
|
|
ASSERT(pProcess);
|
|
|
|
// CheckLocalSecurity will throw an exception if something is wrong
|
|
CheckLocalSecurity(hClient, pProcess);
|
|
|
|
if (! dsaValid(pdsaServerBindings))
|
|
{
|
|
return(OR_BADPARAM);
|
|
}
|
|
|
|
// If wProtseqId == ID_DCOMHTTP, we should impersonate
|
|
// because RPC will read some info from HKEY_CURRENT_USER.
|
|
// Sometimes ole32 will call us with 0 wProtseqId, in
|
|
// which cases we'll have to look at pdsaServerBindings.
|
|
|
|
if(wProtseqId == ID_DCOMHTTP)
|
|
{
|
|
fImpersonating = (RpcImpersonateClient(hClient) == RPC_S_OK);
|
|
}
|
|
else if(wProtseqId == 0)
|
|
{
|
|
if(pdsaServerBindings && pdsaServerBindings->aStringArray
|
|
&& *(pdsaServerBindings->aStringArray) == ID_DCOMHTTP)
|
|
{
|
|
fImpersonating = (RpcImpersonateClient(hClient) == RPC_S_OK);
|
|
}
|
|
}
|
|
|
|
// Attempt to lookup MID and OXID
|
|
|
|
gpClientLock->LockExclusive();
|
|
|
|
CMidKey midkey(pdsaServerBindings);
|
|
|
|
pMid = (CMid *)gpMidTable->Lookup(midkey);
|
|
|
|
if (0 == pMid)
|
|
{
|
|
fReference = TRUE;
|
|
pMid = new(pdsaServerBindings->wNumEntries * sizeof(WCHAR)) CMid(pdsaServerBindings, FALSE);
|
|
if (pMid)
|
|
{
|
|
gpMidTable->Add(pMid);
|
|
pMid->SetAuthnSvc(wAuthnSvc);
|
|
}
|
|
|
|
if (0 == pMid)
|
|
{
|
|
status = OR_NOMEM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fReference = FALSE;
|
|
}
|
|
|
|
if (status == OR_OK)
|
|
{
|
|
CId2Key oxidkey(*poxidServer, pMid->Id());
|
|
|
|
pOxid = (CClientOxid *)gpClientOxidTable->Lookup(oxidkey);
|
|
|
|
if (0 == pOxid)
|
|
{
|
|
if (!fReference)
|
|
{
|
|
pMid->Reference();
|
|
fReference = TRUE;
|
|
}
|
|
|
|
// Need to allocate the OXID. First step is too resolve it
|
|
// either locally or remotely.
|
|
|
|
gpClientLock->UnlockExclusive();
|
|
|
|
if (pMid->IsLocal())
|
|
{
|
|
// Local OXID, lookup directly
|
|
|
|
gpServerLock->LockShared();
|
|
|
|
CIdKey key(*poxidServer);
|
|
pServerOxid = (CServerOxid *)gpServerOxidTable->Lookup(key);
|
|
|
|
if (pServerOxid)
|
|
{
|
|
status = pServerOxid->GetInfo(poxidInfo, TRUE);
|
|
fServerApartment = pServerOxid->Apartment();
|
|
// reset the protseq id so we use LRPC.
|
|
wProtseqId = 0;
|
|
pMachineName = NULL;
|
|
}
|
|
else
|
|
{
|
|
status = OR_BADOXID;
|
|
}
|
|
ASSERT(status != OR_OK || dsaValid(poxidInfo->psa));
|
|
gpServerLock->UnlockShared();
|
|
|
|
}
|
|
else if (0 == poxidInfo->psa)
|
|
{
|
|
// Remote OXID, call ResolveOxid
|
|
|
|
handle_t hRemoteOr;
|
|
void *pAuthId = NULL;
|
|
USHORT iBinding;
|
|
|
|
poxidInfo->psa = 0;
|
|
|
|
ASSERT(!pMachineName);
|
|
status = OR_NOMEM;
|
|
|
|
hRemoteOr = pMid->GetBinding();
|
|
|
|
if (hRemoteOr)
|
|
{
|
|
if (pMid->IsSecure())
|
|
{
|
|
i = 0;
|
|
|
|
// Form server principal name
|
|
pMachineNameFromBindings = ExtractMachineName( pMid->GetStringBinding() );
|
|
if (pMachineNameFromBindings)
|
|
{
|
|
pPrincipal = new WCHAR[lstrlenW(pMachineNameFromBindings) +
|
|
(sizeof(RPCSS_SPN_PREFIX) / sizeof(WCHAR)) + 1];
|
|
if (pPrincipal)
|
|
{
|
|
lstrcpyW(pPrincipal, RPCSS_SPN_PREFIX);
|
|
lstrcatW(pPrincipal, pMachineNameFromBindings);
|
|
}
|
|
delete pMachineNameFromBindings;
|
|
}
|
|
// It is possible that we already impersonated above,
|
|
// so it might be unnecessary to do it here.
|
|
if(!fImpersonating)
|
|
{
|
|
status = RpcImpersonateClient(hClient);
|
|
if (status != RPC_S_OK)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Unable to impersonate for resolve %d\n",
|
|
status));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
i = s_cRpcssSvc+1;
|
|
}
|
|
|
|
// The loop index has the following meanings:
|
|
// 0 - Try pMid->GetAuthnSvc
|
|
// 1 through s_cRpcssSvc - Try s_aRpcssSvc
|
|
// s_cRpcssSvc+1 - Try unsecure
|
|
USHORT wFirstSvc = pMid->GetAuthnSvc();
|
|
for (; i < s_cRpcssSvc + 2; i++)
|
|
{
|
|
BOOL bSetSecurityCallBack = FALSE;
|
|
USHORT usAuthSvcFromCallback;
|
|
|
|
// Choose an authentication service.
|
|
if (i < s_cRpcssSvc+1)
|
|
{
|
|
if (i == 0)
|
|
wAuthnSvc = wFirstSvc;
|
|
else
|
|
{
|
|
// Skip this authentication service if already
|
|
// tried.
|
|
wAuthnSvc = s_aRpcssSvc[i-1].wId;
|
|
if (wAuthnSvc == wFirstSvc)
|
|
continue;
|
|
}
|
|
|
|
// See if the server uses this authentication service.
|
|
if (ValidAuthnSvc( pMid->GetStrings(), wAuthnSvc ))
|
|
{
|
|
RPC_SECURITY_QOS qos;
|
|
qos.Version = RPC_C_SECURITY_QOS_VERSION;
|
|
qos.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH;
|
|
qos.IdentityTracking = RPC_C_QOS_IDENTITY_DYNAMIC;
|
|
qos.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE;
|
|
if (wAuthnSvc == RPC_C_AUTHN_GSS_NEGOTIATE)
|
|
{
|
|
pAuthId = ComputeSvcList( pMid->GetStrings() );
|
|
if (pAuthId)
|
|
{
|
|
// if using snego, we need to know what sec pkg is eventually negotiated:
|
|
if (gpCRpcSecurityCallbackMgr->RegisterForRpcAuthSvcCallBack(hRemoteOr))
|
|
bSetSecurityCallBack = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// ComputeSvcList couldn't get the memory; just keep going
|
|
status = OR_NOMEM;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Set the security info
|
|
// AuthnSvc is unsigned long and 0xFFFF gets 0 extended
|
|
status = RpcBindingSetAuthInfoEx(hRemoteOr,
|
|
pPrincipal,
|
|
RPC_C_AUTHN_LEVEL_CONNECT,
|
|
wAuthnSvc != 0xFFFF ? wAuthnSvc
|
|
: RPC_C_AUTHN_DEFAULT,
|
|
pAuthId,
|
|
0,
|
|
&qos);
|
|
if (status != RPC_S_OK)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: RpcBindingSetAuthInfo to %d failed!! %d\n",
|
|
wAuthnSvc,
|
|
status));
|
|
|
|
if (bSetSecurityCallBack)
|
|
{
|
|
// Get rid of our callback registration
|
|
gpCRpcSecurityCallbackMgr->GetAuthSvcAndTurnOffCallback(hRemoteOr, NULL);
|
|
}
|
|
delete pAuthId;
|
|
pAuthId = NULL;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
continue;
|
|
}
|
|
|
|
// Force the binding handle unsecure.
|
|
else if (pMid->IsSecure())
|
|
{
|
|
wAuthnSvc = RPC_C_AUTHN_NONE;
|
|
status = RpcBindingSetAuthInfo(hRemoteOr,
|
|
0,
|
|
RPC_C_AUTHN_LEVEL_NONE,
|
|
RPC_C_AUTHN_NONE,
|
|
0,
|
|
0);
|
|
if (status != RPC_S_OK)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: RpcBindingSetAuthInfo to NONE failed!! %d\n",
|
|
status));
|
|
}
|
|
}
|
|
|
|
// try calling ResolveOxid2 first, if that fails,
|
|
// try ResolveOxid.
|
|
status = ResolveOxid2(hRemoteOr,
|
|
poxidServer,
|
|
cMyProtseqs,
|
|
aMyProtseqs,
|
|
&poxidInfo->psa,
|
|
&poxidInfo->ipidRemUnknown,
|
|
&poxidInfo->dwAuthnHint,
|
|
&poxidInfo->version
|
|
);
|
|
|
|
if (status == RPC_S_PROCNUM_OUT_OF_RANGE)
|
|
{
|
|
// must be a downlevel server (COMVERSION == 5.1), try calling on
|
|
// the old ResolveOXID method.
|
|
|
|
// REVIEW if it's a downlevel server what does this mean wrt
|
|
// bug 406902 and the snego mess? I think we're still okay.
|
|
poxidInfo->version.MajorVersion = COM_MAJOR_VERSION;
|
|
poxidInfo->version.MinorVersion = COM_MINOR_VERSION_1;
|
|
poxidInfo->dwFlags = 0;
|
|
|
|
status = ResolveOxid(hRemoteOr,
|
|
poxidServer,
|
|
cMyProtseqs,
|
|
aMyProtseqs,
|
|
&poxidInfo->psa,
|
|
&poxidInfo->ipidRemUnknown,
|
|
&poxidInfo->dwAuthnHint
|
|
);
|
|
}
|
|
|
|
// At this point we are done making calls on the binding handle
|
|
|
|
// Turn off the security callback no matter what the result of
|
|
// the call was
|
|
if (bSetSecurityCallBack)
|
|
{
|
|
if (status == OR_OK)
|
|
{
|
|
if (!gpCRpcSecurityCallbackMgr->GetAuthSvcAndTurnOffCallback(hRemoteOr,
|
|
&usAuthSvcFromCallback))
|
|
{
|
|
// something went wrong. Basically we don't trust what the callback
|
|
// told us. Fall back on the original behavior
|
|
bSetSecurityCallBack = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// call did not go through; just cancel the callback registration
|
|
gpCRpcSecurityCallbackMgr->GetAuthSvcAndTurnOffCallback(hRemoteOr, NULL);
|
|
bSetSecurityCallBack = FALSE;
|
|
}
|
|
}
|
|
|
|
if (status == OR_OK)
|
|
{
|
|
status = NegotiateDCOMVersion(&poxidInfo->version);
|
|
}
|
|
|
|
if (status == OR_OK)
|
|
{
|
|
// Remember which auth.svc got used:
|
|
if (bSetSecurityCallBack)
|
|
{
|
|
// we should not have set it unless we're using snego, and we
|
|
// should have gotten something other than snego back
|
|
ASSERT(wAuthnSvc == RPC_C_AUTHN_GSS_NEGOTIATE &&
|
|
usAuthSvcFromCallback != RPC_C_AUTHN_GSS_NEGOTIATE);
|
|
|
|
// Set the negotiated pkg; if we got back Kerberos, then cache
|
|
// Snego. This will partially fix the fact that we are
|
|
// hard-coding the authn svc for all future users of this mid
|
|
if (usAuthSvcFromCallback == RPC_C_AUTHN_GSS_KERBEROS)
|
|
pMid->SetAuthnSvc( RPC_C_AUTHN_GSS_NEGOTIATE );
|
|
else
|
|
pMid->SetAuthnSvc( usAuthSvcFromCallback );
|
|
}
|
|
else
|
|
{
|
|
// Just use whatever was set on the call
|
|
pMid->SetAuthnSvc( wAuthnSvc );
|
|
}
|
|
|
|
if (dsaValid(poxidInfo->psa))
|
|
{
|
|
wProtseqId = poxidInfo->psa->aStringArray[0];
|
|
}
|
|
else
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Server %s returned a bogus string array: %p\n",
|
|
pMid->PrintableName(),
|
|
poxidInfo->psa));
|
|
|
|
ASSERT(0);
|
|
if (poxidInfo->psa)
|
|
{
|
|
MIDL_user_free(poxidInfo->psa);
|
|
poxidInfo->psa = 0;
|
|
}
|
|
status = OR_BADOXID;
|
|
}
|
|
break;
|
|
}
|
|
else if (status != RPC_S_ACCESS_DENIED &&
|
|
status != RPC_S_UNKNOWN_AUTHN_SERVICE &&
|
|
status != RPC_S_UNKNOWN_AUTHZ_SERVICE &&
|
|
status != RPC_S_SEC_PKG_ERROR )
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Remote resolve OXID failed %d\n",
|
|
status));
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
RpcBindingFree(&hRemoteOr);
|
|
delete pPrincipal;
|
|
pPrincipal = NULL;
|
|
delete pAuthId;
|
|
pAuthId = NULL;
|
|
}
|
|
}
|
|
// Else it's a remote MID, but we were given the OXID info
|
|
// and protseq id from the SCM after a remote activation.
|
|
else
|
|
fResolved = TRUE;
|
|
|
|
gpClientLock->LockExclusive();
|
|
|
|
ASSERT(fReference);
|
|
|
|
if ( OR_OK == status
|
|
&& (pMid->GetAuthnSvc() == RPC_C_AUTHN_NONE ||
|
|
TRUE == fUnsecure) )
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_INFO_LEVEL,
|
|
"OR: Machine %S, unsecure retry ok, assuming no sec\n",
|
|
pMid->PrintableName()));
|
|
pMid->SecurityFailed();
|
|
}
|
|
|
|
if (status == OR_OK)
|
|
{
|
|
// Lookup the oxid again to make sure it hasn't been added in the meantime.
|
|
|
|
pOxid = (CClientOxid *)gpClientOxidTable->Lookup(oxidkey);
|
|
|
|
if (0 == pOxid)
|
|
{
|
|
ASSERT(dsaValid(poxidInfo->psa));
|
|
pOxid = new CClientOxid(*poxidServer,
|
|
pMid,
|
|
wProtseqId,
|
|
pMachineName,
|
|
fServerApartment);
|
|
|
|
if (0 != pOxid)
|
|
{
|
|
status = pOxid->UpdateInfo(poxidInfo);
|
|
|
|
if (OR_OK == status)
|
|
{
|
|
gpClientOxidTable->Add(pOxid);
|
|
}
|
|
else
|
|
{
|
|
// Will release mid, will also remove it (unnecessarily)
|
|
// from the table.
|
|
delete pOxid;
|
|
pOxid = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = OR_NOMEM;
|
|
pMid->Release(); // May actually go away..
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Release our now extra reference on the MID
|
|
DWORD t = pMid->Release();
|
|
ASSERT(t > 0);
|
|
pOxid->Reference();
|
|
}
|
|
|
|
MIDL_user_free(poxidInfo->psa);
|
|
poxidInfo->psa = 0;
|
|
}
|
|
else
|
|
{
|
|
// Resolve failed, get rid of our extra reference.
|
|
pMid->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Found the OXID, must also have found the MID
|
|
ASSERT(fReference == FALSE);
|
|
|
|
fResolved = TRUE;
|
|
|
|
if ( poxidInfo->psa )
|
|
{
|
|
MIDL_user_free(poxidInfo->psa);
|
|
poxidInfo->psa = 0;
|
|
}
|
|
|
|
pOxid->Reference();
|
|
}
|
|
}
|
|
|
|
ASSERT( (status != OR_OK) || (pOxid && pMid) );
|
|
|
|
if ( status == OR_OK
|
|
&& pOxid->IsLocal() == FALSE)
|
|
{
|
|
if (fResolved)
|
|
*poxidServer = 0;
|
|
}
|
|
|
|
if(NULL != pIsLocalOxid)
|
|
{
|
|
if((status == OR_OK) && pOxid->IsLocal())
|
|
*pIsLocalOxid = TRUE;
|
|
else
|
|
*pIsLocalOxid = FALSE;
|
|
}
|
|
|
|
if (status == OR_OK)
|
|
{
|
|
*pDestinationMid = pMid->Id();
|
|
|
|
*pusAuthnSvc = pMid->GetAuthnSvc();
|
|
|
|
// GetInfo may release the lock
|
|
status = pOxid->GetInfo(fApartment, poxidInfo);
|
|
}
|
|
|
|
if (pOxid)
|
|
{
|
|
pOxid->Release();
|
|
}
|
|
gpClientLock->UnlockExclusive();
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
void
|
|
FreeServerOids(
|
|
CProcess *pProcess,
|
|
ULONG cServerOidsToFree,
|
|
OID aServerOidsToFree[]
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees the OIDs passed in.
|
|
|
|
Arguments:
|
|
|
|
phProcess - The context handle of the process.
|
|
Since this is called from SCM directly this function
|
|
CAN BE called on the same process by more then one
|
|
thread at a time.
|
|
|
|
cServerOidsToFree - Count of entries in aServerOidsToFree
|
|
|
|
aServerOidsToFree - OIDs allocated by the server process
|
|
and no longer needed.
|
|
|
|
--*/
|
|
{
|
|
// KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
// DPFLTR_WARNING_LEVEL,
|
|
// "OR: FreeServerOids: pProcess:%x cOids:%x pOids:%x\n",
|
|
// pProcess,
|
|
// cServerOidsToFree,
|
|
// aServerOidsToFree));
|
|
|
|
ASSERT(gpServerLock->HeldExclusive());
|
|
|
|
if (cServerOidsToFree)
|
|
{
|
|
CServerOid *pOid;
|
|
CServerOxid *pOxid;
|
|
|
|
for (ULONG i = 0; i < cServerOidsToFree; i++)
|
|
{
|
|
CIdKey oidkey(aServerOidsToFree[i]);
|
|
|
|
pOid = (CServerOid *)gpServerOidTable->Lookup(oidkey);
|
|
if (pOid && pOid->IsRunningDown() == FALSE)
|
|
{
|
|
pOxid = pOid->GetOxid();
|
|
ASSERT(pOxid);
|
|
if (pProcess->IsOwner(pOxid))
|
|
{
|
|
if (pOid->References() == 0)
|
|
{
|
|
pOid->Remove();
|
|
pOid->SetRundown(TRUE);
|
|
delete pOid;
|
|
}
|
|
else
|
|
{
|
|
pOid->Free();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Process %p tried to free OID %p it didn't own\n",
|
|
pProcess,
|
|
pOid));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Process %p freed OID %p that didn't exist\n",
|
|
pProcess,
|
|
&aServerOidsToFree[i]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
error_status_t
|
|
_BulkUpdateOIDs(
|
|
IN handle_t hClient,
|
|
IN PHPROCESS phProcess,
|
|
IN ULONG cOidsToBeAdded,
|
|
IN OXID_OID_PAIR aOidsToBeAdded[],
|
|
OUT LONG aStatusOfAdds[],
|
|
IN ULONG cOidsToBeRemoved,
|
|
IN OID_MID_PAIR aOidsToBeRemoved[],
|
|
IN ULONG cServerOidsToFree,
|
|
IN OID aServerOidsToFree[],
|
|
IN ULONG cServerOidsToUnPin,
|
|
IN OID aServerOidsToUnPin[],
|
|
IN ULONG cClientOxidsToFree,
|
|
IN OXID_REF aClientOxidsToFree[]
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Updates the set of remote OIDs in use by a process.
|
|
|
|
Note:
|
|
|
|
An OID maybe removed before it is added. This means that
|
|
the client was using it and is no longer using it. In
|
|
this case a single delete from set ping is made to keep
|
|
the object alive. This is only needed if the client
|
|
has remarshalled a pointer to the object.
|
|
|
|
Arguments:
|
|
|
|
phProcess - Context handle for the process.
|
|
|
|
cOidsToBeAdded - Count of aOidsToBeAdded and aStatusOfAdds
|
|
|
|
aOidsToBeAdded - OID-OXID-MID pairs representing the
|
|
oids and the owning oxids to add.
|
|
|
|
aStatusOfAdds - Some adds may succeed when other fail.
|
|
OR_NOMEM - couldn't allocate storage
|
|
OR_BADOXID - OXID doesn't exist.
|
|
OR_OK (0) - added to set
|
|
|
|
cOidsToBeRemoved - Count of entries in aOidsToBeRemoved.
|
|
|
|
aOidsToBeRemoved - OID-MID pairs to be removed.
|
|
|
|
cServerOidsToFree - Count of entries in aServerOidsToFree
|
|
|
|
aServerOidsToFree - OIDs allocated by the client process
|
|
and no longer needed.
|
|
|
|
cServerOidsToUnPin - Count of entries in aServerOidsToUnPin
|
|
|
|
aServerOidsToUnPin - OIDs that the client process previously
|
|
told us were pinned\locked, and now no longer are.
|
|
|
|
cClientOxidsToFree - COunt of enties in aClientOxidsToFree
|
|
|
|
aClientOxidsToFree - OXIDs owned by a process (due to a direct
|
|
or indirect call to ClientResolveOxid) which are no longer
|
|
in use by the client.
|
|
|
|
Return Value:
|
|
|
|
OR_OK - All updates completed ok.
|
|
|
|
OR_PARTIAL_UPDATE - At least one entry in aStatusOfAdds is not OR_OK
|
|
|
|
--*/
|
|
{
|
|
CProcess *pProcess;
|
|
CClientOxid *pOxid;
|
|
CClientOid *pOid;
|
|
CClientSet *pSet;
|
|
CMid *pMid;
|
|
CToken *pToken;
|
|
BOOL fPartial = FALSE;
|
|
BOOL fNewSet = FALSE;
|
|
DUALSTRINGARRAY *pdsa = NULL;
|
|
ULONG i;
|
|
|
|
// Parameter validation. If zero is passed for the size-of-array param
|
|
// then we don't care about the array param itself (since we never
|
|
// look at it)
|
|
if (!(cOidsToBeAdded > 0 ? (aOidsToBeAdded != NULL) : TRUE) ||
|
|
!(cOidsToBeAdded > 0 ? (aStatusOfAdds != NULL) : TRUE) ||
|
|
!(cOidsToBeRemoved > 0 ? (aOidsToBeRemoved != NULL) : TRUE) ||
|
|
!(cServerOidsToFree > 0 ? (aServerOidsToFree != NULL): TRUE) ||
|
|
!(cServerOidsToUnPin > 0 ? (aServerOidsToUnPin != NULL): TRUE) ||
|
|
!(cClientOxidsToFree > 0 ? (aClientOxidsToFree != NULL) : TRUE))
|
|
{
|
|
return OR_BADPARAM;
|
|
}
|
|
|
|
pProcess = ReferenceProcess(phProcess);
|
|
ASSERT(pProcess);
|
|
|
|
CheckLocalSecurity(hClient, pProcess);
|
|
|
|
if (cOidsToBeAdded || cOidsToBeRemoved)
|
|
{
|
|
ORSTATUS status = CopyMyOrBindings(&pdsa, NULL);
|
|
if (status != RPC_S_OK)
|
|
{
|
|
return status;
|
|
}
|
|
gpClientLock->LockExclusive();
|
|
}
|
|
|
|
// /////////////////////////////////////////////////////////////////
|
|
// Process Adds.
|
|
|
|
for (i = 0; i < cOidsToBeAdded; i++)
|
|
{
|
|
// Lookup up the oxid owning this new oid.
|
|
|
|
CId2Key oxidkey(aOidsToBeAdded[i].oxid, aOidsToBeAdded[i].mid);
|
|
|
|
pOxid = (CClientOxid *)gpClientOxidTable->Lookup(oxidkey);
|
|
|
|
if (0 == pOxid)
|
|
{
|
|
OXID_INFO infoT;
|
|
ORSTATUS status;
|
|
MID mid;
|
|
|
|
gpClientLock->UnlockExclusive();
|
|
|
|
infoT.psa = 0;
|
|
|
|
USHORT usAuthnSvc;
|
|
status = _ClientResolveOXID(hClient,
|
|
phProcess,
|
|
&aOidsToBeAdded[i].oxid,
|
|
pdsa,
|
|
TRUE,
|
|
&infoT,
|
|
&mid,
|
|
&usAuthnSvc);
|
|
|
|
gpClientLock->LockExclusive();
|
|
|
|
if (status == OR_OK)
|
|
{
|
|
ASSERT(infoT.psa);
|
|
ASSERT(mid == gLocalMid);
|
|
MIDL_user_free(infoT.psa);
|
|
pOxid = (CClientOxid *)gpClientOxidTable->Lookup(oxidkey);
|
|
if (pOxid == 0)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_INFO_LEVEL,
|
|
"OR: Auto resolving oxid %p failed, wrong machine\n",
|
|
&oxidkey));
|
|
|
|
status = OR_BADOXID;
|
|
}
|
|
}
|
|
|
|
if (status != OR_OK)
|
|
{
|
|
aStatusOfAdds[i] = OR_BADOXID;
|
|
fPartial = TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
// Find or create the set.
|
|
|
|
CId2Key setkey(aOidsToBeAdded[i].mid, (ID)pProcess->GetToken());
|
|
|
|
pSet = (CClientSet *)gpClientSetTable->Lookup(setkey);
|
|
|
|
if (pSet == 0)
|
|
{
|
|
pSet = new CClientSet(pOxid->GetMid(), pProcess->GetToken());
|
|
|
|
if (pSet == 0)
|
|
{
|
|
aStatusOfAdds[i] = OR_NOMEM;
|
|
fPartial = TRUE;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
gpClientSetTable->Add(pSet);
|
|
pSet->Insert();
|
|
fNewSet = TRUE;
|
|
}
|
|
}
|
|
|
|
// Find or create the oid. If we create it, add a reference
|
|
// to the oxid for the new oid.
|
|
|
|
CId3Key oidkey(aOidsToBeAdded[i].oid, aOidsToBeAdded[i].mid, pProcess->GetToken());
|
|
|
|
pOid = (CClientOid *)gpClientOidTable->Lookup(oidkey);
|
|
|
|
if (0 == pOid)
|
|
{
|
|
pOid = new CClientOid(aOidsToBeAdded[i].oid,
|
|
aOidsToBeAdded[i].mid,
|
|
pProcess->GetToken(),
|
|
pOxid,
|
|
pSet
|
|
);
|
|
if (fNewSet)
|
|
{
|
|
// pOid either owns a refernce now or we need to
|
|
// cleanup the set anyway.
|
|
pSet->Release();
|
|
fNewSet = FALSE;
|
|
}
|
|
|
|
if (pOid)
|
|
{
|
|
|
|
aStatusOfAdds[i] = pSet->RegisterObject(pOid);
|
|
|
|
if (aStatusOfAdds[i] == OR_OK)
|
|
{
|
|
gpClientOidTable->Add(pOid);
|
|
}
|
|
else
|
|
{
|
|
pOid->Release();
|
|
pOid = 0;
|
|
fPartial = TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
aStatusOfAdds[i] = OR_NOMEM;
|
|
fPartial = TRUE;
|
|
continue;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
ASSERT(fNewSet == FALSE);
|
|
pOid->ClientReference();
|
|
}
|
|
|
|
// If this fails it will release the oid.
|
|
aStatusOfAdds[i] = pProcess->AddOid(pOid);
|
|
if (aStatusOfAdds[i] != OR_OK)
|
|
{
|
|
fPartial = TRUE;
|
|
}
|
|
} // for oids to add
|
|
|
|
// /////////////////////////////////////////////////////////////////
|
|
// Process deletes
|
|
|
|
for (i = 0; i < cOidsToBeRemoved; i++)
|
|
{
|
|
CId3Key oidkey(aOidsToBeRemoved[i].oid,
|
|
aOidsToBeRemoved[i].mid,
|
|
pProcess->GetToken());
|
|
|
|
pOid = (CClientOid *)gpClientOidTable->Lookup(oidkey);
|
|
|
|
if (pOid)
|
|
{
|
|
CClientOid *pT = pProcess->RemoveOid(pOid);
|
|
|
|
if (pT == 0)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Client process %p tried to remove oid %p which"
|
|
"it didn't own\n",
|
|
pProcess,
|
|
&aOidsToBeRemoved[i]));
|
|
}
|
|
else
|
|
ASSERT(pT == pOid);
|
|
}
|
|
else
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_INFO_LEVEL,
|
|
"OR: Client %p removed an OID that doesn't exist\n",
|
|
pProcess));
|
|
|
|
} // for oids to delete
|
|
|
|
|
|
if (cOidsToBeAdded || cOidsToBeRemoved)
|
|
{
|
|
gpClientLock->UnlockExclusive();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
// Process server oid deletes
|
|
//
|
|
if (cServerOidsToFree > 0)
|
|
{
|
|
gpServerLock->LockExclusive();
|
|
FreeServerOids(pProcess, cServerOidsToFree, aServerOidsToFree);
|
|
gpServerLock->UnlockExclusive();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
//
|
|
// Process server oid unpins. We do not have an array of individual
|
|
// status update values for this operation; if the client gets back
|
|
// a success code from BulkUpdateOids, it can assume that all of the
|
|
// requested unpins were executed correctly.
|
|
//
|
|
// Also note that it is *always* safe to unpin an oid even if it should
|
|
// remain pinned; the worse that can happen is extra rundown calls.
|
|
//
|
|
if (cServerOidsToUnPin > 0)
|
|
{
|
|
gpServerLock->LockExclusive();
|
|
|
|
for (i = 0; i < cServerOidsToUnPin; i++)
|
|
{
|
|
CServerOid* pOid;
|
|
|
|
CIdKey key(aServerOidsToUnPin[i]);
|
|
|
|
pOid = (CServerOid *)gpServerOidTable->Lookup(key);
|
|
|
|
// Only unpin the oid if the calling process owns it. Don't
|
|
// assert if we didn't find it, since under stress we may
|
|
// be executing this code after the owning process\apt died.
|
|
if (pOid && pProcess->IsOwner(pOid->GetOxid()))
|
|
{
|
|
if (pOid->IsPinned())
|
|
{
|
|
pOid->SetPinned(FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
gpServerLock->UnlockExclusive();
|
|
}
|
|
|
|
|
|
// Done
|
|
if (pdsa)
|
|
{
|
|
MIDL_user_free(pdsa);
|
|
}
|
|
|
|
if (fPartial)
|
|
{
|
|
return(OR_PARTIAL_UPDATE);
|
|
}
|
|
|
|
return(OR_OK);
|
|
}
|
|
|
|
error_status_t
|
|
_ServerAllocateOXIDAndOIDs(
|
|
IN handle_t hClient,
|
|
IN PHPROCESS phProcess,
|
|
OUT OXID *poxidServer,
|
|
IN LONG fApartment,
|
|
IN ULONG cOids,
|
|
OUT OID aOid[],
|
|
OUT PULONG pOidsAllocated,
|
|
IN OXID_INFO *poxidInfo, // No bindings
|
|
IN DUALSTRINGARRAY *pdsaStringBindings, // Expanded
|
|
IN DUALSTRINGARRAY *pdsaSecurityBindings, // Compressed
|
|
OUT DWORD64 *pdwBindingsID,
|
|
OUT DUALSTRINGARRAY **ppdsaOrBindings
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates an OXID and 0 or more OIDs from the OR.
|
|
|
|
Arguments:
|
|
|
|
phProcess - The context handle of the process containing the OXID.
|
|
|
|
poxidServer - The OXID to register. May only register once.
|
|
|
|
cOids - Count of apOids
|
|
|
|
apOid - The OIDs to register within the OXID.
|
|
|
|
pcOidsAllocated - The number of OIDs actually allocated. Usually the
|
|
same as cOids unless a resource failure occures. Maybe 0.
|
|
|
|
poxidInfo - The OXID_INFO structure for the OXID without bindings.
|
|
|
|
pdsaStringBindings - Expanded string binding of the server.
|
|
|
|
pdsaSecurityBindings - The compressed security bindings of the server.
|
|
|
|
pOidsAllocated - The number of OIDs actually allocated. >= 0 and <= cOids.
|
|
|
|
pdwBindingsID -- The id of the bindings returned in ppdsaOrBindings
|
|
|
|
ppdsaOrBindings -- The current resolver bindings. Normally this is not
|
|
ever allocated, unless dynamic address tracking is enabled.
|
|
|
|
Return Value:
|
|
|
|
OR_OK - success. Returned even if some OID allocations fail. See the
|
|
pOidsAllocated parameter.
|
|
|
|
OR_NOMEM - Allocation of OXID failed.
|
|
|
|
OR_ACCESS_DENIDED - Raised if non-local client
|
|
|
|
OR_BADPARAM - if string arrays are incorrect.
|
|
|
|
--*/
|
|
{
|
|
ORSTATUS status = OR_OK;
|
|
CServerOxid *pNewOxid;
|
|
CProcess *pProcess = ReferenceProcess(phProcess);
|
|
ASSERT(pProcess);
|
|
|
|
CheckLocalSecurity(hClient, pProcess);
|
|
|
|
// Parameter validation
|
|
if (!poxidServer ||
|
|
!(cOids > 0 ? (aOid != NULL) : TRUE) ||
|
|
!pOidsAllocated || !poxidInfo ||
|
|
!pdsaStringBindings || !pdsaSecurityBindings ||
|
|
!pdwBindingsID || !ppdsaOrBindings)
|
|
{
|
|
return OR_BADPARAM;
|
|
}
|
|
|
|
gpServerLock->LockExclusive();
|
|
|
|
// Save the string bindings back to the process
|
|
|
|
if (!dsaValid(pdsaStringBindings) )
|
|
{
|
|
status = OR_BADPARAM;
|
|
}
|
|
|
|
if (!dsaValid(pdsaSecurityBindings))
|
|
{
|
|
status = OR_BADPARAM;
|
|
}
|
|
|
|
if (status == OR_OK)
|
|
{
|
|
status = pProcess->ProcessBindings(pdsaStringBindings,
|
|
pdsaSecurityBindings);
|
|
}
|
|
|
|
*pdwBindingsID = 0;
|
|
*ppdsaOrBindings = NULL;
|
|
|
|
VALIDATE((status, OR_NOMEM, OR_BADPARAM, 0));
|
|
|
|
if (status != OR_OK)
|
|
{
|
|
gpServerLock->UnlockExclusive();
|
|
return(status);
|
|
}
|
|
|
|
pNewOxid = new CServerOxid(pProcess,
|
|
fApartment,
|
|
poxidInfo
|
|
);
|
|
|
|
if (0 == pNewOxid)
|
|
{
|
|
gpServerLock->UnlockExclusive();
|
|
return(OR_NOMEM);
|
|
}
|
|
|
|
// Add to process and lookup table.
|
|
|
|
status = pProcess->AddOxid(pNewOxid);
|
|
|
|
VALIDATE((status, OR_NOMEM, 0));
|
|
|
|
pNewOxid->Release(); // process has a reference now or failed
|
|
|
|
gpServerLock->UnlockExclusive();
|
|
|
|
if (status == OR_OK)
|
|
{
|
|
*poxidServer = pNewOxid->Id();
|
|
|
|
status = _ServerAllocateOIDs(0,
|
|
phProcess,
|
|
poxidServer,
|
|
0,
|
|
NULL,
|
|
cOids,
|
|
aOid,
|
|
pOidsAllocated);
|
|
}
|
|
|
|
if (status == OR_OK && gbDynamicIPChangesEnabled)
|
|
{
|
|
if (ppdsaOrBindings && pProcess->NeedsORBindings())
|
|
{
|
|
// If doing dynamic IP changes, give process the current
|
|
// OR bindings
|
|
status = CopyMyOrBindings(ppdsaOrBindings, pdwBindingsID);
|
|
if (status == OR_OK)
|
|
{
|
|
pProcess->BindingsUpdated();
|
|
}
|
|
}
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
error_status_t _ServerAllocateOIDs(
|
|
IN handle_t hClient,
|
|
IN PHPROCESS phProcess,
|
|
IN OXID *poxidServer,
|
|
IN ULONG cOidsReturn,
|
|
IN OID aOidsReturn[],
|
|
IN ULONG cOids,
|
|
OUT OID aOids[],
|
|
OUT PULONG pOidsAllocated
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Registers additional OIDs on behalf of an existing OXID.
|
|
|
|
Arguments:
|
|
|
|
phProcess - The context handle of the process containing the OXID and OIDs.
|
|
|
|
poxidServer - The OXID associated with the OIDs.
|
|
|
|
cOidsReturn - Count of aOidsReturn
|
|
|
|
aOidsReturn - Array of OIDs the process is no longer using.
|
|
|
|
cOids - Count of aOids
|
|
|
|
aOids - The OIDs to register within the OXID.
|
|
|
|
pOidsAllocate - Contains the number of OIDs actually allocated
|
|
when this function returns success.
|
|
|
|
Return Value:
|
|
|
|
OR_OK (0) - Success.
|
|
|
|
OR_PARTIAL_UPDATE - No all elements in aStatus are 0.
|
|
|
|
OR_NOMEM - OXID or one or more OIDs
|
|
|
|
--*/
|
|
{
|
|
ORSTATUS status = OR_OK;
|
|
CServerOxid *pOxid;
|
|
CServerOid *pOid;
|
|
BOOL fPartial = FALSE;
|
|
CProcess *pProcess = ReferenceProcess(phProcess);
|
|
ASSERT(pProcess);
|
|
|
|
CheckLocalSecurity(hClient, pProcess);
|
|
|
|
// Parameter validation
|
|
if (!poxidServer ||
|
|
!(cOidsReturn > 0 ? (aOidsReturn != NULL) : TRUE) ||
|
|
!((cOids > 0 ? (aOids != NULL) : TRUE) ||
|
|
!pOidsAllocated))
|
|
{
|
|
return OR_BADPARAM;
|
|
}
|
|
|
|
|
|
gpServerLock->LockExclusive();
|
|
|
|
CIdKey oxidkey(*poxidServer);
|
|
|
|
pOxid = (CServerOxid *)gpServerOxidTable->Lookup(oxidkey);
|
|
|
|
if (0 == pOxid)
|
|
{
|
|
gpServerLock->UnlockExclusive();
|
|
status = OR_BADOXID;
|
|
return(status);
|
|
}
|
|
|
|
if (cOidsReturn)
|
|
{
|
|
// free the Oids returned
|
|
FreeServerOids(pProcess, cOidsReturn, aOidsReturn);
|
|
}
|
|
|
|
|
|
*pOidsAllocated = 0;
|
|
|
|
for (ULONG i = 0; i < cOids; i++)
|
|
{
|
|
pOid = new CServerOid(pOxid);
|
|
|
|
if (0 != pOid)
|
|
{
|
|
(*pOidsAllocated)++;
|
|
aOids[i] = pOid->Id();
|
|
gpServerOidTable->Add(pOid);
|
|
|
|
// The server doesn't want to keep the OID alive.
|
|
// This will cause the OID to rundown in six minutes
|
|
// unless a set references it in the meantime...
|
|
|
|
pOid->Release();
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
gpServerLock->UnlockExclusive();
|
|
|
|
ASSERT(status == OR_OK);
|
|
|
|
return(status);
|
|
}
|
|
|
|
error_status_t
|
|
_ServerFreeOXIDAndOIDs(
|
|
IN handle_t hClient,
|
|
IN PHPROCESS phProcess,
|
|
IN OXID oxidServer,
|
|
IN ULONG cOids,
|
|
IN OID aOids[])
|
|
|
|
{
|
|
CServerOxid *pOxid;
|
|
CServerOid *pOid;
|
|
CProcess *pProcess = ReferenceProcess(phProcess);
|
|
ORSTATUS status;
|
|
UINT i;
|
|
|
|
ASSERT(pProcess);
|
|
|
|
CheckLocalSecurity(hClient, pProcess);
|
|
|
|
// Parameter validation
|
|
if (!(cOids > 0 ? (aOids != NULL) : TRUE))
|
|
return OR_BADPARAM;
|
|
|
|
gpServerLock->LockExclusive();
|
|
|
|
CIdKey oxidkey(oxidServer);
|
|
|
|
pOxid = (CServerOxid *)gpServerOxidTable->Lookup(oxidkey);
|
|
|
|
if (0 != pOxid)
|
|
{
|
|
if (pProcess->RemoveOxid(pOxid) == TRUE)
|
|
{
|
|
// Found the OXID and this caller owns it.
|
|
status = OR_OK;
|
|
}
|
|
else
|
|
{
|
|
// Found but not owned by this caller.
|
|
status = OR_NOACCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Oxid not found.
|
|
status = OR_BADOXID;
|
|
}
|
|
|
|
// Note pOxid maybe invalid once the last OID is removed.
|
|
|
|
if (status == OR_OK)
|
|
{
|
|
for (i = 0; i < cOids; i++)
|
|
{
|
|
CIdKey key(aOids[i]); // PERF REVIEW
|
|
|
|
pOid = (CServerOid *)gpServerOidTable->Lookup(key);
|
|
|
|
if ( (0 != pOid)
|
|
&& (pOid->IsRunningDown() == FALSE)
|
|
&& (pOid->GetOxid() == pOxid) )
|
|
{
|
|
if (pOid->References() == 0)
|
|
{
|
|
// Unreferenced by any sets; run it down now..
|
|
pOid->Remove();
|
|
pOid->SetRundown(TRUE);
|
|
delete pOid;
|
|
}
|
|
// else - marking it as Free() not need as Oxid is
|
|
// now marked as not running.
|
|
}
|
|
else
|
|
{
|
|
ASSERT(pOid == 0 || pOxid == pOid->GetOxid());
|
|
}
|
|
}
|
|
}
|
|
|
|
gpServerLock->UnlockExclusive();
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
//
|
|
// Manager (server-side) calls to the remote OR interface. objex.idl
|
|
//
|
|
error_status_t
|
|
_ResolveOxid(
|
|
IN handle_t hRpc,
|
|
IN OXID *poxid,
|
|
IN USHORT cRequestedProtseqs,
|
|
IN USHORT aRequestedProtseqs[],
|
|
OUT DUALSTRINGARRAY **ppdsaOxidBindings,
|
|
OUT IPID *pipidRemUnknown,
|
|
OUT DWORD *pAuthnHint
|
|
)
|
|
{
|
|
COMVERSION ComVersion;
|
|
|
|
// just forward to the new manager routine (parameter
|
|
// validation done by callee)
|
|
return _ResolveOxid2(hRpc,
|
|
poxid,
|
|
cRequestedProtseqs,
|
|
aRequestedProtseqs,
|
|
ppdsaOxidBindings,
|
|
pipidRemUnknown,
|
|
pAuthnHint,
|
|
&ComVersion);
|
|
}
|
|
|
|
//
|
|
// Manager (server-side) calls to the remote OR interface. objex.idl
|
|
//
|
|
|
|
error_status_t
|
|
_ResolveOxid2(
|
|
IN handle_t hRpc,
|
|
IN OXID *poxid,
|
|
IN USHORT cRequestedProtseqs,
|
|
IN USHORT aRequestedProtseqs[],
|
|
OUT DUALSTRINGARRAY **ppdsaOxidBindings,
|
|
OUT IPID *pipidRemUnknown,
|
|
OUT DWORD *pAuthnHint,
|
|
OUT COMVERSION *pComVersion
|
|
)
|
|
{
|
|
|
|
ORSTATUS status;
|
|
BOOL fDidLazy;
|
|
CServerOxid *pServerOxid;
|
|
OXID_INFO oxidInfo;
|
|
|
|
// Parameter validation. Note that it would be exceedingly odd for
|
|
// a client to request zero protseqs, but we handle this anyway.
|
|
if (!poxid ||
|
|
!(cRequestedProtseqs > 0 ? (aRequestedProtseqs != NULL) : TRUE) ||
|
|
!ppdsaOxidBindings || !pipidRemUnknown ||
|
|
!pipidRemUnknown || !pAuthnHint || !pComVersion)
|
|
{
|
|
return OR_BADPARAM;
|
|
}
|
|
|
|
oxidInfo.psa = 0;
|
|
|
|
// No security check required (possible?). OXID info is not private.
|
|
|
|
#if DBG
|
|
UINT fLocal;
|
|
status = I_RpcBindingIsClientLocal(hRpc, &fLocal);
|
|
|
|
if (status != OR_OK)
|
|
{
|
|
fLocal = FALSE;
|
|
}
|
|
ASSERT(fLocal == FALSE); // Shouldn't be called locally...
|
|
#endif
|
|
|
|
fDidLazy = FALSE;
|
|
|
|
// intersect allowed protseqs with those
|
|
// requested by the client.
|
|
//
|
|
// NOTE: we are modifying memory passed in. This will not cause side
|
|
// effects because this call is always in the context of an RPC, and
|
|
// aRequestedProtseqs is an IN parameter and therefore will not change
|
|
// in the calling process.
|
|
|
|
gpClientLock->LockExclusive();
|
|
USHORT cAllowedProtseqs = 0;
|
|
for (USHORT iReqProtseq=0; iReqProtseq < cRequestedProtseqs; iReqProtseq++)
|
|
{
|
|
for (USHORT iAllowProtseq=0; iAllowProtseq < cMyProtseqs; iAllowProtseq++)
|
|
{
|
|
if (aRequestedProtseqs[iReqProtseq] == aMyProtseqs[iAllowProtseq])
|
|
{
|
|
// this protocol is in the allowed list, shift it up
|
|
// if necessary.
|
|
aRequestedProtseqs[cAllowedProtseqs] = aRequestedProtseqs[iReqProtseq];
|
|
cAllowedProtseqs++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
cRequestedProtseqs = cAllowedProtseqs;
|
|
gpClientLock->UnlockExclusive();
|
|
|
|
|
|
gpServerLock->LockShared();
|
|
|
|
for (;;)
|
|
{
|
|
CIdKey key(*poxid);
|
|
|
|
pServerOxid = (CServerOxid *)gpServerOxidTable->Lookup(key);
|
|
if (!pServerOxid)
|
|
{
|
|
status = OR_BADOXID;
|
|
break;
|
|
}
|
|
|
|
status = pServerOxid->GetRemoteInfo(&oxidInfo,
|
|
cRequestedProtseqs,
|
|
aRequestedProtseqs);
|
|
|
|
// Work around: original intersection of clients requested protocols
|
|
// with this SCM's did'nt match. But we know that client
|
|
// does'nt send it's entire set, just one so break here
|
|
//
|
|
// Note: W2K sends all protocols on both activations and ResolveOxid
|
|
// calls; NT4 only sends one protocol on activations, and all on
|
|
// ResolveOxid calls.
|
|
//
|
|
if ((cRequestedProtseqs == 0) && (status == OR_I_NOPROTSEQ))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( status == OR_I_NOPROTSEQ
|
|
&& FALSE == fDidLazy )
|
|
{
|
|
// Ask the server to start listening, but only try this once.
|
|
|
|
fDidLazy = TRUE;
|
|
|
|
status =
|
|
pServerOxid->LazyUseProtseq(cRequestedProtseqs,
|
|
aRequestedProtseqs
|
|
);
|
|
|
|
ASSERT(gpServerLock->HeldExclusive()); // Changed during UseProtseq!
|
|
|
|
if (status == OR_OK)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else if (status == OR_I_NOPROTSEQ)
|
|
{
|
|
// We didn't manage to use a matching protseq.
|
|
// Since we can call on any protocal this is possible
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Failed to use a matching protseq: %p %p\n",
|
|
pServerOxid,
|
|
aRequestedProtseqs));
|
|
|
|
status = OR_NOSERVER;
|
|
}
|
|
break;
|
|
}
|
|
|
|
gpServerLock->Unlock();
|
|
|
|
if (status == OR_OK)
|
|
{
|
|
*pipidRemUnknown = oxidInfo.ipidRemUnknown;
|
|
*ppdsaOxidBindings = oxidInfo.psa;
|
|
*pAuthnHint = oxidInfo.dwAuthnHint;
|
|
*pComVersion = oxidInfo.version;
|
|
}
|
|
else
|
|
{
|
|
// Work around: original intersection of clients requested protocols
|
|
// with this SCM's did'nt match. But we know that client
|
|
// does'nt send it's entire set, just one, so send back
|
|
// an empty set but o.k activation result for bindings
|
|
//
|
|
// Note: W2K sends all protocols on both activations and ResolveOxid
|
|
// calls; NT4 only sends one protocol on activations, and all on
|
|
// ResolveOxid calls.
|
|
//
|
|
if ((cRequestedProtseqs == 0) && (status == OR_I_NOPROTSEQ))
|
|
{
|
|
*pipidRemUnknown = oxidInfo.ipidRemUnknown;
|
|
*ppdsaOxidBindings = NULL;
|
|
*pAuthnHint = oxidInfo.dwAuthnHint;
|
|
*pComVersion = oxidInfo.version;
|
|
status = OR_OK;
|
|
}
|
|
}
|
|
return(status);
|
|
}
|
|
|
|
error_status_t
|
|
_SimplePing(
|
|
IN handle_t hRpc,
|
|
IN SETID *pSetId
|
|
)
|
|
{
|
|
ORSTATUS status;
|
|
CServerSet *pServerSet;
|
|
BOOL fShared = TRUE;
|
|
|
|
// Parameter validation
|
|
if (!pSetId)
|
|
return OR_BADPARAM;
|
|
|
|
if (*pSetId == 0)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"Client %p simple pinged with a setid of 0\n",
|
|
hRpc,
|
|
pSetId));
|
|
|
|
return(OR_BADSET);
|
|
}
|
|
|
|
gpServerLock->LockShared();
|
|
|
|
pServerSet = (CServerSet *)gpServerSetTable->Lookup(*pSetId);
|
|
|
|
if (pServerSet)
|
|
{
|
|
fShared = pServerSet->Ping(TRUE);
|
|
// The lock maybe exclusive now.
|
|
status = OR_OK;
|
|
}
|
|
else
|
|
{
|
|
status = OR_BADSET;
|
|
}
|
|
|
|
// See if another set in the table needs to rundown.
|
|
// PERF REVIEW - how often should I do this? 0 mod 4?
|
|
|
|
// Similar code in worker threads.
|
|
|
|
ID setid = gpServerSetTable->CheckForRundowns();
|
|
|
|
if (setid)
|
|
{
|
|
if (fShared)
|
|
{
|
|
gpServerLock->ConvertToExclusive();
|
|
fShared = FALSE;
|
|
}
|
|
|
|
gpServerSetTable->RundownSetIfNeeded(setid);
|
|
}
|
|
|
|
gpServerLock->Unlock();
|
|
|
|
return(status);
|
|
}
|
|
|
|
error_status_t
|
|
ComplexPingInternal(
|
|
IN handle_t hRpc,
|
|
IN SETID *pSetId,
|
|
IN USHORT SequenceNum,
|
|
IN ULONG cAddToSet,
|
|
IN ULONG cDelFromSet,
|
|
IN OID AddToSet[],
|
|
IN OID DelFromSet[],
|
|
OUT USHORT *pPingBackoffFactor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes a complex (delta to set) ping for a given set. This call
|
|
will create the set if necessary. The call will only be processed
|
|
if the caller is in fact the creator of the set.
|
|
|
|
algorithm:
|
|
|
|
if set is not allocated
|
|
lookup security info if possible
|
|
allocate set
|
|
else
|
|
lookup set
|
|
|
|
|
|
if found or created a set
|
|
do a standard ping, updating time stamp and sequence number.
|
|
else return failure.
|
|
|
|
if oids to add, add each one.
|
|
ignore unknown OIDs
|
|
if resource allocation fails, abort.
|
|
|
|
if oids to delete, process each one.
|
|
ignore unknown OIDs
|
|
|
|
if resource failure in adds, return OR_BADOID
|
|
else return success.
|
|
|
|
Arguments:
|
|
|
|
hRpc - Handle (SCONN/SCALL) of client. Used to check security. If it is
|
|
NULL the call is local and is assumed to be secure.
|
|
|
|
REVIEW:
|
|
Since the OR _only_ uses NT system security providers it is assumed
|
|
that impersonation will work. Other security providers will not.
|
|
|
|
We need a generic way to ask for a token and compare tokens in a
|
|
security provider independent way.
|
|
|
|
pSetId - The setid to ping. If it is NULL a new set will be created,
|
|
otherwise, it is assumed to be a set previously allocated by a
|
|
call with a NULL setid to this server.
|
|
|
|
SequenceNum - A sequence number shared between the client and server
|
|
to make sure old and out-of-order pings are not processed in a
|
|
non-healthy way. Note that pings are usually datagram RPC calls
|
|
which are marked as idempotent.
|
|
|
|
cAddToSet
|
|
cDelFromSet - The count of element in AddTo/DelFromSet parameter.
|
|
|
|
AddToSet
|
|
DelFromSet - OID mostly likly belonging to servers on this machine
|
|
to Add/Remove from the set of OIDs in use by this client.
|
|
|
|
pPingBackoffFactor - Maybe set by servers which want to reduce the
|
|
ping load on the server. Serves only as a HINT for the client.
|
|
Clients do not to ping more offten then:
|
|
(1<<*pPingBackoffFactor)*BasePingInterval seconds.
|
|
Clients may choose to assume this parameter is always 0.
|
|
|
|
Return Value:
|
|
|
|
OR_OK - completed normally
|
|
|
|
OR_BADSET - non-zero and unknown setid.
|
|
|
|
OR_NOMEM - unable to allocate a resource. Note that
|
|
on the first ping a set maybe allocated (setid is non-zero
|
|
after call) but some OIDs failed to be allocated.
|
|
|
|
OR_BADOID - everything went okay, but some OIDs added where
|
|
not recognized.
|
|
|
|
--*/
|
|
|
|
{
|
|
CServerSet *pServerSet;
|
|
BOOL fProcessPing;
|
|
BOOL fBad = FALSE;
|
|
PSID psid = 0;
|
|
ORSTATUS status = OR_OK;
|
|
|
|
// Parameter validation.
|
|
if (!pSetId ||
|
|
!(cAddToSet > 0 ? (AddToSet != NULL) : TRUE) ||
|
|
!(cDelFromSet > 0 ? (DelFromSet != NULL) : TRUE) ||
|
|
!pPingBackoffFactor)
|
|
{
|
|
return OR_BADPARAM;
|
|
}
|
|
|
|
gpServerLock->LockExclusive();
|
|
|
|
// Lookup the set
|
|
|
|
if (0 != *pSetId)
|
|
{
|
|
pServerSet = (CServerSet *)gpServerSetTable->Lookup(*pSetId);
|
|
if (0 == pServerSet)
|
|
{
|
|
status = OR_BADSET;
|
|
}
|
|
|
|
if (status == OR_OK)
|
|
{
|
|
if (pServerSet->CheckSecurity(hRpc) != TRUE)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Security check on set failed! (%d)\n",
|
|
GetLastError()));
|
|
|
|
status = OR_NOACCESS;
|
|
}
|
|
}
|
|
}
|
|
else if (hRpc == 0)
|
|
{
|
|
// Local client
|
|
psid = 0;
|
|
pServerSet = gpServerSetTable->Allocate(SequenceNum,
|
|
psid,
|
|
hRpc == 0,
|
|
*pSetId);
|
|
|
|
if (0 == pServerSet)
|
|
status = OR_NOMEM;
|
|
else
|
|
status = OR_OK;
|
|
|
|
}
|
|
else
|
|
{
|
|
HANDLE hT;
|
|
BOOL f;
|
|
// Unallocated set, lookup security info and allocate the set.
|
|
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_INFO_LEVEL,
|
|
"OR: New client started pinging: %p\n",
|
|
hRpc));
|
|
|
|
status = RpcImpersonateClient(hRpc);
|
|
|
|
if (status == RPC_S_OK)
|
|
{
|
|
f = OpenThreadToken(GetCurrentThread(),
|
|
TOKEN_IMPERSONATE | TOKEN_QUERY,
|
|
TRUE,
|
|
&hT);
|
|
|
|
if (!f)
|
|
{
|
|
status = GetLastError();
|
|
}
|
|
else
|
|
{
|
|
status = RPC_S_OK;
|
|
}
|
|
|
|
}
|
|
|
|
if (status != RPC_S_OK)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Unsecure client started pinging: %d %p\n",
|
|
status,
|
|
hRpc));
|
|
|
|
status = OR_OK;
|
|
}
|
|
else
|
|
{
|
|
ULONG needed = DEBUG_MIN(1, 24);
|
|
PTOKEN_USER ptu;
|
|
|
|
do
|
|
{
|
|
ptu = (PTOKEN_USER)alloca(needed);
|
|
ASSERT(ptu);
|
|
|
|
f = GetTokenInformation(hT,
|
|
TokenUser,
|
|
(PBYTE)ptu,
|
|
needed,
|
|
&needed);
|
|
|
|
} while ( f == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
|
|
|
|
if (f)
|
|
{
|
|
ASSERT(needed > sizeof(SID));
|
|
psid = new(needed - sizeof(SID)) SID;
|
|
if (psid)
|
|
{
|
|
f = CopySid(needed, psid, ptu->User.Sid);
|
|
ASSERT(f == TRUE);
|
|
}
|
|
else
|
|
{
|
|
status = OR_NOMEM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Error %d from GetTokenInformation\n",
|
|
GetLastError()));
|
|
|
|
ASSERT(0);
|
|
// Why did this happen. Either return failure to client or
|
|
// continue and make the set unsecure.
|
|
status = OR_NOMEM;
|
|
}
|
|
|
|
CloseHandle(hT);
|
|
}
|
|
|
|
// Allocate the set
|
|
|
|
if (status == OR_OK)
|
|
{
|
|
ASSERT(gpPingSetQuotaManager);
|
|
if (gpPingSetQuotaManager->IsUserQuotaExceeded(psid))
|
|
status = OR_NORESOURCE;
|
|
|
|
if (OR_OK == status)
|
|
{
|
|
if (!gpPingSetQuotaManager->ManageQuotaForUser(psid, TRUE))
|
|
{
|
|
status = OR_NOMEM;
|
|
}
|
|
else
|
|
{
|
|
pServerSet = gpServerSetTable->Allocate(SequenceNum,
|
|
psid,
|
|
hRpc == 0,
|
|
*pSetId);
|
|
|
|
if (0 == pServerSet)
|
|
{
|
|
gpPingSetQuotaManager->ManageQuotaForUser(psid, FALSE);
|
|
status = OR_NOMEM;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (status != OR_OK)
|
|
{
|
|
VALIDATE((status, OR_NOMEM, OR_BADSET, OR_NOACCESS, 0));
|
|
gpServerLock->UnlockExclusive();
|
|
return(status);
|
|
}
|
|
|
|
ASSERT(pServerSet);
|
|
|
|
fProcessPing = pServerSet->CheckAndUpdateSequenceNumber(SequenceNum);
|
|
|
|
if (fProcessPing)
|
|
{
|
|
// Do regular ping
|
|
|
|
pServerSet->Ping(FALSE);
|
|
|
|
*pPingBackoffFactor = 0;
|
|
|
|
// Process Add's
|
|
for (int i = cAddToSet; i ; i--)
|
|
{
|
|
status = pServerSet->AddObject(AddToSet[i - 1]);
|
|
|
|
if (status == OR_BADOID)
|
|
{
|
|
fBad = TRUE;
|
|
}
|
|
else if ( status != OR_OK )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Process Deletes - even some adds failed!
|
|
|
|
for (i = cDelFromSet; i; i--)
|
|
{
|
|
// Removing can't fail, no way to cleanup.
|
|
pServerSet->RemoveObject(DelFromSet[i - 1]);
|
|
}
|
|
}
|
|
|
|
gpServerLock->UnlockExclusive();
|
|
|
|
if (status == OR_OK && fBad)
|
|
{
|
|
return(OR_BADOID);
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
error_status_t
|
|
_ComplexPing(
|
|
IN handle_t hRpc,
|
|
IN SETID *pSetId,
|
|
IN USHORT SequenceNum,
|
|
IN USHORT cAddToSet,
|
|
IN USHORT cDelFromSet,
|
|
IN OID AddToSet[],
|
|
IN OID DelFromSet[],
|
|
OUT USHORT *pPingBackoffFactor
|
|
)
|
|
/*--
|
|
Routine Description:
|
|
|
|
This is the exposed RPC entry point for this function. We
|
|
simply call the internal function below. See description
|
|
for ComplexPingInternal. The reason for a separate function
|
|
is because the RPC method is typed to to take USHORT's, but
|
|
internally it is more convienent to pass ULONG's.
|
|
|
|
Arguments:
|
|
|
|
See argument list for ComplexPingInternal
|
|
|
|
--*/
|
|
{
|
|
return ComplexPingInternal(hRpc,
|
|
pSetId,
|
|
SequenceNum,
|
|
cAddToSet,
|
|
cDelFromSet,
|
|
AddToSet,
|
|
DelFromSet,
|
|
pPingBackoffFactor);
|
|
}
|
|
|
|
|
|
|
|
error_status_t
|
|
_ServerAlive(
|
|
RPC_ASYNC_STATE *pAsync,
|
|
RPC_BINDING_HANDLE hServer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Ping API for the client to validate a binding. Used when the client
|
|
is unsure of the correct binding for the server. (Ie. If the server
|
|
has multiple IP addresses).
|
|
|
|
Arguments:
|
|
|
|
hServer - RPC call binding
|
|
|
|
Return Value:
|
|
|
|
OR_OK
|
|
|
|
--*/
|
|
{
|
|
error_status_t RetVal = OR_OK;
|
|
RPC_STATUS rpcstatus;
|
|
RegisterAuthInfoIfNecessary();
|
|
rpcstatus = RpcAsyncCompleteCall(pAsync, &RetVal);
|
|
return (rpcstatus == RPC_S_OK) ? RetVal : rpcstatus;
|
|
}
|
|
|
|
|
|
|
|
error_status_t
|
|
_ServerAlive2(
|
|
RPC_ASYNC_STATE *pAsync,
|
|
RPC_BINDING_HANDLE hServer,
|
|
COMVERSION *pComVersion,
|
|
DUALSTRINGARRAY **ppdsaOrBindings,
|
|
DWORD *pReserved
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Ping API for the client to validate a binding. Used when the client
|
|
is unsure of the correct binding or authentication service for the server.
|
|
(Ie. If the server has multiple IP addresses).
|
|
|
|
Arguments:
|
|
|
|
hServer - RPC call binding
|
|
|
|
Return Value:
|
|
|
|
OR_OK
|
|
|
|
--*/
|
|
{
|
|
error_status_t RetVal = OR_OK;
|
|
RPC_STATUS rpcstatus = RPC_S_OK;
|
|
|
|
// Parameter validation
|
|
if (!pComVersion || !ppdsaOrBindings || !pReserved)
|
|
return OR_BADPARAM;
|
|
|
|
RegisterAuthInfoIfNecessary();
|
|
pComVersion->MajorVersion = COM_MAJOR_VERSION;
|
|
pComVersion->MinorVersion = COM_MINOR_VERSION;
|
|
*pReserved = 0;
|
|
|
|
RetVal = CopyMyOrBindings(ppdsaOrBindings, NULL);
|
|
if (RetVal == OR_OK)
|
|
{
|
|
rpcstatus = RpcAsyncCompleteCall(pAsync, &RetVal);
|
|
}
|
|
|
|
return (rpcstatus == RPC_S_OK) ? RetVal : rpcstatus;
|
|
}
|
|
|
|
|
|
|
|
void __RPC_USER PHPROCESS_rundown(LPVOID ProcessKey)
|
|
{
|
|
CProcess *pProcess = ReferenceProcess(ProcessKey);
|
|
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_INFO_LEVEL,
|
|
"OR: Client died\n"));
|
|
|
|
ASSERT(pProcess);
|
|
|
|
//
|
|
// This revokes OLE class registrations which were not revoked by this
|
|
// dead process. This must be done here rather then CProcess::Rundown
|
|
// because these things have references to the CProcess object.
|
|
//
|
|
pProcess->RevokeClassRegs();
|
|
|
|
pProcess->Cleanup();
|
|
|
|
ReleaseProcess(pProcess);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRpcSecurityCallbackManager::RegisterForRpcAuthSvcCallBack
|
|
//
|
|
// Synopsis: Register the security callback with RPC. A return value of
|
|
// TRUE means the calling thread may make a call on the supplied
|
|
// binding handle, then call the the RetrieveAuthSvc method
|
|
// for the negotiated authentication svc used on the rpc call.
|
|
//
|
|
// Parameters: hRpc -- the binding handle for which the calling thread wants
|
|
// the negotiated auth. svc.
|
|
//
|
|
// Algorithm: Register the security callback with RPC. Create a new list
|
|
// element to represent this particular callback, and add to to
|
|
// the list of callbacks.
|
|
//
|
|
// Notes: There is a limitation: a thread can only be registered for one
|
|
// callback at a time. This should be ok for expected usage.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL CRpcSecurityCallbackManager::RegisterForRpcAuthSvcCallBack(handle_t hRpc)
|
|
{
|
|
RPC_STATUS status;
|
|
CRpcSecurityCallback* pNewCallback;
|
|
|
|
ASSERT(hRpc != 0 && "Callbacks are meant to be used only for remote calls!");
|
|
|
|
// Create a new list element to represent this callback
|
|
pNewCallback = new CRpcSecurityCallback(hRpc, GetCurrentThreadId());
|
|
if (!pNewCallback)
|
|
return FALSE; // out-of-memory, not much we can do
|
|
|
|
// Try to register the callback
|
|
status = RpcBindingSetOption(hRpc,
|
|
RPC_C_OPT_SECURITY_CALLBACK,
|
|
(ULONG_PTR)CRpcSecurityCallbackManager::RpcSecurityCallbackFunction);
|
|
ASSERT(status == RPC_S_OK); // this should never fail AFAIK
|
|
if (status != RPC_S_OK)
|
|
{
|
|
// again, not much we can do
|
|
delete pNewCallback;
|
|
return FALSE;
|
|
}
|
|
|
|
// Okay now we got all we need; add it to the list
|
|
_plistlock->LockExclusive();
|
|
_CallbackList.Insert(pNewCallback);
|
|
_plistlock->UnlockExclusive();
|
|
|
|
return TRUE;
|
|
};
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRpcSecurityCallbackManager::GetAuthSvcAndTurnOffCallback
|
|
//
|
|
// Synopsis: Tries to retrieve the negotiated authentication service for the
|
|
// call just completed on the supplied binding handle. Also
|
|
// turns off callbacks on the binding handle.
|
|
//
|
|
// Parameters: pusAuthSvc -- if the rpc call was made, this must be a valid ptr;
|
|
// if that is the case, then upon a return value of TRUE, it
|
|
// will contain the auth. svc used for the call; a value of NULL
|
|
// passed here means the caller doesn't care about the result, he
|
|
// just wants things cleaned up (typically this means either the
|
|
// call failed or was never made).
|
|
//
|
|
// Returns: TRUE -- if everything worked
|
|
// FALSE -- something is wrong or you passed a NULL pusAuthSvc
|
|
//
|
|
// Algorithm: Use the caller's thread id to search for the callback result
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL CRpcSecurityCallbackManager::GetAuthSvcAndTurnOffCallback(handle_t hRpc, USHORT* pusAuthSvc)
|
|
{
|
|
RPC_STATUS status;
|
|
CRpcSecurityCallback* pCallback;
|
|
DWORD dwCurrentThread = GetCurrentThreadId();
|
|
|
|
_plistlock->LockExclusive();
|
|
|
|
// No matter what happens after this, we should turn off callbacks for the handle
|
|
TurnOffCallback(hRpc);
|
|
|
|
// Look for the callback result for the calling thread
|
|
for (pCallback = (CRpcSecurityCallback*)_CallbackList.First();
|
|
pCallback;
|
|
pCallback = (CRpcSecurityCallback*)pCallback->Next() )
|
|
{
|
|
if (dwCurrentThread == pCallback->GetRegisteredThreadId())
|
|
{
|
|
// found it
|
|
break;
|
|
}
|
|
};
|
|
|
|
// Given the normal usage of this stuff, we should always find a result
|
|
ASSERT(pCallback && "Didn't find rpc sec. callback result; this is unexpected");
|
|
if (!pCallback)
|
|
{
|
|
_plistlock->UnlockExclusive();
|
|
return FALSE;
|
|
};
|
|
|
|
if (pusAuthSvc)
|
|
{
|
|
ASSERT(pCallback->WasAuthSvcSet() && "Caller is retrieving auth svc before it was set");
|
|
*pusAuthSvc = pCallback->GetAuthSvcResult();
|
|
}
|
|
|
|
// The callback's work is done, so remove it from the list and delete it
|
|
_CallbackList.Remove(pCallback);
|
|
delete pCallback;
|
|
|
|
// Check list to see if other threads also had registered callbacks on the
|
|
// same handle. We will record the result for them in case we turned off the
|
|
// callback above before they got recorded. In fact, any time two or more threads
|
|
// register for a callback on the same handle we will hit this scenario.
|
|
//
|
|
// I don't think this is 100% foolproof, but it constitutes a best-faith effort
|
|
//
|
|
for (pCallback = (CRpcSecurityCallback*)_CallbackList.First();
|
|
pCallback;
|
|
pCallback = (CRpcSecurityCallback*)pCallback->Next() )
|
|
{
|
|
if (hRpc == pCallback->RegisteredHandle())
|
|
{
|
|
// found a match
|
|
ASSERT(dwCurrentThread != pCallback->GetRegisteredThreadId());
|
|
pCallback->SetAuthSvc(*pusAuthSvc);
|
|
}
|
|
};
|
|
|
|
_plistlock->UnlockExclusive();
|
|
|
|
return pusAuthSvc ? (*pusAuthSvc != ERROR_AUTHNSVC_VALUE) : FALSE;
|
|
};
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRpcSecurityCallbackManager::TurnOffCallback
|
|
//
|
|
// Synopsis: Function to turn off the callback on an rpc binding handle
|
|
//
|
|
// Parameters: hRpc -- the binding handle to turn off callbacks on
|
|
//
|
|
BOOL CRpcSecurityCallbackManager::TurnOffCallback(handle_t hRpc)
|
|
{
|
|
RPC_STATUS status;
|
|
status = RpcBindingSetOption(hRpc,
|
|
RPC_C_OPT_SECURITY_CALLBACK,
|
|
(ULONG_PTR) NULL );
|
|
|
|
ASSERT(status == RPC_S_OK && "RpcBindingSetOption failed when turning off security callback");
|
|
|
|
return TRUE;
|
|
};
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRpcSecurityCallbackManager::StoreCallbackResult
|
|
//
|
|
// Synopsis: This is a helper function for the callback function; no one
|
|
// else should use it obviously.
|
|
//
|
|
// Parameters: usAuthSvc -- the negotiated authentication service for the rpc
|
|
// call that the calling thread presumably just completed.
|
|
//
|
|
void CRpcSecurityCallbackManager::StoreCallbackResult(USHORT usAuthSvc)
|
|
{
|
|
CRpcSecurityCallback* pCallback;
|
|
DWORD dwCurrentThread = GetCurrentThreadId();
|
|
|
|
// Only need a shared lock for this
|
|
_plistlock->LockShared();
|
|
|
|
for (pCallback = (CRpcSecurityCallback*)_CallbackList.First();
|
|
pCallback;
|
|
pCallback = (CRpcSecurityCallback*)pCallback->Next() )
|
|
{
|
|
if (dwCurrentThread == pCallback->GetRegisteredThreadId())
|
|
{
|
|
// found it
|
|
break;
|
|
}
|
|
};
|
|
|
|
ASSERT(pCallback); // we should always find it
|
|
|
|
pCallback->SetAuthSvc(usAuthSvc);
|
|
|
|
_plistlock->UnlockShared();
|
|
|
|
return;
|
|
};
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CRpcSecurityCallbackManager::RpcSecurityCallbackFunction
|
|
//
|
|
// Synopsis: This is a callback function; we use this by setting it on a
|
|
// binding handle (using the RPC_C_OPT_SECURITY_CALLBACK option)
|
|
// so that RPC will call us back; this gives us a chance to
|
|
// determine what authentication svc was negotiated when using
|
|
// snego.
|
|
//
|
|
// Parameters: pvContext -- an opaque parameter
|
|
//
|
|
// Algorithm: call I_RpcBindingInqWireIdForSnego (undocumented rpc call)
|
|
// passing it the opaque pvContext parameter.
|
|
//
|
|
// Notes: it would be nice if we could get some info in this callback
|
|
// as to *which* handle the callback is for, but unfortunately
|
|
// kamenm was explicit on this point: pvContext is to remain
|
|
// opaque. :)
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void RPC_ENTRY CRpcSecurityCallbackManager::RpcSecurityCallbackFunction(void* pvContext)
|
|
{
|
|
RPC_STATUS status;
|
|
UCHAR ucWireId;
|
|
|
|
// Call back to get the authsvc:
|
|
status = I_RpcBindingInqWireIdForSnego((RPC_BINDING_HANDLE)pvContext, &ucWireId);
|
|
|
|
ASSERT(status != RPC_S_SEC_PKG_ERROR); // RPC should not have called us back
|
|
// in the first place, so assert on this
|
|
|
|
ASSERT(status != RPC_S_INVALID_BINDING); // RPC folks say this can be returned for the
|
|
// following reasons: 1) unauthenticated call;
|
|
// 2) invalid context; 3) snego was not used in
|
|
// the first place. None of these should apply
|
|
// to us, so assert
|
|
if (status == RPC_S_OK)
|
|
{
|
|
ASSERT( (ucWireId != RPC_C_AUTHN_GSS_NEGOTIATE) && "We're supposed to get back the real deal not snego");
|
|
|
|
gpCRpcSecurityCallbackMgr->StoreCallbackResult(ucWireId);
|
|
}
|
|
else
|
|
{
|
|
// something went wrong (most likely in the lower level security code).
|
|
gpCRpcSecurityCallbackMgr->StoreCallbackResult(ERROR_AUTHNSVC_VALUE);
|
|
}
|
|
|
|
return;
|
|
};
|
|
DWORD CPingSetQuotaManager::_dwPerUserPingSetQuota = 1000;
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CPingSetQuotaManager::ManageQuotaForUser
|
|
//
|
|
// Synopsis: This function manages the pingset quota for the user indicated by
|
|
// pSID. fAlloc = TRUE means alloc pingset quota, else deduct quota
|
|
//
|
|
BOOL CPingSetQuotaManager::ManageQuotaForUser(PSID pSid, BOOL fAlloc)
|
|
{
|
|
CUserPingSetCount *pNode = NULL;
|
|
RPC_STATUS status = OR_NOMEM;
|
|
|
|
// first see if have the user in the list
|
|
_plistlock->LockExclusive();
|
|
|
|
for (pNode = (CUserPingSetCount*)_UserPingSetCountList.First();
|
|
pNode;
|
|
pNode = (CUserPingSetCount*)pNode->Next() )
|
|
{
|
|
if (pNode->IsEqual(pSid))
|
|
{
|
|
// found it
|
|
break;
|
|
}
|
|
};
|
|
if (pNode)
|
|
{
|
|
// Have one, munge count
|
|
if (fAlloc)
|
|
{
|
|
pNode->Increment();
|
|
}
|
|
else
|
|
{
|
|
pNode->Decrement();
|
|
// last ping set for this user, delete node
|
|
if (pNode->GetCount() == 0)
|
|
{
|
|
_UserPingSetCountList.Remove(pNode);
|
|
delete pNode;
|
|
}
|
|
}
|
|
status = OR_OK;
|
|
}
|
|
else
|
|
{
|
|
// Don't have one, add.
|
|
ASSERT( fAlloc == TRUE );
|
|
pNode = new CUserPingSetCount(status, pSid);
|
|
if (pNode && (status == OR_OK) )
|
|
{
|
|
// start with 1
|
|
pNode->Increment();
|
|
_UserPingSetCountList.Insert(pNode);
|
|
}
|
|
}
|
|
_plistlock->UnlockExclusive();
|
|
return (OR_OK == status);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CPingSetQuotaManager::IsUserQuotaExceeded
|
|
//
|
|
// Synopsis: This function determines if the quota is above limit for a given user
|
|
// pSID. returns TRUE if the limit is reached, FALSE otherwise
|
|
//
|
|
BOOL CPingSetQuotaManager::IsUserQuotaExceeded(PSID pSid)
|
|
{
|
|
CUserPingSetCount *pNode = NULL;
|
|
// first see if have the user in the list
|
|
_plistlock->LockShared();
|
|
|
|
for (pNode = (CUserPingSetCount*)_UserPingSetCountList.First();
|
|
pNode;
|
|
pNode = (CUserPingSetCount*)pNode->Next() )
|
|
{
|
|
if (pNode->IsEqual(pSid))
|
|
{
|
|
// found it
|
|
break;
|
|
}
|
|
};
|
|
if (pNode)
|
|
{
|
|
DWORD dw = pNode->GetCount();
|
|
if (dw >= _dwPerUserPingSetQuota )
|
|
{
|
|
_plistlock->UnlockShared();
|
|
return TRUE;
|
|
}
|
|
}
|
|
_plistlock->UnlockShared();
|
|
return FALSE;
|
|
}
|
|
|
|
void CPingSetQuotaManager::SetPerUserPingSetQuota(DWORD dwQuota)
|
|
{
|
|
_dwPerUserPingSetQuota = dwQuota;
|
|
}
|