/*++ 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 #include #include #include #include #include #include #include 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 ) { 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) { 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 ) { 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); } 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.BindExchange.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.BindExchange.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; 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). LpcPortName = new RPC_CHAR[RpcpStringLength(Endpoint) + RpcpStringLength(LRPC_DIRECTORY_NAME) + 1]; 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); 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, OUT NETWORK_ADDRESS_VECTOR **ppNetworkAddressVector ) /*++ 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. --*/ { BOOL Boolean; RPC_CHAR * String; RPC_STATUS Status ; RPC_CHAR DynamicEndpoint[64]; static unsigned int DynamicEndpointCount = 0; DWORD NetworkAddressLength = MAX_COMPUTERNAME_LENGTH + 1; ULONG EndpointLength; UNUSED(PendingQueueSize); if (*Endpoint) { // the maximum allowed length in bytes is the // string length in bytes (string length * 2) + the NULL // terminator EndpointLength = RpcpStringLength(*Endpoint) * 2 + 2; if (EndpointLength > BIND_BACK_PORT_NAME_LEN) { RpcpErrorAddRecord(EEInfoGCRuntime, RPC_S_INVALID_ENDPOINT_FORMAT, EEInfoDLLRPC_ADDRESS__ServerSetupAddress10, *Endpoint, EndpointLength, BIND_BACK_PORT_NAME_LEN); return RPC_S_INVALID_ENDPOINT_FORMAT; } } Status = InitializeLrpcIfNecessary() ; if (Status != RPC_S_OK) { return Status ; } ASSERT(GlobalLrpcServer != 0) ; *ppNetworkAddressVector = (NETWORK_ADDRESS_VECTOR *) new char[ sizeof(NETWORK_ADDRESS_VECTOR) + sizeof(RPC_CHAR *) + sizeof(RPC_CHAR) * (MAX_COMPUTERNAME_LENGTH + 1)]; if (*ppNetworkAddressVector == NULL) { return RPC_S_OUT_OF_MEMORY; } (*ppNetworkAddressVector)->Count = 1; (*ppNetworkAddressVector)->NetworkAddresses[0] = (RPC_CHAR *) (((char *) *ppNetworkAddressVector) + sizeof(NETWORK_ADDRESS_VECTOR) + sizeof(RPC_CHAR *)); Boolean = GetComputerNameW( (*ppNetworkAddressVector)->NetworkAddresses[0], &NetworkAddressLength); if (Boolean != TRUE) { Status = GetLastError(); #if DBG PrintToDebugger("RPC : GetComputerNameW : %d\n", Status); #endif // DBG if (Status == ERROR_NOT_ENOUGH_MEMORY) { Status = RPC_S_OUT_OF_MEMORY; } else if ((Status == ERROR_NOT_ENOUGH_QUOTA) || (Status == ERROR_NO_SYSTEM_RESOURCES)) { Status = RPC_S_OUT_OF_RESOURCES; } else { ASSERT(0); Status = RPC_S_OUT_OF_MEMORY; } goto Cleanup; } 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); } } } Cleanup: if (Status != RPC_S_OK) { delete *ppNetworkAddressVector; *ppNetworkAddressVector = 0; } 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) { FreeMessage(SCall->LrpcRequestMessage) ; NewSCall = Association->GetNextSCall(SCall) ; 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; } 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; char *PaddedMessage; LRPC_MESSAGE * Reply ; LRPC_MESSAGE * LrpcMessage = 0; LRPC_MESSAGE * LrpcReplyMessage = 0; int AssociationType = 0; int Flags = 0; BOOL PartialFlag ; BOOL fStatus ; RPC_STATUS Status; unsigned long ReplyKey = -1; LARGE_INTEGER LongTimeout; LARGE_INTEGER ShortTimeout; PLARGE_INTEGER pliTimeout = &ShortTimeout; ULONG_PTR Key; THREAD *ThisThread; DebugThreadInfo *DebugCell; FormatTimeOut(&ShortTimeout, gThreadTimeout); FormatTimeOut(&LongTimeout, LRPC_LISTEN_TIMEOUT); pliTimeout = &ShortTimeout; PaddedMessage = (char *) _alloca(PadToNaturalBoundary(sizeof(LRPC_MESSAGE) + 1) + sizeof(LRPC_MESSAGE)); Reply = (LRPC_MESSAGE *) AlignOnNaturalBoundary(PaddedMessage) ; ThisThread = RpcpGetThreadPointer(); ASSERT(ThisThread); DebugCell = ThisThread->DebugCell; if (DebugCell) { if (this->DebugCell) { GetDebugCellIDFromDebugCell( (DebugCellUnion *)this->DebugCell, &this->DebugCellTag, &ThisThread->DebugCell->Endpoint); } } 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); 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)) { 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 (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) { BeginLongCall(); LrpcReplyMessage = 0; CAssociation->ProcessResponse(LrpcMessage, &LrpcReplyMessage); // // 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; } ReplyKey = AssociationKey; Flags = LrpcMessage->Rpc.RpcHeader.Flags ; PartialFlag = FALSE ; 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 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 { 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 ; ASSERT(0); } // switch } // else } // if else { switch (NtStatus) { case STATUS_NO_MEMORY: case STATUS_INSUFFICIENT_RESOURCES: case STATUS_UNSUCCESSFUL: PauseExecution(500L); 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 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) { 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; 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(); Association->DictionaryKey = (unsigned short) AssociationDictionary.Insert(Association); 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) ; 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; } 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) /*++ --*/ { ObjectType = LRPC_SASSOCIATION_TYPE; LpcServerPort = 0; LpcReplyPort = 0 ; this->Address = Address; AssociationReferenceCount = 1; Aborted = 0 ; Deleted = -1 ; if (*Status == RPC_S_OK) { 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) { 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 && SCall->SBinding->RpcInterface->IsAutoListenInterface()) { SCall->SBinding->RpcInterface->EndAutoListenCall() ; } 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 ) /*++ 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); // Hack Hack, where do I get the real QOS values from ?? SecurityQualityOfService.EffectiveOnly = TRUE; SecurityQualityOfService.ContextTrackingMode = SECURITY_STATIC_TRACKING; SecurityQualityOfService.ImpersonationLevel = SecurityAnonymous; SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); BindExchange.ConnectType = LRPC_CONNECT_RESPONSE ; BindExchange.AssocKey = AssocKey ; 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->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; 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 ASSERT(0); return RPC_S_PROTOCOL_ERROR; } 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]); DictKey = (unsigned char) Bindings.Insert(Binding); 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: 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 return RPC_S_INVALID_AUTH_IDENTITY ; } Status = RPC_S_OK; if (OpenThreadToken (GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_QUERY, TRUE, pTokenHandle) == FALSE) { *pTokenHandle = 0; if (GetLastError() == ERROR_CANT_OPEN_ANONYMOUS) { Status = ERROR_CANT_OPEN_ANONYMOUS; } else { #if DBG PrintToDebugger("LRPC: OpenThreadToken failed\n") ; #endif } } 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; 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; } 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(); 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(); } 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 { 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 ; 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 ) { if ( (RpcInterface->SequenceNumber == SequenceNumber) || (RpcInterface->IsSecurityCallbackReqd() == 0)) { return (RPC_S_OK); } RPC_STATUS Status = RpcInterface->CheckSecurityIfNecessary(Context); NukeStaleEEInfoIfNecessary(Status); Context->RevertToSelf(); if (Status == RPC_S_OK) { 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; if (SBinding->RpcInterface->IsAutoListenInterface()) { LrpcBinding->RpcInterface->BeginAutoListenCall() ; } 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; } 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(); } } else { // // The rest of the response headers are set in ::GetBuffer. // LrpcReplyMessage->Rpc.RpcHeader.MessageType = LRPC_MSG_RESPONSE; } } void LRPC_SCALL::SendReply ( ) { RPC_STATUS Status; BOOL Shutup ; LRPC_SASSOCIATION *LocalAssociation; if (IsSyncCall()) { if (IsClientAsync()) { if (LrpcReplyMessage->Fault.RpcHeader.MessageType == LRPC_MSG_FAULT) { SendDGReply(LrpcReplyMessage); } else { RpcMessage.RpcFlags = 0; Status = SendRequest(&RpcMessage, &Shutup) ; if (Status != RPC_S_OK) { #if DBG PrintToDebugger("RPC: SendRequest failed: %d\n", Status); #endif 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 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; 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 Association->Delete(); } } } 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) ; ASSERT(ReceiveEvent) ; 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; } // // 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: description Arguments: arg1 - description Return Value: RPC_S_OK - Function succeeded RPC_S_OUT_OF_MEMORY - we ran out of memory --*/ { 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; 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; NtStatus = SendDGReply(LrpcReplyMessage) ; if (NT_ERROR(NtStatus)) { return RPC_S_CALL_FAILED ; } } 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(); 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) ; SBinding->RpcInterface->EndCall(0, 1) ; 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 { Status = SendRequest(Message, &Shutup) ; } if (PARTIAL(Message)) { if (Status == RPC_S_OK || Status == RPC_S_SEND_INCOMPLETE) { if ((pAsync->Flags & RPC_C_NOTIFY_ON_SEND_COMPLETE) && !Shutup) { CallMutex->Request() ; if (!IssueNotification(RpcSendComplete)) { Status = RPC_S_OUT_OF_MEMORY ; } CallMutex->Clear() ; } return Status; } } 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; LrpcReplyMessage->Rpc.RpcHeader.CallId = CallId ; INITMSG(LrpcReplyMessage, ClientId, CallbackId, MessageId) ; this->pAsync = pAsync ; 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)) { 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 ; } } 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 } } 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_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.Rpc.RpcHeader.MessageType = LRPC_CLIENT_SEND_MORE; SendMore.LpcHeader.u1.s1.DataLength = sizeof(LRPC_MESSAGE) - sizeof(PORT_MESSAGE); SendMore.LpcHeader.u2.ZeroInit = 0; SendMore.Rpc.RpcHeader.CallId = CallId ; NtStatus = SendDGReply(&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); } 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. --*/ { BOOL Boolean; RPC_STATUS Status; RPC_CHAR UuidString[37]; RPC_CHAR * StringBinding; RPC_CHAR * NetworkAddress; DWORD NetworkAddressLength = MAX_COMPUTERNAME_LENGTH + 1; if (ObjectUuidFlag != 0) { ObjectUuid.ConvertToString(UuidString); UuidString[36] = '\0'; } NetworkAddress = new RPC_CHAR[NetworkAddressLength]; if (NetworkAddress == 0) { return(RPC_S_OUT_OF_MEMORY); } Boolean = GetComputerNameW( NetworkAddress, &NetworkAddressLength); if (Boolean != TRUE) { Status = GetLastError(); #if DBG PrintToDebugger("RPC : GetComputerNameW : %d\n", Status); #endif // DBG if (Status == ERROR_NOT_ENOUGH_MEMORY) { Status = RPC_S_OUT_OF_MEMORY; } else if ((Status == ERROR_NOT_ENOUGH_QUOTA) || (Status == ERROR_NO_SYSTEM_RESOURCES)) { Status = RPC_S_OUT_OF_RESOURCES; } else { ASSERT(0); Status = RPC_S_OUT_OF_MEMORY; } delete NetworkAddress; return Status; } Status = RpcStringBindingComposeW( (ObjectUuidFlag != 0 ? UuidString : 0), RPC_STRING_LITERAL("ncalrpc"), NetworkAddress, 0, 0, &StringBinding); delete NetworkAddress; 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; ASSERT(LrpcMessage->LpcHeader.u1.s1.DataLength >= sizeof(LRPC_RPC_HEADER)); 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 ; } Message->Buffer = RpcpFarAllocate(Message->BufferLength) ; if (Message->Buffer == 0) { Status = RPC_S_OUT_OF_MEMORY ; 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()) { COPYMSG((&ReplyMessage), LrpcMessage) ; ReplyMessage.Ack.MessageType = LRPC_MSG_ACK ; ReplyMessage.Ack.RpcStatus = Status; ReplyMessage.Ack.Shutup = (short) Choked ; 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 { ASSERT((LrpcMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_IMMEDIATE) || (LrpcMessage->Rpc.RpcHeader.Flags & LRPC_BUFFER_REQUEST)); } 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; Association->Bindings.Reset(cursor); while ((CurBinding = Association->Bindings.Next(cursor))) { if (CurBinding->GetPresentationContext() == PresentContextId) { return CurBinding; } } return NULL; } 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 **UserName ) { unsigned long UserLength = USER_NAME_LEN ; unsigned long OldDomainLen, OldUserLen; unsigned long DomainLen = DOMAIN_NAME_LEN ; RPC_CHAR *DomainName = NULL, *MyUserName = NULL; SID_NAME_USE Name ; RPC_STATUS Status = RPC_S_OK ; MyUserName = new RPC_CHAR[UserLength]; if (MyUserName == 0) { Status = RPC_S_OUT_OF_MEMORY ; goto Cleanup ; } DomainLen += UserLength ; DomainName = new RPC_CHAR [DomainLen]; if (DomainName == 0) { Status = RPC_S_OUT_OF_MEMORY ; goto Cleanup ; } OldDomainLen = DomainLen ; OldUserLen = UserLength ; while (1) { if (LookupAccountSidW(NULL, pSid, MyUserName, &UserLength, DomainName, &DomainLen, &Name) == FALSE) { if ((UserLength > OldUserLen) || (DomainLen > OldDomainLen)) { if (UserLength > OldUserLen) { OldUserLen = UserLength ; delete MyUserName; MyUserName = new RPC_CHAR[UserLength]; if (MyUserName == 0) { Status = RPC_S_OUT_OF_MEMORY ; goto Cleanup ; } } if (DomainLen > OldDomainLen) { DomainLen += UserLength; OldDomainLen = DomainLen; delete DomainName; DomainName = new RPC_CHAR[DomainLen]; if (DomainName == 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(DomainName, RPC_CONST_STRING("\\")) ; RpcpStringConcatenate(DomainName, MyUserName) ; delete MyUserName; *UserName = DomainName ; ASSERT(Status == RPC_S_OK); Cleanup: if (Status) { if (MyUserName) delete MyUserName; if (DomainName) delete DomainName; 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) { 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};