Leaked source code of windows server 2003
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.
 
 
 
 
 
 

7560 lines
216 KiB

/*++
Copyright (C) Microsoft Corporation, 1992 - 1999
Module Name:
lpcsvr.cxx
Abstract:
Implementation of the RPC on LPC protocol engine for the server.
Revision History:
Mazhar Mohammed: Code fork from spcsvr.cxx, 08/02/95
05-06-96: Merged WMSG and LRPC into a single protocol
Mazhar Mohammed Added Pipes Support
Mazhar Mohammed Added support for Async RPC 08-14-96
Mazhar Mohammed No more WMSG 9/22/97
Kamen Moutafov (kamenm) Jan-2000 Support for multiple transfer syntaxes
Kamen Moutafov (KamenM) Dec 99 - Feb 2000 - Support for cell debugging stuff
Kamen Moutafov (KamenM) Mar-2000 Support for extended error info
--*/
#include <precomp.hxx>
#include <queue.hxx>
#include <hndlsvr.hxx>
#include <lpcpack.hxx>
#include <lpcsvr.hxx>
#include <ProtBind.hxx>
#include <lpcclnt.hxx>
#include <CharConv.hxx>
inline BOOL
RecvLotsaCallsWrapper(
LRPC_ADDRESS * Address
)
{
Address->ReceiveLotsaCalls();
return(FALSE);
}
inline RPC_STATUS
InitializeLrpcIfNecessary(
)
/*++
Routine Description:
description
Arguments:
arg1 - description
--*/
{
int nIndex ;
RPC_STATUS Status ;
if (GlobalLrpcServer == 0)
{
if ((Status = InitializeLrpcServer()) != RPC_S_OK)
{
return Status ;
}
}
return (RPC_S_OK) ;
}
LRPC_SERVER::LRPC_SERVER(
IN OUT RPC_STATUS *Status
) : ServerMutex(Status,
TRUE // pre-allocate semaphore
)
{
Address = NULL ;
EndpointInitialized = 0 ;
}
RPC_STATUS
LRPC_SERVER::InitializeAsync (
)
{
RPC_CHAR Endpoint[20];
RPC_STATUS Status = RPC_S_OK ;
if (EndpointInitialized == 0)
{
swprintf(Endpoint, RPC_CONST_STRING("MSAsyncRPC_%d"),
GetCurrentProcessId()) ;
Status = RpcServerUseProtseqEpW (
RPC_STRING_LITERAL("ncalrpc"), 100, Endpoint, NULL) ;
if (Status != RPC_S_OK)
{
return Status ;
}
Status = GlobalRpcServer->ServerListen(1, 100, 1) ;
if (Status == RPC_S_OK)
{
Status = SetEndpoint(Endpoint) ;
}
}
return Status ;
}
RPC_STATUS
InitializeLrpcServer (
)
/*++
Routine Description:
description
Arguments:
arg1 - description
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RPC_STATUS Status = RPC_S_OK ;
GlobalMutexRequest() ;
if (GlobalLrpcServer == 0)
{
GlobalLrpcServer = new LRPC_SERVER(&Status) ;
if (GlobalLrpcServer == 0)
{
#if DBG
PrintToDebugger("LRPC: LRPC_SERVER initialization failed\n") ;
#endif
GlobalMutexClear() ;
return (RPC_S_OUT_OF_MEMORY) ;
}
if (Status != RPC_S_OK)
{
GlobalMutexClear() ;
delete GlobalLrpcServer ;
GlobalLrpcServer = 0 ;
return Status ;
}
}
GlobalMutexClear() ;
return (RPC_S_OK) ;
}
void
SetCommonFaultFields (
IN LRPC_MESSAGE *LrpcMessage,
IN RPC_STATUS Status,
IN int Flags,
IN int AdditionalLength
)
{
// zero out unused fields
LrpcMessage->Fault.RpcHeader.Pad = 0;
LrpcMessage->Fault.RpcHeader.PresentContext = 0;
LrpcMessage->Fault.RpcHeader.ProcedureNumber = 0;
LrpcMessage->Fault.RpcHeader.SecurityContextId = 0;
RpcpMemorySet(&LrpcMessage->Fault.RpcHeader.ObjectUuid, 0, sizeof(UUID));
LrpcMessage->Fault.Padding1 = 0;
LrpcMessage->Fault.RpcHeader.MessageType = LRPC_MSG_FAULT;
LrpcMessage->Fault.RpcStatus = Status;
LrpcMessage->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_FAULT_MESSAGE) - sizeof(PORT_MESSAGE)
- sizeof(LrpcMessage->Fault.Buffer) + (CSHORT)AdditionalLength;
LrpcMessage->LpcHeader.u1.s1.TotalLength =
sizeof(LRPC_FAULT_MESSAGE) - sizeof(LrpcMessage->Fault.Buffer)
+ (CSHORT)AdditionalLength;
if ((Flags & LRPC_SYNC_CLIENT) == 0)
{
SanitizeLpcHeader(&LrpcMessage->LpcHeader);
LrpcMessage->LpcHeader.u2.ZeroInit = 0;
LrpcMessage->LpcHeader.CallbackId = 0 ;
LrpcMessage->LpcHeader.MessageId = 0 ;
LrpcMessage->LpcHeader.u2.s2.DataInfoOffset = 0;
}
}
void
SetCommonFault2Fields (
IN LRPC_MESSAGE *LrpcMessage,
IN RPC_STATUS Status,
IN unsigned int Length,
IN void *Buffer
)
{
// zero out unused fields
LrpcMessage->Fault.RpcHeader.Pad = 0;
LrpcMessage->Fault.RpcHeader.PresentContext = 0;
LrpcMessage->Fault.RpcHeader.ProcedureNumber = 0;
LrpcMessage->Fault.RpcHeader.SecurityContextId = 0;
RpcpMemorySet(&LrpcMessage->Fault.RpcHeader.ObjectUuid, 0, sizeof(UUID));
LrpcMessage->Fault2.RpcHeader.MessageType = LRPC_MSG_FAULT2;
LrpcMessage->Fault2.RpcStatus = Status;
LrpcMessage->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_FAULT2_MESSAGE) - sizeof(PORT_MESSAGE);
LrpcMessage->LpcHeader.u1.s1.TotalLength =
sizeof(LRPC_FAULT2_MESSAGE);
// the Server/DataEntries must have been set in GetBuffer - no
// need to reset them here
LrpcMessage->Fault2.RpcHeader.Flags |= LRPC_EEINFO_PRESENT;
}
void
TrimIfNecessaryAndSetImmediateBuffer (
IN LRPC_MESSAGE *LrpcMessage,
IN RPC_STATUS Status,
IN int Flags,
IN size_t EstimatedEEInfoSize,
IN BOOL fTrimEEInfo,
IN ExtendedErrorInfo *CurrentEEInfo
)
{
size_t NeededLength;
RPC_STATUS RpcStatus;
if (fTrimEEInfo)
{
ASSERT(MAXIMUM_FAULT_MESSAGE >= MinimumTransportEEInfoLength);
TrimEEInfoToLength (MAXIMUM_FAULT_MESSAGE, &NeededLength);
if (NeededLength == 0)
{
SetCommonFaultFields(LrpcMessage, Status, Flags, 0);
return;
}
ASSERT(NeededLength <= MAXIMUM_FAULT_MESSAGE);
EstimatedEEInfoSize = NeededLength;
// fall through to the next if - it will succeed
// as we know the length is trimmed.
}
else
{
ASSERT(EstimatedEEInfoSize <= MAXIMUM_FAULT_MESSAGE);
}
RpcStatus = PickleEEInfo(CurrentEEInfo,
LrpcMessage->Fault.Buffer,
MAXIMUM_FAULT_MESSAGE);
if (RpcStatus != RPC_S_OK)
{
SetCommonFaultFields(LrpcMessage, Status, Flags, 0);
return;
}
SetCommonFaultFields(LrpcMessage, Status, Flags, EstimatedEEInfoSize);
LrpcMessage->Fault.RpcHeader.Flags |= LRPC_EEINFO_PRESENT;
}
void
SetFaultPacket (
IN LRPC_MESSAGE *LrpcMessage,
IN RPC_STATUS Status,
IN int Flags,
IN LRPC_SCALL *CurrentCall OPTIONAL
)
/*++
Routine Description:
Initialize a fault packet
Arguments:
LrpcMessage - Fault message
Status - Fault status
Flags - Flags from the request message
--*/
{
THREAD *Thread;
ExtendedErrorInfo *CurrentEEInfo;
size_t EstimatedEEInfoSize;
RPC_STATUS RpcStatus;
RPC_MESSAGE RpcMessage;
// we will see whether there is extended error information here
// and try to send it. If we run in out-of-memory, or there is
// no EEInfo, send plain old fault.
Thread = ThreadSelf();
if (Thread && g_fSendEEInfo)
{
CurrentEEInfo = Thread->GetEEInfo();
if (CurrentEEInfo)
{
// if this function runs in out-of-memory, it will
// return 0.
EstimatedEEInfoSize = EstimateSizeOfEEInfo();
if (EstimatedEEInfoSize == 0)
{
SetCommonFaultFields(LrpcMessage, Status, Flags, 0);
return;
}
// if there is no current call, we cannot send arbitrary length
// data, so we must trim the EEInfo
if (CurrentCall == NULL)
{
TrimIfNecessaryAndSetImmediateBuffer(LrpcMessage,
Status,
Flags,
EstimatedEEInfoSize,
TRUE,
CurrentEEInfo);
return;
}
if (EstimatedEEInfoSize <= MAXIMUM_FAULT_MESSAGE)
{
TrimIfNecessaryAndSetImmediateBuffer(LrpcMessage,
Status,
Flags,
EstimatedEEInfoSize,
FALSE,
CurrentEEInfo);
return;
}
ASSERT(CurrentCall != NULL);
// here, the estimated EEInfo size is larger that the available
// space in the fault packet. We have a call, so we must try
// sending a fault2 packet.
RpcMessage.Handle = CurrentCall;
RpcMessage.RpcFlags = CurrentCall->RpcMessage.RpcFlags;
// increase the buffer lenght in case we fall in the window
// b/n MAXIMUM_MESSAGE_BUFFER and the EstimatedEEInfoSize. If we
// do, GetBuffer will return us an immediate buffer, and this is
// not something we want
RpcMessage.BufferLength = max(EstimatedEEInfoSize, MAXIMUM_MESSAGE_BUFFER + 4);
RpcStatus = CurrentCall->LRPC_SCALL::GetBuffer(&RpcMessage, NULL);
if (RpcStatus != RPC_S_OK)
{
// can't send the full data - trim and send
TrimIfNecessaryAndSetImmediateBuffer(LrpcMessage,
Status,
Flags,
EstimatedEEInfoSize,
TRUE,
CurrentEEInfo);
return;
}
ASSERT(CurrentCall->LrpcReplyMessage->Rpc.RpcHeader.Flags != LRPC_BUFFER_IMMEDIATE);
// on success, GetBuffer has allocated a buffer in RpcMessage.Buffer
// and has setup CurrentCall->LrpcReplyMessage for sending to
// the client.
// Fill in the EEInfo
RpcStatus = PickleEEInfo(CurrentEEInfo,
(unsigned char *)RpcMessage.Buffer,
EstimatedEEInfoSize);
if (RpcStatus != RPC_S_OK)
{
if (!CurrentCall->IsClientAsync())
CurrentCall->Association->Buffers.DeleteItemByBruteForce(RpcMessage.Buffer);
RpcpFarFree(RpcMessage.Buffer);
// can't send the full data - trim and send
TrimIfNecessaryAndSetImmediateBuffer(LrpcMessage,
Status,
Flags,
EstimatedEEInfoSize,
TRUE,
CurrentEEInfo);
return;
}
// Send to the client
if (CurrentCall->IsClientAsync())
{
if (!CurrentCall->IsSyncCall())
{
ASSERT(CurrentCall->LrpcAsyncReplyMessage == CurrentCall->LrpcReplyMessage);
}
SetCommonFault2Fields(CurrentCall->LrpcReplyMessage,
Status,
RpcMessage.BufferLength,
RpcMessage.Buffer);
}
else
{
// send the data for sync client
// set fault2 fields
SetCommonFault2Fields(CurrentCall->LrpcReplyMessage,
Status,
RpcMessage.BufferLength,
RpcMessage.Buffer);
// our caller will do the sending
}
return;
}
}
SetCommonFaultFields(LrpcMessage, Status, Flags, 0);
}
// This is a compile-time verification of the alignment assert:
// IsBufferAligned(LrpcMessage->Bind.BindExchangeReturn.Buffer)
C_ASSERT((FIELD_OFFSET(LRPC_BIND_MESSAGE, BindExchangeReturn)+
FIELD_OFFSET(LRPC_BIND_EXCHANGE_RETURN, Buffer)) % RPCRT_NATURAL_BOUNDARY == 0);
void
SetBindAckFault (
IN LRPC_MESSAGE *LrpcMessage,
IN RPC_STATUS Status
)
/*++
Routine Description:
Initialize a fault bind ack packet (bind_nak). It will add extended
error info if there is some, and sending of eeinfo is enabled.
Arguments:
LrpcMessage - Bind message
Status - Fault status
--*/
{
size_t NeededLength;
ExtendedErrorInfo *EEInfo;
ASSERT(IsBufferAligned(LrpcMessage->Bind.BindExchangeReturn.Buffer));
ASSERT(MAX_BIND_NAK >= MinimumTransportEEInfoLength);
LrpcMessage->Bind.BindExchange.RpcStatus = Status;
if (g_fSendEEInfo)
{
EEInfo = RpcpGetEEInfo();
if (EEInfo)
{
TrimEEInfoToLength (MAX_BIND_NAK, &NeededLength);
if (NeededLength != 0)
{
Status = PickleEEInfo(EEInfo,
LrpcMessage->Bind.BindExchangeReturn.Buffer,
MAX_BIND_NAK);
if (Status == RPC_S_OK)
{
LrpcMessage->Bind.BindExchange.Flags |= EXTENDED_ERROR_INFO_PRESENT;
LrpcMessage->LpcHeader.u1.s1.DataLength = (CSHORT)NeededLength +
BIND_NAK_PICKLE_BUFFER_OFFSET
- sizeof(PORT_MESSAGE);
}
}
}
}
if (!(LrpcMessage->Bind.BindExchange.Flags & EXTENDED_ERROR_INFO_PRESENT))
{
LrpcMessage->LpcHeader.u1.s1.DataLength = sizeof(LRPC_BIND_MESSAGE)
- sizeof(PORT_MESSAGE);
}
}
LRPC_ADDRESS::LRPC_ADDRESS (
OUT RPC_STATUS * Status
) : RPC_ADDRESS(Status),
ThreadsDoingLongWait(0)
/*++
--*/
{
ObjectType = LRPC_ADDRESS_TYPE;
LpcAddressPort = 0;
CallThreadCount = 0;
ActiveCallCount = 0;
ServerListeningFlag = 0;
AssociationCount = 0;
fServerThreadsStarted = 0;
SequenceNumber = 1;
fTickleMessageAvailable = FALSE;
TickleMessage = NULL;
if (IsServerSideDebugInfoEnabled())
{
DebugCell = (DebugEndpointInfo *)AllocateCell(&DebugCellTag);
if (DebugCell == NULL)
{
*Status = RPC_S_OUT_OF_MEMORY;
}
else
{
DebugCell->TypeHeader = 0;
DebugCell->Type = dctEndpointInfo;
DebugCell->ProtseqType = (UCHAR)LRPC_TOWER_ID;
DebugCell->Status = desAllocated;
memset(DebugCell->EndpointName, 0, sizeof(DebugCell->EndpointName));
}
}
else
DebugCell = NULL;
}
RPC_STATUS
LRPC_ADDRESS::ServerStartingToListen (
IN unsigned int MinimumCallThreads,
IN unsigned int MaximumConcurrentCalls
)
/*++
Routine Description:
This routine gets called when RpcServerListen is called by the application.
We need to create the threads we need to receive remote procedure calls.
Arguments:
MinimumCallThreads - Supplies the minimum number of threads which we
must create.
MaximumConcurrentCalls - Unused.
Return Value:
RPC_S_OK - Ok, this address is all ready to start listening for remote
procedure calls.
RPC_S_OUT_OF_THREADS - We could not create enough threads so that we
have at least the minimum number of call threads required (as
specified by the MinimumCallThreads argument).
--*/
{
RPC_STATUS Status;
UNUSED(MaximumConcurrentCalls);
if (fServerThreadsStarted == 0)
{
Status = InitializeServerSideCellHeapIfNecessary();
if (Status != RPC_S_OK)
return Status;
this->MinimumCallThreads = MinimumCallThreads;
AddressMutex.Request();
if (CallThreadCount < this->MinimumCallThreads)
{
Status = Server->CreateThread((THREAD_PROC)&RecvLotsaCallsWrapper,
this);
if (Status != RPC_S_OK)
{
AddressMutex.Clear();
VALIDATE(Status)
{
RPC_S_OUT_OF_THREADS,
RPC_S_OUT_OF_MEMORY
} END_VALIDATE;
return(Status);
}
CallThreadCount += 1;
}
AddressMutex.Clear();
fServerThreadsStarted = 1;
}
ServerListeningFlag = 1;
return(RPC_S_OK);
}
void
LRPC_ADDRESS::ServerStoppedListening (
)
/*++
Routine Description:
We just need to indicate that the server is no longer listening, and
set the minimum call thread count to one.
--*/
{
ServerListeningFlag = 0;
MinimumCallThreads = 1;
}
#ifdef DEBUGRPC
// Hard coded world (aka EveryOne) SID
const SID World = { 1, 1, { 0, 0, 0, 0, 0, 1}, 0};
// Hard coded anonymous SID
const SID AnonymousLogonSid = { 1, 1, SECURITY_NT_AUTHORITY, SECURITY_ANONYMOUS_LOGON_RID};
SECURITY_DESCRIPTOR *DefaultPortSD = NULL;
RPC_STATUS
CreateAndGetDefaultPortSDIfNecessary (
OUT SECURITY_DESCRIPTOR **PortSD
)
/*++
Function Name: CreateAndGetDefaultPortSDIfNecessary
Parameters:
PortSD - receives the default port SD on success.
Undefined on failure
Description:
If the default port SD is not created, creates it,
and returns it. If it is already created, it simply
returns it. The function is thread-safe.
Returns:
RPC_S_OK or other codes for error.
--*/
{
DWORD DaclSize;
PACL Dacl;
ULONG LengthOfDacl;
SECURITY_DESCRIPTOR *LocalDefaultSD; // we work on a local copy to make
// this thread safe
if (DefaultPortSD)
{
*PortSD = DefaultPortSD;
return RPC_S_OK;
}
LocalDefaultSD = new SECURITY_DESCRIPTOR;
if ( LocalDefaultSD == 0
|| !InitializeSecurityDescriptor(LocalDefaultSD,
SECURITY_DESCRIPTOR_REVISION) )
{
return(RPC_S_OUT_OF_MEMORY);
}
ASSERT(GetSidLengthRequired(SID_MAX_SUB_AUTHORITIES) <= 0x44);
DaclSize = 2 * sizeof(ACCESS_ALLOWED_ACE) + sizeof(World) + sizeof(AnonymousLogonSid) + 0x44;
LengthOfDacl = DaclSize + sizeof(ACL);
Dacl = (ACL *) new char[LengthOfDacl];
if (NULL == Dacl)
{
return(RPC_S_OUT_OF_MEMORY);
}
ASSERT(IsValidSid((PVOID)&World));
ASSERT(IsValidSid((PVOID)&AnonymousLogonSid));
InitializeAcl(Dacl, LengthOfDacl, ACL_REVISION);
if (!AddAccessAllowedAce(Dacl, ACL_REVISION,
PORT_ALL_ACCESS,
(PVOID)&World))
{
// this should never fail unless we messed up the
// parameters or there is a version mismatch
ASSERT(0);
delete [] Dacl;
delete LocalDefaultSD;
return(RPC_S_OUT_OF_RESOURCES);
}
if (!AddAccessAllowedAce(Dacl, ACL_REVISION,
PORT_ALL_ACCESS,
(PVOID)&AnonymousLogonSid ))
{
// this should never fail unless we messed up the
// parameters or there is a version mismatch
ASSERT(0);
delete [] Dacl;
delete LocalDefaultSD;
return(RPC_S_OUT_OF_RESOURCES);
}
if (!SetSecurityDescriptorDacl(LocalDefaultSD, TRUE, Dacl, FALSE))
{
delete [] Dacl;
delete LocalDefaultSD;
return(RPC_S_OUT_OF_RESOURCES);
}
if (InterlockedCompareExchangePointer((PVOID *)&DefaultPortSD,
LocalDefaultSD,
NULL) != NULL)
{
// somebody beat us to the punch - free our local copy
delete [] Dacl;
delete LocalDefaultSD;
}
*PortSD = DefaultPortSD;
return RPC_S_OK;
}
#endif
RPC_STATUS
LRPC_ADDRESS::ActuallySetupAddress (
IN RPC_CHAR * Endpoint,
IN void * SecurityDescriptor OPTIONAL
)
/*++
Function Name:ActuallySetupAddress
Parameters:
Description:
Returns:
--*/
{
NTSTATUS NtStatus;
RPC_CHAR * LpcPortName;
unsigned int LpcPortNameLen;
UNICODE_STRING UnicodeString;
OBJECT_ATTRIBUTES ObjectAttributes;
RPC_STATUS Status;
#ifdef DEBUGRPC
BOOL Result;
BOOL DaclPresent;
PACL Dacl;
BOOL Ignored;
#endif
//
// Allocate and initialize the port name. We need to stick the
// LRPC_DIRECTORY_NAME on the front of the endpoint. This is for
// security reasons (so that anyone can create LRPC endpoints).
//
// Also note that the port name can be at most PORT_NAME_LEN bytes since
// it may have to end up in LRPC_BIND_EXCHANGE::szPortName.
//
LpcPortNameLen = RpcpStringLength(Endpoint) + RpcpStringLength(LRPC_DIRECTORY_NAME) + 1;
if (LpcPortNameLen > PORT_NAME_LEN/sizeof(RPC_CHAR))
{
return(RPC_S_STRING_TOO_LONG);
}
LpcPortName = new RPC_CHAR[LpcPortNameLen];
if (LpcPortName == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
RpcpMemoryCopy(
LpcPortName,
LRPC_DIRECTORY_NAME,
RpcpStringLength(LRPC_DIRECTORY_NAME) *sizeof(RPC_CHAR));
RpcpMemoryCopy(
LpcPortName + RpcpStringLength(LRPC_DIRECTORY_NAME),
Endpoint,
(RpcpStringLength(Endpoint) + 1) *sizeof(RPC_CHAR));
RtlInitUnicodeString(&UnicodeString, LpcPortName);
#ifdef DEBUGRPC
// in checked builds we check the security descriptor for NULL Dacl,
// and if present, we replace it with a default "allow everyone"
// Dacl. This was requested by ChrisW (12/14/2000) from the Security
// Team so that they can get LPC ports out of the picture, and then
// ASSERT on NULL Dacls for other objects
if (SecurityDescriptor)
{
Result = GetSecurityDescriptorDacl(SecurityDescriptor,
&DaclPresent,
&Dacl,
&Ignored // lpbDaclDefaulted
);
if (!Result)
{
// invalid security descriptor is the only reason this could fail
delete [] LpcPortName;
return RPC_S_INVALID_ENDPOINT_FORMAT;
}
if (DaclPresent && (Dacl == NULL))
{
Status = CreateAndGetDefaultPortSDIfNecessary((SECURITY_DESCRIPTOR **)&SecurityDescriptor);
if (Status != RPC_S_OK)
{
delete [] LpcPortName;
return Status;
}
// We were able to grab a default port SD - just let it through
}
// else
// {
// the security descriptor supplied by caller has non NULL Dacl - let
// it through
// }
}
#endif
InitializeObjectAttributes(
&ObjectAttributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
0,
SecurityDescriptor);
NtStatus = NtCreatePort(
&LpcAddressPort,
&ObjectAttributes,
sizeof(LRPC_BIND_EXCHANGE),
PORT_MAXIMUM_MESSAGE_LENGTH,
0);
delete [] LpcPortName;
if (NT_SUCCESS(NtStatus))
{
Status = LrpcSetEndpoint(Endpoint);
return(Status);
}
if (NtStatus == STATUS_NO_MEMORY)
{
return(RPC_S_OUT_OF_MEMORY);
}
if ((NtStatus == STATUS_INSUFFICIENT_RESOURCES)
|| (NtStatus == STATUS_QUOTA_EXCEEDED))
{
return(RPC_S_OUT_OF_RESOURCES);
}
if ((NtStatus == STATUS_OBJECT_PATH_INVALID)
|| (NtStatus == STATUS_OBJECT_PATH_NOT_FOUND)
|| (NtStatus == STATUS_OBJECT_NAME_INVALID)
|| (NtStatus == STATUS_OBJECT_TYPE_MISMATCH)
|| (NtStatus == STATUS_INVALID_OWNER))
{
return(RPC_S_INVALID_ENDPOINT_FORMAT);
}
#if DBG
if (NtStatus != STATUS_OBJECT_NAME_COLLISION)
{
PrintToDebugger("RPC : NtCreatePort : %lx\n", NtStatus);
}
#endif // DBG
ASSERT(NtStatus == STATUS_OBJECT_NAME_COLLISION);
// This error code may be returned if someone has put a SD
// on the LPC port. We should return a reasonable error on free build
// and assert on checked builds to catch this practice.
if (NtStatus == STATUS_PRIVILEGE_NOT_HELD)
{
return(RPC_S_ACCESS_DENIED);
}
return(RPC_S_DUPLICATE_ENDPOINT);
}
extern RPC_CHAR *
ULongToHexString (
IN RPC_CHAR * String,
IN unsigned long Number
);
RPC_STATUS
LRPC_ADDRESS::ServerSetupAddress (
IN RPC_CHAR * NetworkAddress,
IN RPC_CHAR * *Endpoint,
IN unsigned int PendingQueueSize,
IN void * SecurityDescriptor, OPTIONAL
IN unsigned long EndpointFlags,
IN unsigned long NICFlags
)
/*++
Routine Description:
We need to setup the connection port and get ready to receive remote
procedure calls. We will use the name of this machine as the network
address.
Arguments:
Endpoint - Supplies the endpoint to be used will this address.
NetworkAddress - Returns the network address for this server. The
ownership of the buffer allocated to contain the network address
passes to the caller.
SecurityDescriptor - Optionally supplies a security descriptor to
be placed on this address.
PendingQueueSize - Unused.
RpcProtocolSequence - Unused.
Return Value:
RPC_S_OK - We successfully setup this address.
RPC_S_INVALID_SECURITY_DESC - The supplied security descriptor is
invalid.
RPC_S_CANT_CREATE_ENDPOINT - The endpoint format is correct, but
the endpoint can not be created.
RPC_S_INVALID_ENDPOINT_FORMAT - The endpoint is not a valid
endpoint for this particular transport interface.
RPC_S_OUT_OF_RESOURCES - Insufficient resources are available to
setup the address.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to setup
the address.
--*/
{
RPC_CHAR * String;
RPC_STATUS Status ;
RPC_CHAR DynamicEndpoint[64];
static unsigned int DynamicEndpointCount = 0;
ULONG EndpointLength;
UNUSED(PendingQueueSize);
if (*Endpoint)
{
// the maximum allowed length in bytes is the
// string length in bytes (string length * sizeof(RPC_CHAR)) + the NULL
// terminator
EndpointLength = (RpcpStringLength(*Endpoint) + 1) * sizeof(RPC_CHAR);
if (EndpointLength > PORT_NAME_LEN)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_INVALID_ENDPOINT_FORMAT,
EEInfoDLLRPC_ADDRESS__ServerSetupAddress10,
*Endpoint,
EndpointLength,
PORT_NAME_LEN);
return RPC_S_INVALID_ENDPOINT_FORMAT;
}
}
Status = InitializeLrpcIfNecessary() ;
if (Status != RPC_S_OK)
{
return Status ;
}
ASSERT(GlobalLrpcServer != 0) ;
pNetworkAddressVector = (NETWORK_ADDRESS_VECTOR *)
new char[ sizeof(NETWORK_ADDRESS_VECTOR) + sizeof(RPC_CHAR *) + sizeof(RPC_CHAR) * (MAX_COMPUTERNAME_LENGTH + 1)];
if (pNetworkAddressVector == NULL)
{
return RPC_S_OUT_OF_MEMORY;
}
(pNetworkAddressVector)->Count = 1;
(pNetworkAddressVector)->NetworkAddresses[0] = (RPC_CHAR *)
(((char *) pNetworkAddressVector) + sizeof(NETWORK_ADDRESS_VECTOR) + sizeof(RPC_CHAR *));
RpcpStringCopy((pNetworkAddressVector)->NetworkAddresses[0], gLocalComputerName);
if (*Endpoint)
{
Status = ActuallySetupAddress(*Endpoint, SecurityDescriptor);
}
else
{
for (;;)
{
String = DynamicEndpoint;
*String++ = RPC_CONST_CHAR('L');
*String++ = RPC_CONST_CHAR('R');
*String++ = RPC_CONST_CHAR('P');
*String++ = RPC_CONST_CHAR('C');
String = ULongToHexString(String,
PtrToUlong(NtCurrentTeb()->ClientId.UniqueProcess));
DynamicEndpointCount += 1;
*String++ = RPC_CONST_CHAR('.');
String = ULongToHexString(String, DynamicEndpointCount);
*String = 0;
Status = ActuallySetupAddress(DynamicEndpoint, SecurityDescriptor);
if (Status != RPC_S_DUPLICATE_ENDPOINT)
{
break;
}
}
if (Status == RPC_S_OK)
{
*Endpoint = DuplicateString(DynamicEndpoint);
if (*Endpoint == 0)
{
Status = RPC_S_OUT_OF_MEMORY;
}
else
{
return(RPC_S_OK);
}
}
}
if (Status != RPC_S_OK)
{
delete pNetworkAddressVector;
pNetworkAddressVector = NULL;
}
return Status;
}
RPC_STATUS
LRPC_ADDRESS::CompleteListen (
)
/*++
Function Name:CompleteListen
Parameters:
Description:
Returns:
--*/
{
LRPC_ADDRESS *LocalAddress;
if (DebugCell)
{
CStackAnsi AnsiEndpoint;
int i;
RPC_STATUS RpcStatus;
i = RpcpStringLength(InqEndpoint()) + 1;
*(AnsiEndpoint.GetPAnsiString()) = (char *)_alloca(i);
RpcStatus = AnsiEndpoint.Attach(InqEndpoint(), i, i * 2);
// note that effectively we ignore the result. That's ok - we don't
// want servers to be unable to start because of code page issues
// in the debug path. If this fails and we ignore it, the worse
// that can happen is to have empty endpoint in the debug cell
// - not a big deal.
if (RpcStatus == RPC_S_OK)
{
strncpy(DebugCell->EndpointName, AnsiEndpoint, sizeof(DebugCell->EndpointName));
}
DebugCell->Status = desActive;
}
do
{
AddressChain = LrpcAddressList;
}
while (InterlockedCompareExchangePointer((PVOID *)&LrpcAddressList, this, LrpcAddressList) != AddressChain);
return(RPC_S_OK);
}
inline LRPC_SASSOCIATION *
LRPC_ADDRESS::ReferenceAssociation (
IN unsigned long AssociationKey
)
/*++
Routine Description:
Given an assocation key, we need to map it into an association. The
association may already have been deleted, in which case, we need to
return zero.
Arguments:
AssociationKey - Supplies the key to be used to map into an association.
Return Value:
If the association still exists, it will be returned; otherwise, zero
will be returned.
--*/
{
LRPC_SASSOCIATION * Association;
LPC_KEY *LpcKey = (LPC_KEY *) &AssociationKey;
USHORT MySequenceNumber;
ASSERT(SERVERKEY(AssociationKey));
MySequenceNumber = LpcKey->SeqNumber & ~SERVER_KEY_MASK;
AddressMutex.Request();
Association = AssociationDictionary.Find(LpcKey->AssocKey);
if (Association == 0
|| Association->SequenceNumber != MySequenceNumber)
{
AddressMutex.Clear();
return(0);
}
Association->AssociationReferenceCount++;
LogEvent(SU_SASSOC, EV_INC, Association, 0,
Association->AssociationReferenceCount, 1, 1);
AddressMutex.Clear();
return(Association);
}
inline LRPC_CASSOCIATION *
LRPC_ADDRESS::ReferenceClientAssoc (
IN unsigned long AssociationKey
)
/*++
Routine Description:
Given an assocation key, we need to map it into an association. The
association may already have been deleted, in which case, we need to
return zero.
Arguments:
AssociationKey - Supplies the key to be used to map into an association.
Return Value:
If the association still exists, it will be returned; otherwise, zero
will be returned.
--*/
{
LRPC_CASSOCIATION * Association;
LPC_KEY *LpcKey = (LPC_KEY *) &AssociationKey;
LrpcMutexRequest();
Association = LrpcAssociationDict->Find(LpcKey->AssocKey);
if (Association == 0
|| Association->SequenceNumber != LpcKey->SeqNumber)
{
LrpcMutexClear();
return(0);
}
Association->AddReference();
LrpcMutexClear();
return(Association);
}
#if defined(_WIN64)
#define BAD_HANDLE_CONST ((HANDLE)0xbaaaaaadbaaaaaad)
#else
#define BAD_HANDLE_CONST (ULongToHandle(0xbaaaaaad))
#endif
inline void
LRPC_ADDRESS::DereferenceAssociation (
IN LRPC_SASSOCIATION * Association
)
/*++
Routine Description:
We are done using this address, so the reference count can be decremented.
If no one is referencing this association, then we can go ahead and
delete it.
Arguments:
Association - Supplies the association whose reference count should be
decremented.
--*/
{
NTSTATUS NtStatus;
AddressMutex.Request();
Association->AssociationReferenceCount -= 1;
ASSERT(Association->AssociationReferenceCount >= 0);
LogEvent(SU_SASSOC, EV_DEC, Association, 0,
Association->AssociationReferenceCount, 1, 1);
if (Association->AssociationReferenceCount <= 0)
{
AssociationDictionary.Delete(Association->DictionaryKey);
AssociationCount--;
AddressMutex.Clear();
if (Association->LpcServerPort)
{
NtStatus = NtClose(Association->LpcServerPort);
Association->LpcServerPort = BAD_HANDLE_CONST;
LogEvent(SU_SASSOC, EV_STOP, Association, Association->LpcServerPort,
Association->AssociationReferenceCount, 1, 1);
#if DBG
if (!NT_SUCCESS(NtStatus))
{
PrintToDebugger("RPC : NtClose : %lx\n", NtStatus);
ASSERT(0) ;
}
#endif // DBG
}
if (Association->LpcReplyPort)
{
NtStatus = NtClose(Association->LpcReplyPort);
#if DBG
if (!NT_SUCCESS(NtStatus))
{
PrintToDebugger("RPC : NtClose : %lx\n", NtStatus);
ASSERT(0) ;
}
#endif // DBG
}
delete Association;
}
else
{
AddressMutex.Clear();
}
}
BOOL
LRPC_ADDRESS::DealWithLRPCRequest (
IN LRPC_MESSAGE * LrpcMessage,
IN LRPC_MESSAGE * LrpcReply,
IN LRPC_SASSOCIATION *Association,
OUT LRPC_MESSAGE **LrpcResponse
)
/*++
Routine Description:
Deal with a new LRPC request.
Arguments:
LrpcMessage - request message
LrpcReply - the reply is placed here
Association - the association on which the request arrived
Return Value:
FALSE if the thread should stay, or !FALSE if the thread should go
--*/
{
int retval ;
LRPC_SCALL *SCall;
NTSTATUS NtStatus ;
RPC_STATUS Status;
LRPC_SCALL *NewSCall ;
int Flags = LrpcMessage->Rpc.RpcHeader.Flags ;
if (ServerListeningFlag == 0
&& GlobalRpcServer->InqNumAutoListenInterfaces() == 0)
{
*LrpcResponse = LrpcMessage ;
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_SERVER_TOO_BUSY,
EEInfoDLDealWithLRPCRequest10,
(ULONG)ServerListeningFlag,
(ULONG)GlobalRpcServer->InqNumAutoListenInterfaces());
SetFaultPacket(*LrpcResponse,
RPC_S_SERVER_TOO_BUSY, Flags, NULL);
return 0;
}
Status = Association->AllocateSCall(LrpcMessage,
LrpcReply,
Flags,
&SCall) ;
if (Status != RPC_S_OK)
{
*LrpcResponse = LrpcMessage ;
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLDealWithLRPCRequest20);
SetFaultPacket(*LrpcResponse, Status, Flags, NULL);
return 0 ;
}
ASSERT(SCall);
Status = SCall->LrpcMessageToRpcMessage(LrpcMessage,
&(SCall->RpcMessage));
if (Status != RPC_S_OK)
{
#if DBG
PrintToDebugger("LRPC: LrpcMessageToRpcMessage failed: %d\n",
Status) ;
#endif
*LrpcResponse = LrpcMessage ;
SetFaultPacket(*LrpcResponse, Status, Flags, NULL);
Association->FreeSCall (SCall) ;
return 0;
}
AddressMutex.Request();
if (SCall->Flags & LRPC_CAUSAL)
{
retval = Association->MaybeQueueSCall(SCall) ;
switch (retval)
{
case 0:
break;
case 1:
AddressMutex.Clear();
*LrpcResponse = NULL ;
return 0;
case -1:
AddressMutex.Clear();
*LrpcResponse = LrpcMessage ;
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_OUT_OF_MEMORY,
EEInfoDLDealWithLRPCRequest30);
SetFaultPacket(*LrpcResponse, LRPC_MSG_FAULT, Flags, NULL);
Association->FreeSCall (SCall) ;
return 0;
}
}
ActiveCallCount += 1;
if (ActiveCallCount >= CallThreadCount)
{
Status = Server->CreateThread(
(THREAD_PROC)&RecvLotsaCallsWrapper,
this);
if (Status == RPC_S_OK)
{
CallThreadCount += 1;
}
else
{
// If the above SCall is causal and creating the thread has failed
// then the call has been put into the dictionary and needs
// to be removed. It will be the only scall for the key.
if (SCall->Flags & LRPC_CAUSAL)
Association->ClientThreadDict.Delete(MsgClientIdToClientId(SCall->LrpcRequestMessage->Rpc.LpcHeader.ClientId).UniqueThread) ;
ActiveCallCount -= 1;
ASSERT((int)ActiveCallCount >= 0);
AddressMutex.Clear();
*LrpcResponse = LrpcMessage ;
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLDealWithLRPCRequest40);
SetFaultPacket(*LrpcResponse,
RPC_S_SERVER_TOO_BUSY, Flags, NULL);
Association->FreeSCall(SCall) ;
return 0;
}
}
AddressMutex.Clear();
while (1)
{
LrpcReply->Rpc.RpcHeader.Flags = 0;
SCall->DealWithRequestMessage();
if ((SCall->Flags & LRPC_CAUSAL) == 0)
{
break;
}
NewSCall = Association->GetNextSCall(SCall) ;
if (NewSCall)
{
SCall->SendReply();
SCall = NewSCall ;
while (SCall->Deleted)
{
NewSCall = Association->GetNextSCall(SCall) ;
// GetNextSCall will touch SCall->LrpcRequestMessage so it should
// be freed after the call.
FreeMessage(SCall->LrpcRequestMessage) ;
AddressMutex.Request();
//
// N.B. If a causally ordered call fails
// in DealWithRequestMessage, this is fine, because
// in SendReply, if we send back a fault,
// we will mark all calls in the SCallDict with Deleted,
// and in this loop, we will skip them.
//
if (NewSCall == 0)
{
if (fKeepThread())
{
retval = 0;
}
else
{
CallThreadCount -= 1;
retval = 1;
}
ActiveCallCount -= 1;
ASSERT((int)ActiveCallCount >= 0);
AddressMutex.Clear();
Association->FreeSCall(SCall) ;
DereferenceAssociation(Association) ;
*LrpcResponse = NULL ;
return retval;
}
AddressMutex.Clear();
Association->FreeSCall(SCall) ;
DereferenceAssociation(Association) ;
SCall = NewSCall ;
}
RpcpPurgeEEInfo();
}
else
{
break;
}
// Make sure that the LrpcReplyMessage is always pointing to the
// one located on this thread's stack. It is possible that we
// will pick up a queued scall that was put into the queue on
// another thread. In this case, the LrpcReplyMessage may point to
// that thread's stack - a recepie for disaster.
// Note that LrpcReply is located on the stack for the current thread.
LrpcReply->Rpc.RpcHeader.CallId = SCall->CallId ;
SCall->LrpcReplyMessage = LrpcReply;
}
AddressMutex.Request();
if (fKeepThread())
{
if (SCall->IsSyncCall() && SCall->IsClientAsync() == 0)
{
ActiveCallCount -= 1;
ASSERT((int)ActiveCallCount >= 0);
AddressMutex.Clear();
*LrpcResponse = SCall->InitMsg();
Association->FreeSCall(SCall) ;
}
else
{
AddressMutex.Clear();
*LrpcResponse = NULL;
SCall->SendReply();
AddressMutex.Request();
ActiveCallCount -= 1;
ASSERT((int)ActiveCallCount >= 0);
AddressMutex.Clear();
}
return 0 ;
}
//
// This thread is extraneous, reply and return this
// thread to the system.
//
ActiveCallCount -= 1;
ASSERT((int)ActiveCallCount >= 0);
CallThreadCount -= 1;
AddressMutex.Clear();
SCall->SendReply();
return 1 ;
}
#define LRPC_LISTEN_TIMEOUT 5*60*1000
inline void
FormatTimeOut(
OUT PLARGE_INTEGER TimeOut,
IN DWORD Milliseconds
)
{
ASSERT(Milliseconds != -1);
TimeOut->QuadPart = UInt32x32To64( Milliseconds, 10000 );
TimeOut->QuadPart *= -1;
}
RPC_STATUS
LRPC_ADDRESS::BeginLongCall(
void
)
{
RPC_STATUS Status = RPC_S_OK;
AddressMutex.Request();
if (ActiveCallCount + 1 >= CallThreadCount)
{
AddressMutex.Clear();
Status = Server->CreateThread(
(THREAD_PROC)&RecvLotsaCallsWrapper,
this);
AddressMutex.Request();
// N.B. We increase the active call count
// regrdless of Status. This is OK, because
// if we return failure, the caller of this function
// is responsible to decrease it
ActiveCallCount ++;
if (Status == RPC_S_OK)
{
CallThreadCount += 1;
}
}
else
{
ActiveCallCount ++;
}
AddressMutex.Clear();
return Status;
}
void LRPC_ADDRESS::HandleInvalidAssociationReference (
IN LRPC_MESSAGE *RequestMessage,
IN OUT LRPC_MESSAGE **ReplyMessage,
IN ULONG AssociationKey
)
{
ASSERT(RequestMessage != NULL);
ASSERT(ReplyMessage != NULL);
// we handle only binds, requests and copies
if ((RequestMessage->Bind.MessageType != LRPC_MSG_REQUEST)
&& (RequestMessage->Bind.MessageType != LRPC_MSG_BIND)
&& (RequestMessage->Bind.MessageType != LRPC_MSG_COPY))
{
*ReplyMessage = NULL;
return;
}
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_CALL_FAILED_DNE,
EEInfoDLLRPC_ADDRESS__HandleInvalidAssociationReference10,
AssociationKey);
if (RequestMessage->Bind.MessageType == LRPC_MSG_BIND)
{
SetBindAckFault(RequestMessage, RPC_S_CALL_FAILED_DNE);
// if this is bind, patch up the fields a bit, as SetFaultPacket
// does not set everything right for the bind case
RequestMessage->Bind.MessageType = LRPC_BIND_ACK;
}
else
{
SetFaultPacket(RequestMessage,
RPC_S_CALL_FAILED_DNE,
RequestMessage->Rpc.RpcHeader.Flags,
NULL);
}
*ReplyMessage = RequestMessage;
}
BOOL
LRPC_ADDRESS::EndLongCall(
void
)
{
AddressMutex.Request();
ActiveCallCount -= 1;
int SpareThreads = CallThreadCount -
(ActiveCallCount + MinimumCallThreads);
if (SpareThreads > 0)
{
ASSERT(CallThreadCount > ActiveCallCount);
AddressMutex.Clear();
return TRUE;
}
AddressMutex.Clear();
return FALSE;
}
#define GC_TIMER_CHECK_INTERVAL (256)
#define GC_TIME_INTERVAL LRPC_LISTEN_TIMEOUT
ULONG LastGCTime = 0;
void
LRPC_ADDRESS::ReceiveLotsaCalls (
)
/*++
Routine Description:
Here is where we receive remote procedure calls to this address. One
more threads will be executing this routine at once.
--*/
{
NTSTATUS NtStatus;
LRPC_SASSOCIATION * Association;
LRPC_CASSOCIATION *CAssociation;
unsigned long AssociationKey;
LRPC_MESSAGE * Reply;
LRPC_MESSAGE * LrpcMessage = 0;
LRPC_MESSAGE * LrpcReplyMessage = 0;
int AssociationType = 0;
int Flags = 0;
BOOL PartialFlag ;
BOOL fStatus ;
RPC_STATUS Status;
// The key for the association for which we had just processed an LPC message.
// If we get an error while replying for the message on this association, then
// it will be torn down.
// This value will remain valid only while we are
// replying to the message on that association. After that, we may handle an invalid
// association reference, etc. making the key no longer valid. After a reply message is sent,
// the ReplyKey will be invalidated so that we don't tear up wrong associations on error.
unsigned long ReplyKey = -1;
LARGE_INTEGER LongTimeout;
LARGE_INTEGER ShortTimeout;
PLARGE_INTEGER pliTimeout = &ShortTimeout;
ULONG_PTR Key;
THREAD *ThisThread;
DebugThreadInfo *DebugCell;
// The number of LRPC calls that have been processed since the last GC
// has been done. We will check the timer to see if garbage collection
// needs to be done once every GC_TIMER_CHECK_INTERVAL calls.
// Then, if more then GC_TIME_INTERVAL milliseconds have passed since
// the last GC, we will GC.
ULONG NumProcessedLrpcCalls = 0;
FormatTimeOut(&ShortTimeout, gThreadTimeout);
FormatTimeOut(&LongTimeout, LRPC_LISTEN_TIMEOUT);
pliTimeout = &ShortTimeout;
Reply = (LRPC_MESSAGE *)AlignOnNaturalBoundary(
_alloca(sizeof(LRPC_MESSAGE) + RPCRT_NATURAL_BOUNDARY_ALIGNMENT_MAX_SHIFT));
ThisThread = RpcpGetThreadPointer();
ASSERT(ThisThread);
DebugCell = ThisThread->DebugCell;
if (DebugCell)
{
if (this->DebugCell)
{
GetDebugCellIDFromDebugCell(
(DebugCellUnion *)this->DebugCell,
&this->DebugCellTag,
&ThisThread->DebugCell->Endpoint);
}
}
// We need to check if GC is needed. This is necessary because
// this thread may have been recycled before it had a chance to execute
// GC_TIMER_CHECK_INTERVAL calls. If this keeps repeating we will never
// get to GC.
if (NtGetTickCount() - LastGCTime >= GC_TIME_INTERVAL)
{
LastGCTime = NtGetTickCount();
PerformGarbageCollection();
}
for (;;)
{
if (LrpcMessage == 0)
{
while ((LrpcMessage = AllocateMessage()) == 0)
{
Sleep(100) ;
}
}
ASSERT(LrpcReplyMessage == 0
|| LrpcReplyMessage->Rpc.RpcHeader.MessageType <= MAX_LRPC_MSG);
if (DebugCell)
{
DebugCell->Status = dtsIdle;
DebugCell->LastUpdateTime = NtGetTickCount();
}
RpcpPurgeEEInfoFromThreadIfNecessary(ThisThread);
#if DBG
if (LrpcReplyMessage)
{
LogEvent(SU_SASSOC, EV_PKT_OUT, this, UlongToHandle(LrpcReplyMessage->LpcHeader.MessageId),
(HandleToULong(LrpcReplyMessage->LpcHeader.ClientId.UniqueProcess)<<16 | \
(HandleToULong(LrpcReplyMessage->LpcHeader.ClientId.UniqueThread))), 0, 0);
}
#endif // DBG
NtStatus = NtReplyWaitReceivePortEx(LpcAddressPort,
(PVOID *) &Key,
(PORT_MESSAGE *) LrpcReplyMessage,
(PORT_MESSAGE *) LrpcMessage,
pliTimeout);
AssociationKey = (ULONG) Key; // need this for 64bit
if (NtStatus != STATUS_TIMEOUT
&& NT_SUCCESS(NtStatus))
{
// We have sucessfully sent a reply, forget the ReplyKey since we may now deal with
// another association.
ReplyKey = -1;
if (pliTimeout != &ShortTimeout)
{
#if defined (RPC_GC_AUDIT)
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) LPC Thread %X: coming back from long wait\n",
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
#endif
ASSERT((pliTimeout == NULL) || (pliTimeout == &LongTimeout));
ThreadsDoingLongWait.Decrement();
pliTimeout = &ShortTimeout;
}
if (DebugCell)
{
DebugCell->Status = dtsProcessing;
DebugCell->LastUpdateTime = NtGetTickCount();
}
#if 0
if (LrpcMessage->LpcHeader.u2.s2.Type == LPC_CONNECTION_REQUEST)
LogEvent(SU_PACKET, EV_PKT_IN, (void *) LrpcMessage->LpcHeader.u2.ZeroInit,
(void *)LrpcMessage->Connect.BindExchange.ConnectType, AssociationKey);
else
LogEvent(SU_PACKET, EV_PKT_IN, (void *) LrpcMessage->LpcHeader.u2.ZeroInit,
0, AssociationKey);
#endif
#if defined RPC_LRPC_CORRUPTION
unsigned int BufferLength = PORT_MAXIMUM_MESSAGE_LENGTH
if (gfRPCVerifierEnabled)
{
CorruptionInject(ServerReceive,
&BufferLength,
(void **)&LrpcMessag);
}
#endif // RPC_LRPC_CORRUPTION
if (LrpcMessage->LpcHeader.u2.s2.Type == LPC_DATAGRAM
|| LrpcMessage->LpcHeader.u2.s2.Type == LPC_REQUEST)
{
if (!SERVERKEY(AssociationKey))
{
VALIDATE(LrpcMessage->Bind.MessageType)
{
LRPC_MSG_FAULT,
LRPC_MSG_FAULT2,
LRPC_MSG_RESPONSE,
LRPC_CLIENT_SEND_MORE
} END_VALIDATE;
//
// response or fault on the back connection.
// we are using async rpc or pipes
//
CAssociation = ReferenceClientAssoc(AssociationKey);
if (CAssociation)
{
LrpcReplyMessage = 0;
//
// In order to avoid a possible deadlock, we must not wait on the AssociationMutex
// if we cannot create a thread to take our place.
// The deadlock scenario is: CAssociation is aborted due to a client failure
// with some active calls. The client makes a new call which leads to the client reconnecting
// to the server and asking for a bind back (which holding the AssociationMutex).
// The server, before binding back, sends a reply to one of the active calls.
// This gets dispatched to here, but BeginLongCall fails. The request ends up waiting
// on the AssociationMutex while the server's bind back gets queued behind it
// with no worker threads to service it and we are deadlocked.
//
while(1){
Status = BeginLongCall();
if (Status == RPC_S_OK)
{
CAssociation->ProcessResponse(LrpcMessage, FALSE, &LrpcReplyMessage);
break;
}
else if (CAssociation->AssociationMutex.TryRequest() == TRUE)
{
//
// We failed to create a worker thread to take our place, but we took the association
// mutex, so we can process the response without fear of deadlock
//
CAssociation->ProcessResponse(LrpcMessage, TRUE, &LrpcReplyMessage);
break;
}
else
{
//
// We couldn't create a thread and we also could not get the mutex, we need
// to retry.
//
Sleep(200);
EndLongCall();
}
}
//
// the receive thread needs to allocate a new message
//
LrpcMessage = 0 ;
CAssociation->RemoveReference() ;
EndLongCall();
}
else
{
HandleInvalidAssociationReference(LrpcMessage,
&LrpcReplyMessage,
AssociationKey);
}
continue;
}
Association = ReferenceAssociation(AssociationKey);
if (Association == 0)
{
HandleInvalidAssociationReference(LrpcMessage,
&LrpcReplyMessage,
AssociationKey);
continue;
}
// Since we had sucessfully found an association for this Key, remember it
// for sending reply.
ReplyKey = AssociationKey;
Flags = LrpcMessage->Rpc.RpcHeader.Flags ;
PartialFlag = FALSE ;
// If we had processed enough calls, check if enough time
// has passed to GC, and GC if necessary.
if (NumProcessedLrpcCalls++ > GC_TIMER_CHECK_INTERVAL)
{
NumProcessedLrpcCalls = 0;
if (NtGetTickCount() - LastGCTime >= GC_TIME_INTERVAL)
{
LastGCTime = NtGetTickCount();
PerformGarbageCollection();
}
}
if (LrpcMessage->Bind.MessageType == LRPC_MSG_REQUEST)
{
//
// Optimize the common case
//
fStatus = DealWithLRPCRequest (
LrpcMessage,
Reply,
Association,
&LrpcReplyMessage) ;
if (fStatus)
{
// this is the first of two exits from the loop
// (the second is below)
if (DebugCell)
{
DebugCell->Status = dtsAllocated;
DebugCell->LastUpdateTime = NtGetTickCount();
}
return;
}
if (LrpcReplyMessage == 0)
{
LrpcMessage = 0;
}
else
{
DereferenceAssociation(Association);
}
}
else
{
switch (LrpcMessage->Bind.MessageType)
{
case LRPC_PARTIAL_REQUEST:
case LRPC_SERVER_SEND_MORE:
case LRPC_MSG_CANCEL:
LrpcReplyMessage = Association->
DealWithPartialRequest(&LrpcMessage) ;
break;
case LRPC_MSG_COPY:
LrpcReplyMessage = Association->DealWithCopyMessage(
(LRPC_COPY_MESSAGE *)LrpcMessage);
break;
case LRPC_MSG_BIND :
Association->DealWithBindMessage(LrpcMessage);
LrpcReplyMessage = 0 ;
break;
case LRPC_MSG_BIND_BACK:
BeginLongCall();
LrpcReplyMessage = Association->
DealWithBindBackMessage(LrpcMessage);
EndLongCall();
break;
default:
#if DBG
PrintToDebugger("RPC : Bad Message Type (%d) - %d\n",
LrpcMessage->Bind.MessageType,
LrpcMessage->LpcHeader.u2.s2.Type);
#endif // DBG
CORRUPTION_ASSERT(0) ;
LrpcReplyMessage = 0 ;
Association->Delete();
break;
}
DereferenceAssociation(Association);
}
}
else
{
switch (LrpcMessage->LpcHeader.u2.s2.Type)
{
case LPC_CONNECTION_REQUEST:
if (LrpcMessage->Connect.BindExchange.ConnectType
== LRPC_CONNECT_REQUEST)
{
BeginLongCall();
DealWithNewClient(LrpcMessage) ;
EndLongCall();
}
else if (LrpcMessage->Connect.BindExchange.ConnectType
== LRPC_CONNECT_RESPONSE)
{
DealWithConnectResponse(LrpcMessage) ;
}
else if (LrpcMessage->Connect.BindExchange.ConnectType
== LRPC_CONNECT_TICKLE)
{
HANDLE Ignore;
// always reject - this just has the purpose of tickling
// a thread on a long wait
NtStatus = NtAcceptConnectPort(&Ignore,
NULL,
(PORT_MESSAGE *) LrpcMessage,
FALSE,
NULL,
NULL);
#if defined (RPC_GC_AUDIT)
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) LPC Thread %X: tickled\n",
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
#endif
}
else
{
CORRUPTION_ASSERT(0) ;
}
LrpcReplyMessage = 0;
break;
case LPC_CLIENT_DIED:
LrpcReplyMessage = 0;
break;
case LPC_PORT_CLOSED:
if (SERVERKEY(AssociationKey))
{
Association = ReferenceAssociation(AssociationKey);
if (Association == 0)
{
LrpcReplyMessage = 0;
continue;
}
BeginLongCall();
Association->Delete();
DereferenceAssociation(Association);
LrpcReplyMessage = 0;
EndLongCall();
}
else
{
CAssociation = ReferenceClientAssoc(AssociationKey);
if (CAssociation)
{
BeginLongCall();
CAssociation->AbortAssociation(1) ;
CAssociation->RemoveReference() ;
EndLongCall();
}
LrpcReplyMessage = 0;
}
continue;
default:
LrpcReplyMessage = 0 ;
CORRUPTION_ASSERT(0);
} // switch
} // else
} // if
else
{
switch (NtStatus)
{
case STATUS_NO_MEMORY:
case STATUS_INSUFFICIENT_RESOURCES:
PauseExecution(500L);
break;
//
// This error is returned after the reply has been sent successfully, so do not send
// the reply again.
case STATUS_UNSUCCESSFUL:
LrpcReplyMessage = 0;
break;
case STATUS_TIMEOUT:
#if defined (RPC_GC_AUDIT)
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) LPC Thread %X: timed out - gc\n",
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
#endif
// We have sucessfully sent a reply.
ReplyKey = -1;
PerformGarbageCollection();
if (pliTimeout == &ShortTimeout)
{
// be conservative and presume we will
// be doing long wait. If later we find out
// we won't, we'll reverse that. Also, this must
// be done nefore we check for
// GarbageCollectedRequested - this allows other
// threads to safely count the number of threads
// on short wait without taking a mutex
ThreadsDoingLongWait.Increment();
LrpcReplyMessage = 0;
// if there is garbage collection
// requested, don't switch to long
// wait
if (GarbageCollectionRequested)
{
#if defined (RPC_GC_AUDIT)
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) LPC Thread %X: gc requested - can't do long wait\n",
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
#endif
ThreadsDoingLongWait.Decrement();
}
else
{
#if defined (RPC_GC_AUDIT)
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) LPC Thread %X: going to long wait\n",
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
#endif
// there is no garbage collection requested
// switch to longer wait (but not infinite yet)
pliTimeout = &LongTimeout;
}
}
else if (pliTimeout == &LongTimeout)
{
// if this is a long wait, and we're a spare
// thread, we can go
AddressMutex.Request();
if (CallThreadCount - ActiveCallCount > 1)
{
CallThreadCount -= 1;
ASSERT(CallThreadCount > ActiveCallCount);
AddressMutex.Clear();
// decrease the counter of threads doing long
// listen after we decrease the CallThreadCount
// This allows other threads to use the number
// of threads doing short wait without taking
// a mutex
ThreadsDoingLongWait.Decrement();
FreeMessage(LrpcMessage);
// N.B. This is the second exit from the loop (see above)
if (DebugCell)
{
DebugCell->Status = dtsAllocated;
DebugCell->LastUpdateTime = NtGetTickCount();
}
return;
}
else
{
//
// We are assuming that if the call has timed out, the reply has
// been sent
//
LrpcReplyMessage = 0;
pliTimeout = NULL;
#if defined (RPC_GC_AUDIT)
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) LPC Thread %X: going to infinite wait\n",
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
#endif
}
AddressMutex.Clear();
}
else
{
ASSERT(!"We cannot get a timeout on wait with infinite timeout");
}
if (DebugCell)
{
RelocateCellIfPossible((void **) &DebugCell, &ThisThread->DebugCellTag);
ThisThread->DebugCell = DebugCell;
}
break;
default:
if (LrpcReplyMessage)
{
LogEvent(SU_SASSOC, EV_ABORT, this, UlongToHandle(NtStatus), ((HandleToULong(((PORT_MESSAGE*)LrpcReplyMessage)->ClientId.UniqueProcess))<<16 | \
(HandleToULong(((PORT_MESSAGE*)LrpcReplyMessage)->ClientId.UniqueThread))), 1, 0);
LrpcReplyMessage = 0;
if (ReplyKey != -1)
{
Association = ReferenceAssociation(ReplyKey);
if (Association == 0)
{
continue;
}
}
else
continue;
BeginLongCall();
Association->Delete();
DereferenceAssociation(Association);
EndLongCall();
}
break;
} // switch
} // else
} // for
}
#define DEFAULT_PORT_DIR "\\RPC Control\\"
#define DEFAULT_PORT_NAME "ARPC Port1"
#define DEFAULT_REPLY_NAME "ARPC Reply Port"
void
LRPC_ADDRESS::DealWithNewClient (
IN LRPC_MESSAGE * ConnectionRequest
)
/*++
Routine Description:
A new client has connected with our address port. We need to take
care of the new client and send a response.
Arguments:
ConnectionRequest - Supplies information need by LPC to abort the
connect request. Includes the bind request from the client.
This contains the information about which interface the client
wants to bind with. and which we use to send the status code
back in.
--*/
{
LRPC_SASSOCIATION * Association;
NTSTATUS NtStatus;
RPC_STATUS Status = RPC_S_OK;
DWORD Key, tmpKey;
LPC_KEY *LpcKey = (LPC_KEY *) &Key;
Association = new LRPC_SASSOCIATION(this,
&Status);
if (Association == 0)
{
RejectNewClient(ConnectionRequest, RPC_S_OUT_OF_MEMORY);
return;
}
if (Status != RPC_S_OK)
{
delete Association ;
RejectNewClient(ConnectionRequest, RPC_S_OUT_OF_MEMORY);
return ;
}
AddressMutex.Request();
// Insert the association. The dictionary can fit
// 2^32 associations, but since our DictionaryKey is only
// 16 bits, we must treat the dictionary as full if the key
// returned is greater than 0xFFFF
tmpKey = AssociationDictionary.Insert(Association);
if (tmpKey > 0xFFFF)
{
AssociationDictionary.Delete(tmpKey);
tmpKey = -1;
}
Association->DictionaryKey = (USHORT) tmpKey;
AssociationCount++;
SequenceNumber = (SequenceNumber+1) % (0x7FFF);
Association->SequenceNumber = SequenceNumber;
AddressMutex.Clear();
if (Association->DictionaryKey == -1)
{
AddressMutex.Request();
AssociationCount-- ;
AddressMutex.Clear();
delete Association ;
RejectNewClient(ConnectionRequest, RPC_S_OUT_OF_MEMORY);
return;
}
if (ConnectionRequest->Connect.BindExchange.Flags & BIND_BACK_FLAG)
{
ConnectionRequest->Connect.BindExchange.szPortName[PORT_NAME_LEN-1] = NULL;
Status = Association->BindBack(
(RPC_CHAR *)ConnectionRequest->Connect.BindExchange.szPortName,
ConnectionRequest->Connect.BindExchange.AssocKey,
ConnectionRequest->Connect.BindExchange.Cookie);
if (Status != RPC_S_OK)
{
RejectNewClient(ConnectionRequest, RPC_S_OUT_OF_MEMORY);
Association->Delete() ;
return;
}
}
ConnectionRequest->Connect.BindExchange.RpcStatus = RPC_S_OK;
ASSERT(sizeof(unsigned long) <= sizeof(PVOID));
ASSERT((Association->SequenceNumber & SERVER_KEY_MASK) == 0);
LpcKey->SeqNumber = Association->SequenceNumber | SERVER_KEY_MASK;
LpcKey->AssocKey = Association->DictionaryKey;
// After the call to NtAcceptConnectPort, the client will become unblocked
// the association will be in the dictionary and will have refcount 1. If the client quits
// or closes port the association will be deleted. Then NtCompleteConnectPort
// may touch invalid memory or operate on a bad handle. To prevent that we
// need to hold an extra count between the two calls.
//
// Since this thread is the only one playing with the association up to now,
// there is no need for a lock.
Association->AssociationReferenceCount++;
NtStatus = NtAcceptConnectPort(&(Association->LpcServerPort),
ULongToPtr(Key),
(PORT_MESSAGE *) ConnectionRequest,
TRUE,
NULL,
NULL);
if (NT_ERROR(NtStatus))
{
Association->Delete();
// We just have to dereference the association to remove the extra
// count added above. This should cause its deletion.
DereferenceAssociation(Association);
#if DBG
PrintToDebugger("RPC : NtAcceptConnectPort : %lx\n", NtStatus);
#endif // DBG
return;
}
NtStatus = NtCompleteConnectPort(Association->LpcServerPort);
if (NT_ERROR(NtStatus))
{
#if DBG
PrintToDebugger("RPC : NtCompleteConnectPort : %lx\n", NtStatus);
#endif // DBG
// If Association->Delete() has already been called on a different
// theread due to a closed client port, this call will be ignored...
Association->Delete();
// and the final reference will be removed here causing a deletion.
DereferenceAssociation(Association);
return;
}
// Remove the extra-reference.
DereferenceAssociation(Association);
}
void
LRPC_ADDRESS::DealWithConnectResponse (
IN LRPC_MESSAGE * ConnectResponse
)
/*++
Routine Description:
Just received a connect response from the remove server,
need to handle that.
Arguments:
ConnectionRequest -
Needed to get the pAssoc
--*/
{
NTSTATUS NtStatus;
HANDLE temp ;
LRPC_CASSOCIATION * Association ;
DWORD Key;
Key = ConnectResponse->Connect.BindExchange.AssocKey;
Association = ReferenceClientAssoc(Key);
if (Association == 0)
{
RejectNewClient(ConnectResponse, RPC_S_PROTOCOL_ERROR);
return;
}
//
// Verify that the response is expected and is from the server
// who knows our Cookie.
//
if (Association->BindBackCookie == NULL)
{
// This is an "unsolicited" response.
CORRUPTION_ASSERT(0 && "Unexpected connect response");
RejectNewClient(ConnectResponse, RPC_S_PROTOCOL_ERROR);
return;
}
if (RpcpMemoryCompare(ConnectResponse->Connect.BindExchange.Cookie,
Association->BindBackCookie,
BIND_BACK_COOKIE_SIZE) != 0)
{
// The server replied with an incorrect bind back cookie.
CORRUPTION_ASSERT(0 && "Incorrect bind back cookie");
RejectNewClient(ConnectResponse, RPC_S_PROTOCOL_ERROR);
return;
}
NtStatus = NtAcceptConnectPort(&temp,
ULongToPtr(Key),
(PPORT_MESSAGE) ConnectResponse,
TRUE,
NULL,
NULL);
if (NT_SUCCESS(NtStatus))
{
Association->SetReceivePort(temp) ;
NtStatus = NtCompleteConnectPort(temp);
if (!NT_SUCCESS(NtStatus))
{
#if DBG
PrintToDebugger("LRPC: NtCompleteConnectPort(1) failed: %lx\n",
NtStatus) ;
#endif
Association->Delete();
}
}
else
{
#if DBG
PrintToDebugger("LRPC: NtAcceptConnectionPort(1) failed: %lx\n",
NtStatus) ;
#endif
Association->Delete();
}
//
// Remove the reference we added above
//
Association->RemoveReference() ;
}
void
LRPC_ADDRESS::RejectNewClient (
IN LRPC_MESSAGE * ConnectionRequest,
IN RPC_STATUS Status
)
/*++
Routine Description:
A new client has connected with our address port. We need to reject
the client.
Arguments:
ConnectionRequest - Supplies information need by LPC to abort the
connect request. Includes the bind request from the client,
which we use to send the status code back in.
Status - Supplies the reason the client is being rejected.
--*/
{
NTSTATUS NtStatus;
HANDLE Ignore;
ASSERT(Status != RPC_S_OK);
ConnectionRequest->Connect.BindExchange.RpcStatus = Status;
ConnectionRequest->Connect.BindExchange.Flags |= SERVER_BIND_EXCH_RESP;
NtStatus = NtAcceptConnectPort(&Ignore,
NULL,
(PORT_MESSAGE *) ConnectionRequest,
FALSE,
NULL,
NULL);
#if DBG
if (!NT_SUCCESS(NtStatus))
{
PrintToDebugger("RPC : NtAcceptConnectPort : %lx\n", NtStatus);
// if the client thread dies for whatever reason, NtAcceptConnectPort
// can return STATUS_REPLY_MESSAGE_MISMATCH
VALIDATE(NtStatus)
{
STATUS_INVALID_CID,
STATUS_REPLY_MESSAGE_MISMATCH
} END_VALIDATE;
}
#endif // DBG
}
void
LRPC_ADDRESS::EnumerateAndCallEachAssociation (
IN AssociationCallbackType asctType,
IN OUT void *Context OPTIONAL
)
/*++
Function Name: EnumerateAndCallEachAssociation
Parameters:
asctType - type of callback to make
Context - opaque memory block specific for the callback
type.
Description:
Common infrastructure for calling into each association
Returns:
--*/
{
LRPC_SASSOCIATION *CurrentAssociation;
BOOL CopyOfDictionaryUsed;
LRPC_SASSOCIATION_DICT AssocDictCopy;
LRPC_SASSOCIATION_DICT *AssocDictToUse;
BOOL Res;
DictionaryCursor cursor;
DestroyContextHandleCallbackContext *CallbackContext;
AddressMutex.Request();
CopyOfDictionaryUsed = AssocDictCopy.ExpandToSize(AssociationDictionary.Size());
if (CopyOfDictionaryUsed)
{
AssociationDictionary.Reset(cursor);
while ( (CurrentAssociation = AssociationDictionary.Next(cursor)) != 0 )
{
Res = AssocDictCopy.Insert(CurrentAssociation);
ASSERT(Res != -1);
// artifically add a count to keep it alive
// while we destroy the contexts
CurrentAssociation->AssociationReferenceCount++;
}
AddressMutex.Clear();
AssocDictToUse = &AssocDictCopy;
}
else
{
AssocDictToUse = &AssociationDictionary;
}
AssocDictToUse->Reset(cursor);
while ( (CurrentAssociation = AssocDictToUse->Next(cursor)) != 0 )
{
switch (asctType)
{
case asctDestroyContextHandle:
CallbackContext = (DestroyContextHandleCallbackContext *)Context;
// call into the association to destroy the context handles
CurrentAssociation->DestroyContextHandlesForInterface(
CallbackContext->RpcInterfaceInformation,
CallbackContext->RundownContextHandles);
break;
case asctCleanupIdleSContext:
CurrentAssociation->CleanupIdleSContexts();
break;
default:
ASSERT(0);
}
}
if (CopyOfDictionaryUsed)
{
while ( (CurrentAssociation = AssocDictCopy.Next(cursor)) != 0 )
{
// remove the extra refcounts
DereferenceAssociation(CurrentAssociation);
}
}
else
{
AddressMutex.Clear();
}
}
void
LRPC_ADDRESS::DestroyContextHandlesForInterface (
IN RPC_SERVER_INTERFACE PAPI * RpcInterfaceInformation,
IN BOOL RundownContextHandles
)
/*++
Function Name: DestroyContextHandlesForInterface
Parameters:
RpcInterfaceInformation - the interface for which context handles
are to be unregistered
RundownContextHandles - if non-zero, rundown the context handles. If
FALSE, destroy the runtime portion of the context handle resource,
but don't call the user rundown routine.
Description:
The implementation for context handle destruction for the local RPC
(LRPC). Using the callback infrastructure it will walk the list of
associations, and for each one it will ask the association to
destroy the context handles for that interface.
Returns:
--*/
{
DestroyContextHandleCallbackContext CallbackContext;
CallbackContext.RpcInterfaceInformation = RpcInterfaceInformation;
CallbackContext.RundownContextHandles = RundownContextHandles;
EnumerateAndCallEachAssociation(asctDestroyContextHandle,
&CallbackContext);
}
void
LRPC_ADDRESS::CleanupIdleSContexts (
void
)
/*++
Function Name: CleanupIdleSContexts
Parameters:
Description:
The implementation for idle SContext cleanup for the local RPC
(LRPC). Using the callback infrastructure it will walk the list of
associations, and for each one it will ask the association to
destroy the idle scontexts
Returns:
--*/
{
LogEvent(SU_GC, EV_PRUNE, this, 0, 0, 0, 0);
EnumerateAndCallEachAssociation(asctCleanupIdleSContext,
NULL);
}
BOOL
LRPC_ADDRESS::PrepareForLoopbackTickling (
void
)
{
RPC_CHAR * LpcPortName;
int DirectoryNameLength;
int EndpointLength;
LrpcMutexVerifyOwned();
DirectoryNameLength = RpcpStringLength(LRPC_DIRECTORY_NAME);
EndpointLength = RpcpStringLength(InqEndpoint());
LpcPortName = new RPC_CHAR[
EndpointLength
+ DirectoryNameLength + 1];
if (LpcPortName == 0)
{
return FALSE;
}
TickleMessage = new LRPC_BIND_EXCHANGE;
if (TickleMessage == NULL)
{
delete [] LpcPortName;
return FALSE;
}
RpcpMemoryCopy(LpcPortName, LRPC_DIRECTORY_NAME,
DirectoryNameLength * sizeof(RPC_CHAR));
RpcpMemoryCopy(LpcPortName + DirectoryNameLength,
InqEndpoint(),
(EndpointLength + 1) * sizeof(RPC_CHAR));
RtlInitUnicodeString(&ThisAddressLoopbackString, LpcPortName);
return TRUE;
}
BOOL
LRPC_ADDRESS::LoopbackTickle (
void
)
{
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
HANDLE LoopbackPort;
ULONG TickleMessageLength = sizeof(LRPC_BIND_EXCHANGE);
NTSTATUS NtStatus;
ASSERT (IsPreparedForLoopbackTickling());
SecurityQualityOfService.EffectiveOnly = FALSE;
SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
SecurityQualityOfService.ImpersonationLevel = SecurityAnonymous;
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
TickleMessage->ConnectType = LRPC_CONNECT_TICKLE ;
// TickleMessage->AssocKey = Key;
TickleMessage->Flags = 0;
NtStatus = NtConnectPort(
&LoopbackPort,
&ThisAddressLoopbackString,
&SecurityQualityOfService,
NULL,
NULL,
NULL,
TickleMessage,
&TickleMessageLength);
if (NtStatus == STATUS_PORT_CONNECTION_REFUSED)
return TRUE;
else
{
ASSERT(NtStatus != RPC_S_OK);
return FALSE;
}
}
LRPC_SASSOCIATION::LRPC_SASSOCIATION (
IN LRPC_ADDRESS * Address,
IN RPC_STATUS *Status
) : AssociationMutex(Status),
BindingsCollectionLock (0)
/*++
--*/
{
ObjectType = LRPC_SASSOCIATION_TYPE;
LpcServerPort = 0;
LpcReplyPort = 0 ;
this->Address = Address;
AssociationReferenceCount = 1;
Aborted = 0 ;
Deleted = -1;
if (*Status == RPC_S_OK)
{
// Create an SCALL object.
// If the app verifier is enabled, we will create an object which
// supports the verifier checks.
if (gfRPCVerifierEnabled)
{
CachedSCall = new LRPC_SCALL_AVRF(Status);
}
else
{
CachedSCall = new LRPC_SCALL(Status);
}
if (CachedSCall == 0)
{
*Status = RPC_S_OUT_OF_MEMORY;
}
}
else
{
CachedSCall = NULL;
}
CachedSCallAvailable = 0;
fFirstCall = 0;
}
LRPC_SASSOCIATION::~LRPC_SASSOCIATION (
)
/*++
Routine Description:
We will call this routine when the client has notified us that this port
has closed, and there are no calls outstanding on it.
--*/
{
PVOID Buffer;
LRPC_SBINDING * Binding;
LRPC_SCALL *SCall ;
unsigned int Length ;
LRPC_SCONTEXT *SContext;
DictionaryCursor cursor;
while (SCall = (LRPC_SCALL *) FreeSCallQueue.TakeOffQueue(&Length))
{
delete SCall;
}
Bindings.Reset(cursor);
while ((Binding = Bindings.Next(cursor)) != 0)
{
delete Binding;
}
if (CachedSCall)
{
delete CachedSCall;
}
SContextDict.Reset(cursor);
while ((SContext = SContextDict.Next(cursor)) != 0)
{
delete SContext;
}
}
RPC_STATUS
LRPC_SASSOCIATION::AllocateSCall (
IN LRPC_MESSAGE * LrpcMessage,
IN LRPC_MESSAGE * LrpcReplyMessage,
IN unsigned int Flags,
IN LRPC_SCALL **SCall
)
/*++
Routine Description:
Allocate an SCall
Arguments:
LrpcMessage - Request message
LrpcReplyMessage - Reply message
Flags - Request flags
Return Value:
Pointer to the SCall
--*/
{
unsigned int Length ;
RPC_STATUS Status ;
LRPC_SCALL *NewSCall;
*SCall = NULL;
if (InterlockedIncrement(&CachedSCallAvailable) == 1)
{
NewSCall = CachedSCall;
}
else
{
AssociationMutex.Request() ;
NewSCall = (LRPC_SCALL *) FreeSCallQueue.TakeOffQueue(&Length) ;
AssociationMutex.Clear() ;
if (NewSCall == 0)
{
// Create an SCALL object.
// If the app verifier is enabled, we will create an object which
// supports the verifier checks.
if (gfRPCVerifierEnabled)
{
NewSCall = new LRPC_SCALL_AVRF(&Status) ;
}
else
{
NewSCall = new LRPC_SCALL(&Status) ;
}
if (NewSCall == 0)
{
return RPC_S_OUT_OF_MEMORY;
}
if (Status != RPC_S_OK)
{
delete NewSCall;
return Status;
}
}
}
Status = NewSCall->ActivateCall(this,
LrpcMessage,
LrpcReplyMessage,
Flags) ;
if ((Flags & LRPC_BUFFER_PARTIAL)
|| NewSCall->IsClientAsync())
{
Status = NewSCall->SetupCall() ;
if (Status != RPC_S_OK)
{
if (NewSCall != CachedSCall)
{
delete NewSCall ;
}
return RPC_S_OUT_OF_MEMORY ;
}
}
LogEvent(SU_SCALL, EV_CREATE, NewSCall, 0, Flags, 1);
*SCall = NewSCall;
return RPC_S_OK;
}
void
LRPC_SASSOCIATION::FreeSCall (
IN LRPC_SCALL *SCall
)
/*++
Routine Description:
Free the SCall
Arguments:
SCall - Pointer to the SCall object
--*/
{
ASSERT(SCall->pAsync != (PRPC_ASYNC_STATE) -1);
if (SCall->pAsync)
{
SCall->DoPostDispatchProcessing();
if (SCall->SBinding)
{
RPC_INTERFACE *CallInterface = SCall->SBinding->RpcInterface;
CallInterface->EndCall(0, 1);
if (CallInterface->IsAutoListenInterface())
{
// This is the path where async calls complete.
// We need to decrement CallNumber.
CallInterface->EndAutoListenCall(TRUE);
}
}
}
if (SCall->ReceiveEvent)
{
AssociationMutex.Request() ;
SCallDict.Delete(ULongToPtr(SCall->CallId));
AssociationMutex.Clear() ;
}
LogEvent(SU_SCALL, EV_DELETE, SCall, SCall->pAsync, SCall->Flags, 1);
SCall->pAsync = (PRPC_ASYNC_STATE) -1;
if (SCall->SContext)
{
SCall->SContext->RemoveReference();
}
if (SCall->ClientPrincipalName != NULL)
{
delete SCall->ClientPrincipalName;
SCall->ClientPrincipalName = NULL;
}
SCall->DeactivateCall();
if (SCall == CachedSCall)
{
CachedSCallAvailable = 0;
}
else
{
AssociationMutex.Request() ;
SCall->pAsync = (PRPC_ASYNC_STATE) -1;
if (FreeSCallQueue.PutOnQueue(SCall, 0))
delete SCall ;
AssociationMutex.Clear() ;
}
}
int
LRPC_SASSOCIATION::MaybeQueueSCall (
IN LRPC_SCALL *SCall
)
/*++
Routine Description:
if the thread is currently executing a call, the call
is queued up, otherwise it is signalled to be dispatched.
Arguments:
SCall - the SCall to be dispatched.
Return Value:
0: dispatch the call
1: don't dispatch the call
-1: error
--*/
{
LRPC_SCALL *FirstSCall ;
int Status ;
AssociationMutex.Request() ;
FirstSCall = ClientThreadDict.Find(
MsgClientIdToClientId(SCall->LrpcRequestMessage->Rpc.LpcHeader.ClientId).UniqueThread) ;
if (FirstSCall == 0)
{
Status = ClientThreadDict.Insert(
MsgClientIdToClientId(SCall->LrpcRequestMessage->Rpc.LpcHeader.ClientId).UniqueThread,
SCall) ;
SCall->LastSCall = SCall ;
AssociationMutex.Clear() ;
VALIDATE(Status)
{
0,
-1
} END_VALIDATE;
return Status ;
}
ASSERT(FirstSCall->LastSCall);
FirstSCall->LastSCall->NextSCall = SCall ;
FirstSCall->LastSCall = SCall ;
AssociationMutex.Clear() ;
return 1 ;
}
LRPC_SCALL *
LRPC_SASSOCIATION::GetNextSCall (
IN LRPC_SCALL *SCall
)
/*++
Routine Description:
description
Arguments:
SCall - description
Return Value:
--*/
{
LRPC_SCALL *NextSCall ;
ASSERT(SCall) ;
AssociationMutex.Request() ;
NextSCall = SCall->NextSCall ;
if (NextSCall != 0)
{
ASSERT(SCall->LastSCall);
NextSCall->LastSCall = SCall->LastSCall ;
ClientThreadDict.Update (
MsgClientIdToClientId(SCall->LrpcRequestMessage->Rpc.LpcHeader.ClientId).UniqueThread,
NextSCall) ;
}
else
{
ClientThreadDict.Delete (
MsgClientIdToClientId(SCall->LrpcRequestMessage->Rpc.LpcHeader.ClientId).UniqueThread) ;
}
AssociationMutex.Clear() ;
return NextSCall ;
}
void
LRPC_SASSOCIATION::Delete(
)
/*++
Routine Description:
description
Arguments:
arg1 - description
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
LRPC_SCALL *SCall ;
DictionaryCursor cursor;
if (InterlockedIncrement(&Deleted) == 0)
{
AssociationMutex.Request() ;
SCallDict.Reset(cursor) ;
while ((SCall = SCallDict.Next(cursor)) != 0)
{
SCall->Deleted = 1;
if (SCall->ReceiveEvent)
{
SCall->ReceiveEvent->Raise();
}
}
AssociationMutex.Clear() ;
LogEvent(SU_SASSOC, EV_DELETE,
this, 0, AssociationReferenceCount, 1, 1);
Address->DereferenceAssociation(this);
}
}
RPC_STATUS
LRPC_SASSOCIATION::BindBack (
IN RPC_CHAR *Endpoint,
IN DWORD AssocKey,
IN BYTE *Cookie
)
/*++
Routine Description:
Create a back connection to the client.
Arguments:
LrpcThread - LrpcThread to connect to.
pAssoc - Pointer to client association.
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
NTSTATUS NtStatus;
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
RPC_CHAR * LpcPortName ;
UNICODE_STRING unicodePortName;
LRPC_BIND_EXCHANGE BindExchange;
unsigned long BindExchangeLength = sizeof(LRPC_BIND_EXCHANGE);
LpcPortName = new RPC_CHAR[RpcpStringLength(Endpoint)
+ RpcpStringLength(LRPC_DIRECTORY_NAME) + 1];
if (LpcPortName == 0)
{
#if DBG
PrintToDebugger("LRPC: Out of memory in DealWithNewClient\n") ;
#endif
return RPC_S_OUT_OF_MEMORY ;
}
RpcpMemoryCopy(LpcPortName,
LRPC_DIRECTORY_NAME,
RpcpStringLength(LRPC_DIRECTORY_NAME) * sizeof(RPC_CHAR));
RpcpMemoryCopy(LpcPortName + RpcpStringLength(LRPC_DIRECTORY_NAME),
Endpoint,
(RpcpStringLength(Endpoint) + 1) * sizeof(RPC_CHAR));
RtlInitUnicodeString(&unicodePortName, LpcPortName);
// QOS values used for the bind-back.
SecurityQualityOfService.EffectiveOnly = TRUE;
SecurityQualityOfService.ContextTrackingMode = SECURITY_STATIC_TRACKING;
// This prevents the client from impersonating us.
SecurityQualityOfService.ImpersonationLevel = SecurityAnonymous;
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
BindExchange.ConnectType = LRPC_CONNECT_RESPONSE ;
BindExchange.AssocKey = AssocKey ;
RpcpMemoryCopy(BindExchange.Cookie, Cookie, sizeof(BindExchange.Cookie));
NtStatus = NtConnectPort(&LpcReplyPort,
&unicodePortName,
&SecurityQualityOfService,
0,
0,
0,
&BindExchange,
&BindExchangeLength);
delete [] LpcPortName ;
if (!NT_SUCCESS(NtStatus))
{
#if DBG
PrintToDebugger("LRPC: NtConnectPort : %lx\n", NtStatus);
#endif // DBG
return RPC_S_OUT_OF_MEMORY ;
}
return RPC_S_OK ;
}
LRPC_MESSAGE *
LRPC_SASSOCIATION::DealWithBindBackMessage (
IN LRPC_MESSAGE *BindBackMessage
)
/*++
Routine Description:
Used in conjuction with Async RPC. This function
creates a back connection to the client so that two asynchronous
flow of data can occur.
Arguments:
BindBackMessage - The message receive from the client
Return Value:
reply message.
--*/
{
RPC_STATUS Status ;
BindBackMessage->BindBack.szPortName[PORT_NAME_LEN-1] = NULL;
Status = BindBack((RPC_CHAR *) BindBackMessage->BindBack.szPortName,
BindBackMessage->BindBack.AssocKey,
BindBackMessage->BindBack.Cookie);
BindBackMessage->Ack.MessageType = LRPC_MSG_ACK ;
BindBackMessage->Ack.RpcStatus = Status ;
BindBackMessage->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_BIND_MESSAGE) - sizeof(PORT_MESSAGE);
BindBackMessage->LpcHeader.u1.s1.TotalLength =
sizeof(LRPC_BIND_MESSAGE);
if (Status != RPC_S_OK)
{
Delete() ;
}
return BindBackMessage ;
}
RPC_STATUS
LRPC_SASSOCIATION::AddBinding (
IN OUT LRPC_BIND_EXCHANGE * BindExchange
)
/*++
Routine Description:
We will attempt to add a new binding to this association.
Arguments:
BindExchange - Supplies a description of the interface to which the
client wish to bind.
Return Value:
--*/
{
RPC_STATUS Status;
RPC_SYNTAX_IDENTIFIER TransferSyntax;
RPC_INTERFACE * RpcInterface;
LRPC_SBINDING * Binding;
BOOL fIgnored;
int DictKey;
RPC_SYNTAX_IDENTIFIER ProposedSyntaxes[MaximumNumberOfTransferSyntaxes];
int PresentationContexts[MaximumNumberOfTransferSyntaxes];
int TransferSyntaxFlagSettings[MaximumNumberOfTransferSyntaxes];
int NextProposedSyntax;
int ChosenProposedTransferSyntax;
int ChosenAvailableTransferSyntax;
long OldLockValue;
NextProposedSyntax = 0;
if (BindExchange->TransferSyntaxSet & TS_NDR20_FLAG)
{
RpcpMemoryCopy(&ProposedSyntaxes[NextProposedSyntax],
NDR20TransferSyntax, sizeof(RPC_SYNTAX_IDENTIFIER));
PresentationContexts[NextProposedSyntax] = (int)BindExchange->PresentationContext[0];
TransferSyntaxFlagSettings[NextProposedSyntax] = TS_NDR20_FLAG;
NextProposedSyntax ++;
}
if (BindExchange->TransferSyntaxSet & TS_NDR64_FLAG)
{
RpcpMemoryCopy(&ProposedSyntaxes[NextProposedSyntax],
NDR64TransferSyntax, sizeof(RPC_SYNTAX_IDENTIFIER));
PresentationContexts[NextProposedSyntax] = (int)BindExchange->PresentationContext[1];
TransferSyntaxFlagSettings[NextProposedSyntax] = TS_NDR64_FLAG;
NextProposedSyntax ++;
}
if (BindExchange->TransferSyntaxSet & TS_NDRTEST_FLAG)
{
RpcpMemoryCopy(&ProposedSyntaxes[NextProposedSyntax],
NDRTestTransferSyntax, sizeof(RPC_SYNTAX_IDENTIFIER));
PresentationContexts[NextProposedSyntax] = (int)BindExchange->PresentationContext[2];
TransferSyntaxFlagSettings[NextProposedSyntax] = TS_NDRTEST_FLAG;
NextProposedSyntax ++;
}
if (NextProposedSyntax == 0)
{
// no syntaxes proposed - protocol error
CORRUPTION_ASSERT(0);
return RPC_S_PROTOCOL_ERROR;
}
CORRUPTION_ASSERT(NextProposedSyntax <= MaximumNumberOfTransferSyntaxes);
Status = Address->FindInterfaceTransfer(&(BindExchange->InterfaceId),
ProposedSyntaxes,
NextProposedSyntax,
&TransferSyntax,
&RpcInterface,
&fIgnored,
&ChosenProposedTransferSyntax,
&ChosenAvailableTransferSyntax);
if (Status != RPC_S_OK)
{
return(Status);
}
ASSERT (ChosenProposedTransferSyntax < NextProposedSyntax);
Binding = new LRPC_SBINDING(RpcInterface,
ChosenAvailableTransferSyntax);
if (Binding == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
Binding->SetPresentationContext(PresentationContexts[ChosenProposedTransferSyntax]);
// busy wait until the readers and other writers in the bindings collection go away
while (BindingsCollectionLock.CompareExchange(-1, 0) != 0)
{
Sleep (2);
}
DictKey = (unsigned char) Bindings.Insert(Binding);
// release the lock
BindingsCollectionLock.Exchange (0);
if (DictKey == -1)
{
delete Binding;
return(RPC_S_OUT_OF_MEMORY);
}
BindExchange->TransferSyntaxSet = TransferSyntaxFlagSettings[ChosenProposedTransferSyntax];
return(RPC_S_OK);
}
RPC_STATUS
LRPC_SASSOCIATION::SaveToken (
IN LRPC_MESSAGE *LrpcMessage,
OUT HANDLE *pTokenHandle,
IN BOOL fRestoreToken
)
/*++
Routine Description:
Impersonate the client and save away the token.
Arguments:
LrpcMessage - request message
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
NTSTATUS NtStatus ;
HANDLE ImpersonationToken = 0;
RPC_STATUS Status;
if (fRestoreToken)
{
//
// Save away the old token
//
if (OpenThreadToken (GetCurrentThread(),
TOKEN_IMPERSONATE | TOKEN_QUERY,
TRUE,
&ImpersonationToken) == FALSE)
{
ImpersonationToken = 0;
#if DBG
if (GetLastError() != ERROR_NO_TOKEN)
{
PrintToDebugger("LRPC: First OpenThreadToken failed %d\n", GetLastError());
}
#endif
}
}
NtStatus = NtImpersonateClientOfPort(LpcServerPort,
(PORT_MESSAGE *) LrpcMessage);
if (NT_ERROR(NtStatus))
{
#if DBG
PrintToDebugger("LRPC: NtImpersonateClientOfPort failed: 0x%lX\n",
NtStatus) ;
#endif
if (ImpersonationToken)
{
CloseHandle(ImpersonationToken);
}
return RPC_S_INVALID_AUTH_IDENTITY ;
}
Status = RPC_S_OK;
if (OpenThreadToken (GetCurrentThread(),
TOKEN_IMPERSONATE | TOKEN_QUERY,
TRUE,
pTokenHandle) == FALSE)
{
*pTokenHandle = 0;
Status = GetLastError();
if (Status != ERROR_CANT_OPEN_ANONYMOUS)
{
#if DBG
PrintToDebugger("LRPC: Second OpenThreadToken failed : %lx\n", Status) ;
#endif
switch(Status)
{
case ERROR_NO_TOKEN:
Status = RPC_S_ACCESS_DENIED;
break;
case ERROR_NOT_ENOUGH_MEMORY:
case ERROR_NOT_ENOUGH_QUOTA:
case ERROR_NO_SYSTEM_RESOURCES:
Status = RPC_S_OUT_OF_MEMORY;
break;
default:
ASSERT(0);
}
}
}
if (fRestoreToken)
{
//
// Restore the token
//
NtStatus = NtSetInformationThread(NtCurrentThread(),
ThreadImpersonationToken,
&ImpersonationToken,
sizeof(HANDLE));
#if DBG
if (!NT_SUCCESS(NtStatus))
{
PrintToDebugger("RPC : NtSetInformationThread : %lx\n", NtStatus);
}
#endif // DBG
if (ImpersonationToken)
{
CloseHandle(ImpersonationToken);
}
}
return Status;
}
RPC_STATUS
LRPC_SASSOCIATION::GetClientName (
IN LRPC_SCALL *SCall,
IN OUT ULONG *ClientPrincipalNameBufferLength OPTIONAL, // in bytes
OUT RPC_CHAR **ClientPrincipalName
)
/*++
Routine Description:
Gets the client name for the given scall
Arguments:
SCall - the SCall for which to get the client name
ClientPrincipalNameBufferLength - if present, *ClientPrincipalName must
point to a caller supplied buffer, which if big enough,
will be filled with the client principal name. If not present,
*ClientPrincipalName must be NULL.
ClientPrincipalName - see ClientPrincipalNameBufferLength
Return Value:
RPC_S_OK for success, or RPC_S_* / Win32 error code for error.
--*/
{
RPC_STATUS Status = RPC_S_OK;
BOOL Result;
unsigned long Size;
HANDLE TokenHandle = 0;
LRPC_SCONTEXT *SContext = 0;
TOKEN_STATISTICS TokenStatisticsInformation;
DictionaryCursor cursor;
BOOL fAnonymous;
BOOL fMutexHeld = FALSE;
BOOL fAssociationSContextUsed = FALSE;
RPC_CHAR *CurrentUserName;
ULONG CurrentUserNameLength;
if (SCall->SContext == NULL)
{
// take the lock opportunistically
AssociationMutex.Request();
fMutexHeld = TRUE;
if (SCall->SContext == NULL)
{
Status = SaveToken(
SCall->LrpcRequestMessage,
&TokenHandle, 1);
if ((Status != RPC_S_OK) && (Status != ERROR_CANT_OPEN_ANONYMOUS))
{
goto Cleanup;
}
if (Status == RPC_S_OK)
{
Result = GetTokenInformation(
TokenHandle,
TokenStatistics,
&TokenStatisticsInformation,
sizeof(TokenStatisticsInformation),
&Size
);
if (Result != TRUE)
{
Status = RPC_S_INVALID_AUTH_IDENTITY;
goto Cleanup;
}
fAnonymous = FALSE;
}
else
{
ASSERT(Status == ERROR_CANT_OPEN_ANONYMOUS);
fAnonymous = TRUE;
TokenHandle = 0;
}
SContextDict.Reset(cursor);
while ((SContext = SContextDict.Next(cursor)) != 0)
{
// if either input and found are anonymous, or the modified
// ids match, we have found it
if ((fAnonymous && SContext->GetAnonymousFlag())
||
FastCompareLUIDAligned(&SContext->ClientLuid,
&TokenStatisticsInformation.ModifiedId))
{
break;
}
}
if (SContext == 0)
{
SContext = new LRPC_SCONTEXT(NULL,
fAnonymous ? NULL : ((LUID *) &TokenStatisticsInformation.ModifiedId),
this,
FALSE, // fDefaultLogonId
fAnonymous
);
if (SContext == 0)
{
Status = RPC_S_OUT_OF_MEMORY;
goto Cleanup;
}
if (SContextDict.Insert(SContext) == -1)
{
delete SContext;
Status = RPC_S_OUT_OF_MEMORY;
goto Cleanup;
}
// mark the context as server side only
SContext->SetServerSideOnlyFlag();
// record that we have used this recently to prevent it from being
// garbage collected
SContext->UpdateLastAccessTime();
EnableIdleLrpcSContextsCleanup();
// tell the garbage collector that we have something to be
// collected
GarbageCollectionNeeded(FALSE, // fOneTimeCleanup
LRPC_SCONTEXT::CONTEXT_IDLE_TIMEOUT);
}
else
{
// record that we have used this recently to prevent it from being
// garbage collected
SContext->UpdateLastAccessTime();
}
// we have taken or created the current SContext in the association
// we need to prevent the garbage collection thread from destroying
// it underneath us. We add one refcount for the purpose and record
// this
SContext->AddReference();
fAssociationSContextUsed = TRUE;
}
else
{
SContext = SCall->SContext;
// record that we have used this recently to prevent it from being
// garbage collected
SContext->UpdateLastAccessTime();
}
AssociationMutex.Clear() ;
fMutexHeld = FALSE;
}
else
{
SContext = SCall->SContext;
// record that we have used this recently to prevent it from being
// garbage collected
SContext->UpdateLastAccessTime();
}
ASSERT(SContext);
// if we go through the path where the token is retrieved from
// the SContext, passing NULL TokenHandle to get user name is Ok
// as it will retrieve the token from the SContext
Status = SContext->GetUserName(ClientPrincipalNameBufferLength, ClientPrincipalName, TokenHandle);
// If ARGUMENT_PRESENT(ClientPrincipalNameBufferLength), Status may be
// ERROR_MORE_DATA, which is a success error code.
if (fAssociationSContextUsed)
{
if ((Status == RPC_S_OK)
&& (!ARGUMENT_PRESENT(ClientPrincipalNameBufferLength)))
{
// we weren't supplied a user buffer. Copy the principal
// name to a call variable to avoid the garbage collector
// collecting this under the feet of our caller. Then
// we can release the refcount
if (SCall->ClientPrincipalName == NULL)
{
CurrentUserNameLength = (RpcpStringLength(*ClientPrincipalName) + 1) * sizeof(RPC_CHAR);
// CurrentUserNameLength is in bytes. Allocate chars for it and cast it back
CurrentUserName = (RPC_CHAR *) new char [CurrentUserNameLength];
if (CurrentUserName != NULL)
{
RpcpMemoryCopy(CurrentUserName,
*ClientPrincipalName,
CurrentUserNameLength);
SCall->ClientPrincipalName = CurrentUserName;
*ClientPrincipalName = CurrentUserName;
}
else
{
Status = RPC_S_OUT_OF_MEMORY;
// fall through in cleanup path
}
}
else
{
*ClientPrincipalName = SCall->ClientPrincipalName;
}
}
// succeeded or not, drop the refcount
SContext->RemoveReference();
}
if (Status != RPC_S_OK)
{
// N.B. failure of this function doesn't mean we have
// to delete a newly created scontext. scontexts without
// names are perfectly valid, and since we know the only
// missing part from this scontext is the name, we can
// leave it alone, return failure, and attempt to get the
// name next time
goto Cleanup;
}
Cleanup:
if (fMutexHeld)
{
AssociationMutex.Clear() ;
}
if (TokenHandle)
{
CloseHandle(TokenHandle);
}
return Status;
}
#if defined(_WIN64)
C_ASSERT((FIELD_OFFSET(TOKEN_STATISTICS, ModifiedId) % 8) == 0);
C_ASSERT((FIELD_OFFSET(LRPC_SCONTEXT, ClientLuid) % 8) == 0);
#endif
void
LRPC_SASSOCIATION::DealWithBindMessage (
IN LRPC_MESSAGE * LrpcMessage
)
/*++
Routine Description:
LRPC_ADDRESS::ReceiveLotsaCalls will call this routine when the client
sends a bind message. We need to process the bind message, and send
a response to the client.
Arguments:
LrpcMessage - Supplies the bind message. We will also use this to send
the response.
Return Value:
The reply message to be sent to the client will be returned.
--*/
{
RPC_STATUS Status = RPC_S_OK;
NTSTATUS NtStatus ;
HANDLE ImpersonationToken = 0;
HANDLE TokenHandle;
unsigned long Size;
BOOL Result;
LRPC_SCONTEXT *SContext;
ULONG SecurityContextId = -1;
DictionaryCursor cursor;
BOOL fBindDefaultLogonId;
BOOL fAnonymous;
ULONG TotalMessageLength;
TotalMessageLength = LrpcMessage->Bind.LpcHeader.u1.s1.TotalLength;
if (TotalMessageLength < sizeof(LRPC_BIND_MESSAGE))
{
RpcpMemorySet((char *)LrpcMessage + TotalMessageLength,
0,
sizeof(LRPC_BIND_MESSAGE) - TotalMessageLength
);
}
if (LrpcMessage->Bind.BindExchange.Flags & NEW_SECURITY_CONTEXT_FLAG)
{
TOKEN_STATISTICS TokenStatisticsInformation;
//
// If SaveToken succeeds, as a side-effect, it will
// fill in the SecurityContextId field of the BindExchange
//
Status = SaveToken(
LrpcMessage,
&TokenHandle) ;
if ((Status != RPC_S_OK) && (Status != ERROR_CANT_OPEN_ANONYMOUS))
{
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLDealWithBindMessage10);
goto Cleanup;
}
else if (TokenHandle || (Status == ERROR_CANT_OPEN_ANONYMOUS))
{
if (TokenHandle)
{
Result = GetTokenInformation(
TokenHandle,
TokenStatistics,
&TokenStatisticsInformation,
sizeof(TokenStatisticsInformation),
&Size
);
if (Result != TRUE)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_INVALID_AUTH_IDENTITY,
EEInfoDLDealWithBindMessage20,
GetLastError());
CloseHandle(TokenHandle);
Status = RPC_S_INVALID_AUTH_IDENTITY;
goto Cleanup;
}
fAnonymous = FALSE;
}
else
{
fAnonymous = TRUE;
Status = RPC_S_OK;
}
AssociationMutex.Request();
unsigned int Key = 0;
fBindDefaultLogonId =
(LrpcMessage->Bind.BindExchange.Flags & DEFAULT_LOGONID_FLAG)
? TRUE : FALSE;
SContextDict.Reset(cursor);
while ((SContext = SContextDict.NextWithKey(cursor, &Key)) != 0)
{
if ((fAnonymous && SContext->GetAnonymousFlag())
||
(FastCompareLUIDAligned(&SContext->ClientLuid,
&TokenStatisticsInformation.ModifiedId)
&&
(SContext->GetDefaultLogonIdFlag() == fBindDefaultLogonId)))
{
SecurityContextId = Key;
SContext->ClearServerSideOnlyFlag();
break;
}
}
if (SContext == 0)
{
if (fAnonymous)
{
SContext = new LRPC_SCONTEXT(TokenHandle,
(LUID *) NULL,
this,
0,
fAnonymous);
}
else
{
SContext = new LRPC_SCONTEXT(TokenHandle,
(LUID *) &TokenStatisticsInformation.ModifiedId,
this,
fBindDefaultLogonId,
0 // fAnonymousToken
);
}
if (SContext == 0)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_OUT_OF_MEMORY,
EEInfoDLDealWithBindMessage30,
sizeof(LRPC_SCONTEXT));
CloseHandle(TokenHandle);
Status = RPC_S_OUT_OF_MEMORY;
AssociationMutex.Clear();
goto Cleanup;
}
if ((SecurityContextId = SContextDict.Insert(SContext)) == -1)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_OUT_OF_MEMORY,
EEInfoDLDealWithBindMessage40);
delete SContext;
Status = RPC_S_OUT_OF_MEMORY;
AssociationMutex.Clear();
goto Cleanup;
}
}
else if (SContext->hToken == NULL)
{
// if the context had no token, add one. This can happen
// if previous callers for this modified id just queried
// the user name. In this case, we won't cache the token
SContext->hToken = TokenHandle;
}
else
{
CloseHandle(TokenHandle);
}
AssociationMutex.Clear();
}
else
{
ASSERT(0);
}
Cleanup:
//
// Revert
//
NtStatus = NtSetInformationThread(NtCurrentThread(),
ThreadImpersonationToken,
&ImpersonationToken,
sizeof(HANDLE));
#if DBG
if (!NT_SUCCESS(NtStatus))
{
PrintToDebugger("RPC : NtSetInformationThread : %lx\n", NtStatus);
}
#endif // DBG
}
if (Status == RPC_S_OK
&& LrpcMessage->Bind.BindExchange.Flags & NEW_PRESENTATION_CONTEXT_FLAG)
{
Status = AddBinding(&(LrpcMessage->Bind.BindExchange));
if (Status != RPC_S_OK)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLDealWithBindMessage50);
}
}
LrpcMessage->Bind.BindExchange.RpcStatus = Status ;
if (LrpcMessage->Bind.OldSecurityContexts.NumContexts > 0)
{
DWORD i;
LRPC_SCONTEXT *SContext;
DWORD NumContexts = LrpcMessage->Bind.OldSecurityContexts.NumContexts;
DWORD CalculatedSize = ((NumContexts-1) * sizeof(DWORD))+sizeof(LRPC_BIND_MESSAGE);
if (NumContexts > MAX_LRPC_CONTEXTS
|| CalculatedSize > (DWORD) LrpcMessage->LpcHeader.u1.s1.TotalLength)
{
//
// Bogus request
//
LrpcMessage->Bind.BindExchange.RpcStatus = RPC_S_PROTOCOL_ERROR;
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_PROTOCOL_ERROR,
EEInfoDLDealWithBindMessage60,
NumContexts,
CalculatedSize,
(DWORD) LrpcMessage->LpcHeader.u1.s1.TotalLength);
goto Reply;
}
AssociationMutex.Request();
for (i = 0; i < NumContexts; i++)
{
SContext = SContextDict.Delete(
LrpcMessage->Bind.OldSecurityContexts.SecurityContextId[i]);
if (SContext)
{
SContext->Destroy();
}
else
{
CORRUPTION_ASSERT(0);
}
}
AssociationMutex.Clear();
}
Reply:
// if failure, check out of EEInfo
if ((LrpcMessage->Bind.BindExchange.RpcStatus != RPC_S_OK) && (g_fSendEEInfo))
{
SetBindAckFault(LrpcMessage,
LrpcMessage->Bind.BindExchange.RpcStatus);
}
LrpcMessage->Bind.MessageType = LRPC_BIND_ACK;
//
// We will never send back a bind-ack with success for a new security context
// binding exhange.
//
// If we could not insert the SContext into SContextDict, SecurityContextId is -1.
// This is fine since we will be sending back a bind-ack with failure.
//
ASSERT(!(LrpcMessage->Bind.BindExchange.Flags & NEW_SECURITY_CONTEXT_FLAG) ||
SecurityContextId != -1 ||
LrpcMessage->Bind.BindExchange.RpcStatus != RPC_S_OK);
LrpcMessage->Bind.BindExchange.SecurityContextId = SecurityContextId;
if (!(LrpcMessage->Bind.BindExchange.Flags & EXTENDED_ERROR_INFO_PRESENT))
{
LrpcMessage->LpcHeader.u1.s1.DataLength = sizeof(LRPC_BIND_MESSAGE)
- sizeof(PORT_MESSAGE);
}
ReplyMessage(LrpcMessage);
}
RPC_STATUS LRPC_SASSOCIATION::CreateThread(void)
{
RPC_STATUS status;
status = Address->BeginLongCall();
if (status != RPC_S_OK)
{
Address->EndLongCall();
}
return status;
}
void LRPC_SASSOCIATION::RundownNotificationCompleted(void)
{
Address->EndLongCall();
}
RPC_STATUS
LRPC_SBINDING::CheckSecurity (
SCALL * Context
)
{
LRPC_SCONTEXT *SContext;
// This is verified by the caller.
ASSERT(RpcInterface->IsSecurityCallbackReqd());
ASSERT(Context->InvalidHandle(LRPC_SCALL_TYPE) == 0);
SContext = ((LRPC_SCALL *)Context)->SContext;
// Because we use FastCopyLUIDAligned to copy into LRPC_SBINDING::ClientLuid
// it has to be aligned. It is enough to be aligned in the structure.
C_ASSERT(FIELD_OFFSET(LRPC_SBINDING, ClientLuid) % RPCRT_DEFAULT_STRUCT_ALIGNMENT == 0);
//
// We may be able to skip a security callback.
//
// We skip the callback iff:
// - the binding has executed a security callback for the interface and
// the SequenceNumber and the SContext->ClientLuid have not changed since.
//
// Thus, the only way to skip a security callback for a given binding is to have
// executed it previously.
// We will always execute security callbacks if no RPC security is used. This
// is necessary so that the user code may have a chance to impersonate and
// do their own checks since each call may be under a different identity.
//
// We will only compare initialized ClientLuid since it will be compared iff
// SequenceNumber has been initialized and the SequenceNumber is initialized after
// ClientLuid is.
//
if ( SContext
&& RpcInterface->SequenceNumber == SequenceNumber
&& FastCompareLUID(&(SContext->ClientLuid), &ClientLuid) )
{
return (RPC_S_OK);
}
RPC_STATUS Status = RpcInterface->CheckSecurityIfNecessary(Context);
NukeStaleEEInfoIfNecessary(Status);
Context->RevertToSelf();
if (Status == RPC_S_OK)
{
if (SContext)
{
FastCopyLUIDAligned(&ClientLuid, &(SContext->ClientLuid));
SequenceNumber = RpcInterface->SequenceNumber;
}
return (RPC_S_OK);
}
else
{
SequenceNumber = 0;
RpcpErrorAddRecord(EEInfoGCApplication,
RPC_S_ACCESS_DENIED,
EEInfoDLCheckSecurity10,
Status);
return (RPC_S_ACCESS_DENIED);
}
}
void
LRPC_SCALL::DealWithRequestMessage (
)
/*++
Routine Description:
We will process the original request message in this routine, dispatch
the remote procedure call to the stub, and then send the response
message.
Arguments:
RpcMessage - Contains the request buffer
Return Value:
none
--*/
{
RPC_STATUS Status, ExceptionCode;
int Flags = LrpcRequestMessage->Rpc.RpcHeader.Flags ;
LRPC_SBINDING *LrpcBinding ;
THREAD *ThisThread;
DebugThreadInfo *ThreadDebugCell;
DebugCallInfo *CallDebugCell;
ULONG TickCount;
PRPC_DISPATCH_TABLE DispatchTableToUse;
RuntimeInfo.Length = sizeof(RPC_RUNTIME_INFO) ;
ClientId = MsgClientIdToClientId(LrpcRequestMessage->LpcHeader.ClientId);
MessageId = LrpcRequestMessage->LpcHeader.MessageId;
CallbackId = LrpcRequestMessage->LpcHeader.CallbackId;
LrpcBinding = LookupBinding(
LrpcRequestMessage->Rpc.RpcHeader.PresentContext);
if (LrpcBinding == 0)
{
COPYMSG(LrpcReplyMessage, LrpcRequestMessage) ;
FreeBuffer(&RpcMessage);
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_UNKNOWN_IF,
EEInfoDLDealWithRequestMessage10,
LrpcRequestMessage->Rpc.RpcHeader.PresentContext);
SetFaultPacket(LrpcReplyMessage, RPC_S_UNKNOWN_IF, Flags, NULL);
return;
}
SBinding = LrpcBinding;
LrpcBinding->GetSelectedTransferSyntaxAndDispatchTable(&RpcMessage.TransferSyntax,
&DispatchTableToUse);
RpcMessage.ProcNum = LrpcRequestMessage->Rpc.RpcHeader.ProcedureNumber;
RpcMessage.Handle = this;
RpcMessage.ReservedForRuntime = &RuntimeInfo ;
// NDR_DREP_ASCII | NDR_DREP_LITTLE_ENDIAN | NDR_DREP_IEEE
RpcMessage.DataRepresentation = 0x00 | 0x10 | 0x0000;
if ((LrpcRequestMessage->Rpc.RpcHeader.Flags & LRPC_OBJECT_UUID))
{
ObjectUuidFlag = 1;
RpcpMemoryCopy(&ObjectUuid,
&(LrpcRequestMessage->Rpc.RpcHeader.ObjectUuid), sizeof(UUID));
}
ThisThread = RpcpGetThreadPointer();
ASSERT(ThisThread);
RpcpSetThreadContextWithThread(ThisThread, this);
ThreadDebugCell = ThisThread->DebugCell;
//
// Check IF Level Security
//
if (LrpcBinding->RpcInterface->IsSecurityCallbackReqd() != 0)
{
Status = LrpcBinding->CheckSecurity(this);
if (Status != RPC_S_OK)
{
COPYMSG(LrpcReplyMessage, LrpcRequestMessage) ;
FreeBuffer(&RpcMessage);
// the error record (if any) was already added
// by CheckSecurity
SetFaultPacket(LrpcReplyMessage,
RPC_S_ACCESS_DENIED,
Flags,
NULL) ;
RpcpSetThreadContextWithThread(ThisThread, 0) ;
return;
}
}
if (ThreadDebugCell)
{
TickCount = NtGetTickCount();
ThreadDebugCell->Status = dtsDispatched;
ThreadDebugCell->LastUpdateTime = TickCount;
CallDebugCell = DebugCell;
CallDebugCell->InterfaceUUIDStart = LrpcBinding->RpcInterface->GetInterfaceFirstDWORD();
CallDebugCell->CallID = CallId;
CallDebugCell->LastUpdateTime = TickCount;
// shoehorn the PID and TID into shorts - most of the time
// it doesn't actually truncate important information
CallDebugCell->PID = (USHORT)ClientId.UniqueProcess;
CallDebugCell->TID = (USHORT)ClientId.UniqueThread;
CallDebugCell->ProcNum = (unsigned short)RpcMessage.ProcNum;
CallDebugCell->Status = csDispatched;
GetDebugCellIDFromDebugCell((DebugCellUnion *)ThreadDebugCell,
&ThisThread->DebugCellTag, &CallDebugCell->ServicingTID);
if (LrpcBinding->RpcInterface->IsPipeInterface())
CallDebugCell->CallFlags |= DBGCELL_PIPE_CALL;
}
//
// The rest of the response headers are set in ::GetBuffer.
// We have to set MessageType before dispatch since as soon as we dispatch an async
// call, AbortAsyncCall may get called on another thread, and will use LrpcReplyMessage
// to send back a fault.
//
LrpcReplyMessage->Rpc.RpcHeader.MessageType = LRPC_MSG_RESPONSE;
if (ObjectUuidFlag != 0)
{
Status = LrpcBinding->RpcInterface->DispatchToStubWithObject(
&RpcMessage,
&ObjectUuid,
0,
DispatchTableToUse,
&ExceptionCode);
}
else
{
Status = LrpcBinding->RpcInterface->DispatchToStub(
&RpcMessage,
0,
DispatchTableToUse,
&ExceptionCode);
}
RpcpSetThreadContextWithThread(ThisThread, 0);
LRPC_SCALL::RevertToSelf();
if (ThreadDebugCell)
{
ThreadDebugCell->Status = dtsProcessing;
ThreadDebugCell->LastUpdateTime = NtGetTickCount();
}
if (Status != RPC_S_OK)
{
if (Status == RPC_P_EXCEPTION_OCCURED)
{
SetFaultPacket(LrpcReplyMessage,
LrpcMapRpcStatus(ExceptionCode),
Flags,
this) ;
}
else
{
VALIDATE(Status)
{
RPC_S_PROCNUM_OUT_OF_RANGE,
RPC_S_UNKNOWN_IF,
RPC_S_NOT_LISTENING,
RPC_S_SERVER_TOO_BUSY,
RPC_S_UNSUPPORTED_TYPE
} END_VALIDATE;
if (Status == RPC_S_NOT_LISTENING)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLDealWithRequestMessage20);
Status = RPC_S_SERVER_TOO_BUSY;
}
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLDealWithRequestMessage30);
SetFaultPacket(LrpcReplyMessage,
LrpcMapRpcStatus(Status),
Flags,
this);
}
if (IsSyncCall())
{
INITMSG(LrpcReplyMessage,
ClientId,
CallbackId,
MessageId) ;
}
else
{
if (Flags & LRPC_NON_PIPE)
{
INITMSG(LrpcReplyMessage,
ClientId,
CallbackId,
MessageId) ;
Association->ReplyMessage(LrpcReplyMessage);
}
else
{
if ((LrpcReplyMessage->Rpc.RpcHeader.MessageType != LRPC_MSG_FAULT2)
|| (!IsClientAsync()))
{
SendDGReply(LrpcReplyMessage);
}
}
RemoveReference();
}
}
}
void
LRPC_SCALL::SendReply (
)
{
RPC_STATUS Status;
BOOL Shutup ;
LRPC_SASSOCIATION *LocalAssociation;
if (IsSyncCall())
{
if (IsClientAsync())
{
if (LrpcReplyMessage->Fault.RpcHeader.MessageType == LRPC_MSG_FAULT)
{
// Guard against attackers or broken clients that may induce a reply
// before the reply port has been allocated.
CORRUPTION_ASSERT(Association->LpcReplyPort);
if (Association->LpcReplyPort)
{
SendDGReply(LrpcReplyMessage);
}
else
{
Status = RPC_S_PROTOCOL_ERROR;
Association->Delete();
}
}
else
{
RpcMessage.RpcFlags = 0;
Status = SendRequest(&RpcMessage, &Shutup) ;
if (Status != RPC_S_OK)
{
#if DBG
PrintToDebugger("RPC: SendRequest failed: %d\n", Status);
#endif
if (Status != RPC_S_CALL_FAILED_DNE){
Association->Delete();
}
}
}
}
else
{
INITMSG(LrpcReplyMessage,
ClientId,
CallbackId,
MessageId) ;
Association->ReplyMessage(LrpcReplyMessage);
}
FreeMessage(LrpcRequestMessage) ;
LocalAssociation = Association;
Association->FreeSCall(this) ;
// don't touch the this pointer after FreeSCall - it may be freed
LocalAssociation->Address->DereferenceAssociation(LocalAssociation);
}
else
{
if ((LrpcReplyMessage->Rpc.RpcHeader.MessageType != LRPC_MSG_FAULT2)
|| (!IsClientAsync()))
{
RemoveReference();
}
else
{
BOOL Shutup;
RpcMessage.RpcFlags = 0;
Status = SendRequest(&RpcMessage, &Shutup) ;
if (Status != RPC_S_OK)
{
#if DBG
PrintToDebugger("RPC: SendRequest failed: %d\n", Status);
#endif
if (Status != RPC_S_CALL_FAILED_DNE){
Association->Delete();
}
}
}
}
}
LRPC_MESSAGE *
LRPC_SASSOCIATION::DealWithCopyMessage (
IN LRPC_COPY_MESSAGE * LrpcMessage
)
/*++
Routine Description:
We will process a copy message in this routine; this means that we need
to copy a buffer of data from the server into the client's address
space.
Arguments:
LrpcMessage - Supplies the copy message which was received from
the client.
Return Value:
The reply message to be sent to the client will be returned.
--*/
{
NTSTATUS NtStatus;
SIZE_T NumberOfBytesWritten;
PVOID Buffer;
ASSERT(LrpcMessage->IsPartial == 0);
AssociationMutex.Request() ;
// We need this only to prevent an attack
// Also, the pointer is to a server address. It is ok to just cast it
// to the server's pointer type and it won't hurt anything in the case
// of 32/64 bit LRPC.
Buffer = Buffers.DeleteItemByBruteForce(MsgPtrToPtr(LrpcMessage->Server.Buffer));
AssociationMutex.Clear() ;
if (LrpcMessage->RpcStatus == RPC_S_OK)
{
if (Buffer == 0)
{
LrpcMessage->RpcStatus = RPC_S_PROTOCOL_ERROR;
}
else
{
NtStatus = NtWriteRequestData(LpcServerPort,
(PORT_MESSAGE *) LrpcMessage,
0,
(PVOID) Buffer,
LrpcMessage->Server.Length,
&NumberOfBytesWritten);
if (NT_ERROR(NtStatus))
{
LrpcMessage->RpcStatus = RPC_S_OUT_OF_MEMORY;
}
else
{
ASSERT(LrpcMessage->Server.Length == NumberOfBytesWritten);
LrpcMessage->RpcStatus = RPC_S_OK;
}
}
}
LrpcMessage->LpcHeader.u1.s1.DataLength = sizeof(LRPC_COPY_MESSAGE)
- sizeof(PORT_MESSAGE);
LrpcMessage->LpcHeader.u1.s1.TotalLength = sizeof(LRPC_COPY_MESSAGE);
if (Buffer != 0)
{
RpcpFarFree(Buffer);
}
return((LRPC_MESSAGE *) LrpcMessage);
}
LRPC_MESSAGE *
LRPC_SASSOCIATION::DealWithPartialRequest (
IN LRPC_MESSAGE **LrpcMessage
)
/*++
Routine Description:
Deal with more data on a dispatched call. This
only happens when you have pipes. Pipe data on
async calls is handled differently from sync calls.
Arguments:
LrpcMessage - the LRPC message. For pipe data, we always
take the slow path (ie: NtReadRequestData).
Return Value:
NULL: if the request was processed.
not NULL: if there was a problem. the return value contains the
reply message.
--*/
{
LRPC_SCALL *SCall ;
RPC_STATUS Status ;
AssociationMutex.Request() ;
SCall = SCallDict.Find(ULongToPtr((*LrpcMessage)->Rpc.RpcHeader.CallId));
AssociationMutex.Clear() ;
// we have to wait until the server either calls
// Receive or calls Register. If it Calls Receive,
// we know that it is synchronous. If it calls
// Register, we know that it is async.
if (SCall)
{
Status = SCall->ProcessResponse(LrpcMessage) ;
}
else
{
#if DBG
PrintToDebugger("LRPC: No call corresponding the the pipe request\n");
#endif
Status = RPC_S_OUT_OF_MEMORY ;
}
if (Status != RPC_S_OK)
{
SetFaultPacket(*LrpcMessage,
Status,
LRPC_SYNC_CLIENT,
NULL) ;
return *LrpcMessage ;
}
return NULL ;
}
void
LRPC_SASSOCIATION::CleanupIdleSContexts (
void
)
/*++
Routine Description:
Walks the list of SContexts, finds the ones
that are idle and server side only, and cleans
them up.
Arguments:
Return Value:
--*/
{
LRPC_SCONTEXT *SContext;
DictionaryCursor cursor;
SContextDict.Reset(cursor);
AssociationMutex.Request();
while ((SContext = SContextDict.Next(cursor)) != 0)
{
if (SContext->GetServerSideOnlyFlag())
{
if (SContext->IsIdle())
{
SContext = (LRPC_SCONTEXT *)SContextDict.DeleteItemByBruteForce(SContext);
ASSERT(SContext);
SContext->Destroy();
}
}
}
AssociationMutex.Clear();
}
RPC_STATUS
LRPC_SCALL::SetupCall(
)
/*++
Routine Description:
Helper function that does the setup needed to use the
call in conjuction with Pipes or Async RPC.
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RPC_STATUS Status = RPC_S_OK ;
//
// Stuff from ActivateCall
//
RcvBufferLength = 0;
CallId = LrpcRequestMessage->Rpc.RpcHeader.CallId ;
ReceiveComplete = 0;
AsyncReply = 0;
CachedAPCInfoAvailable = 1;
Choked = 0;
fSendMoreExpected = FALSE;
AsyncStatus = RPC_S_OK ;
NeededLength = 0;
NotificationIssued = -1;
if (ReceiveEvent == 0)
{
ReceiveEvent = new EVENT(&Status, 0);
if (ReceiveEvent == 0 || Status)
{
delete ReceiveEvent;
ReceiveEvent = 0;
return RPC_S_OUT_OF_MEMORY ;
}
CallMutex = new MUTEX(&Status) ;
if (CallMutex == 0 || Status)
{
Association->SCallDict.Delete(ULongToPtr(CallId));
goto Cleanup;
}
}
else
{
ReceiveEvent->Lower();
}
Association->AssociationMutex.Request() ;
if (Association->SCallDict.Insert(ULongToPtr(CallId), this) == -1)
{
Association->AssociationMutex.Clear() ;
goto Cleanup;
}
Association->AssociationMutex.Clear() ;
LrpcReplyMessage->Rpc.RpcHeader.CallId = CallId ;
return (RPC_S_OK) ;
Cleanup:
delete CallMutex ;
delete ReceiveEvent;
CallMutex = 0;
ReceiveEvent = 0;
return RPC_S_OUT_OF_MEMORY ;
}
RPC_STATUS
LRPC_SCALL::NegotiateTransferSyntax (
IN OUT PRPC_MESSAGE Message
)
{
// this can happen in the callback case only.
// Just return the already negotiated transfer syntax
PRPC_DISPATCH_TABLE Ignored;
SBinding->GetSelectedTransferSyntaxAndDispatchTable(&Message->TransferSyntax,
&Ignored);
return RPC_S_OK;
}
RPC_STATUS
LRPC_SCALL::GetBuffer (
IN OUT PRPC_MESSAGE Message,
IN UUID *
)
/*++
Routine Description:
We will allocate a buffer which will be used to either send a request
or receive a response.
Arguments:
Message - Supplies the length of the buffer that is needed. The buffer
will be returned.
Return Value:
RPC_S_OK - A buffer has been successfully allocated. It will be of at
least the required length.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate that
large a buffer.
--*/
{
int BufferKey ;
ASSERT(LrpcReplyMessage != 0) ;
if (PARTIAL(Message))
{
CurrentBufferLength =
(Message->BufferLength < MINIMUM_PARTIAL_BUFFLEN)
? MINIMUM_PARTIAL_BUFFLEN:Message->BufferLength ;
Message->Buffer = RpcpFarAllocate(CurrentBufferLength) ;
if (Message->Buffer == 0)
{
CurrentBufferLength = 0;
return (RPC_S_OUT_OF_MEMORY) ;
}
}
else if (Message->BufferLength <= MAXIMUM_MESSAGE_BUFFER)
{
ASSERT(((ULONG_PTR) LrpcReplyMessage->Rpc.Buffer) % 8 == 0);
// uncomment this to check for 16 byte alignment on 64 bits
// ASSERT(IsBufferAligned(LrpcReplyMessage->Rpc.Buffer));
Message->Buffer = LrpcReplyMessage->Rpc.Buffer;
LrpcReplyMessage->LpcHeader.u2.ZeroInit = 0;
LrpcReplyMessage->Rpc.RpcHeader.Flags = LRPC_BUFFER_IMMEDIATE;
LrpcReplyMessage->LpcHeader.u1.s1.DataLength = (USHORT)
(Align4(Message->BufferLength) + sizeof(LRPC_RPC_HEADER));
return (RPC_S_OK) ;
}
else
{
Message->Buffer = RpcpFarAllocate(Message->BufferLength);
if (Message->Buffer == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
}
LrpcReplyMessage->Rpc.RpcHeader.Flags = LRPC_BUFFER_SERVER;
LrpcReplyMessage->LpcHeader.u2.ZeroInit = 0;
if (PARTIAL(Message) || IsClientAsync())
{
LrpcReplyMessage->Rpc.Request.CountDataEntries = 1;
LrpcReplyMessage->LpcHeader.MessageId = 0;
LrpcReplyMessage->LpcHeader.CallbackId = 0;
LrpcReplyMessage->LpcHeader.u2.s2.DataInfoOffset =
sizeof(PORT_MESSAGE) + sizeof(LRPC_RPC_HEADER);
LrpcReplyMessage->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_RPC_HEADER) + sizeof(PORT_DATA_INFORMATION);
LrpcReplyMessage->Rpc.Request.DataEntries[0].Base = PtrToMsgPtr(Message->Buffer);
LrpcReplyMessage->Rpc.Request.DataEntries[0].Size = Message->BufferLength;
}
else
{
Association->AssociationMutex.Request() ;
BufferKey = Association->Buffers.Insert((LRPC_CLIENT_BUFFER *) Message->Buffer) ;
Association->AssociationMutex.Clear() ;
if (BufferKey == -1)
{
RpcpFarFree(Message->Buffer) ;
return RPC_S_OUT_OF_MEMORY ;
}
LrpcReplyMessage->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_RPC_HEADER) + sizeof(LRPC_SERVER_BUFFER) ;
ASSERT(Message->BufferLength < 0x80000000);
LrpcReplyMessage->Rpc.Server.Length = Message->BufferLength ;
LrpcReplyMessage->Rpc.Server.Buffer = PtrToMsgPtr(Message->Buffer) ;
}
return(RPC_S_OK);
}
void
LRPC_SCALL::FreeBuffer (
IN PRPC_MESSAGE Message
)
/*++
Routine Description:
We will free the supplied buffer.
Arguments:
Message - Supplies the buffer to be freed.
--*/
{
ASSERT(LrpcReplyMessage != NULL) ;
if (!(Message->Buffer == LrpcRequestMessage->Rpc.Buffer
|| Message->Buffer == LrpcReplyMessage->Rpc.Buffer))
{
if (!PARTIAL(Message) && !IsClientAsync())
{
Association->AssociationMutex.Request() ;
Association->Buffers.DeleteItemByBruteForce((LRPC_CLIENT_BUFFER *) Message->Buffer);
Association->AssociationMutex.Clear() ;
}
RpcpFarFree(Message->Buffer);
}
}
void
LRPC_SCALL::FreePipeBuffer (
IN PRPC_MESSAGE Message
)
/*++
Routine Description:
description
Arguments:
arg1 - description
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RpcpFarFree(Message->Buffer) ;
}
RPC_STATUS
LRPC_SCALL::ReallocPipeBuffer (
IN PRPC_MESSAGE Message,
IN unsigned int NewSize
)
/*++
Routine Description:
description
Arguments:
arg1 - description
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
int BufferKey;
PVOID Buffer ;
void *NewBuffer ;
BOOL BufferChanged = FALSE ;
if (NewSize > CurrentBufferLength)
{
NewBuffer = RpcpFarAllocate(NewSize) ;
if (NewBuffer == 0)
{
RpcpFarFree(Message->Buffer) ;
return (RPC_S_OUT_OF_MEMORY) ;
}
if (CurrentBufferLength > 0)
{
RpcpMemoryCopy(NewBuffer, Message->Buffer, Message->BufferLength) ;
FreePipeBuffer(Message) ;
}
Message->Buffer = NewBuffer ;
CurrentBufferLength = NewSize ;
BufferChanged = TRUE ;
}
Message->BufferLength = NewSize ;
LrpcReplyMessage->Rpc.RpcHeader.Flags = LRPC_BUFFER_SERVER;
ASSERT(Message->BufferLength < 0x80000000);
LrpcReplyMessage->Rpc.Request.DataEntries[0].Base = PtrToMsgPtr(Message->Buffer);
LrpcReplyMessage->Rpc.Request.DataEntries[0].Size = Message->BufferLength;
return (RPC_S_OK) ;
}
RPC_STATUS
LRPC_SCALL::AbortAsyncCall (
IN PRPC_ASYNC_STATE pAsync,
IN unsigned long ExceptionCode
)
{
NTSTATUS NtStatus;
RPC_STATUS Status = RPC_S_OK;
NukeStaleEEInfoIfNecessary(ExceptionCode);
RpcpErrorAddRecord(EEInfoGCApplication,
ExceptionCode,
EEInfoDLAbortCall,
SBinding->GetInterfaceFirstDWORD(),
(short)RpcMessage.ProcNum,
RpcMessage.RpcFlags);
SetFaultPacket(LrpcReplyMessage, ExceptionCode, Flags, this);
if (IsClientAsync())
{
if (LrpcReplyMessage->Rpc.RpcHeader.MessageType != LRPC_MSG_FAULT2)
{
NtStatus = SendDGReply(LrpcReplyMessage) ;
}
else
{
BOOL Ignored;
RpcMessage.RpcFlags = 0;
RpcMessage.Buffer = NULL;
Status = SendRequest(&RpcMessage,
&Ignored // shutup parameter - it is not relevant for us
);
if (Status != RPC_S_OK)
{
#if DBG
PrintToDebugger("RPC: SendRequest failed: %d\n", Status);
#endif
if (Status != RPC_S_CALL_FAILED_DNE){
Association->Delete();
}
}
}
// This is the only path that does not call an API returning NtStatus.
// We need to initialize it.
NtStatus = ERROR_SUCCESS;
}
else
{
INITMSG(LrpcReplyMessage,
ClientId,
CallbackId,
MessageId);
NtStatus = Association->ReplyMessage(LrpcReplyMessage);
}
if (NT_ERROR(NtStatus))
{
Status = RPC_S_CALL_FAILED ;
}
RemoveReference();
return Status ;
}
RPC_STATUS
LRPC_SCALL::Receive (
IN PRPC_MESSAGE Message,
IN unsigned int Size
)
/*++
Routine Description:
Receive routine used by pipes
Arguments:
Message - contains to buffer to receive in
pSize - pointer to a size value that contains the minimum amount of
data that needs to be received.
Return Value:
RPC_S_OK - We have successfully converted the message.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to do the
conversion.
--*/
{
int RequestedSize;
unsigned long Extra = IsExtraMessage(Message) ;
Message->DataRepresentation = 0x00 | 0x10 | 0x0000;
if (!Extra && Message->Buffer)
{
ASSERT(LrpcRequestMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_REQUEST);
RpcpFarFree(Message->Buffer);
Message->Buffer = 0;
Message->BufferLength = 0;
}
// A broken or malicious client may make us think that he is doing a regular sync call
// and then induce a pipe send or an async send. We should break on a checked build unless
// corruption injection is enabled. On free builds we should return a failure.
CORRUPTION_ASSERT(ReceiveEvent);
if (ReceiveEvent == 0)
{
// Also, keep in mind that GetCoalescedBuffer may be expected to allocate Message->Buffer.
Message->Buffer = 0;
Message->BufferLength = 0;
((PRPC_RUNTIME_INFO) Message->ReservedForRuntime)->OldBuffer = 0;
return RPC_S_PROTOCOL_ERROR;
}
//
// It is ok for us to find out that the buffer is complete
// before SavedBuffer is set,
// we need to take the CallMutex in GetCoalescedBuffer
//
while (!BufferComplete && (!PARTIAL(Message) || RcvBufferLength < Size))
{
if (ReceiveEvent->Wait() == WAIT_FAILED)
{
return RPC_S_CALL_FAILED;
}
if (AsyncStatus != RPC_S_OK)
{
return AsyncStatus;
}
}
return GetCoalescedBuffer(Message, Extra) ;
}
RPC_STATUS
LRPC_SCALL::Send (
IN OUT PRPC_MESSAGE Message
)
{
BOOL Shutup ;
Message->RpcFlags |= RPC_BUFFER_PARTIAL;
return SendRequest(Message, &Shutup) ;
}
RPC_STATUS
LRPC_SCALL::SendRequest (
IN OUT PRPC_MESSAGE Message,
OUT BOOL *Shutup
)
/*++
Routine Description:
Sends a message on the LpcReplyPort (back connection).
The message is sent either using RequestWaitReply or just Request.
If we wait for a reply, it can either be LRPC_MSG_ACK or
LRPC_MSG_FAULT. In the case of a fault, the error code sent
with the fault is returned to the caller of this function.
Arguments:
arg1 - description
Return Value:
RPC_S_OK - Function succeeded
RPC_S_* - Low memory, a misbehaving client or an invalid LpcReplyPort have
prevented us from either sending the message or receiving the response.
Its also possible that a client has sent us a fault packet.
NOTE: Currently RPC_S_CALL_FAILED_DNE will be returned by this method
only in the event that we are sending a message to a client which no
longer exists (for instance, if it the call has been canceled). In this
case the client will send back a fault with RPC_S_CALL_FAILED_DNE. It
is not necessary to delete the association in this case.
--*/
{
RPC_STATUS Status;
NTSTATUS NtStatus ;
int RemainingLength = 0;
LRPC_MESSAGE ReplyMessage ;
*Shutup = 0;
if (PARTIAL(Message))
{
if (Message->BufferLength < MINIMUM_PARTIAL_BUFFLEN)
{
return (RPC_S_SEND_INCOMPLETE) ;
}
if (NOT_MULTIPLE_OF_EIGHT(Message->BufferLength))
{
RemainingLength = Message->BufferLength & LOW_BITS ;
Message->BufferLength &= ~LOW_BITS ;
}
LrpcReplyMessage->Rpc.RpcHeader.Flags |= LRPC_BUFFER_PARTIAL ;
}
if (FirstSend)
{
// this code will get executed only in
// the non async case
FirstSend = 0;
if (ReceiveEvent == 0)
{
Status = SetupCall() ;
if (Status != RPC_S_OK)
{
if (PARTIAL(Message)
&& LrpcReplyMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_SERVER)
{
RpcpFarFree(Message->Buffer);
}
return Status ;
}
}
}
if (LrpcReplyMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_SERVER)
{
ASSERT((Message->Buffer == NULL)
|| (PtrToMsgPtr(Message->Buffer) == LrpcReplyMessage->Rpc.Request.DataEntries[0].Base));
LrpcReplyMessage->LpcHeader.u1.s1.TotalLength =
LrpcReplyMessage->LpcHeader.u1.s1.DataLength
+ sizeof(PORT_MESSAGE);
if (LrpcReplyMessage->Rpc.RpcHeader.Flags & LRPC_EEINFO_PRESENT)
{
LrpcReplyMessage->Rpc.RpcHeader.MessageType = LRPC_MSG_FAULT2;
// for FAULT2, the length has already been set
}
else
{
LrpcReplyMessage->Rpc.RpcHeader.MessageType = LRPC_MSG_RESPONSE;
LrpcReplyMessage->Rpc.Request.DataEntries[0].Size =
Message->BufferLength ;
}
LrpcReplyMessage->Rpc.RpcHeader.CallId = CallId;
LrpcReplyMessage->Rpc.RpcHeader.Pad = 0;
// Make sure we can't be impersonated on the transport level.
// A legitimate Lrpc client will never do this.
LrpcReplyMessage->LpcHeader.u2.s2.Type |= LPC_NO_IMPERSONATE;
NtStatus = NtRequestWaitReplyPort(Association->LpcReplyPort,
(PORT_MESSAGE *) LrpcReplyMessage,
(PORT_MESSAGE *) &ReplyMessage) ;
if (NT_ERROR(NtStatus))
{
if (Message->Buffer)
{
RpcpFarFree(Message->Buffer);
}
return RPC_S_CALL_FAILED ;
}
else
{
ASSERT((ReplyMessage.Rpc.RpcHeader.MessageType == LRPC_MSG_ACK)
||
(ReplyMessage.Rpc.RpcHeader.MessageType == LRPC_MSG_FAULT));
if (!PARTIAL(Message) &&
(LrpcReplyMessage->Rpc.RpcHeader.MessageType != LRPC_MSG_FAULT2) &&
(LrpcReplyMessage->Rpc.RpcHeader.MessageType != LRPC_MSG_FAULT))
{
if (Message->Buffer)
{
RpcpFarFree(Message->Buffer);
}
}
if (ReplyMessage.Rpc.RpcHeader.MessageType == LRPC_MSG_ACK)
{
*Shutup = ReplyMessage.Ack.Shutup;
}
else
{
Status = ReplyMessage.Fault.RpcStatus;
return Status;
}
}
}
else
{
ASSERT(!PARTIAL(Message)) ;
LrpcReplyMessage->Rpc.RpcHeader.MessageType = LRPC_MSG_RESPONSE;
// If we are dealing with a broken or an attacking client
// LpcReplyPort may not have been allocated.
CORRUPTION_ASSERT(Association->LpcReplyPort);
if (Association->LpcReplyPort)
{
NtStatus = SendDGReply(LrpcReplyMessage);
if (NT_ERROR(NtStatus))
{
return RPC_S_CALL_FAILED;
}
}
else
{
return RPC_S_PROTOCOL_ERROR;
}
}
if (RemainingLength)
{
ASSERT(PARTIAL(Message)) ;
RpcpMemoryMove(Message->Buffer,
(char *) Message->Buffer + Message->BufferLength,
RemainingLength) ;
Message->BufferLength = RemainingLength ;
return (RPC_S_SEND_INCOMPLETE) ;
}
return RPC_S_OK ;
}
inline RPC_STATUS
LRPC_SCALL::GetBufferDo(
IN OUT PRPC_MESSAGE Message,
IN unsigned long NewSize,
IN BOOL fDataValid
)
/*++
Routine Description:
description
Arguments:
arg1 - description
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
void *NewBuffer ;
if (NewSize < CurrentBufferLength)
{
Message->BufferLength = NewSize ;
}
else
{
NewBuffer = RpcpFarAllocate(NewSize) ;
if (NewBuffer == 0)
{
RpcpFarFree(Message->Buffer) ;
Message->BufferLength = 0;
return RPC_S_OUT_OF_MEMORY ;
}
if (fDataValid && Message->BufferLength > 0)
{
RpcpMemoryCopy(NewBuffer,
Message->Buffer,
Message->BufferLength) ;
}
if (EXTRA(Message))
{
ASSERT(Message->ReservedForRuntime) ;
((PRPC_RUNTIME_INFO)Message->ReservedForRuntime)->OldBuffer =
NewBuffer;
}
RpcpFarFree(Message->Buffer) ;
Message->Buffer = NewBuffer ;
Message->BufferLength = NewSize ;
}
return RPC_S_OK ;
}
RPC_STATUS
LRPC_SCALL::SendReceive (
IN OUT PRPC_MESSAGE Message
)
/*++
Routine Description:
Arguments:
Message - Supplies the request and returns the response of a remote
procedure call.
Return Value:
RPC_S_OK - The remote procedure call completed successful.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to perform the
remote procedure call.
RPC_S_OUT_OF_RESOURCES - Insufficient resources are available to complete
the remote procedure call.
--*/
{
NTSTATUS NtStatus;
RPC_STATUS ExceptionCode, Status;
LRPC_MESSAGE *LrpcSavedMessage;
SIZE_T NumberOfBytesRead;
RPC_MESSAGE RpcMessage ;
RPC_RUNTIME_INFO RuntimeInfo ;
PRPC_DISPATCH_TABLE DispatchTableToUse;
// The LrpcMessage must be saved, it is in use by the stub. The current
// LrpcReplyMessage can be used for the callback request message and reply.
//
// We must:
// Save the current LrpcRequestMessage
// Make the current LrpcReplyMessage the LrpcRequestMessage
// Allocate a new LrpcReplyMessage.
LrpcSavedMessage = LrpcRequestMessage;
LrpcRequestMessage = LrpcReplyMessage;
LrpcReplyMessage = 0; // Only needed if we receive a recursive request.
Association->Address->Server->OutgoingCallback();
// NDR_DREP_ASCII | NDR_DREP_LITTLE_ENDIAN | NDR_DREP_IEEE
Message->DataRepresentation = 0x00 | 0x10 | 0x0000;
LrpcRequestMessage->LpcHeader.u1.s1.TotalLength = sizeof(PORT_MESSAGE)
+ LrpcRequestMessage->LpcHeader.u1.s1.DataLength;
LrpcRequestMessage->LpcHeader.u2.s2.Type = LPC_REQUEST;
INITMSG(LrpcRequestMessage, ClientId, CallbackId, MessageId);
LrpcRequestMessage->Rpc.RpcHeader.MessageType = LRPC_MSG_CALLBACK;
LrpcRequestMessage->Rpc.RpcHeader.ProcedureNumber = (unsigned short) Message->ProcNum;
LrpcRequestMessage->Rpc.RpcHeader.PresentContext =
SBinding->GetOnTheWirePresentationContext();
LrpcRequestMessage->Rpc.RpcHeader.Pad = 0;
// Make sure we can't be impersonated on the transport level.
// A legitimate Lrpc client will never do this.
LrpcRequestMessage->LpcHeader.u2.s2.Type |= LPC_NO_IMPERSONATE;
NtStatus = NtRequestWaitReplyPort(Association->LpcServerPort,
(PORT_MESSAGE *) LrpcRequestMessage,
(PORT_MESSAGE *) LrpcRequestMessage);
if (NT_ERROR(NtStatus))
{
LrpcReplyMessage = LrpcRequestMessage;
LrpcRequestMessage = LrpcSavedMessage;
if (NtStatus == STATUS_NO_MEMORY)
{
return(RPC_S_OUT_OF_MEMORY);
}
if (NtStatus == STATUS_INSUFFICIENT_RESOURCES)
{
return(RPC_S_OUT_OF_RESOURCES);
}
#if DBG
if ((NtStatus != STATUS_INVALID_PORT_HANDLE)
&& (NtStatus != STATUS_INVALID_HANDLE)
&& (NtStatus != STATUS_INVALID_CID)
&& (NtStatus != STATUS_PORT_DISCONNECTED)
&& (NtStatus != STATUS_LPC_REPLY_LOST))
{
PrintToDebugger("RPC : NtRequestWaitReplyPort : %lx\n",
NtStatus);
ASSERT(0) ;
}
#endif // DBG
return(RPC_S_CALL_FAILED);
}
for (;;)
{
if (LrpcRequestMessage->Rpc.RpcHeader.MessageType
== LRPC_MSG_FAULT)
{
Status = LrpcRequestMessage->Fault.RpcStatus;
break;
}
if (LrpcRequestMessage->Rpc.RpcHeader.MessageType
== LRPC_MSG_RESPONSE)
{
if (LrpcRequestMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_REQUEST)
{
LrpcRequestMessage->LpcHeader.ClientId = LrpcSavedMessage->Rpc.LpcHeader.ClientId;
LrpcRequestMessage->LpcHeader.CallbackId = LrpcRequestMessage->Rpc.LpcHeader.CallbackId + 1;
LrpcSavedMessage->LpcHeader.MessageId = LrpcSavedMessage->Rpc.LpcHeader.MessageId;
}
Status = LrpcMessageToRpcMessage(LrpcRequestMessage, Message);
break;
}
if (LrpcRequestMessage->Rpc.RpcHeader.MessageType
== LRPC_MSG_PUSH)
{
ASSERT(PushedResponse == 0);
PushedResponse = RpcpFarAllocate(
(unsigned int)
LrpcRequestMessage->Push.Response.DataEntries[0].Size);
if (PushedResponse == 0)
{
LrpcRequestMessage->Push.RpcStatus = RPC_S_OUT_OF_MEMORY;
}
else
{
NtStatus = NtReadRequestData(
Association->LpcServerPort,
(PORT_MESSAGE *) LrpcRequestMessage,
0,
PushedResponse,
LrpcRequestMessage->Push.Response.DataEntries[0].Size,
&NumberOfBytesRead);
if (NT_ERROR(NtStatus))
{
RpcpFarFree(PushedResponse);
PushedResponse = 0;
LrpcRequestMessage->Push.RpcStatus = RPC_S_OUT_OF_MEMORY;
}
else
{
ASSERT(LrpcRequestMessage->Push.Response.DataEntries[0].Size
== NumberOfBytesRead);
LrpcRequestMessage->Push.RpcStatus = RPC_S_OK;
}
}
INITMSG(LrpcRequestMessage,
ClientId,
CallbackId,
MessageId) ;
NtStatus = NtReplyWaitReplyPort(Association->LpcServerPort,
(PORT_MESSAGE *) LrpcRequestMessage);
if (PushedResponse)
{
RpcpFarFree(PushedResponse);
PushedResponse = 0;
}
}
else
{
VALIDATE(LrpcRequestMessage->Rpc.RpcHeader.MessageType)
{
LRPC_MSG_REQUEST
} END_VALIDATE;
Status = LrpcMessageToRpcMessage(LrpcRequestMessage,
Message);
if (Status != RPC_S_OK)
{
LrpcRequestMessage->Fault.RpcHeader.MessageType =
LRPC_MSG_FAULT;
LrpcRequestMessage->Fault.RpcStatus = LrpcMapRpcStatus(Status);
LrpcRequestMessage->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_FAULT_MESSAGE) - sizeof(PORT_MESSAGE);
LrpcRequestMessage->LpcHeader.u1.s1.TotalLength =
sizeof(LRPC_FAULT_MESSAGE);
INITMSG(LrpcRequestMessage,
ClientId,
CallbackId,
MessageId) ;
NtStatus = NtReplyWaitReplyPort(Association->LpcServerPort,
(PORT_MESSAGE *) LrpcRequestMessage);
}
else
{
LrpcReplyMessage = new LRPC_MESSAGE;
if (LrpcReplyMessage != 0)
{
SBinding->GetSelectedTransferSyntaxAndDispatchTable(&Message->TransferSyntax,
&DispatchTableToUse);
Message->ProcNum =
LrpcRequestMessage->Rpc.RpcHeader.ProcedureNumber;
RuntimeInfo.Length = sizeof(RPC_RUNTIME_INFO) ;
RpcMessage = *Message ;
RpcMessage.ReservedForRuntime = &RuntimeInfo ;
if (ObjectUuidFlag != 0)
{
Status = SBinding->RpcInterface->
DispatchToStubWithObject(
&RpcMessage,
&ObjectUuid,
1,
DispatchTableToUse,
&ExceptionCode);
}
else
{
Status = SBinding->RpcInterface->
DispatchToStub(
&RpcMessage,
1,
DispatchTableToUse,
&ExceptionCode);
}
*Message = RpcMessage ;
// Because we must send the reply and recieve the
// reply into the same message, we just copy the
// response into the LrpcRequestMessage
RpcpMemoryCopy(LrpcRequestMessage,
LrpcReplyMessage,
sizeof(LRPC_MESSAGE));
delete LrpcReplyMessage;
LrpcReplyMessage = 0;
}
else
Status = RPC_S_OUT_OF_MEMORY;
if (Status != RPC_S_OK)
{
VALIDATE(Status)
{
RPC_S_OUT_OF_MEMORY,
RPC_P_EXCEPTION_OCCURED,
RPC_S_PROCNUM_OUT_OF_RANGE
} END_VALIDATE;
if (Status == RPC_P_EXCEPTION_OCCURED)
{
Status = LrpcMapRpcStatus(ExceptionCode);
}
LrpcRequestMessage->Fault.RpcStatus = Status;
LrpcRequestMessage->LpcHeader.u1.s1.DataLength =
sizeof(LRPC_FAULT_MESSAGE) - sizeof(PORT_MESSAGE);
LrpcRequestMessage->LpcHeader.u1.s1.TotalLength =
sizeof(LRPC_FAULT_MESSAGE);
LrpcRequestMessage->Fault.RpcHeader.MessageType =
LRPC_MSG_FAULT;
}
else
{
LrpcRequestMessage->LpcHeader.u1.s1.TotalLength =
sizeof(PORT_MESSAGE)
+ LrpcRequestMessage->LpcHeader.u1.s1.DataLength;
LrpcRequestMessage->Rpc.RpcHeader.MessageType =
LRPC_MSG_RESPONSE;
}
INITMSG(LrpcRequestMessage,
ClientId,
CallbackId,
MessageId) ;
NtStatus = NtReplyWaitReplyPort(Association->LpcServerPort,
(PORT_MESSAGE *) LrpcRequestMessage);
}
}
if (NT_ERROR(NtStatus))
{
if (NtStatus == STATUS_NO_MEMORY)
{
Status = RPC_S_OUT_OF_MEMORY;
}
else if (NtStatus == STATUS_INSUFFICIENT_RESOURCES)
{
Status = RPC_S_OUT_OF_RESOURCES;
}
else
{
VALIDATE(NtStatus)
{
STATUS_INVALID_PORT_HANDLE,
STATUS_INVALID_HANDLE,
STATUS_INVALID_CID,
STATUS_PORT_DISCONNECTED,
STATUS_LPC_REPLY_LOST
} END_VALIDATE;
Status = RPC_S_CALL_FAILED;
}
break;
}
}
if (Status == RPC_S_OK)
{
Message->Handle = (RPC_BINDING_HANDLE) this;
}
ASSERT(LrpcReplyMessage == 0);
LrpcReplyMessage = LrpcRequestMessage;
LrpcRequestMessage = LrpcSavedMessage;
return(Status);
}
void
LRPC_SCALL::FreeObject (
)
{
LRPC_SASSOCIATION *MyAssociation;
ASSERT(pAsync) ;
ASSERT(DispatchBuffer) ;
if (DispatchBuffer != LrpcRequestMessage->Rpc.Buffer)
{
RpcpFarFree(DispatchBuffer);
}
FreeMessage(LrpcRequestMessage) ;
MyAssociation = Association;
MyAssociation->FreeSCall(this) ;
MyAssociation->Address->DereferenceAssociation(MyAssociation);
// Warning: The SCALL could have been nuked at this point.
// DO NOT touch the SCALL after this
}
RPC_STATUS
LRPC_SCALL::AsyncSend (
IN OUT PRPC_MESSAGE Message
)
/*++
Routine Description:
Send an async reply. This request can either be partial or complete.
If it is a complete request, we cleanup the SCall.
Arguments:
Message - contains the request
Return Value:
RPC_S_OK - Function succeeded
RPC_S_SEND_INCOMPLETE - some data still needs to be sent.
Message->Buffer pointes to the remaining data, and
Message->BufferLength is the length of the remaining data.
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RPC_STATUS Status = RPC_S_OK;
NTSTATUS NtStatus ;
BOOL fRetVal ;
BOOL Shutup ;
ASSERT(ReceiveEvent) ;
if (AsyncStatus != RPC_S_OK)
{
if (PARTIAL(Message))
{
Status = AsyncStatus;
}
goto Cleanup;
}
FirstSend = 0;
if (Flags & LRPC_NON_PIPE)
{
LrpcReplyMessage->Rpc.RpcHeader.MessageType = LRPC_MSG_RESPONSE;
ASSERT(!IsClientAsync()) ;
NtStatus = Association->ReplyMessage(LrpcReplyMessage);
if (!NT_SUCCESS(NtStatus))
{
Status = RPC_S_OUT_OF_MEMORY ;
}
}
else
{
// Take the call mutex if this is an async pipe call, we can assume that
// it is async since we are in AsyncSend, we must check if its pipe.
// We need to take the mutex to avoid a race between setting fSendMoreExpected in AsyncSend and
// checking it in the thread pool when servicing a LRPC_SERVER_SEND_MORE (ProcessResponse). Without this
// we could end up not getting a notification for this send
if (PARTIAL(Message))
CallMutex->Request();
Status = SendRequest(Message, &Shutup) ;
}
if (PARTIAL(Message))
{
ASSERT(!(Flags & LRPC_NON_PIPE));
if (Status == RPC_S_OK
|| Status == RPC_S_SEND_INCOMPLETE)
{
if (pAsync->Flags & RPC_C_NOTIFY_ON_SEND_COMPLETE)
{
if (Shutup)
{
fSendMoreExpected = TRUE;
}
else
{
// The client should not become un choked. We could check here that we are not
// transitioning from a Shutup to a non-shutup (choked to unchoked on the client).
// Instead, we will set fSendMoreExpected to false so that we can remove this assumption
// without permiting any attack scenarios.
fSendMoreExpected = FALSE;
if (!IssueNotification(RpcSendComplete))
{
Status = RPC_S_OUT_OF_MEMORY ;
}
}
}
CallMutex->Clear();
return Status;
}
CallMutex->Clear();
}
else
{
//
// Non partial async sends will always succeed
// if they fail, we will hide the error
//
Status = RPC_S_OK;
}
Cleanup:
//
// on the server, the stub never calls FreeBuffer
//
RemoveReference();
return Status;
}
RPC_STATUS
LRPC_SCALL::AsyncReceive (
IN OUT PRPC_MESSAGE Message,
IN unsigned int Size
)
/*++
Routine Description:
On the server, this routine is only called when the stub needs
more data to unmarshall the non pipe parameters, or when it needs
pipe data.
Arguments:
Message - contains information about the request
Size - needed size
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RPC_STATUS Status ;
int Extra = IsExtraMessage(Message);
ASSERT(ReceiveEvent) ;
if (PARTIAL(Message) == 0)
{
return Receive(Message, Size);
}
if (Extra)
{
Status = Receive(Message, Size);
//
// don't need to check the status. If Receive failed, we are
// never going to access dispatch buffer anyway
//
DispatchBuffer = Message->Buffer ;
return Status;
}
CallMutex->Request();
Message->DataRepresentation = 0x00 | 0x10 | 0x0000;
if (BufferComplete == 0
&& RcvBufferLength < Size)
{
if (NOTIFY(Message))
{
NeededLength = Size ;
}
CallMutex->Clear() ;
return RPC_S_ASYNC_CALL_PENDING;
}
else
{
Status = GetCoalescedBuffer(Message, 0);
}
CallMutex->Clear();
return Status ;
}
RPC_STATUS
LRPC_SCALL::SetAsyncHandle (
IN PRPC_ASYNC_STATE pAsync
)
/*++
Routine Description:
Set the async handle corresponding this SCALL. This call is made
by the stubs.
Arguments:
pAsync - The async handle to association with this SCall
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RPC_STATUS Status ;
THREAD *Thread = RpcpGetThreadPointer();
ASSERT(Thread);
ASSERT(pAsync);
Thread->fAsync = TRUE;
if (DebugCell)
{
ASSERT(IsServerSideDebugInfoEnabled());
DebugCell->CallFlags |= DBGCELL_ASYNC_CALL;
}
if (ReceiveEvent == 0)
{
Status = SetupCall();
if (Status != RPC_S_OK)
{
return Status;
}
}
if (LrpcAsyncReplyMessage == 0)
{
LrpcAsyncReplyMessage = AllocateMessage() ;
if (LrpcAsyncReplyMessage == 0)
{
return RPC_S_OUT_OF_MEMORY ;
}
}
LrpcReplyMessage = LrpcAsyncReplyMessage;
// transfer/set the parts that the rest of the code expects to be set
LrpcReplyMessage->Rpc.RpcHeader.CallId = CallId ;
LrpcReplyMessage->Rpc.RpcHeader.MessageType = LRPC_MSG_RESPONSE;
INITMSG(LrpcReplyMessage,
ClientId,
CallbackId,
MessageId) ;
this->pAsync = pAsync;
if (SBinding->RpcInterface->IsAutoListenInterface())
{
SBinding->RpcInterface->BeginAutoListenCall() ;
}
SBinding->RpcInterface->BeginNullManagerCall();
return RPC_S_OK ;
}
RPC_STATUS
LRPC_SCALL::ProcessResponse (
IN LRPC_MESSAGE **LrpcMessage
)
/*++
Routine Description:
A buffer has just arrived, process it. If some other buffer is already
processing buffers, simply queue it and go away. Otherwise, does
the processing ourselves.
Arguments:
Message - Details on the arrived message
--*/
{
RPC_MESSAGE Message ;
RPC_STATUS Status ;
switch ((*LrpcMessage)->Rpc.RpcHeader.MessageType)
{
case LRPC_SERVER_SEND_MORE:
if (pAsync && (pAsync->Flags & RPC_C_NOTIFY_ON_SEND_COMPLETE))
{
// Take the call mutex if this is an async pipe call, we can assume that
// it is async since we are in AsyncSend, we must check if its pipe.
// We need to take the mutex to avoid a race between setting fSendMoreExpected in AsyncSend and
// checking it in the thread pool when servicing a LRPC_SERVER_SEND_MORE (ProcessResponse). Without this
// we could end up not getting a notification for this send
CallMutex->Request();
if (fSendMoreExpected)
{
fSendMoreExpected = FALSE;
CallMutex->Clear();
if (!IssueNotification(RpcSendComplete))
{
AsyncStatus = RPC_S_OUT_OF_MEMORY ;
#if DBG
PrintToDebugger("RPC: IssueNotification failed\n") ;
#endif
//
// We are pretty much hosed here, but we'll try to
// queue notification anyway.
//
IssueNotification() ;
return RPC_S_OUT_OF_MEMORY ;
}
}
else
{
CallMutex->Clear();
CORRUPTION_ASSERT(0);
return RPC_S_PROTOCOL_ERROR;
}
}
return RPC_S_OK ;
case LRPC_MSG_CANCEL:
InterlockedExchange(&CancelPending, 1);
return RPC_S_OK;
default:
break;
}
CallMutex->Request() ;
ASSERT(BufferComplete == 0);
Message.RpcFlags = 0;
Status = LrpcMessageToRpcMessage(
*LrpcMessage,
&Message) ;
if (Status != RPC_S_OK)
{
#if DBG
PrintToDebugger("RPC: LrpcMessageToRpcMessage failed: %x\n", Status) ;
#endif
AsyncStatus = Status ;
IssueNotification() ;
return Status ;
}
if (COMPLETE(&Message))
{
ASSERT(BufferComplete == 0);
BufferComplete = 1;
}
if (Message.BufferLength)
{
RcvBufferLength += Message.BufferLength ;
if (BufferQueue.PutOnQueue(Message.Buffer,
Message.BufferLength))
{
AsyncStatus = Status = RPC_S_OUT_OF_MEMORY ;
#if DBG
PrintToDebugger("RPC: PutOnQueue failed\n") ;
#endif
}
}
else
{
// Zero BufferLength - this is the end of the IN pipe
FreeBuffer(&Message);
}
if (IsSyncCall())
{
CallMutex->Clear() ;
ReceiveEvent->Raise();
}
else
{
if (Status == RPC_S_OK
&& NeededLength > 0
&& RcvBufferLength >= NeededLength)
{
IssueNotification(RpcReceiveComplete);
}
CallMutex->Clear() ;
}
return Status ;
}
RPC_STATUS
LRPC_SCALL::GetCoalescedBuffer (
IN PRPC_MESSAGE Message,
IN BOOL BufferValid
)
/*++
Routine Description:
Remove buffers from the queue and coalesce them into a single buffer.
Arguments:
Message - on return this will contain the coalesced buffer, Message->BufferLength
gives us the length of the coalesced buffer.
BufferValid - Tells us if Message->Buffer is valid on entry.
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
void *NewBuffer, *Buffer ;
char *Current ;
unsigned int bufferlength ;
unsigned int TotalLength ;
LRPC_SENDMORE_MESSAGE SendMore ;
NTSTATUS NtStatus ;
CallMutex->Request() ;
ASSERT(RcvBufferLength);
if (BufferValid)
{
TotalLength = RcvBufferLength + Message->BufferLength ;
}
else
{
TotalLength = RcvBufferLength ;
}
NewBuffer = RpcpFarAllocate(TotalLength) ;
if (NewBuffer == 0)
{
CallMutex->Clear() ;
return RPC_S_OUT_OF_MEMORY;
}
if (BufferValid)
{
RpcpMemoryCopy(NewBuffer, Message->Buffer, Message->BufferLength) ;
Current = (char *) NewBuffer + Message->BufferLength ;
}
else
{
Current = (char *) NewBuffer;
}
while ((Buffer = BufferQueue.TakeOffQueue(&bufferlength)) != 0)
{
RpcpMemoryCopy(Current, Buffer, bufferlength) ;
Current += bufferlength ;
RpcpFarFree(Buffer);
}
if (BufferValid && Message->Buffer)
{
RpcpFarFree(Message->Buffer);
//
// Update the dispatch buffer
//
ASSERT(Message->ReservedForRuntime) ;
((PRPC_RUNTIME_INFO)Message->ReservedForRuntime)->OldBuffer = NewBuffer;
if (Message->Buffer == DispatchBuffer)
DispatchBuffer = NewBuffer;
}
Message->Buffer = NewBuffer ;
Message->BufferLength = TotalLength ;
RcvBufferLength = 0;
if (BufferComplete)
{
Message->RpcFlags |= RPC_BUFFER_COMPLETE ;
}
else
{
if (Choked)
{
CallMutex->Clear() ;
//
// send a message to the client
// to start sending data again
//
SendMore.LpcHeader.u1.s1.DataLength =
sizeof(SendMore) - sizeof(PORT_MESSAGE);
SendMore.LpcHeader.u2.ZeroInit = 0;
SanitizeLpcHeader (&SendMore.LpcHeader);
// zero out everything b/n MessageType and CallId
RPCP_ZERO_OUT_STRUCT_RANGE(LRPC_RPC_HEADER,
&SendMore.RpcHeader,
Pad,
CallId
);
SendMore.RpcHeader.MessageType = LRPC_CLIENT_SEND_MORE;
SendMore.RpcHeader.CallId = CallId ;
NtStatus = SendDGReply((LRPC_MESSAGE *)&SendMore) ;
if (!NT_SUCCESS(NtStatus))
{
return RPC_S_CALL_FAILED ;
}
return RPC_S_OK;
}
}
CallMutex->Clear() ;
return RPC_S_OK ;
}
RPC_STATUS
LRPC_SCALL::ImpersonateClient (
)
/*++
Routine Description:
We will impersonate the client which made the remote procedure call.
--*/
{
NTSTATUS NtStatus;
RPC_STATUS Status;
HANDLE hToken;
DWORD LastError;
Status = SetThreadSecurityContext((SECURITY_CONTEXT *) MAXUINT_PTR);
if (RPC_S_OK != Status)
{
return Status;
}
if (SContext)
{
if (SContext->GetAnonymousFlag())
{
NtStatus = NtImpersonateAnonymousToken(NtCurrentThread());
if (!NT_SUCCESS(NtStatus))
{
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_ACCESS_DENIED,
EEInfoDLLRPC_SCALL__ImpersonateClient10,
(ULONG)NtStatus,
(ULONG)GetCurrentThreadId());
ClearThreadSecurityContext();
return RPC_S_ACCESS_DENIED;
}
}
else if (SetThreadToken(NULL, SContext->hToken) == FALSE)
{
LastError = GetLastError();
ClearThreadSecurityContext();
if (LastError == ERROR_OUTOFMEMORY)
{
return (RPC_S_OUT_OF_MEMORY) ;
}
return RPC_S_ACCESS_DENIED;
}
}
else
{
NtStatus = NtImpersonateClientOfPort(Association->LpcServerPort,
(PORT_MESSAGE *) LrpcRequestMessage);
if ((NtStatus == STATUS_INVALID_CID)
|| (NtStatus == STATUS_PORT_DISCONNECTED)
|| (NtStatus == STATUS_REPLY_MESSAGE_MISMATCH))
{
ClearThreadSecurityContext();
return RPC_S_NO_CONTEXT_AVAILABLE;
}
if (!NT_SUCCESS(NtStatus))
{
#if DBG
PrintToDebugger("RPC : NtImpersonateClientOfPort : %lx\n",NtStatus);
#endif // DBG
return RPC_S_ACCESS_DENIED;
}
}
return(RPC_S_OK);
}
RPC_STATUS
LRPC_SCALL::RevertToSelf (
)
/*++
Routine Description:
This reverts a server thread back to itself after impersonating a client.
We just check to see if the server thread is impersonating; this optimizes
the common case.
--*/
{
HANDLE ImpersonationToken = 0;
NTSTATUS NtStatus;
if (ClearThreadSecurityContext())
{
NtStatus = NtSetInformationThread(
NtCurrentThread(),
ThreadImpersonationToken,
&ImpersonationToken,
sizeof(HANDLE));
#if DBG
if (!NT_SUCCESS(NtStatus))
{
PrintToDebugger("RPC : NtSetInformationThread : %lx\n", NtStatus);
}
#endif // DBG
if (!NT_SUCCESS(NtStatus))
{
if (NtStatus == STATUS_NO_MEMORY)
{
return RPC_S_OUT_OF_MEMORY;
}
return RPC_S_ACCESS_DENIED;
}
}
return(RPC_S_OK);
}
RPC_STATUS
LRPC_SCALL::GetAuthorizationContext (
IN BOOL ImpersonateOnReturn,
IN AUTHZ_RESOURCE_MANAGER_HANDLE AuthzResourceManager,
IN PLARGE_INTEGER pExpirationTime OPTIONAL,
IN LUID Identifier,
IN DWORD Flags,
IN PVOID DynamicGroupArgs OPTIONAL,
OUT PAUTHZ_CLIENT_CONTEXT_HANDLE pAuthzClientContext
)
/*++
Routine Description:
Gets an authorization context for the client that can be used
with Authz functions. The resulting context is owned by the caller
and must be freed by it.
Arguments:
ImpersonateOnReturn - if TRUE, when we return, we should be impersonating.
AuthzResourceManager - the resource manager to use (passed to Authz)
pExpirationTime - the expiration time to use (passed to Authz)
Identifier - the LUID (passed to Authz)
Flags - Flags (passed to Authz)
DynamicGroupArgs - parameter required by Authz (passed to Authz)
pAuthzClientContext - the authorization context, returned on success.
Undefined on failure.
--*/
{
RPC_STATUS Status = RPC_S_OK;
RPC_STATUS RevertStatus;
BOOL fNeedToRevert = FALSE;
HANDLE ImpersonationToken;
BOOL Result;
BOOL fImpersonating = FALSE;
PAUTHZ_CLIENT_CONTEXT_HANDLE pAuthzClientContextPlaceholder;
ASSERT (AuthzResourceManager != NULL);
if (ImpersonateOnReturn
|| (SContext == NULL)
|| (SContext->AuthzClientContext == NULL))
{
Status = LRPC_SCALL::ImpersonateClient();
if (Status != RPC_S_OK)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLLRPC_SCALL__GetAuthorizationContext10,
(ULONG)ImpersonateOnReturn,
(ULONGLONG)SContext);
return Status;
}
fImpersonating = TRUE;
if (!ImpersonateOnReturn)
{
fNeedToRevert = TRUE;
}
}
if (SContext && SContext->AuthzClientContext)
{
Status = DuplicateAuthzContext(SContext->AuthzClientContext,
pExpirationTime,
Identifier,
Flags,
DynamicGroupArgs,
pAuthzClientContext);
}
else
{
// either we don't have an scontext, or its
// AuthzClientContext is not set yet.
// Get the token from the thread
Result = OpenThreadToken(GetCurrentThread(),
TOKEN_QUERY,
TRUE,
&ImpersonationToken);
if (Result)
{
if (SContext)
pAuthzClientContextPlaceholder = &SContext->AuthzClientContext;
else
pAuthzClientContextPlaceholder = NULL;
Status = CreateAndSaveAuthzContextFromToken(pAuthzClientContextPlaceholder,
ImpersonationToken,
AuthzResourceManager,
pExpirationTime,
Identifier,
Flags,
DynamicGroupArgs,
pAuthzClientContext);
CloseHandle(ImpersonationToken);
}
else
{
Status = GetLastError();
if (Status == ERROR_CANT_OPEN_ANONYMOUS)
{
Result = AuthzInitializeContextFromSidFn(
AUTHZ_SKIP_TOKEN_GROUPS,
(PSID)&AnonymousSid,
AuthzResourceManager,
pExpirationTime,
Identifier,
DynamicGroupArgs,
pAuthzClientContext);
if (Result)
{
if (SContext)
{
if (InterlockedCompareExchangePointer((PVOID *)&SContext->AuthzClientContext,
pAuthzClientContext,
NULL) != NULL)
{
// somebody beat us to the punch - free the context we obtained
AuthzFreeContextFn(*pAuthzClientContext);
*pAuthzClientContext = SContext->AuthzClientContext;
}
}
// else
// the authz context is already loaded in pAuthzClientContext
Status = RPC_S_OK;
}
else
{
Status = GetLastError();
RpcpErrorAddRecord(EEInfoGCAuthz,
Status,
EEInfoDLLRPC_SCALL__GetAuthorizationContext30,
GetCurrentThreadId(),
(ULONGLONG)AuthzResourceManager);
}
}
else
{
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLLRPC_SCALL__GetAuthorizationContext20,
GetCurrentThreadId());
}
}
}
// if caller didn't ask us to impersonate and we are,
// or we if he did ask us, but we failed somewhere,
// revert to self
if (fNeedToRevert || (Status && fImpersonating))
{
RevertStatus = LRPC_SCALL::RevertToSelf();
ASSERT(RevertStatus == RPC_S_OK);
}
return Status;
}
RPC_STATUS
LRPC_SCALL::IsClientLocal (
OUT unsigned int * ClientLocalFlag
)
/*++
Routine Description:
A client using LRPC will always be local.
Arguments:
ClientLocalFlag - Returns a flag which will always be set to a non-zero
value indicating that the client is local.
--*/
{
UNUSED(this);
*ClientLocalFlag = 1;
return(RPC_S_OK);
}
void
LRPC_SCALL::IsClientDisconnected (
OUT BOOL *ClientIsDisconnected
)
/*++
Routine Description:
Checks whether the client for the given call has disconnected.
Arguments:
ClientIsDisconnected - on output it will contain non-zero if the client is
disconnected. If the client is still connected, it will contain 0.
Return Value:
Note:
If called on async RPC calls, it is the obligation of the caller to make sure that
the call is not completed while this API is in progress.
--*/
{
*ClientIsDisconnected = Association->IsDeleted();
}
RPC_STATUS
LRPC_SCALL::ConvertToServerBinding (
OUT RPC_BINDING_HANDLE __RPC_FAR * ServerBinding
)
/*++
Routine Description:
If possible, convert this call into a server binding, meaning a
binding handle pointing back to the client.
Arguments:
ServerBinding - Returns the server binding.
Return Value:
RPC_S_OK - The server binding has successfully been created.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate
a new binding handle.
--*/
{
RPC_STATUS Status;
RPC_CHAR UuidString[37];
RPC_CHAR * StringBinding;
if (ObjectUuidFlag != 0)
{
ObjectUuid.ConvertToString(UuidString);
UuidString[36] = '\0';
}
Status = RpcStringBindingComposeW(
(ObjectUuidFlag != 0 ? UuidString : 0),
RPC_STRING_LITERAL("ncalrpc"),
gLocalComputerName,
0,
0,
&StringBinding);
if (Status != RPC_S_OK)
{
return(Status);
}
Status = RpcBindingFromStringBindingW(
StringBinding,
ServerBinding);
RpcStringFreeW(&StringBinding);
return(Status);
}
void
LRPC_SCALL::InquireObjectUuid (
OUT RPC_UUID * ObjectUuid
)
/*++
Routine Description:
This routine copies the object uuid from the call into the supplied
ObjectUuid argument.
Arguments:
ObjectUuid - Returns a copy of the object uuid passed by the client
in the remote procedure call.
--*/
{
if (ObjectUuidFlag == 0)
{
ObjectUuid->SetToNullUuid();
}
else
{
ObjectUuid->CopyUuid(&(this->ObjectUuid));
}
}
RPC_STATUS
LRPC_SCALL::ToStringBinding (
OUT RPC_CHAR ** StringBinding
)
/*++
Routine Description:
We need to convert this call into a string binding. We will ask the
address for a binding handle which we can then convert into a string
binding.
Arguments:
StringBinding - Returns the string binding for this call.
Return Value:
--*/
{
RPC_STATUS Status;
BINDING_HANDLE * BindingHandle
= Association->Address->InquireBinding();
if (BindingHandle == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
Status = BindingHandle->ToStringBinding(StringBinding);
BindingHandle->BindingFree();
return(Status);
}
RPC_STATUS
LRPC_SCALL::GetAssociationContextCollection (
OUT ContextCollection **CtxCollection
)
{
return Association->GetAssociationContextCollection(CtxCollection);
}
inline RPC_STATUS
LRPC_SCALL::LrpcMessageToRpcMessage (
IN LRPC_MESSAGE * LrpcMessage,
IN OUT PRPC_MESSAGE Message
)
/*++
Routine Description:
We will convert from an LRPC_MESSAGE representation of a buffer (and
its length) to an RPC_MESSAGE representation.
Arguments:
RpcMessage - Returns the RPC_MESSAGE representation.
Return Value:
RPC_S_OK - We have successfully converted the message.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to do the
conversion.
--*/
{
NTSTATUS NtStatus;
SIZE_T NumberOfBytesRead;
unsigned char MessageType = LrpcMessage->Rpc.RpcHeader.MessageType;
RPC_STATUS Status = RPC_S_OK ;
LRPC_MESSAGE ReplyMessage ;
if(LrpcMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_IMMEDIATE)
{
Message->Buffer = LrpcMessage->Rpc.Buffer;
if (LrpcMessage->LpcHeader.u1.s1.DataLength
< sizeof(LRPC_RPC_HEADER))
{
CORRUPTION_ASSERT(LrpcMessage->LpcHeader.u1.s1.DataLength
>= sizeof(LRPC_RPC_HEADER));
return (RPC_S_PROTOCOL_ERROR);
}
Message->BufferLength =
(unsigned int) LrpcMessage->LpcHeader.u1.s1.DataLength
- sizeof(LRPC_RPC_HEADER);
Message->RpcFlags |= RPC_BUFFER_COMPLETE ;
}
else if (LrpcMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_REQUEST)
{
Message->BufferLength = LrpcMessage->Rpc.Request.DataEntries[0].Size;
if (LrpcMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_PARTIAL)
{
CallMutex->Request() ;
//
// If the user ever specifies a Size > LRPC_THRESHOLD_SIZE
// our performance will be bad.
//
if (RcvBufferLength >= LRPC_THRESHOLD_SIZE)
{
Choked = 1;
}
CallMutex->Clear() ;
}
else
{
Message->RpcFlags |= RPC_BUFFER_COMPLETE ;
}
if (Message->BufferLength >= 0x80000000)
{
ASSERT( Message->BufferLength < 0x80000000);
Message->Buffer = 0;
Status = RPC_S_PROTOCOL_ERROR;
}
else
{
Message->Buffer = RpcpFarAllocate(Message->BufferLength) ;
if (Message->Buffer == 0)
{
Status = RPC_S_OUT_OF_MEMORY ;
}
}
if (Message->Buffer == 0)
{
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLLrpcMessageToRpcMessage10,
Message->BufferLength);
}
else
{
NtStatus = NtReadRequestData(Association->LpcServerPort,
(PORT_MESSAGE *) LrpcMessage,
0,
Message->Buffer,
Message->BufferLength,
&NumberOfBytesRead) ;
if (NT_ERROR(NtStatus))
{
RpcpFarFree(Message->Buffer) ;
Message->Buffer = 0;
Status = RPC_S_OUT_OF_MEMORY ;
RpcpErrorAddRecord(EEInfoGCRuntime,
Status,
EEInfoDLLrpcMessageToRpcMessage20,
NtStatus);
}
else
{
ASSERT(Message->BufferLength == NumberOfBytesRead) ;
}
}
if (IsClientAsync())
{
SanitizeLpcHeader(&ReplyMessage.Ack.LpcHeader);
COPYMSG((&ReplyMessage), LrpcMessage) ;
ReplyMessage.Ack.MessageType = LRPC_MSG_ACK ;
ReplyMessage.Ack.Pad = 0;
ReplyMessage.Ack.RpcStatus = Status;
ReplyMessage.Ack.Shutup = (short) Choked ;
ReplyMessage.Ack.ValidDataSize = 0;
ReplyMessage.Ack.Flags = 0;
ReplyMessage.LpcHeader.u1.s1.DataLength =
sizeof(LRPC_ACK_MESSAGE) - sizeof(PORT_MESSAGE) ;
NtStatus = Association->ReplyMessage(&ReplyMessage);
if (NT_ERROR(NtStatus))
{
RpcpFarFree(Message->Buffer);
RpcpErrorAddRecord(EEInfoGCRuntime,
RPC_S_OUT_OF_MEMORY,
EEInfoDLLrpcMessageToRpcMessage30,
NtStatus);
return(RPC_S_OUT_OF_MEMORY);
}
}
}
else
{
CORRUPTION_ASSERT((LrpcMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_IMMEDIATE)
|| (LrpcMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_REQUEST));
return (RPC_S_PROTOCOL_ERROR);
}
return(Status);
}
RPC_STATUS
LRPC_SCALL::InquireAuthClient (
OUT RPC_AUTHZ_HANDLE * Privileges,
OUT RPC_CHAR * * ServerPrincipalName, OPTIONAL
OUT unsigned long * AuthenticationLevel,
OUT unsigned long * AuthenticationService,
OUT unsigned long * AuthorizationService,
IN unsigned long Flags
)
/*++
Routine Description:
Each protocol module must define this routine: it is used to obtain
the authentication and authorization information about a client making
the remote procedure call represented by this.
Arguments:
Privileges - Returns a the privileges of the client.
ServerPrincipalName - Returns the server principal name which the client
specified.
AuthenticationLevel - Returns the authentication level requested by
the client.
AuthenticationService - Returns the authentication service requested by
the client.
AuthorizationService - Returns the authorization service requested by
the client.
Return Value:
RPC_S_OK or RPC_S_* / Win32 error
--*/
{
RPC_STATUS Status;
if(ARGUMENT_PRESENT(Privileges))
{
*(RPC_CHAR **)Privileges = NULL;
Status = Association->GetClientName(this,
NULL, // ClientPrincipalNameBufferLength
(RPC_CHAR **) Privileges);
if (Status != RPC_S_OK)
{
return Status;
}
}
if (ARGUMENT_PRESENT(ServerPrincipalName))
{
*ServerPrincipalName = NULL;
}
if(ARGUMENT_PRESENT(AuthenticationLevel))
{
*AuthenticationLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY ;
}
if(ARGUMENT_PRESENT(AuthenticationService))
{
*AuthenticationService = RPC_C_AUTHN_WINNT ;
}
if(ARGUMENT_PRESENT(AuthorizationService))
{
*AuthorizationService = RPC_C_AUTHZ_NONE ;
}
return(RPC_S_OK);
}
RPC_STATUS
LRPC_SCALL::InquireCallAttributes (
IN OUT void *RpcCallAttributes
)
/*++
Routine Description:
Inquire the security context attributes for the LRPC client
Arguments:
RpcCallAttributes - a pointer to
RPC_CALL_ATTRIBUTES_V1_W structure. The Version
member must be initialized.
Return Value:
RPC_S_OK or RPC_S_* / Win32 error. EEInfo will be returned.
--*/
{
RPC_CALL_ATTRIBUTES_V1 *CallAttributes;
RPC_STATUS Status = RPC_S_OK;
CallAttributes =
(RPC_CALL_ATTRIBUTES_V1 *)RpcCallAttributes;
CallAttributes->AuthenticationLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY;
CallAttributes->AuthenticationService = RPC_C_AUTHN_WINNT;
CallAttributes->NullSession = FALSE;
if (CallAttributes->Flags & RPC_QUERY_CLIENT_PRINCIPAL_NAME)
{
Status = Association->GetClientName(this,
&CallAttributes->ClientPrincipalNameBufferLength,
&CallAttributes->ClientPrincipalName);
if ((Status != RPC_S_OK) && (Status != ERROR_MORE_DATA))
{
return Status;
}
}
if (CallAttributes->Flags & RPC_QUERY_SERVER_PRINCIPAL_NAME)
{
CallAttributes->ServerPrincipalNameBufferLength = 0;
}
return Status;
}
LRPC_SBINDING *
LRPC_SCALL::LookupBinding (
IN unsigned short PresentContextId
)
/*++
Function Name:LookupBinding
Parameters:
Description:
Returns:
--*/
{
LRPC_SBINDING *CurBinding;
DictionaryCursor cursor;
long OldLockValue, NewLockValue;
Association->Bindings.Reset(cursor);
// lock out addition of new elements to make sure we are getting a consistent view
while (TRUE)
{
OldLockValue = Association->BindingsCollectionLock.GetInteger();
NewLockValue = OldLockValue + 1;
if ((OldLockValue >= 0)
&& (Association->BindingsCollectionLock.CompareExchange(NewLockValue, OldLockValue) == OldLockValue))
{
break;
}
Sleep (2);
}
while ((CurBinding = Association->Bindings.Next(cursor)))
{
if (CurBinding->GetPresentationContext() == PresentContextId)
{
ASSERT (Association->BindingsCollectionLock.GetInteger() > 0);
Association->BindingsCollectionLock.Decrement ();
return CurBinding;
}
}
ASSERT (Association->BindingsCollectionLock.GetInteger() > 0);
Association->BindingsCollectionLock.Decrement ();
return NULL;
}
RPC_STATUS
LRPC_SCALL_AVRF::ImpersonateClient (
)
/*++
Routine Description:
Overwrites the default ImpersonateClient routine for LPC_SCALL to
provide fault injection.
Arguments:
None
Return Value:
RPC_S_OK or RPC_S_* on failure
--*/
{
// If we are running with RPC veirfier and fault injection is enabled
// for RpcImpersonateClient, see if we should fail it.
if (pRpcVerifierSettings->fFaultInjectImpersonateClient)
{
// See if enough time has passed to start the fault injection.
if (!pRpcVerifierSettings->DelayFaultInjectImpersonateClient ||
GetTickCount() - gProcessStartTime > pRpcVerifierSettings->DelayFaultInjectImpersonateClient*1000)
{
if (RndBool(pRpcVerifierSettings->ProbFaultInjectImpersonateClient))
{
return RPC_S_OUT_OF_MEMORY;
}
}
}
return LRPC_SCALL::ImpersonateClient();
}
LRPC_SCONTEXT::LRPC_SCONTEXT (
IN HANDLE MyToken,
IN LUID *UserLuid,
IN LRPC_SASSOCIATION *MyAssociation,
IN BOOL fDefaultLogonId,
IN BOOL fAnonymousToken
)
{
hToken = MyToken;
ClientName = NULL;
RefCount = 1;
ClearDeletedFlag();
Association = MyAssociation;
AuthzClientContext = NULL;
if (fAnonymousToken)
SetAnonymousFlag();
else
ClearAnonymousFlag();
if (fAnonymousToken)
{
ASSERT(fDefaultLogonId == FALSE);
ASSERT(UserLuid == NULL);
}
else
{
ASSERT(fAnonymousToken == FALSE);
if (fDefaultLogonId)
SetDefaultLogonIdFlag();
else
ClearDefaultLogonIdFlag();
FastCopyLUID(&ClientLuid, UserLuid);
}
}
LRPC_SCONTEXT::~LRPC_SCONTEXT (
void
)
{
if (hToken)
{
CloseHandle(hToken);
}
RpcpFarFree(ClientName);
if (AuthzClientContext)
{
AuthzFreeContextFn(AuthzClientContext);
AuthzClientContext = NULL;
}
if (GetServerSideOnlyFlag())
{
// if this is server side only context, remove us
// from the garbage collection count
InterlockedDecrement(&PeriodicGarbageCollectItems);
}
}
RPC_STATUS
LRPC_SCONTEXT::GetUserName (
IN OUT ULONG *ClientPrincipalNameBufferLength OPTIONAL,
OUT RPC_CHAR **UserName,
IN HANDLE hUserToken OPTIONAL
)
/*++
Routine Description:
Gets the user name for the given context.
Arguments:
ClientPrincipalNameBufferLength - if present, *UserName must
point to a caller supplied buffer, which if big enough,
will be filled with the client principal name. If not present,
*UserName must be NULL.
UserName - see ClientPrincipalNameBufferLength
hUserToken - if present, the user name for the given token will
be retrieved instead of the user name for the token inside
the LRPC_SCONTEXT
Return Value:
RPC_S_OK for success, or RPC_S_* / Win32 error code for error.
--*/
{
TOKEN_USER *pUser;
RPC_STATUS Status;
RPC_CHAR *ClientPrincipalName;
ULONG ClientPrincipalNameLength; // in bytes, including NULL terminator
if (ClientName == 0)
{
if (GetAnonymousFlag() == 0)
{
if (hUserToken == NULL)
{
ASSERT(hToken != NULL);
hUserToken = hToken;
}
pUser = GetSID(hUserToken);
if (pUser == 0)
{
return RPC_S_OUT_OF_MEMORY;
}
Status = LookupUser((SID *)pUser->User.Sid, &ClientPrincipalName);
delete pUser;
}
else
{
Status = LookupUser((SID *)&AnonymousSid, &ClientPrincipalName);
}
if (Status != RPC_S_OK)
{
return Status;
}
if (InterlockedCompareExchangePointer((PVOID *)&ClientName, ClientPrincipalName, NULL) != NULL)
{
// somebody beat us to the punch. Free the allocated string
delete ClientPrincipalName;
}
}
// at this stage, ClientName must contain the client principal name
ASSERT(ClientName);
// See where our caller wants us to put it
if (ARGUMENT_PRESENT(ClientPrincipalNameBufferLength))
{
// in the future, we may think of caching the length to avoid
// computing it every time
ClientPrincipalNameLength = (RpcpStringLength(ClientName) + 1) * sizeof(RPC_CHAR);
// if there is enough space in the data, copy it to user buffer
if (ClientPrincipalNameLength <= *ClientPrincipalNameBufferLength)
{
RpcpMemoryCopy(*UserName,
ClientName,
ClientPrincipalNameLength);
Status = RPC_S_OK;
}
else
{
Status = ERROR_MORE_DATA;
}
*ClientPrincipalNameBufferLength = ClientPrincipalNameLength;
return Status;
}
else
{
ASSERT(*UserName == NULL);
*UserName = ClientName;
}
return RPC_S_OK;
}
TOKEN_USER *
LRPC_SCONTEXT::GetSID (
IN HANDLE hToken
)
{
char *Buf = NULL;
ULONG Bufflen = 64 ;
ULONG Length;
Buf = new char[Bufflen];
if (Buf == 0)
{
return NULL;
}
while (1)
{
if (GetTokenInformation(hToken,
TokenUser, Buf, Bufflen,
&Length) == FALSE)
{
if (Length > Bufflen)
{
Bufflen = Length ;
delete [] Buf;
Buf = new char[Bufflen];
if (Buf == 0)
{
return NULL;
}
continue;
}
else
{
#if DBG
PrintToDebugger("LRPC: GetTokenInformation failed\n") ;
#endif
return NULL;
}
}
break;
}
return (TOKEN_USER *) Buf;
}
RPC_STATUS
LRPC_SCONTEXT::LookupUser (
IN SID *pSid,
OUT RPC_CHAR **ReturnedUserName
)
{
RPC_CHAR *FullName = NULL, *UserName = NULL;
unsigned long DomainNameLength = DOMAIN_NAME_LEN ;
unsigned long UserNameLength = USER_NAME_LEN;
unsigned long OldDomainNameLength, OldUserNameLength;
SID_NAME_USE Name ;
RPC_STATUS Status = RPC_S_OK ;
UserName = new RPC_CHAR[UserNameLength];
if (UserName == 0)
{
Status = RPC_S_OUT_OF_MEMORY ;
goto Cleanup ;
}
// Allocate space for the whole name: "domain\user"
FullName = new RPC_CHAR [DomainNameLength+1+UserNameLength];
if (FullName == 0)
{
Status = RPC_S_OUT_OF_MEMORY ;
goto Cleanup ;
}
OldDomainNameLength = DomainNameLength ;
OldUserNameLength = UserNameLength ;
while (1)
{
// For optimization we will try to retrieve the domain name directly into the
// beginning of the full name.
if (LookupAccountSidW(NULL, pSid,
UserName, &UserNameLength,
FullName, &DomainNameLength, &Name) == FALSE)
{
// Check if the buffers specified were insufficient.
// If the function fails because the buffer is too small,
// DomainName/UserName receives the required buffer size,
// including the terminating null character.
if ((UserNameLength > OldUserNameLength) || (DomainNameLength > OldDomainNameLength))
{
if (UserNameLength > OldUserNameLength)
{
OldUserNameLength = UserNameLength ;
delete [] UserName;
UserName = new RPC_CHAR[UserNameLength];
if (UserName == 0)
{
Status = RPC_S_OUT_OF_MEMORY ;
goto Cleanup ;
}
}
if (DomainNameLength > OldDomainNameLength)
{
OldDomainNameLength = DomainNameLength;
delete [] FullName;
FullName = new RPC_CHAR [DomainNameLength+1+UserNameLength];
if (FullName == 0)
{
Status = RPC_S_OUT_OF_MEMORY ;
goto Cleanup ;
}
}
continue;
}
else
{
#if DBG
PrintToDebugger("LRPC: LookupAccountSid failed\n");
#endif
Status = RPC_S_UNKNOWN_PRINCIPAL;
goto Cleanup ;
}
}
break;
}
RpcpStringConcatenate(FullName, RPC_CONST_STRING("\\")) ;
RpcpStringConcatenate(FullName, UserName) ;
delete [] UserName;
*ReturnedUserName = FullName ;
ASSERT(Status == RPC_S_OK);
Cleanup:
if (Status)
{
if (UserName)
delete [] UserName;
if (FullName)
delete [] FullName;
return Status ;
}
return RPC_S_OK;
}
LRPC_ADDRESS *LrpcAddressList = NULL;
RPC_ADDRESS *
LrpcCreateRpcAddress (
)
/*++
Routine Description:
We just to create a new LRPC_ADDRESS. This routine is a proxy for the
new constructor to isolate the other modules.
--*/
{
RPC_STATUS Status = RPC_S_OK;
RPC_ADDRESS * RpcAddress;
RpcAddress = new LRPC_ADDRESS(&Status);
if (Status != RPC_S_OK)
{
delete RpcAddress;
return(0);
}
return(RpcAddress);
}
/*
This private API was requested by KumarP from the LSA group on 04/05/2000.
Here's his justification:
I am adding a new auditing feature to LSA that will allow any local process
to make an rpc call to LSA and generate an arbitrary audit. To be able to
make this call, the clients will first issue one call to get an audit-context
handle from LSA. LSA will maintain a list of handles till the client
explicitly closes the audit-context.
The reason I would like to have this API is to track which processes have
opened audit-contexts. This will help in situations where there is a
rogue/mal-functioning process that opens up a large number of audit-contexts.
In this case, I should be able to break LSA into debugger and dump the context
list and know which process has opened which handles. This may optionally
allow me to prevent certain processes from calling this API (though currently
there is no such requirement).
*/
RPC_STATUS
RPC_ENTRY
I_RpcBindingInqLocalClientPID (
IN RPC_BINDING_HANDLE Binding,
OUT unsigned long *Pid
)
{
LRPC_SCALL * Call;
HANDLE LocalPid;
InitializeIfNecessary();
if (Binding == NULL)
{
Call = (LRPC_SCALL *) RpcpGetThreadContext();
if (Call == NULL)
return RPC_S_NO_CALL_ACTIVE;
}
else
{
Call = (LRPC_SCALL *) Binding;
}
if (Call->InvalidHandle(LRPC_SCALL_TYPE))
return(RPC_S_INVALID_BINDING);
LocalPid = Call->InqLocalClientPID();
*Pid = HandleToUlong(LocalPid);
return RPC_S_OK;
}
const SID AnonymousSid = { 1, 1, SECURITY_NT_AUTHORITY, SECURITY_ANONYMOUS_LOGON_RID};