/*++ Copyright (C) Microsoft Corporation, 1991 - 1999 Module Name: osfsvr.cxx Abstract: This file contains the server side implementation of the OSF connection oriented RPC protocol engine. Author: Michael Montague (mikemon) 17-Jul-1990 Revision History: Mazhar Mohammed (mazharm) 2/1/97 major rehaul to support async - Added support for Async RPC, Pipes - Changed it to operate as a state machine - Changed class structure - Got rid of the TRANS classes 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 #include #include // for UNISP_RPC_ID #include extern long GroupIdCounter; // explicit placement new operator inline PVOID __cdecl operator new( size_t size, PVOID pPlacement ) { return pPlacement; } OSF_ADDRESS::OSF_ADDRESS ( IN TRANS_INFO * RpcTransInfo, IN OUT RPC_STATUS * Status ) : RPC_ADDRESS(Status) /*++ Routine Description: --*/ { RPC_CONNECTION_TRANSPORT *RpcServerInfo = (RPC_CONNECTION_TRANSPORT *) RpcTransInfo->InqTransInfo(); int i; RPC_STATUS OriginalFailureStatus; ObjectType = OSF_ADDRESS_TYPE; ActiveCallCount = 0; ServerListeningFlag = 0; ServerInfo = RpcServerInfo; TransInfo = RpcTransInfo; SetupAddressOccurred = 0; 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)RpcServerInfo->TransId; DebugCell->Status = desAllocated; memset(DebugCell->EndpointName, 0, sizeof(DebugCell->EndpointName)); } } else DebugCell = NULL; #if defined(_WIN64) ASSERT((MutexAllocationSize % 8) == 0); #else ASSERT((MutexAllocationSize % 4) == 0); #endif OriginalFailureStatus = RPC_S_OK; for (i = 0; i < NumberOfAssociationsDictionaries; i ++) { // explicit placement new (GetAssociationBucketMutex(i)) MUTEX (Status, TRUE // pre-allocate semaphores ); // if there is a failure, remember it, so that subsequent successes // don't overwrite the failure if ((*Status != RPC_S_OK) && (OriginalFailureStatus == RPC_S_OK)) { OriginalFailureStatus = *Status; } // don't check the status - the constructors will // check it. Also, we need to invoke all constructors // to give them a chance to initialize enough of the // object so that it can be destroyed properly } if (OriginalFailureStatus != RPC_S_OK) *Status = OriginalFailureStatus; } RPC_STATUS OSF_ADDRESS::ServerSetupAddress ( IN RPC_CHAR * NetworkAddress, IN RPC_CHAR * *Endpoint, IN unsigned int PendingQueueSize, IN void * SecurityDescriptor, OPTIONAL IN unsigned long EndpointFlags, IN unsigned long NICFlags ) /*++ Routine Description: At this point, we need to setup the loadable transport interface. We also need to obtain the network address for this server. After allocating a buffer to hold the network address, we will call the loadable transport interface to let it do its thing. Arguments: NetworkAddress - Returns the network address for this server. The ownership of the buffer allocated to contain the network address passes to the caller. Endpoint - Supplies the endpoint to be used will this address. PendingQueueSize - Supplies the size of the queue of pending requests which should be created by the transport. Some transports will not be able to make use of this value, while others will. SecurityDescriptor - Optionally supplies a security descriptor to be placed on this address. Whether or not this is suppored depends on the particular combination of transport interface and operating system. EndpointFlags - Flags that control dynamic port allocation for IP protseqs. NICFlags - Flags that control network (IP) address binding for IP protseqs. Return Value: RPC_S_OK - We successfully setup this address. RPC_S_INVALID_SECURITY_DESC - The supplied security descriptor is invalid. RPC_S_CANT_CREATE_ENDPOINT - The endpoint format is correct, but the endpoint can not be created. RPC_S_INVALID_ENDPOINT_FORMAT - The endpoint is not a valid endpoint for this particular transport interface. RPC_S_OUT_OF_RESOURCES - Insufficient resources are available to setup the address. RPC_S_OUT_OF_MEMORY - Insufficient memory is available to setup the address. --*/ { RPC_STATUS Status; Status = ServerInfo->Listen(InqRpcTransportAddress(), NetworkAddress, Endpoint, PendingQueueSize, SecurityDescriptor, EndpointFlags, NICFlags); if ( Status == RPC_S_OK ) { SetupAddressOccurred = 1; } VALIDATE(Status) { RPC_S_OK, RPC_S_INVALID_SECURITY_DESC, RPC_S_INVALID_ARG, RPC_S_CANT_CREATE_ENDPOINT, RPC_S_INVALID_ENDPOINT_FORMAT, RPC_S_OUT_OF_RESOURCES, RPC_S_PROTSEQ_NOT_SUPPORTED, RPC_S_DUPLICATE_ENDPOINT, RPC_S_OUT_OF_MEMORY, RPC_S_SERVER_UNAVAILABLE } CORRUPTION_VALIDATE { RPC_S_PROTOCOL_ERROR, RPC_S_INVALID_BOUND } CORRUPTION_END_VALIDATE; return(Status); } #ifndef NO_PLUG_AND_PLAY void OSF_ADDRESS::PnpNotify ( ) { ServerInfo->PnpNotify(); } #endif RPC_STATUS OSF_ADDRESS::CompleteListen ( ) /*++ Function Name:CompleteListen Parameters: Description: Returns: --*/ { if (ServerInfo->CompleteListen != 0) { ServerInfo->CompleteListen(InqRpcTransportAddress()); } 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 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; } return(RPC_S_OK); } RPC_STATUS OSF_ADDRESS::ServerStartingToListen ( IN unsigned int MinimumCallThreads, IN unsigned int MaximumConcurrentCalls ) /*++ Routine Description: Arguments: MinimumCallThreads - Supplies the minimum number of threads to have available to receive remote procedure calls. 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 least the minimum number of call threads required (as specified by the MinimumCallThreads argument). --*/ { RPC_STATUS Status; UNUSED(MaximumConcurrentCalls); UNUSED(MinimumCallThreads); Status = TransInfo->StartServerIfNecessary(); if (Status == RPC_S_OK) { ServerListeningFlag = 1; } return Status; } OSF_ADDRESS::~OSF_ADDRESS ( ) /*++ Routine Description: We need to clean up the address after it has been partially initialized. This routine will only be called before FireUpManager is called, but it may have been called before or after one of SetupAddressWithEndpoint or SetupAddressUnknownEndpoint is called. We will keep track of whether or not SetupAddress* occurred successfully; if so, we need to call AbortSetupAddress to give the loadable transport module a chance to clean things up. --*/ { int i; if (SetupAddressOccurred != 0) ServerInfo->AbortListen(InqRpcTransportAddress()); for (i = 0; i < NumberOfAssociationsDictionaries; i ++) { GetAssociationBucketMutex(i)->Free(); } if (DebugCell != NULL) { FreeCell(DebugCell, &DebugCellTag); } } OSF_SCONNECTION * OSF_ADDRESS::NewConnection ( ) /*++ Routine Description: We will create a new connection which belongs to this address. Arguments: ConnectionKey - Supplies the connection key specified for this connection by the loadable transport. Return Value: The new connection will be returned unless insufficient memory is available, in which case, zero will be returned. --*/ { OSF_SCONNECTION * SConnection; RPC_STATUS Status = RPC_S_OK; SConnection = new (ServerInfo->ServerConnectionSize) OSF_SCONNECTION ( this, ServerInfo, &Status); if ( Status != RPC_S_OK ) { // // Server serverinfo to 0, so it doesn't call close // SConnection->ServerInfo = 0; delete SConnection; SConnection = 0; } if ( SConnection == 0 ) { return(0); } // // Add a reference for the receive that is going to be posted by the // transport // SConnection->AddReference(); // CONN++ return(SConnection); } unsigned int OSF_ADDRESS::TransSecondarySize ( ) { unsigned int Length = RpcpStringLength(InqEndpoint()) + 1; // Will be converted to ANSI in the wire, no need to multiply by // sizeof(RPC_CHAR). return(Length); } RPC_STATUS OSF_ADDRESS::TransSecondary ( IN unsigned char * Address, IN unsigned int AddressLength ) { RPC_STATUS Status; unsigned char *AnsiAddress; AnsiAddress = UnicodeToAnsiString(InqEndpoint(),&Status); if (Status != RPC_S_OK) { ASSERT(Status == RPC_S_OUT_OF_MEMORY); ASSERT(AnsiAddress == 0); return Status; } RpcpMemoryCopy(Address,AnsiAddress,AddressLength); delete AnsiAddress; return (RPC_S_OK); } void OSF_ADDRESS::ServerStoppedListening ( ) /*++ Routine Description: We just need to indicate that the server is no longer listening, and set the minimum call threads to one. --*/ { ServerListeningFlag = 0; } OSF_ASSOCIATION * OSF_ADDRESS::RemoveAssociation ( IN int Key, IN OSF_ASSOCIATION *pAssociation ) { int HashBucketNumber; OSF_ASSOCIATION *pAssociationRemoved; AddressMutex.VerifyNotOwned(); HashBucketNumber = GetHashBucketForAssociation(pAssociation->AssocGroupId()); // verify the the bucket is locked GetAssociationBucketMutex(HashBucketNumber)->VerifyOwned(); pAssociationRemoved = Associations[HashBucketNumber].Delete(Key); return pAssociationRemoved; } int OSF_ADDRESS::AddAssociation ( IN OSF_ASSOCIATION * TheAssociation ) { int HashBucketNumber; int Key; HashBucketNumber = GetHashBucketForAssociation(TheAssociation->AssocGroupId()); AddressMutex.VerifyNotOwned(); // lock the bucket GetAssociationBucketMutex(HashBucketNumber)->Request(); Key = Associations[HashBucketNumber].Insert(TheAssociation); // unlock the bucket GetAssociationBucketMutex(HashBucketNumber)->Clear(); return Key; } OSF_ASSOCIATION * OSF_ADDRESS::FindAssociation ( IN unsigned long AssociationGroupId, IN RPC_CLIENT_PROCESS_IDENTIFIER * ClientProcess ) // The AddressMutex has already been requested. { DictionaryCursor cursor; OSF_ASSOCIATION * Association; OSF_ASSOCIATION_DICT *pAssocDict; int HashBucketNumber; // get the hashed bucket HashBucketNumber = GetHashBucketForAssociation(AssociationGroupId); pAssocDict = &Associations[HashBucketNumber]; AddressMutex.VerifyNotOwned(); // lock the bucket GetAssociationBucketMutex(HashBucketNumber)->Request(); // lookup the association in the bucket pAssocDict->Reset(cursor); while ( (Association = pAssocDict->Next(cursor)) != 0 ) { if ( Association->IsMyAssocGroupId(AssociationGroupId, ClientProcess) != 0 ) { Association->AddConnection(); GetAssociationBucketMutex(HashBucketNumber)->Clear(); return(Association); } } // unlock the bucket GetAssociationBucketMutex(HashBucketNumber)->Clear(); return(0); } void OSF_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 fo context handle destruction for the connection oriented protocols. 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: --*/ { int i; MUTEX *CurrentBucketMutex; DictionaryCursor cursor; OSF_ASSOCIATION_DICT *CurrentAssocDict; OSF_ASSOCIATION *CurrentAssociation; BOOL CopyOfDictionaryUsed; OSF_ASSOCIATION_DICT AssocDictCopy; OSF_ASSOCIATION_DICT *AssocDictToUse; BOOL Res; // N.B. We may or we may not own the ServerMutex here - be prepared // for both occasions. The first implication is not to call functions // that take the server mutex. for (i = 0; i < NumberOfAssociationsDictionaries; i ++) { CurrentBucketMutex = GetAssociationBucketMutex(i); CurrentBucketMutex->Request(); CurrentAssocDict = &Associations[i]; CopyOfDictionaryUsed = AssocDictCopy.ExpandToSize(CurrentAssocDict->Size()); if (CopyOfDictionaryUsed) { CurrentAssocDict->Reset(cursor); while ( (CurrentAssociation = CurrentAssocDict->Next(cursor)) != 0 ) { Res = AssocDictCopy.Insert(CurrentAssociation); ASSERT(Res != -1); // artifically add a connection count to keep it alive // while we destroy the contexts CurrentAssociation->AddConnection(); } CurrentBucketMutex->Clear(); AssocDictToUse = &AssocDictCopy; } else { AssocDictToUse = CurrentAssocDict; } AssocDictToUse->Reset(cursor); while ( (CurrentAssociation = AssocDictToUse->Next(cursor)) != 0 ) { // call into the association to destroy the context handles CurrentAssociation->DestroyContextHandlesForInterface( RpcInterfaceInformation, RundownContextHandles); } if (CopyOfDictionaryUsed) { while ( (CurrentAssociation = AssocDictCopy.Next(cursor)) != 0 ) { // remove the extra refcounts CurrentAssociation->RemoveConnection(); } AssocDictCopy.DeleteAll(); } else { CurrentBucketMutex->Clear(); } } } OSF_SBINDING::OSF_SBINDING ( // Constructor. IN RPC_INTERFACE * TheInterface, IN int PContext, IN int SelectedTransferSyntaxIndex ) { PresentContext = PContext; Interface = TheInterface; SequenceNumber = 0; CurrentSecId = -1; this->SelectedTransferSyntaxIndex = SelectedTransferSyntaxIndex; } inline RPC_STATUS OSF_SBINDING::CheckSecurity( SCALL * Call, unsigned long AuthId ) { OSF_SCALL *OsfSCall = (OSF_SCALL *) Call; ASSERT(Call->InvalidHandle(OSF_SCALL_TYPE) == 0); // This is verified by the caller. ASSERT (Interface->IsSecurityCallbackReqd()); // // We may be able to skip a security callback. // // We skip the callback iff: // - the binding has executed a security callback for the interface and // the SequenceNumber and the AuthId have not changed since. // - RPC security is being used. // // Thus, the only way to skip a security callback for a given binding is to have // executed it previously since Interface->SequenceNumber starts at 1 and // OSF_SBINDING::SequenceNumber starts at 0 and the only assignment to // OSF_SBINDING::SequenceNumber is below. Because bindings can't migrate // among connections, this means that a given connection has executed a given // security callback. // if ( OsfSCall->IsSecure() && SequenceNumber == Interface->SequenceNumber && AuthId == CurrentSecId ) { return (RPC_S_OK); } RPC_STATUS Status = Interface->CheckSecurityIfNecessary(Call); Call->RevertToSelf(); if (Status == RPC_S_OK) { // In order to skip security callbacks in the future we need to init SequenceNumber // and CurrentSecId. We will only do this if RPC security is used. if (OsfSCall->IsSecure()) { CurrentSecId = AuthId; SequenceNumber = Interface->SequenceNumber; } return (RPC_S_OK); } else { SequenceNumber = 0; } return (RPC_S_ACCESS_DENIED); } OSF_SCALL::OSF_SCALL ( IN OSF_SCONNECTION *Connection, IN OUT RPC_STATUS *Status ) : CallMutex(Status), SyncEvent(Status, 0) { ObjectType = OSF_SCALL_TYPE; Thread = 0; CallOrphaned = 0; CancelPending = 0; SavedHeader = 0; SavedHeaderSize = 0; this->Connection = Connection; SendContext = (char *) this+sizeof(OSF_SCALL); SetReferenceCount(0); if (IsServerSideDebugInfoEnabled()) { if (*Status != RPC_S_OK) { DebugCell = NULL; return; } DebugCell = (DebugCallInfo *)AllocateCell(&CellTag); if (DebugCell == NULL) *Status = RPC_S_OUT_OF_MEMORY; else { memset(DebugCell, 0, sizeof(DebugCallInfo)); DebugCell->Type = dctCallInfo; DebugCell->Status = (BYTE)csAllocated; GetDebugCellIDFromDebugCell((DebugCellUnion *)Connection->DebugCell, &Connection->DebugCellTag, &DebugCell->Connection); DebugCell->LastUpdateTime = NtGetTickCount(); // if this is the call for the connection, // it will be NULL. If this is a subsequent // call on the connection, the CachedSCall would // have been set already. if (Connection->CachedSCall == NULL) DebugCell->CallFlags = DBGCELL_CACHED_CALL; } } else DebugCell = NULL; // // we don't need to initialize ObjectUuidSpecified, ActualBufferLength, // FirstFrag and Alertcount // } OSF_SCALL::~OSF_SCALL ( ) { if (SavedHeader != 0) { ASSERT(SavedHeaderSize != 0) ; RpcpFarFree(SavedHeader); } if (DebugCell != NULL) { FreeCell(DebugCell, &CellTag); } } void OSF_SCALL::InquireObjectUuid ( OUT RPC_UUID * ObjectUuid ) /*++ Routine Description: This routine copies the object uuid from the server connection into the supplied ObjectUuid argument. Arguments: ObjectUuid - Returns a copy of the object uuid in the server connection. --*/ { if (ObjectUuidSpecified == 0) ObjectUuid->SetToNullUuid(); else ObjectUuid->CopyUuid(&(this->ObjectUuid)); } void OSF_SCALL::SendFault ( IN RPC_STATUS Status, IN int DidNotExecute ) { p_context_id_t p_cont = 0; if (CurrentBinding) p_cont = (p_context_id_t)CurrentBinding->GetPresentationContext(); Connection->SendFault(Status, DidNotExecute, CallId, p_cont); } RPC_STATUS OSF_SCALL::SendReceive ( IN OUT PRPC_MESSAGE Message ) /*++ Routine Description: Arguments: Message - Supplies the request to send to the server and returns the response received from the server. Return Value: RPC_S_OK - We successfully sent a remote procedure call request to the server and received back a response. --*/ { RPC_STATUS Status, ExceptionCode; unsigned int RemoteFaultOccured = 0; RPC_MESSAGE RpcMessage ; RPC_RUNTIME_INFO RuntimeInfo ; PRPC_DISPATCH_TABLE DispatchTableToUse; if (CurrentState == CallAborted) { return RPC_S_CALL_FAILED; } CallStack += 1; Address->Server->OutgoingCallback(); FirstFrag = 1; SyncEvent.Lower(); Status = SendRequestOrResponse(Message, rpc_request); if (Status != RPC_S_OK) { CallStack -= 1; return Status; } for (;TRUE;) { if (CurrentState == CallAborted) { Status = RPC_S_CALL_FAILED; break; } // // In the callback case, when the receive event is kicked, // we have either received a fault or we have received a complete // response/request // SyncEvent.Wait(); switch (CurrentState) { case ReceivedCallback: // // Just received a new callback, // need to dispatch it // RuntimeInfo.Length = sizeof(RPC_RUNTIME_INFO) ; RpcMessage.Handle = (RPC_BINDING_HANDLE) this; RpcMessage.Buffer = DispatchBuffer ; RpcMessage.BufferLength = DispatchBufferOffset; RpcMessage.RpcFlags = RPC_BUFFER_COMPLETE; RpcMessage.DataRepresentation = Connection->DataRep; RpcMessage.ReservedForRuntime = &RuntimeInfo ; CurrentBinding->GetSelectedTransferSyntaxAndDispatchTable( &RpcMessage.TransferSyntax, &DispatchTableToUse); RpcMessage.ProcNum = ProcNum; // // Dispatch the callback // if ( ObjectUuidSpecified != 0 ) { Status = CurrentBinding->GetInterface() ->DispatchToStubWithObject( &RpcMessage, &ObjectUuid, 1, DispatchTableToUse, &ExceptionCode); } else { Status = CurrentBinding->GetInterface() ->DispatchToStub( &RpcMessage, 1, DispatchTableToUse, &ExceptionCode); } // // Send the reponse // if ( Status != RPC_S_OK ) { VALIDATE(Status) { RPC_P_EXCEPTION_OCCURED, RPC_S_PROCNUM_OUT_OF_RANGE } END_VALIDATE; if ( Status == RPC_S_PROCNUM_OUT_OF_RANGE ) { SendFault(RPC_S_PROCNUM_OUT_OF_RANGE, 1); } else { SendFault(ExceptionCode, 0); Status = ExceptionCode; } continue; } FirstFrag = 1; Status = SendRequestOrResponse(&RpcMessage, rpc_response); if ( Status == RPC_S_CALL_FAILED_DNE ) { Status = RPC_S_CALL_FAILED; } // // if the client went away, it is wise to simple go away // if (Status != RPC_S_OK) { break; } // // Go back to waiting for our original reply // continue; case ReceivedCallbackReply: // // Received a reply to our callback // need to return to the caller with the reply // Message->Buffer = DispatchBuffer; Message->BufferLength = DispatchBufferOffset; Message->DataRepresentation = Connection->DataRep; Status = RPC_S_OK; break; case ReceivedFault: // // Received a fault, fail the call / propagate status // code // Status = AsyncStatus; break; case CallAborted: // // Call aborted, possibly because // Status = RPC_S_CALL_FAILED; break; default: // // Something bad happened, go back to looking ASSERT(0); } break; } // // We need this so the response to the original call can be sent // correctly. // FirstFrag = 1; CallStack -= 1; if ( Status == RPC_S_OK ) { Message->Handle = (RPC_BINDING_HANDLE) this; } return(Status); } RPC_STATUS OSF_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; CurrentBinding->GetSelectedTransferSyntaxAndDispatchTable(&Message->TransferSyntax, &Ignored); return RPC_S_OK; } RPC_STATUS OSF_SCALL::GetBuffer ( IN OUT PRPC_MESSAGE Message, IN UUID *ObjectUuid ) { ULONG BufferLengthToAllocate; Message->Handle = (RPC_BINDING_HANDLE) this; if (Message->RpcFlags & RPC_BUFFER_PARTIAL && Message->BufferLength < Connection->MaxFrag) { ActualBufferLength = Connection->MaxFrag ; } else { ActualBufferLength = Message->BufferLength ; } // In addition to saving space for the request (or response) header, // we want to save space for security information if necessary. BufferLengthToAllocate = ActualBufferLength + sizeof(rpcconn_request) + (2* Connection->AdditionalSpaceForSecurity); if (TransGetBuffer(&Message->Buffer, BufferLengthToAllocate)) { ActualBufferLength = 0 ; RpcpErrorAddRecord(EEInfoGCRuntime, RPC_S_OUT_OF_MEMORY, EEInfoDLOSF_SCALL__GetBuffer10, BufferLengthToAllocate); return(RPC_S_OUT_OF_MEMORY); } Message->Buffer = (unsigned char *) Message->Buffer + sizeof(rpcconn_request); return(RPC_S_OK); } RPC_STATUS OSF_SCALL::GetBufferDo ( OUT void ** ppBuffer, IN unsigned int culRequiredLength, IN BOOL fDataValid, IN unsigned int DataLength, IN unsigned long Extra ) { void *NewBuffer; if (TransGetBuffer(&NewBuffer, culRequiredLength + sizeof(rpcconn_request))) { return(RPC_S_OUT_OF_MEMORY); } if (fDataValid) { ASSERT(DataLength < culRequiredLength); NewBuffer = (unsigned char *) NewBuffer + sizeof(rpcconn_request); RpcpMemoryCopy(NewBuffer, *ppBuffer, DataLength); TransFreeBuffer((unsigned char *) *ppBuffer-sizeof(rpcconn_request)); *ppBuffer = NewBuffer; } else { *ppBuffer = (unsigned char *) NewBuffer + sizeof(rpcconn_request); } return(RPC_S_OK); } void OSF_SCALL::FreeBufferDo ( IN void *pBuffer ) { #if DBG if (pBuffer == DispatchBuffer) { LogEvent(SU_SCALL, EV_DELETE, this, pBuffer, 1, 1); } #endif TransFreeBuffer((unsigned char *) pBuffer - sizeof(rpcconn_request)); } void OSF_SCALL::FreeBuffer ( IN PRPC_MESSAGE Message ) { TransFreeBuffer((unsigned char *) Message->Buffer - sizeof(rpcconn_request)); ActualBufferLength = 0; } void OSF_SCALL::FreePipeBuffer ( IN PRPC_MESSAGE Message ) { TransFreeBuffer((unsigned char *) Message->Buffer - sizeof(rpcconn_request)); } RPC_STATUS OSF_SCALL::ReallocPipeBuffer ( IN PRPC_MESSAGE Message, IN unsigned int NewSize ) { void *TempBuffer ; RPC_STATUS Status ; unsigned int SizeToAlloc ; if (NewSize > ActualBufferLength) { SizeToAlloc = (NewSize > Connection->MaxFrag) ? NewSize:Connection->MaxFrag ; Status = TransGetBuffer(&TempBuffer, SizeToAlloc + sizeof(rpcconn_request) + sizeof(UUID) + (2* Connection->AdditionalSpaceForSecurity) ); if ( Status != RPC_S_OK ) { ASSERT( Status == RPC_S_OUT_OF_MEMORY ); return(RPC_S_OUT_OF_MEMORY); } if (ActualBufferLength > 0) { RpcpMemoryCopy((char *) TempBuffer+sizeof(rpcconn_request), Message->Buffer, Message->BufferLength) ; OSF_SCALL::FreePipeBuffer(Message) ; } Message->Buffer = (char *) TempBuffer + sizeof(rpcconn_request); ActualBufferLength = SizeToAlloc ; } Message->BufferLength = NewSize ; return (RPC_S_OK) ; } RPC_STATUS OSF_SCALL::TransGetBuffer ( OUT void * * Buffer, IN unsigned int BufferLength ) /*++ Routine Description: We need a buffer to receive data into or to put data into to be sent. This should be really simple, but we need to make sure that buffer we return is aligned on an 8 byte boundary. The stubs make this requirement. Arguments: Buffer - Returns a pointer to the buffer. BufferLength - Supplies the required length of the buffer in bytes. Return Value: RPC_S_OK - We successfully allocated a buffer of at least the required size. RPC_S_OUT_OF_MEMORY - There is insufficient memory available to allocate the required buffer. --*/ { void *Memory; // // The NT memory allocator returns memory which is aligned by at least // 8, so we dont need to worry about aligning it. // Memory = CoAllocateBuffer(BufferLength); if ( Memory == 0 ) { return(RPC_S_OUT_OF_MEMORY); } ASSERT( IsBufferAligned(Memory) ); *Buffer = Memory; return(RPC_S_OK); } void OSF_SCALL::TransFreeBuffer ( IN void * Buffer ) /*++ Routine Description: We need to free a buffer which was allocated via TransGetBuffer. The only tricky part is remembering to remove the padding before actually freeing the memory. --*/ { CoFreeBuffer(Buffer); } BOOL OSF_SCALL::BeginRpcCall ( IN rpcconn_common * Packet, IN unsigned int PacketLength ) /*++ Routine Description: Arguments: Packet - Supplies the packet we received from the connection. Ownership of this buffer passes to this routine. PacketLength - Supplies the length of the packet in bytes. Return Value: A non-zero return value indicates that the connection should not be placed in the receive any state; instead, the thread should just forget about the connection and go back to waiting for more new procedure calls. --*/ { RPC_STATUS Status; unsigned long SizeofHeaderToSave = 0; int retval ; BOOL fReceivePosted; unsigned int HeaderSize = sizeof(rpcconn_request); THREAD *ThisThread; unsigned long AuthId; // We need to init the call object before we start partying on it. // For example, CurrentBinding has to be set in case we will be sending a fault. ActivateCall(); // We shoud not receive a request before security negotiation is complete. // Guard against the scenario where we received the first leg but not // the third one. if (Connection->AuthContinueNeeded) { ASSERT(Connection->AdditionalSpaceForSecurity == 0); CORRUPTION_ASSERT(0); Status = RPC_S_PROTOCOL_ERROR; goto Cleanup; } // // Save the unbyteswapped header for the security related stuff // Especially if SECURITY is on. For Request/Resonse we save just // the greater of rpc_req or rpc_resp. We havent byteswapped anything.. // but if auth_length is 0, byteswapping is irrelevant.. // if (Packet->auth_length != 0) { if ((Packet->PTYPE == rpc_request) || (Packet->PTYPE == rpc_response)) { SizeofHeaderToSave = sizeof(rpcconn_request); if ( (Packet->pfc_flags & PFC_OBJECT_UUID) != 0 ) { SizeofHeaderToSave += sizeof(UUID); } } if (SavedHeaderSize < SizeofHeaderToSave) { if (SavedHeader != 0) { ASSERT(SavedHeaderSize != 0); RpcpFarFree(SavedHeader); } SavedHeader = RpcpFarAllocate(SizeofHeaderToSave); if (SavedHeader == 0) { Status = RPC_S_PROTOCOL_ERROR; goto Cleanup; } SavedHeaderSize = SizeofHeaderToSave; RpcpMemoryCopy(SavedHeader, Packet, SizeofHeaderToSave); } else if (SizeofHeaderToSave != 0) { RpcpMemoryCopy(SavedHeader, Packet, SizeofHeaderToSave); } } if (Packet->pfc_flags & PFC_PENDING_CANCEL) { // Cancels are only allowed if there is actually an active call // on the thread. // There is still a window between beginning to queue an APC and // the execution of the APC during which the call may go away and another may // come in its place, but this window is diminished. if (RpcpGetThreadContext() == NULL) { Status = RPC_S_NO_CALL_ACTIVE; goto Cleanup; } RpcCancelThread(GetCurrentThread()); } Status = ValidatePacket(Packet, PacketLength); CallId = Packet->call_id; if (Status != RPC_S_OK) { ASSERT(Status == RPC_S_PROTOCOL_ERROR); CORRUPTION_ASSERT(0); // // It is not the first packet, so we need to send a fault instead, // and then we will blow the connection away. // goto Cleanup; } // // We need to figure out about security: do we need to put authentication // information into each packet, and if so, how much space should we // reserve. When we allocated the buffer (see OSF_SCALL::GetBuffer) // we saved space for security information. We did so we could just // stick the authentication information into there without having to // copy anything // if (Connection->AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE) { ASSERT(Connection->AdditionalSpaceForSecurity >= MAXIMUM_SECURITY_BLOCK_SIZE); MaxSecuritySize = Connection->AdditionalSpaceForSecurity - MAXIMUM_SECURITY_BLOCK_SIZE; if (MaxSecuritySize == sizeof(sec_trailer)) { MaxSecuritySize = 0; } else { // // We need to arrange things so that the length of the stub data // is a multiple of MAXIMUM_SECURITY_BLOCK_SIZE: // this is a requirement of the security package. // MaximumFragmentLength -= ((MaximumFragmentLength - HeaderSize - MaxSecuritySize) % MAXIMUM_SECURITY_BLOCK_SIZE); } } ASSERT(Packet->PTYPE == rpc_request); CurrentBinding = Connection->LookupBinding( ((rpcconn_request *) Packet)->p_cont_id); if (CurrentBinding) { RPC_INTERFACE *CurrentInterface = CurrentBinding->GetInterface(); ASSERT(CurrentState == NewRequest); // // Check the security callback on this connection // - If IF does not require a security callback, just dispatch // - If IF requires a callback and current call is insecure - send a fault // and fail the call // - If IF requires a callback, have the binding confirm that for this id // we did callback once before // - If we never did callback.. ever, SBinding->CheckSecurity will force // a security callback if (CurrentInterface->IsSecurityCallbackReqd() != 0) { if (!CurrentInterface->IsAllowingUnsecureCallbacks() && Connection->CurrentSecurityContext == 0) { Status = RPC_S_ACCESS_DENIED; goto Cleanup; } if (Connection->CurrentSecurityContext != 0) { Status = Connection->CurrentSecurityContext->CheckForFailedThirdLeg(); if (Status != RPC_S_OK) { goto Cleanup; } ASSERT(Connection->CurrentSecurityContext->FullyConstructed() ); AuthId = Connection->CurrentSecurityContext->AuthContextId; } else { // This value will be ignored in CurrentBinding->CheckSecurity if // Call->Connection->CurrentSecurityContext == 0. AuthId = 0; } ThisThread = RpcpGetThreadPointer(); // set the current context for this thread so that the app // can use the security callback. We'll whack it afterwards // as the actual call may not get dispatched on this thread RpcpSetThreadContextWithThread(ThisThread, this); Status = CurrentBinding->CheckSecurity(this, AuthId); RpcpSetThreadContextWithThread(ThisThread, 0); if (Status != RPC_S_OK) { fSecurityFailure = 1; if (Packet->pfc_flags & PFC_LAST_FRAG) { Status = RPC_S_ACCESS_DENIED; Connection->CleanupPac(); goto Cleanup; } SendFault(RPC_S_ACCESS_DENIED, 1); Connection->TransFreeBuffer(Packet); return 0; } } if (CurrentInterface->IsPipeInterface()) { fPipeCall = 1; if (DebugCell) { DebugCell->CallFlags |= DBGCELL_PIPE_CALL; } } fReceivePosted = ProcessReceivedPDU(Packet, PacketLength, 1); return fReceivePosted; } else { // // We did not find a binding, which indicates the client tried // to make a remote procedure call on an unknown interface. // Status = RPC_S_UNKNOWN_IF; } Cleanup: Connection->TransFreeBuffer(Packet); // // No one else can come in until we post the next receive, // so it is ok to send the fault before making the call // available // SendFault(Status,1); if (Status != RPC_S_UNKNOWN_IF) { Connection->fDontFlush = (CurrentState == NewRequest); // // We are going to kill the connection, do't post another receive // fReceivePosted = 1; Connection->OSF_SCONNECTION::Delete(); } else { fReceivePosted = 0; } // // If the call has not been dispatched yet, DispatchBuffer needs to be freed // ASSERT(fCallDispatched == 0); ASSERT(DispatchBuffer == 0); if (Connection->fExclusive) { DeactivateCall(); Connection->CachedSCallAvailable = 1; } // // Remove the reply reference for this call // OSF_SCALL::RemoveReference(); // CALL-- // // Remove the dispatch reference for this call // OSF_SCALL::RemoveReference(); // CALL-- return fReceivePosted; } #define SC_CLEANUP(_status, _dne) {Status = _status; fDNE = _dne; goto Cleanup;} BOOL OSF_SCALL::ProcessReceivedPDU ( IN rpcconn_common * Packet, IN unsigned int PacketLength, IN BOOL fDispatch ) /*++ Function Name:ProcessReceivedPDU Parameters: Description: Returns: --*/ { RPC_STATUS Status = RPC_S_OK; rpcconn_request *Request = (rpcconn_request *) Packet; int FragmentLength = (int) PacketLength; unsigned char PTYPE, Flags; unsigned short OpNum; unsigned long Drep; int MyCallStack = CallStack; BOOL fDNE = 0; BOOL fReceivePosted = 0; BOOL fCallCleanedUp = FALSE; if (fSecurityFailure) { if (Packet->pfc_flags & PFC_LAST_FRAG) { Connection->TransFreeBuffer(Packet); goto Cleanup2; } Connection->TransFreeBuffer(Packet); return 0; } switch (Packet->PTYPE) { case rpc_request : case rpc_response: if (!fDispatch) { // // This must be a request or response // save the maximum of req/resonse size [i.e. sizeof request] // also, we are not saving the first frag. here .. hence // the approp. memory is already set aside // // SavedHeader may not be kept unless AuthLevel is above connect. // This is the case because MS sec providers will not use the token // with RPC_C_AUTHN_LEVEL_CONNECT and we will have nothing to save. CORRUPTION_ASSERT((Connection->AuthInfo.AuthenticationLevel == RPC_C_AUTHN_LEVEL_NONE) || (Connection->AuthInfo.AuthenticationLevel == RPC_C_AUTHN_LEVEL_CONNECT) || (SavedHeaderSize >= sizeof(rpcconn_request))); if (Connection->AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE) { // If there is a SavedHeader - populate it. if (SavedHeader != NULL) { // If the packet has an Object UUID, make sure the saved header // had one also. If we do not, the first fragment may come without the flag // and a subsequent one with the flag. EatAuthInfoFromPacket will get confused // on the second fragment thinking that the saved header contains the UUID when // it does not and the buffer size will be calculated incorrectly. if (Packet->pfc_flags & PFC_OBJECT_UUID) { if (!(((rpcconn_common *)SavedHeader)->pfc_flags & PFC_OBJECT_UUID)) { CORRUPTION_ASSERT(0); SC_CLEANUP(RPC_S_PROTOCOL_ERROR, 1); } ASSERT(SavedHeaderSize == sizeof(rpcconn_request) + sizeof(UUID)); } RpcpMemoryCopy(SavedHeader, Packet, sizeof(rpcconn_request)); } else if (Connection->AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_CONNECT) { CORRUPTION_ASSERT(0); SC_CLEANUP(RPC_S_PROTOCOL_ERROR, 1); } } Status = ValidatePacket(Packet, PacketLength); if (Status != RPC_S_OK ) { CORRUPTION_ASSERT(0); ASSERT(Status == RPC_S_PROTOCOL_ERROR ); SC_CLEANUP(Status, 1); } } if (Packet->call_id != CallId) { CORRUPTION_ASSERT(0); SC_CLEANUP(RPC_S_PROTOCOL_ERROR, 0); } Flags = Request->common.pfc_flags; Drep = *((unsigned long *) Request->common.drep); OpNum = Request->opnum; PTYPE=Request->common.PTYPE; Status = Connection->EatAuthInfoFromPacket( Request, &FragmentLength, &SavedHeader, &SavedHeaderSize); if (Status != RPC_S_OK ) { VALIDATE(Status) { RPC_S_PROTOCOL_ERROR, ERROR_SHUTDOWN_IN_PROGRESS, RPC_S_ACCESS_DENIED, ERROR_PASSWORD_MUST_CHANGE, ERROR_PASSWORD_EXPIRED, ERROR_ACCOUNT_DISABLED, ERROR_INVALID_LOGON_HOURS } END_VALIDATE; fSecurityFailure = 1; if (Packet->pfc_flags & PFC_LAST_FRAG || (Status == RPC_S_PROTOCOL_ERROR)) { SC_CLEANUP(Status, 0); } SendFault(RPC_S_ACCESS_DENIED, 1); Connection->TransFreeBuffer(Packet); return 0; } // // Ok, if the packet contains an object uuid, we need to shift // the stub data so that the packet does not contain an object // uuid. // if ((Flags & PFC_OBJECT_UUID) != 0) { if (CallStack != 0 ) { // // There can not be an object uuid in the message. // This is an error. // CORRUPTION_ASSERT(0); SC_CLEANUP(RPC_S_PROTOCOL_ERROR, 0); } // // First save away the object UUID so that we can get it later. // ObjectUuidSpecified = 1; RpcpMemoryCopy(&ObjectUuid, Request + 1, sizeof(UUID)); if (DataConvertEndian(((unsigned char *) &Drep)) != 0 ) { ByteSwapUuid(&ObjectUuid); } // // Now shift the stub data so that the packet is as if there is // no object UUID in the packet. // RpcpMemoryCopy(Request + 1, ((unsigned char *) (Request + 1)) + sizeof(UUID), FragmentLength); } // // Received the first fragment of a call. // if (Flags & PFC_FIRST_FRAG) { // // Optimize for the single PDU RPC case // if ((Flags & PFC_LAST_FRAG) != 0) { CurrentState = CallCompleted; DispatchBuffer = (void *) (Request+1); DispatchBufferOffset = FragmentLength; // // Buffers will be freed by callee // ASSERT(Status == RPC_S_OK); return DispatchRPCCall (PTYPE, OpNum); } if (Request->alloc_hint) { // We will compare AllocHint against the interface's MaxRpcSize // before we are about to allocate memory. The default bound is // gMaxRpcSize so even if the user hasn't specified a max size we // are safe with taking this off the wire. AllocHint = Request->alloc_hint; } else { // FragmentLength can be at most MAX_USHORT since it comes from the // frag_length field which is unsigned short - we don't have to worry. AllocHint = FragmentLength; } // Check the expected call size. Note that we check it on first frag // only. If they decrease it, we don't care. We will recheck // it in all paths below if caller icnreases it. if (CurrentBinding->GetInterface()->CallSizeLimitReached(AllocHint)) { fSecurityFailure = 1; SendFault(RPC_S_ACCESS_DENIED, 1); Connection->TransFreeBuffer(Packet); return 0; } DispatchBufferOffset = 0; Status = GetBufferDo(&DispatchBuffer, AllocHint); if (Status != RPC_S_OK) { SC_CLEANUP(RPC_S_OUT_OF_MEMORY, 0); } } // // Received an intermediate fragment of a call. // else { if (DispatchBuffer == 0) { // // Looks like it is the first fragment on the call, and it doesn't have // the first-frag bit set // CORRUPTION_ASSERT(0); SC_CLEANUP(RPC_S_PROTOCOL_ERROR, 0); } } if (fPipeCall == 0 || CallStack) { // // Non-pipe case // if (DispatchBufferOffset+FragmentLength > AllocHint) { // If we re-allocate the buffer for an active callback we // will free the dispatch buffer from under a dispatched call. // Prohibit it for the lack of better ideas. if (CallStack) { CORRUPTION_ASSERT(0 && "Re-allocating the dispatch buffer in a callback."); SC_CLEANUP(RPC_S_PROTOCOL_ERROR, 0); } AllocHint = DispatchBufferOffset + FragmentLength; if (CurrentBinding->GetInterface()-> CallSizeLimitReached(AllocHint)) { fSecurityFailure = 1; if (Packet->pfc_flags & PFC_LAST_FRAG) { SC_CLEANUP(RPC_S_ACCESS_DENIED, 0); } SendFault(RPC_S_ACCESS_DENIED, 1); Connection->TransFreeBuffer(Packet); return 0; } Status = GetBufferDo( &DispatchBuffer, AllocHint, 1, DispatchBufferOffset); if (Status != RPC_S_OK) { SC_CLEANUP(RPC_S_OUT_OF_MEMORY, 0); } } // // Copy current buffer into the dispatch buffer // RpcpMemoryCopy( (char *) DispatchBuffer+DispatchBufferOffset, Request+1, FragmentLength); DispatchBufferOffset += FragmentLength; Connection->TransFreeBuffer(Packet); if (Flags & PFC_LAST_FRAG) { CurrentState = CallCompleted; // // Buffers will be freed by callee // ASSERT(Status == RPC_S_OK); return DispatchRPCCall (PTYPE, OpNum); } } else { // // Pipe call // if (PTYPE != rpc_request) { CORRUPTION_ASSERT(0); SC_CLEANUP(RPC_S_PROTOCOL_ERROR, 0); } // // If it is a pipe call, we need to dispatch as soon as we get // at least alloc hint bytes. If it is not a pipe call, we wait until // we get the last fragment. // if (!fCallDispatched) { if (DispatchBufferOffset+FragmentLength > AllocHint) { AllocHint = DispatchBufferOffset + FragmentLength; if (CurrentBinding->GetInterface()-> CallSizeLimitReached(AllocHint)) { fSecurityFailure = 1; if (Packet->pfc_flags & PFC_LAST_FRAG) { SC_CLEANUP(RPC_S_ACCESS_DENIED, 0); } SendFault(RPC_S_ACCESS_DENIED, 1); Connection->TransFreeBuffer(Packet); return 0; } Status = GetBufferDo( &DispatchBuffer, AllocHint, 1, DispatchBufferOffset); if (Status != RPC_S_OK) { SC_CLEANUP(Status, 0); } } // // Copy the buffer in // RpcpMemoryCopy( (char *) DispatchBuffer+DispatchBufferOffset, Request+1, FragmentLength); DispatchBufferOffset += FragmentLength; Connection->TransFreeBuffer(Packet); ASSERT(Status == RPC_S_OK); if (DispatchBufferOffset == AllocHint) { ASSERT(fSecurityFailure == 0); if (Flags & PFC_LAST_FRAG) { CurrentState = CallCompleted; } else { // // Buffers will be freed by callee // DispatchFlags = 0; } return DispatchRPCCall (PTYPE, OpNum); } } else { // // Once a pipe call is dispatched, we don't care about how // big it gets. The manager routine has the option to abandon // the call whenever it wants. // CallMutex.Request(); if ((Connection->fExclusive) && (Connection->CachedSCallAvailable)) { CallMutex.Clear(); ASSERT (Connection->CachedSCall == this); Connection->TransFreeBuffer(Packet); return 0; } // // A pipe call is already in progress. We simply need to queue // the buffer into the buffer queue. It get picked up later. // LogEvent(SU_SCALL, EV_BUFFER_IN, Request, this, 0, 1, 0); if (BufferQueue.PutOnQueue(Request+1, FragmentLength)) { CallMutex.Clear(); SC_CLEANUP(RPC_S_OUT_OF_MEMORY, 0); } RcvBufferLength += FragmentLength; if ((Flags & PFC_LAST_FRAG) != 0) { CurrentState = CallCompleted; } if (pAsync == 0) { if (BufferQueue.Size() >= 4 && Connection->fExclusive && CurrentState != CallCompleted) { fPeerChoked = 1; fReceivePosted = 1; } CallMutex.Clear(); SyncEvent.Raise(); } else { // Issue receive-complete notification if we have enough data // to satisfy the NeededLength requirement. // If this is the last receive, issue notification no matter what, // since we need to wake up the sleeping receive thread. if (NeededLength > 0 && ((RcvBufferLength >= NeededLength) || (CurrentState == CallCompleted))) { // Disarm the notificaiton. NeededLength = 0; IssueNotification(RpcReceiveComplete); } else { // // Cannot do this for non-exclusive connections because // other calls will get blocked // if (BufferQueue.Size() >= 4 && Connection->fExclusive && CurrentState != CallCompleted) { fPeerChoked = 1; fReceivePosted = 1; } } CallMutex.Clear(); } // // We received pipe data // there's nothing to cleanup // return fReceivePosted; } } return 0; case rpc_fault: Status = ((rpcconn_fault *)Packet)->status; if ((Status == 0) && (Packet->frag_length >= FaultSizeWithoutEEInfo + 4)) { // // DCE 1.0.x style fault status: // Zero status and stub data contains the fault. // Status = *(unsigned long *) ((unsigned char *)Packet + FaultSizeWithoutEEInfo); } if (DataConvertEndian(Packet->drep) != 0) { Status = RpcpByteSwapLong(Status); } if (Status == 0) { Status = RPC_S_CALL_FAILED; } AsyncStatus = MapFromNcaStatusCode(Status); CurrentState = ReceivedFault; SyncEvent.Raise(); Connection->TransFreeBuffer(Packet); return 0; case rpc_cancel: case rpc_orphaned: CancelPending = 1; Connection->TransFreeBuffer(Packet); return 0; default : // // We should never reach here // CORRUPTION_ASSERT(0); SC_CLEANUP(RPC_S_PROTOCOL_ERROR, 0); break; } Cleanup: // // If we reach here it means that the call failed // Every call to ProcessReceivedPDU has a reference on the call, // the call is alive here // ASSERT(Status != RPC_S_OK); Connection->TransFreeBuffer(Packet); if ((MyCallStack == 0) && (fCallDispatched == 0)) { CleanupCallAndSendFault(Status, 0); fCallCleanedUp = TRUE; } else { SendFault(Status, 0); } Cleanup2: // // There is a chance that this error happened due to a bogus packet // We need to make sure that we don't something bad in that case // if (MyCallStack == 0) { if (fCallDispatched == 0) { if (fCallCleanedUp == FALSE) CleanupCall(); // // We cannot continue to use this connection // Connection->fDontFlush = (CurrentState == NewRequest); Connection->OSF_SCONNECTION::Delete(); // // Remove the reference held by the dispatch // thread // OSF_SCALL::RemoveReference(); // CALL-- // // We just finished sending the reply (the fault) // remove the reply reference // OSF_SCALL::RemoveReference(); // CALL-- } else { // // The call will go away when the dispatch completes // Connection->OSF_SCONNECTION::Delete(); } return 1; } return 0; } RPC_STATUS OSF_SCALL::Receive ( IN OUT PRPC_MESSAGE Message, IN unsigned int Size ) /*++ Function Name:Receive Parameters: Description: Returns: --*/ { RPC_STATUS Status = RPC_S_OK; BOOL fForceExtra = FALSE; if (!EXTRA(Message) && Message->Buffer) { ASSERT(Message->Buffer != DispatchBuffer); FreeBufferDo((char *)Message->Buffer); Message->Buffer = 0; Message->BufferLength = 0; } if (fSecurityFailure) { return RPC_S_ACCESS_DENIED; } Message->DataRepresentation = Connection->DataRep; while (TRUE) { switch (CurrentState) { case CallCompleted: // // When the last frag is received on this call, the call state // transitions to the Complete state. The call states are valid // only when using Async and Pipes // Status = GetCoalescedBuffer(Message, fForceExtra); break; case CallCancelled: Status = RPC_S_CALL_CANCELLED; break; case CallAborted: ASSERT(AsyncStatus != RPC_S_OK); Status = AsyncStatus; break; default: if (RcvBufferLength > Connection->MaxFrag) { Status = GetCoalescedBuffer(Message, fForceExtra); if (Status != RPC_S_OK) { break; } if (PARTIAL(Message) && Message->BufferLength >= Size) { break; } fForceExtra = TRUE; } else { // // the call is not yet complete, wait for it. // SyncEvent.Wait(); } continue; } break; } return Status; } RPC_STATUS OSF_SCALL::AsyncReceive ( IN OUT PRPC_MESSAGE Message, IN unsigned int Size ) /*++ Function Name:AsyncReceive Parameters: Description: Returns: --*/ { RPC_STATUS Status ; int Extra = IsExtraMessage(Message); ASSERT(EXTRA(Message) == 0 && PARTIAL(Message)); if (Message->Buffer) { ASSERT(Message->Buffer != DispatchBuffer); FreeBufferDo((char *)Message->Buffer); Message->Buffer = 0; } if (fSecurityFailure) { return RPC_S_ACCESS_DENIED; } switch (CurrentState) { case CallCompleted: Status = GetCoalescedBuffer(Message, FALSE); Message->DataRepresentation = Connection->DataRep; break; case CallCancelled: Status = RPC_S_CALL_CANCELLED; break; case CallAborted: Status = AsyncStatus; break; default: CallMutex.Request(); if (RcvBufferLength < Size) { if (NOTIFY(Message)) { NeededLength = Size ; } CallMutex.Clear() ; return RPC_S_ASYNC_CALL_PENDING; } else { Status = GetCoalescedBuffer(Message, FALSE); Message->DataRepresentation = Connection->DataRep; } CallMutex.Clear(); break; } return Status ; } RPC_STATUS OSF_SCALL::SetAsyncHandle ( IN PRPC_ASYNC_STATE pAsync ) /*++ Function Name:SetAsyncHandle Parameters: Description: Returns: --*/ { RPC_INTERFACE *CurrentInterface; this->pAsync = pAsync; Thread->fAsync = TRUE; fAsyncPipeCall = DoesAsyncCallHavePipe((char *)pAsync); // if we are unmarshalling, we must have a binding ASSERT(CurrentBinding != NULL); // make sure this is called on the dispatch thread ASSERT(Thread->Context == this); CurrentInterface = CurrentBinding->GetInterface(); ASSERT(CurrentInterface); // add one extra refcount on the interface for the dispatch thread. // We will remove it in DispatchHelper. The extra refcount is a solution // for a race that occurs in some COM scenarios when the call is async, // as we remove the refcount on the interface in CleanupCall for async calls // on exclusive connections. The extra refcount ensures the call still holds // a refcount on the interface until the manager routine returns to the // dispatching thread CurrentInterface->BeginNullManagerCall(); if (CurrentInterface->IsAutoListenInterface()) { CurrentInterface->BeginAutoListenCall(); } if (DebugCell) { DebugCell->CallFlags |= DBGCELL_ASYNC_CALL; } return RPC_S_OK; } RPC_STATUS OSF_SCALL::AbortAsyncCall ( IN PRPC_ASYNC_STATE pAsync, IN unsigned long ExceptionCode ) /*++ Function Name:AbortAsyncCall Parameters: Description: Returns: --*/ { ASSERT(CurrentBinding); CleanupCallAndSendFault(ExceptionCode, 0); // // The call was aborted asynchronously // Remove the reference held for the reply. // RemoveReference(); // CALL-- return RPC_S_OK; } RPC_STATUS OSF_SCALL::GetCoalescedBuffer ( IN PRPC_MESSAGE Message, BOOL fForceExtra ) /*++ Function Name:GetCoalescedBuffer Parameters: Message - the message structure that will receive the params Description: This routine will coalesce the buffers in the buffer queue into a single buffer and return it in the Message structure. If the RPC_BUFFER_EXTRA flag is set, the data is appended to the existing buffer in Message->Buffer. Returns: RPC_S_OK - the function was successful in doing its job RPC_S_OUT_OF_MEMORY - ran out of memory. --*/ { char *Current; UINT bufferlength; UINT TotalLength; RPC_STATUS Status; void *NewBuffer, *Buffer; int Extra = IsExtraMessage(Message); BOOL fExtendedExtra = Extra | fForceExtra; BOOL fSubmitReceive = 0; CallMutex.Request(); if (RcvBufferLength == 0) { CallMutex.Clear(); return RPC_S_OK; } if (fExtendedExtra) { TotalLength = RcvBufferLength + Message->BufferLength; } else { TotalLength = RcvBufferLength; } Status = TransGetBuffer (&NewBuffer, TotalLength+sizeof(rpcconn_request)); if (Status != RPC_S_OK) { CallMutex.Clear(); return RPC_S_OUT_OF_MEMORY; } NewBuffer = (char *) NewBuffer+sizeof(rpcconn_request); if (fExtendedExtra && Message->Buffer) { RpcpMemoryCopy(NewBuffer, Message->Buffer, Message->BufferLength); Current = (char *) NewBuffer + Message->BufferLength; Connection->TransFreeBuffer((char *) Message->Buffer-sizeof(rpcconn_request)); if (Extra) { // // Update the dispatch buffer, but only for the true EXTRA flag, not // for the forced extra // ASSERT(Message->ReservedForRuntime) ; ((PRPC_RUNTIME_INFO)Message->ReservedForRuntime)->OldBuffer = NewBuffer; if ((CallStack == 0) && (Message->Buffer == DispatchBuffer)) DispatchBuffer = NewBuffer; } } else { Current = (char *) NewBuffer; } while ((Buffer = BufferQueue.TakeOffQueue(&bufferlength)) != 0) { RpcpMemoryCopy(Current, Buffer, bufferlength); Current += bufferlength; Connection->TransFreeBuffer((char *) Buffer-sizeof(rpcconn_request)); } Message->Buffer = NewBuffer; Message->BufferLength = TotalLength; RcvBufferLength = 0; if (CurrentState == CallCompleted) { Message->RpcFlags |= RPC_BUFFER_COMPLETE; } if (fPeerChoked) { fSubmitReceive = 1; fPeerChoked = 0; } CallMutex.Clear(); if (fSubmitReceive) { Connection->TransAsyncReceive(); } return RPC_S_OK; } void OSF_SCALL::DispatchHelper () { THREAD *MyThread; RPC_STATUS Status, ExceptionCode; DebugCallInfo *Cell; DebugThreadInfo *ThreadCell; ULONG TickCount; PRPC_DISPATCH_TABLE DispatchTableToUse; RPC_INTERFACE *CurrentInterface; CurrentInterface = CurrentBinding->GetInterface(); // // We have a new RPC call. We need to dispatch it. // FirstCallRuntimeInfo.Length = sizeof(RPC_RUNTIME_INFO) ; FirstCallRpcMessage.Handle = (RPC_BINDING_HANDLE) this; FirstCallRpcMessage.Buffer = DispatchBuffer; FirstCallRpcMessage.BufferLength = DispatchBufferOffset; FirstCallRpcMessage.RpcFlags = DispatchFlags ; FirstCallRpcMessage.DataRepresentation = Connection->DataRep; FirstCallRpcMessage.ReservedForRuntime = &FirstCallRuntimeInfo ; CurrentBinding->GetSelectedTransferSyntaxAndDispatchTable(&FirstCallRpcMessage.TransferSyntax, &DispatchTableToUse); FirstCallRpcMessage.ProcNum = ProcNum; MyThread = (THREAD *) RpcpGetThreadPointer(); ASSERT(MyThread); RpcpSetThreadContextWithThread(MyThread, this); Thread = MyThread; ThreadCell = Thread->DebugCell; if (ThreadCell) { TickCount = NtGetTickCount(); Cell = DebugCell; Cell->CallID = CallId; Cell->ProcNum = (unsigned short)ProcNum; Cell->Status = csDispatched; Cell->LastUpdateTime = TickCount; Cell->InterfaceUUIDStart = CurrentInterface->GetInterfaceFirstDWORD(); ThreadCell->Status = dtsDispatched; ThreadCell->LastUpdateTime = TickCount; GetDebugCellIDFromDebugCell((DebugCellUnion *)ThreadCell, &MyThread->DebugCellTag, &Cell->ServicingTID); } // // Actually dispatch the RPC call // if ( ObjectUuidSpecified != 0 ) { Status = CurrentInterface->DispatchToStubWithObject( &FirstCallRpcMessage, &ObjectUuid, 0, DispatchTableToUse, &ExceptionCode); } else { Status = CurrentInterface->DispatchToStub( &FirstCallRpcMessage, 0, DispatchTableToUse, &ExceptionCode); } // // We need to insure that the server thread stops impersonating // the client at the end of the call, so we go ahead and call // RevertToSelf, and dont worry about the return value. // OSF_SCALL::RevertToSelf(); if (ThreadCell) { ThreadCell->Status = dtsProcessing; ThreadCell->LastUpdateTime = NtGetTickCount(); } if(Status != RPC_S_OK) { //Thread = 0; 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, RPC_P_EXCEPTION_OCCURED } END_VALIDATE; BOOL fDNE = 1; if( Status == RPC_P_EXCEPTION_OCCURED ) { fDNE=0; Status = ExceptionCode; } else if ( Status == RPC_S_NOT_LISTENING ) { Status = RPC_S_SERVER_TOO_BUSY; } while (CurrentBufferLength) { #if DBG PrintToDebugger("RPC: Waiting for the async send....\n"); #endif Sleep(200); } // // There may be another thread still sending data on this call // This will be taken care of in CleanupCall // CleanupCallAndSendFault(Status, fDNE); // It is tempting to think that since an exception was // raised, there will be no reply. However, in the pipe // case we may make a bunch of sends, and still get // an exception in the end. If there were no sends, // remove the reply reference for the call if (FirstSend) { OSF_SCALL::RemoveReference(); // CALL-- } goto Cleanup; } if (MyThread->IsSyncCall()) { ASSERT( FirstCallRpcMessage.Buffer != 0 ); if ( CallOrphaned ) { CallOrphaned = 0; Thread = 0; // // clear cancel if thread didn\'t notice it. // TestCancel(); goto Cleanup; } FirstCallRpcMessage.RpcFlags = 0; OSF_SCALL::Send(&FirstCallRpcMessage); } Cleanup: RpcpSetThreadContextWithThread(MyThread, 0); } BOOL OSF_SCALL::DispatchRPCCall ( IN unsigned char PTYPE, IN unsigned short OpNum ) /*++ Routine Description: Dispatch an new RPC call, or wake up thread that will dispatch a callback. Arguments: Packet - Supplies the packet we received from the connection. Ownership of this buffer passes to this routine. PacketLength - Supplies the length of the packet in bytes. Return Value: A non-zero return value indicates that the connection should not be placed in the receive any state; instead, the thread should just forget about the connection and go back to waiting for more new procedure calls. --*/ { RPC_STATUS Status; BOOL fNeedToSendFault; OSF_SCONNECTION *LocalConnection; if (CallStack > 0) { // // This is a callback request/response. We just need to signal the Event // and have it pick up the call // if (PTYPE == rpc_request) { CurrentState = ReceivedCallback; ProcNum = OpNum; } else { CurrentState = ReceivedCallbackReply; } SyncEvent.Raise(); return 0; } ProcNum = OpNum; fCallDispatched = 1; if (Connection->fExclusive == 0 && Connection->MaybeQueueThisCall(this)) { // // We don't get to dispatch right now, looks like another call is // currently dispatched. When the current call is done, it will do the // right thing // return 0; } fNeedToSendFault = FALSE; // // Looks like we are really going to dispatch a call // kick off another thread to go and pick up more requests // Status = Address->CreateThread(); if (Status == RPC_S_OK) { // // Post another receive // Status = Connection->TransAsyncReceive(); } else { Status = RPC_S_OUT_OF_MEMORY; fNeedToSendFault = TRUE; } if (Status != RPC_S_OK) { FreeBufferDo(DispatchBuffer); if (fNeedToSendFault) CleanupCallAndSendFault(Status, 0); else CleanupCall(); if (Connection->fExclusive == 0) { // // By the time we get here, calls may have piled up // Connection->AbortQueuedCalls(); } // // We cannot continue to use this connection // Connection->fDontFlush = (CurrentState == NewRequest); Connection->Delete(); // // Remove the reply reference // RemoveReference(); // CALL-- // // Remove the dispatch reference // RemoveReference(); // CALL-- return 1; } // the call may have been cleaned up after this (though not // destroyed) - save the connection in a local variable LocalConnection = Connection; // // Dispatch the current call // DispatchHelper(); if (LocalConnection->fExclusive == 0) { LocalConnection->DispatchQueuedCalls(); } // // Remove the dispatch reference // RemoveReference(); // CALL-- return 1; } RPC_STATUS OSF_SCALL::SendNextFragment ( void ) /*++ Function Name:SendNextFragment Description: Send the next response fragment Returns: --*/ { RPC_STATUS Status; RPC_STATUS RpcStatus2; rpcconn_common * pFragment; BOOL LastFragmentFlag; ULONG PacketLength; ULONG MaxDataLength = MaximumFragmentLength - sizeof(rpcconn_response) - MaxSecuritySize; unsigned char *ReservedForSecurity = (unsigned char *) CurrentBuffer + CurrentOffset + CurrentBufferLength + Connection->AdditionalSpaceForSecurity; pFragment = (rpcconn_common *) ((char *) CurrentBuffer+CurrentOffset-sizeof(rpcconn_response)); if (CurrentBuffer == LastBuffer && CurrentBufferLength <= MaxDataLength) { LastFragmentFlag = 1; PacketLength = CurrentBufferLength; } else { // // Each outstanding send needs to hold a reference // on the call. Since the call holds a reference // on the connection. The connection will also be alive // AddReference(); // CALL++ LastFragmentFlag = 0; PacketLength = MaxDataLength; } ConstructPacket(pFragment, rpc_response, PacketLength+sizeof(rpcconn_response)+MaxSecuritySize); if (FirstSend) { FirstSend = 0; pFragment->pfc_flags |= PFC_FIRST_FRAG; } ((rpcconn_response *) pFragment)->alloc_hint = CurrentBufferLength; ((rpcconn_response *) pFragment)->p_cont_id = (unsigned char) CurrentBinding->GetPresentationContext(); ((rpcconn_response *) pFragment)->alert_count = (unsigned char) 0; ((rpcconn_response *) pFragment)->reserved = 0; pFragment->call_id = CallId; LogEvent(SU_SCALL, EV_BUFFER_OUT, this, pFragment, LastFragmentFlag, 1); if (LastFragmentFlag) { char *BufferToFree = (char *) CurrentBuffer-sizeof(rpcconn_response); int MyMaxFrag = MaximumFragmentLength; int MyMaxSec = MaxSecuritySize; CurrentBufferLength = 0; CleanupCall(); if (Connection->IsHttpTransport()) { RpcStatus2 = Connection->SetLastBufferToFree (BufferToFree); VALIDATE(RpcStatus2) { RPC_S_OK, RPC_S_CANNOT_SUPPORT } END_VALIDATE; // if the transport does not support SetLastBufferToFree, it will // return RPC_S_CANNOT_SUPPORT. In this case we retain ownership of // the buffer. } // // The call should still be alive at this point because the caller // of this function has not release the send reference // Status = Connection->SendFragment( pFragment, LastFragmentFlag, sizeof(rpcconn_response), MyMaxSec, PacketLength, MyMaxFrag, ReservedForSecurity); // // Last send always succeeds // Status = RPC_S_OK; if ((Connection->IsHttpTransport() == FALSE) || (RpcStatus2 != RPC_S_OK)) Connection->TransFreeBuffer(BufferToFree); } else { Status = Connection->SendFragment( pFragment, LastFragmentFlag, sizeof(rpcconn_response), MaxSecuritySize, PacketLength, MaximumFragmentLength, ReservedForSecurity, TRUE, SendContext) ; if (Status != RPC_S_OK) { CurrentBufferLength = 0; // // Remove the reference for the outstanding send // OSF_SCALL::RemoveReference(); // CALL-- } } return Status; } RPC_STATUS OSF_SCALL::Send ( IN OUT PRPC_MESSAGE Message ) /*++ Routine Description: Arguments: Message - Supplies the buffer containing the response to be sent --*/ { void *NewBuffer; int RemainingLength = 0; RPC_STATUS Status = RPC_S_OK; RPC_STATUS StatusToReturn = RPC_S_OK; ULONG MaxDataLength = MaximumFragmentLength - sizeof(rpcconn_response) - MaxSecuritySize; BOOL fOutstandingSend = 0; BOOL fBufferSent = 0; ASSERT(LastBuffer == 0); if (PARTIAL(Message)) { if (Message->BufferLength < MaxDataLength) { return RPC_S_SEND_INCOMPLETE; } // Check if the message contains more data then can be sent. RemainingLength = Message->BufferLength % MaxDataLength; // If there is extra data that will not be sent now, allocate // and fill NewBuffer with it. if (RemainingLength) { Status = GetBufferDo(&NewBuffer, RemainingLength); if (Status != RPC_S_OK) { ASSERT(Status == RPC_S_OUT_OF_MEMORY); FreeBufferDo(Message->Buffer); return Status; } Message->BufferLength -= RemainingLength; RpcpMemoryCopy(NewBuffer, (char *) Message->Buffer+Message->BufferLength, RemainingLength); // Now NewBuffer contains the excess data that has not been sent. } } else { LastBuffer = Message->Buffer; } while (1) { CallMutex.Request(); if (CurrentBuffer == 0) { // // If CurrentBuffer == 0, it means that the call is idle // CurrentOffset = 0; CurrentBuffer = Message->Buffer; CurrentBufferLength = Message->BufferLength; fBufferSent = TRUE; // Check if we have a NewBuffer left over from a message greater then MaxDataLength. if ((CurrentBuffer != LastBuffer) || (CurrentBufferLength > MaxDataLength)) { UpdateBuffersAfterNonLastSend( NewBuffer, RemainingLength, Message); fOutstandingSend = TRUE; } CallMutex.Clear(); Status = SendNextFragment(); if (Status && fOutstandingSend) { // if we failed on a non last send, there will be // nobody else to drive the call - we need to // return the unsent buffer to Message->Buffer, // so that it can be freed below. Message->Buffer = CurrentBuffer; CleanupCall(); } // N.B. Do not touch any call members after // this point if the call succeeded - you may affect // the next call. // This is because once we send, we may get swapped out // and other threads could drive the call to // completion (i.e. send all fragments and cleanup // the call on the last fragment). From then on, // it may be another call we're writing on. if (fOutstandingSend && RemainingLength && (Status == RPC_S_OK)) StatusToReturn = RPC_S_SEND_INCOMPLETE; } else { if ((AsyncStatus == RPC_S_OK) && (pAsync == 0) && (BufferQueue.Size() >= 4)) { fChoked = 1; CallMutex.Clear(); SyncEvent.Wait(); // if the call already failed, bail out if (AsyncStatus != RPC_S_OK) { Status = AsyncStatus; fOutstandingSend = TRUE; break; } continue; } else if (AsyncStatus != RPC_S_OK) { CallMutex.Clear(); Status = AsyncStatus; fOutstandingSend = TRUE; break; } // // Since CurrentBuffer != 0, the call is busy sending the reply // if (BufferQueue.PutOnQueue(Message->Buffer, Message->BufferLength)) { Status = RPC_S_OUT_OF_MEMORY; } else { UpdateBuffersAfterNonLastSend( NewBuffer, RemainingLength, Message); if (RemainingLength) StatusToReturn = RPC_S_SEND_INCOMPLETE; } CallMutex.Clear(); } break; } if (Status) { if (RemainingLength) { FreeBufferDo(NewBuffer); } if (fOutstandingSend) { FreeBufferDo(Message->Buffer); } } else { if (StatusToReturn != RPC_S_OK) Status = StatusToReturn; } if (fBufferSent) { // // Remove the reference for the call (if failure) // or for the outstanding send (if success) // RemoveReference(); // CALL-- } return Status; } RPC_STATUS OSF_SCALL::AsyncSend ( IN OUT PRPC_MESSAGE Message ) /*++ Function Name:AsyncSend Parameters: Message - Supplies the buffer containing the response to be sent Description: It isn't neccassary for us to send the reply using async IO. For the first cut, we will send the request synchronously. This will save us a whole lot of headache. Returns: --*/ { RPC_STATUS Status; ULONG OldBufferLength = Message->BufferLength; ASSERT(FirstSend == 0 || BufferQueue.IsQueueEmpty()); if (FirstSend) { fNotifyOnSendComplete = (pAsync->Flags & RPC_C_NOTIFY_ON_SEND_COMPLETE); } if (AsyncStatus != RPC_S_OK) { Status = AsyncStatus; CleanupCall(); // // Remove the reply reference // RemoveReference(); // CALL-- return Status; } Status = Send(Message); if (Status == RPC_S_SEND_INCOMPLETE) { if (Message->BufferLength == OldBufferLength && (pAsync->Flags & RPC_C_NOTIFY_ON_SEND_COMPLETE)) { CallMutex.Request() ; if (!IssueNotification(RpcSendComplete)) { Status = RPC_S_OUT_OF_MEMORY ; } CallMutex.Clear() ; } } // // In the failure case, the reference has already been removed // by the server // return Status; } RPC_STATUS OSF_SCALL::SendRequestOrResponse ( IN OUT PRPC_MESSAGE Message, IN unsigned char PacketType ) /*++ Routine Description: This routine is used to send to synchoronous sends, as in callbacks and callback response. Arguments: Message - Supplies the buffer containing the request or response to be sent, and returns the first fragment received from the server. PacketType - Supplies the packet type; this must be rpc_request or rpc_response. Return Value: RPC_S_OK - We successfully sent the request and received a fragment from the server. RPC_S_CALL_FAILED_DNE - The connection failed part way through sending the request or response. RPC_S_CALL_FAILED - The connection failed after sending the request or response, and the receive failed. RPC_S_OUT_OF_MEMORY - Insufficient memory is available to perform the operation. RPC_S_OUT_OF_RESOURCES - Insufficient resources are available to perform the operation. --*/ { RPC_STATUS Status; RPC_MESSAGE SendBuffer; rpcconn_common * pFragment; ULONG LastFragmentFlag = 0; ULONG LengthLeft = Message->BufferLength; ULONG HeaderSize = sizeof(rpcconn_request); ULONG MaxDataLength = MaximumFragmentLength - HeaderSize - MaxSecuritySize; unsigned char *ReservedForSecurity = (unsigned char *) Message->Buffer + Message->BufferLength + Connection->AdditionalSpaceForSecurity; ASSERT(!PARTIAL(Message)); ASSERT( sizeof(rpcconn_response) == sizeof(rpcconn_request)); VALIDATE(PacketType) { rpc_request, rpc_response } END_VALIDATE; SendBuffer.Buffer = Message->Buffer; pFragment = (rpcconn_common *) ((char *) Message->Buffer - HeaderSize); for (;;) { // // Check to see if the remaining data will fit into a single // fragment; if so, set the last fragment flag. // if ( LengthLeft <= MaxDataLength ) { LastFragmentFlag = 1; } ConstructPacket(pFragment, PacketType, (LastFragmentFlag != 0 ? LengthLeft+HeaderSize+MaxSecuritySize : MaximumFragmentLength)); if ((LengthLeft == Message->BufferLength)) { if (FirstFrag) { FirstFrag = 0; pFragment->pfc_flags |= PFC_FIRST_FRAG; if (TestCancel()) { pFragment->pfc_flags |= PFC_PENDING_CANCEL; } } } if (PacketType == rpc_request) { ((rpcconn_request *) pFragment)->alloc_hint = LengthLeft; ((rpcconn_request *) pFragment)->p_cont_id = (unsigned short) CurrentBinding->GetPresentationContext(); ((rpcconn_request *) pFragment)->opnum = (unsigned short) Message->ProcNum; } else { ((rpcconn_response *) pFragment)->alloc_hint = LengthLeft; ((rpcconn_response *) pFragment)->p_cont_id = (unsigned short) CurrentBinding->GetPresentationContext(); ((rpcconn_response *) pFragment)->alert_count = (unsigned char) 0; ((rpcconn_response *) pFragment)->reserved = 0; } pFragment->call_id = CallId; Status = Connection->SendFragment( pFragment, LastFragmentFlag, HeaderSize, MaxSecuritySize, LengthLeft, MaximumFragmentLength, ReservedForSecurity) ; if (Status != RPC_S_OK || LastFragmentFlag) { FreeBuffer(&SendBuffer); return (Status) ; } pFragment = (rpcconn_common *) (((unsigned char *) pFragment) + MaxDataLength); LengthLeft -= MaxDataLength; } ASSERT(0); } void OSF_SCALL::ProcessSendComplete ( IN RPC_STATUS EventStatus, IN BUFFER Buffer ) /*++ Function Name:ProcessSendComplete Parameters: Description: Returns: --*/ { RPC_STATUS Status; int MaxDataLength = MaximumFragmentLength - sizeof(rpcconn_response) - MaxSecuritySize; unsigned int MyCurrentBufferLength; LogEvent(SU_SCALL, EV_NOTIFY, this, Buffer, EventStatus, 1); ASSERT(Buffer); ASSERT((char *) Buffer-CurrentOffset +sizeof(rpcconn_request) == CurrentBuffer); ASSERT((((rpcconn_common *) Buffer)->pfc_flags & PFC_LAST_FRAG) == 0); if (EventStatus != RPC_S_OK) { Status = RPC_S_CALL_FAILED; goto Abort; } MyCurrentBufferLength = CurrentBufferLength - MaxDataLength; CurrentOffset += MaxDataLength; CallMutex.Request(); if (MyCurrentBufferLength == 0) { Connection->TransFreeBuffer( (char *) CurrentBuffer-sizeof(rpcconn_response)); CurrentBuffer = BufferQueue.TakeOffQueue(&MyCurrentBufferLength); if (CurrentBuffer == 0) { // // As soon as send-complete notification is issued, a thread may // be released to do another AsyncSend. If by that time // AsyncStatus != RPC_S_OK it will try to clean up the call. // Thus, after IssueNotification, the send reference may be removed. // We should keep an extra ref to guard against this. // AddReference(); if (pAsync && (pAsync->Flags & RPC_C_NOTIFY_ON_SEND_COMPLETE)) { if (!IssueNotification(RpcSendComplete)) { AsyncStatus = RPC_S_OUT_OF_MEMORY; } } CurrentBufferLength = 0; CallMutex.Clear(); RemoveReference(); return; } CurrentOffset = 0; if (fChoked == 1 && pAsync == 0 && BufferQueue.Size() <=1) { fChoked = 0; SyncEvent.Raise(); } } else { // // We know that there is more to send in the current buffer // We need to restore the part of the buffer which we overwrote // with authentication information. // ASSERT(CurrentBuffer); unsigned char *ReservedForSecurity = (unsigned char *) CurrentBuffer + CurrentOffset + MyCurrentBufferLength + Connection->AdditionalSpaceForSecurity; if ((Connection->AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE) && (MaxSecuritySize != 0)) { RpcpMemoryCopy((char *) Buffer+MaximumFragmentLength-MaxSecuritySize, ReservedForSecurity, MaxSecuritySize); } } // MyCurrentBufferLength may be 0 if the last buffer to be // queued had 0 length. This may happen in async pipe scenarios. CurrentBufferLength = MyCurrentBufferLength; CallMutex.Clear(); ASSERT(CurrentBuffer); Status = Address->CreateThread(); if (Status != RPC_S_OK) { goto Abort; } Status = SendNextFragment(); if (Status != RPC_S_OK) { goto Abort; } // // Remove reference held by the outstanding send // or the call reference in the case of the last call // RemoveReference(); // CALL-- return; Abort: ASSERT(CurrentBuffer); ASSERT(Status != RPC_S_OK); Connection->TransFreeBuffer( (char *) CurrentBuffer-sizeof(rpcconn_response)); // // We cannot remove the reference here, if we do // we'll cause the other thread to puke // AsyncStatus = Status; BUFFER MyBuffer; unsigned int ignore; CallMutex.Request(); while (MyBuffer = BufferQueue.TakeOffQueue(&ignore)) { Connection->TransFreeBuffer((char *) MyBuffer-sizeof(rpcconn_response)); } // wake up the thread that was flow controlled, if any if (fChoked == 1 && pAsync == 0) { fChoked = 0; SyncEvent.Raise(); } CallMutex.Clear(); CurrentBufferLength = 0; // For async pipes, if the async send fails we need to wake up // a thread waiting for the send-complete notification and keep // the reference. During the next call to push we will deliver the failure // to the user and free the call. // If the call has already been aborted or completed and notification level is none, // pAsync may be gone and we can't query it. Therefore we need to capture the // pAsync->Flags and use the captured value here. if (fAsyncPipeCall && fNotifyOnSendComplete) { if (!IssueNotification(RpcSendComplete)) { AsyncStatus = RPC_S_OUT_OF_MEMORY; } } else { // // Remove the reply reference // RemoveReference(); // CALL-- } } RPC_STATUS OSF_SCALL::ImpersonateClient ( ) /*++ Function Name:ImpersonateClient Parameters: Description: This is relatively easy: we check to see if there is RPC protocol level security, if there is not, we let the transport try and impersonate the client, and if there is, we let the GSSAPI deal with it. Returns: --*/ { return Connection->ImpersonateClient(); } RPC_STATUS OSF_SCALL::RevertToSelf ( ) /*++ Function Name:RevertToSelf Parameters: Description: As with ImpersonateClient, this is relatively easy. We just check to see if we should let the RPC protocol level security deal with it or the transport. Returns: --*/ { return Connection->RevertToSelf(); } RPC_STATUS OSF_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; HANDLE ImpersonationToken; BOOL Result; PAUTHZ_CLIENT_CONTEXT_HANDLE pAuthzClientContextPlaceholder; SECURITY_CONTEXT *SecurityContext = Connection->CurrentSecurityContext; SECURITY_STATUS SecurityStatus; BOOL fNeedToCloseToken; AUTHZ_CLIENT_CONTEXT_HANDLE AuthzContext; ASSERT (AuthzResourceManager != NULL); if (!SecurityContext) { return RPC_S_NO_CONTEXT_AVAILABLE; } AuthzContext = SecurityContext->GetAuthzContext(); if (ImpersonateOnReturn) { Status = OSF_SCALL::ImpersonateClient(); if (Status != RPC_S_OK) { RpcpErrorAddRecord(EEInfoGCRuntime, Status, EEInfoDLOSF_SCALL__GetAuthorizationContext10, (ULONGLONG)this, (ULONGLONG)0); return Status; } } if (AuthzContext) { Status = DuplicateAuthzContext(AuthzContext, pExpirationTime, Identifier, Flags, DynamicGroupArgs, pAuthzClientContext); if ((Status != RPC_S_OK) && ImpersonateOnReturn) { RevertStatus = OSF_SCALL::RevertToSelf(); ASSERT(RevertStatus == RPC_S_OK); } // EEInfo, if any, has already been added return Status; } // if there was Authz context created, we would have // returned by now. If we are here, this means there // is none. Create it. Status = SecurityContext->GetAccessToken(&ImpersonationToken, &fNeedToCloseToken); if (Status) { if (ImpersonateOnReturn) { RevertStatus = OSF_SCALL::RevertToSelf(); ASSERT(RevertStatus == RPC_S_OK); } return Status; } Status = CreateAndSaveAuthzContextFromToken(SecurityContext->GetAuthzContextAddress(), ImpersonationToken, AuthzResourceManager, pExpirationTime, Identifier, Flags, DynamicGroupArgs, pAuthzClientContext); if (fNeedToCloseToken) { CloseHandle(ImpersonationToken); } if (Status) { if (ImpersonateOnReturn) { RevertStatus = OSF_SCALL::RevertToSelf(); ASSERT(RevertStatus == RPC_S_OK); } return Status; } return RPC_S_OK; } RPC_STATUS OSF_SCALL::GetAssociationContextCollection ( OUT ContextCollection **CtxCollection ) /*++ Function Name: GetAssociationContextCollection Parameters: CtxCollection - a placeholder where to put the pointer to the context collection. Description: The context handle code will call the SCALL to get the collection of context handles for this association. The SCALL method will simply delegate to the association. Returns: RPC_S_OK for success or RPC_S_* for error. --*/ { return Connection->GetAssociationContextCollection(CtxCollection); } void OSF_SCALL::CleanupCallAndSendFault ( IN RPC_STATUS Status, IN int DidNotExecute ) /*++ Function Name:CleanupCallAndSendFault Parameters: Status - the error code for the fault Description: A syntactic sugar function that saves all relevant call members in a local variable, cleans up the call, and then sends the fault directly on the connection. Designed to prevent the case where we send the fault to the client and the next request comes in before we have made this call available - this confuses the server. Returns: --*/ { p_context_id_t p_cont = 0; OSF_SCONNECTION *pLocalConnection; unsigned long LocalCallId = CallId; if (CurrentBinding) p_cont = (p_context_id_t)CurrentBinding->GetPresentationContext(); pLocalConnection = Connection; // make the call available before we send the fault CleanupCall(); pLocalConnection->SendFault(Status, DidNotExecute, LocalCallId, p_cont); } RPC_STATUS OSF_SCALL::ConvertToServerBinding ( OUT RPC_BINDING_HANDLE __RPC_FAR * ServerBinding ) /*++ Routine Description: If possible, convert this connection into a server binding, meaning a binding handle pointing back to the client. Arguments: ServerBinding - Returns the server binding. Return Value: RPC_S_OK - The server binding has successfully been created. RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate a new binding handle. RPC_S_CANNOT_SUPPORT - This will be returned if the transport does not support query the network address of the client. --*/ { RPC_CHAR * NetworkAddress; RPC_STATUS Status; RPC_CHAR * StringBinding; Status = Connection->TransQueryClientNetworkAddress( &NetworkAddress); if ( Status != RPC_S_OK ) { return(Status); } Status = RpcStringBindingCompose(0, Address->InqRpcProtocolSequence(), NetworkAddress, 0, 0, &StringBinding); delete NetworkAddress; if ( Status != RPC_S_OK ) { return(Status); } Status = RpcBindingFromStringBinding(StringBinding, ServerBinding); if ( ObjectUuidSpecified != 0 && RPC_S_OK == Status) { Status = RpcBindingSetObject(*ServerBinding, (UUID *) &ObjectUuid); } RpcStringFree(&StringBinding); return(Status); } OSF_SCONNECTION::OSF_SCONNECTION ( IN OSF_ADDRESS * TheAddress, IN RPC_CONNECTION_TRANSPORT * ServerInfo, IN OUT RPC_STATUS * Status ) : ConnMutex(Status) { ObjectType = OSF_SCONNECTION_TYPE; MaxFrag = 512; Association = 0; AuthContextId = 0; SavedHeader = 0; SavedHeaderSize = 0; CurrentSecurityContext = 0; RpcSecurityBeingUsed = 0; SecurityContextAltered = 0; AdditionalSpaceForSecurity = 0; DceSecurityInfo.SendSequenceNumber = 0; DceSecurityInfo.ReceiveSequenceNumber = 0; AuthContinueNeeded = 0; CurrentCallId=-1; CachedSCallAvailable = 1; this->ServerInfo = ServerInfo; ConnectionClosedFlag = 0; Address = TheAddress; TransConnection = (char *) this+sizeof(OSF_SCONNECTION); fKeepalivesTurnedOn = FALSE; if (IsServerSideDebugInfoEnabled()) { // zero out the CachedSCall - this is a signal that // the OSF_SCALL constructor will use to tell // it is the cached call CachedSCall = NULL; DebugCell = (DebugConnectionInfo *) AllocateCell(&DebugCellTag); if (DebugCell != 0) { memset(DebugCell, 0, sizeof(*DebugCell)); DebugCell->Type = dctConnectionInfo; TheAddress->GetDebugCellIDForThisObject(&DebugCell->Endpoint); } else *Status = RPC_S_OUT_OF_MEMORY; } else DebugCell = NULL; // Create an SCALL object. // If the app verifier is enabled, we will create an object which // supports the verifier checks. if (gfRPCVerifierEnabled) { CachedSCall = new (ServerInfo->SendContextSize) OSF_SCALL_AVRF(this, Status); } else { CachedSCall = new (ServerInfo->SendContextSize) OSF_SCALL(this, Status); } if (CachedSCall == 0) { *Status = RPC_S_OUT_OF_MEMORY; } fExclusive = 0; fDontFlush = 0; fFirstCall = 0; fCurrentlyDispatched = 0; } OSF_SCONNECTION::~OSF_SCONNECTION ( ) { OSF_SBINDING * SBinding; SECURITY_CONTEXT * SecurityContext; DictionaryCursor cursor; if (CachedSCall) { delete CachedSCall; } ASSERT( AuthInfo.PacHandle == 0 ); if ( CurrentSecurityContext && AuthInfo.PacHandle ) { CurrentSecurityContext->DeletePac( AuthInfo.PacHandle ); } SecurityContextDict.Reset(cursor); while ( (SecurityContext = SecurityContextDict.Next(cursor)) != 0 ) delete SecurityContext; Bindings.Reset(cursor); while (SBinding = Bindings.Next(cursor)) delete SBinding; if (Association) Association->RemoveConnection(); if (SavedHeader) { RpcpFarFree(SavedHeader); } if (ServerInfo) { // // ServerInfo will be set to 0 when create on the SCONNECTION fails // look at NewConnection // ServerInfo->Close(TransConnection, fDontFlush); } if (DebugCell) { FreeCell(DebugCell, &DebugCellTag); } } RPC_STATUS OSF_SCONNECTION::TransSend ( IN void * Buffer, IN unsigned int BufferLength ) /*++ --*/ { RPC_STATUS Status; { rpcconn_common * pkt = (rpcconn_common *) Buffer; LogEvent(SU_SCONN, EV_PKT_OUT, this, 0, (pkt->PTYPE << 16) | pkt->frag_length); } if (ConnectionClosedFlag != 0) return(RPC_P_CONNECTION_CLOSED); if (DebugCell) { DebugCell->LastSendTime = NtGetTickCount(); DebugCell->LastTransmitFragmentSize = (USHORT) BufferLength; } DceSecurityInfo.SendSequenceNumber += 1; Status = ServerInfo->SyncSend( TransConnection, BufferLength, Buffer, TRUE, TRUE, INFINITE); // Timeout VALIDATE(Status) { RPC_S_OK, RPC_S_OUT_OF_MEMORY, RPC_S_OUT_OF_RESOURCES, RPC_P_SEND_FAILED } END_VALIDATE; if ( Status == RPC_S_OK ) { GlobalRpcServer->PacketSent(); } if ( Status == RPC_P_SEND_FAILED ) { ConnectionClosedFlag = 1; } return(Status); } RPC_STATUS OSF_SCONNECTION::TransAsyncSend ( IN void * Buffer, IN unsigned int BufferLength, IN void *SendContext ) /*++ Function Name:TransAsyncSend Parameters: Description: Returns: --*/ { RPC_STATUS Status; { rpcconn_common * pkt = (rpcconn_common *) Buffer; LogEvent(SU_SCONN, EV_PKT_OUT, this, 0, (pkt->PTYPE << 16) | pkt->frag_length); } if ( ConnectionClosedFlag != 0 ) { return(RPC_P_CONNECTION_CLOSED); } if (DebugCell) { DebugCell->LastSendTime = NtGetTickCount(); DebugCell->LastTransmitFragmentSize = (USHORT) BufferLength; } DceSecurityInfo.SendSequenceNumber += 1; Status = ServerInfo->Send(TransConnection, BufferLength, (BUFFER) Buffer, SendContext); if (Status == RPC_S_OK) { GlobalRpcServer->PacketSent(); } if ( Status == RPC_P_SEND_FAILED ) { ConnectionClosedFlag = 1; } VALIDATE(Status) { RPC_S_OK, RPC_S_OUT_OF_MEMORY, RPC_S_OUT_OF_RESOURCES, RPC_P_SEND_FAILED } END_VALIDATE; return(Status); } RPC_STATUS OSF_SCONNECTION::TransAsyncReceive ( ) /*++ Function Name:TransAsyncReceive Parameters: Description: Returns: --*/ { RPC_STATUS Status; // // Each outstanding receive will hold a reference // on the connection // AddReference(); // CONN++ if (ConnectionClosedFlag != 0) { AbortConnection(); return(RPC_P_CONNECTION_CLOSED); } Status = ServerInfo->Recv(TransConnection); if (Status != RPC_S_OK) { VALIDATE(Status) { RPC_P_RECEIVE_FAILED, RPC_P_CONNECTION_SHUTDOWN, RPC_P_CONNECTION_CLOSED } CORRUPTION_VALIDATE { RPC_S_PROTOCOL_ERROR } CORRUPTION_END_VALIDATE; if (fExclusive && !CachedSCallAvailable) CachedSCall->WakeUpPipeThreadIfNecessary(RPC_S_CALL_FAILED); ConnectionClosedFlag = 1; AbortConnection(); } return Status; } unsigned int OSF_SCONNECTION::TransMaximumSend ( ) /*++ --*/ { return(ServerInfo->MaximumFragmentSize); } RPC_STATUS OSF_SCONNECTION::TransImpersonateClient ( ) /*++ Function Name:TransImpersonateClient Parameters: Description: If the transport module supports impersonation it will provide the sImpersonateClient entry point, in which case we call it. If an error occurs (indicated by sImpersonateClient returning non-zero), then no context is available. NOTE: this is the correct error code for NT; it may not be the right one (or only one) for other transports which support impersonation. Returns: --*/ { RPC_STATUS Status; if ( ServerInfo->ImpersonateClient == 0 ) { return(RPC_S_CANNOT_SUPPORT); } Status = ServerInfo->ImpersonateClient(TransConnection); VALIDATE(Status) { RPC_S_OK, RPC_S_NO_CONTEXT_AVAILABLE } END_VALIDATE; return(Status); } void OSF_SCONNECTION::TransRevertToSelf ( ) /*++ --*/ // As with TransImpersonateClient, if the transport module supports // impersonation, then sRevertToSelf will be non-zero. We do not have // to worry about errors. // // For revert to self to work in NT, the transport module needs to know // the handle of the calling thread when it was originally created. None // of the other operating systems we support at this point have // impersonation built into the transports. { RPC_STATUS Status; if ( ServerInfo->RevertToSelf != 0 ) { Status = ServerInfo->RevertToSelf(TransConnection); ASSERT( Status == RPC_S_OK ); } } void OSF_SCONNECTION::TransQueryClientProcess ( OUT RPC_CLIENT_PROCESS_IDENTIFIER * ClientProcess ) /*++ Routine Description: We need to obtain the client process identifier for the client process at the other end of this connection. This is necessary so that we can determine whether or not a connection should belong to a given association. We need to do this so that context handles (which hang off of associations) are secure. Arguments: ClientProcess - Returns the client process identifier for the client process at the other end of this connection. --*/ { RPC_STATUS Status; if ( ServerInfo->QueryClientId == 0 ) { ClientProcess->ZeroOut(); } else { Status = ServerInfo->QueryClientId(TransConnection, ClientProcess); ASSERT( Status == RPC_S_OK ); } } void OSF_SCONNECTION::TransQueryClientIpAddress ( IN OUT RPC_CLIENT_IP_ADDRESS *ClientIpAddress ) /*++ Routine Description: Obtain the ip address of the client as precisely as the transport allows. If the transport does not support such functionality, put in a default empty ip address. Arguments: ClientIpAddress - Returns the ip address of the client at the other end of this connection. --*/ { RPC_STATUS Status; if ( ServerInfo->QueryClientIpAddress == 0 ) { ClientIpAddress->ZeroOut(); } else { Status = ServerInfo->QueryClientIpAddress(TransConnection, ClientIpAddress); ASSERT( Status == RPC_S_OK ); } } RPC_STATUS OSF_SCONNECTION::TransQueryClientNetworkAddress ( OUT RPC_CHAR ** NetworkAddress ) /*++ Routine Description: This routine is used to query the network address of the client at the other end of this connection. Arguments: NetworkAddress - Returns the client's network address. Return Value: RPC_S_OK - The client's network address has successfully been obtained. RPC_S_OUT_OF_MEMORY - Insufficient memory is available to complete the operation. RPC_S_CANNOT_SUPPORT - This particular transport implementation does not support this operation. --*/ { RPC_STATUS Status; if ( ( ServerInfo->TransInterfaceVersion < 2 ) || ( ServerInfo->QueryClientAddress == 0 ) ) { return(RPC_S_CANNOT_SUPPORT); } Status = ServerInfo->QueryClientAddress(TransConnection, NetworkAddress); return(Status); } void OSF_SCONNECTION::AbortConnection ( ) /*++ Routine Description: --*/ { DictionaryCursor cursor; // // When AbortConnection is called, // there should be no pending IO // // // Delete the object, ie: remove the object reference // Delete(); // // If there are calls stuck in callbacks, wake them up // if (fExclusive) { ConnMutex.Request(); if (CachedSCallAvailable == 0) { CachedSCall->DeactivateCall(); CachedSCallAvailable = 1; ConnMutex.Clear(); CachedSCall->AbortCall(); } else { ConnMutex.Clear(); } } else { ConnMutex.Request(); OSF_SCALL *NextCall; CallDict.Reset(cursor); while ((NextCall = CallDict.Next(cursor)) != 0) { NextCall->AbortCall(); } ConnMutex.Clear(); } // // Remove the reference held by the pending receive // RemoveReference(); // CONN-- } void OSF_SCONNECTION::FreeObject ( ) { RemoveFromAssociation(); delete this; } void OSF_SCONNECTION::FreeSCall ( IN OSF_SCALL *SCall, IN BOOL fRemove ) /*++ Function Name:FreeSCall Parameters: Description: Returns: --*/ { ASSERT(SCall->BufferQueue.IsQueueEmpty()); if (fExclusive == 0) { if (fRemove) { OSF_SCALL *Call; ConnMutex.Request(); Call = CallDict.Delete(ULongToPtr(SCall->CallId)); ConnMutex.Clear(); ASSERT(Call == 0 || Call == SCall); } // CurrentBinding is initialized in OSF_SCALL::BeginRpcCall // by a call to OSF_CCONNECTION::LookupBinding. That call may // not succeed if we do not find the binding in the dictionary, // or we may fail before initialization. if (SCall->CurrentBinding != NULL) { RPC_INTERFACE *CallInterface; CallInterface = SCall->CurrentBinding->GetInterface(); if (SCall->pAsync) { CallInterface->EndCall(0, 1); if (CallInterface->IsAutoListenInterface()) { // This is the path where async calls complete. // We need to decrement CallNumber. CallInterface->EndAutoListenCall(TRUE); } } } SCall->DeactivateCall(); if (SCall == CachedSCall) { CachedSCallAvailable = 1; } else { delete SCall; } } // // Remove the reference held by the call // RemoveReference(); // CONN-- } RPC_STATUS OSF_SCONNECTION::TransGetBuffer ( OUT void * * Buffer, IN unsigned int BufferLength ) { int * Memory; // // Our memory allocator returns memory which is aligned by at least // 8, so we dont need to worry about aligning it. // Memory = (int *) CoAllocateBuffer(BufferLength); if ( Memory == 0 ) { return(RPC_S_OUT_OF_MEMORY); } ASSERT( IsBufferAligned(Memory) ); *Buffer = Memory; return(RPC_S_OK); } void OSF_SCONNECTION::TransFreeBuffer ( // Free a buffer. IN void * Buffer ) { CoFreeBuffer(Buffer); } void OSF_SCONNECTION::ProcessReceiveComplete ( IN RPC_STATUS EventStatus, IN BUFFER Buffer, IN UINT BufferLength ) /*++ Function Name:ProcessReceiveComplete Parameters: Description: Returns: --*/ { rpcconn_common *Packet = (rpcconn_common *) Buffer; rpcconn_auth3 * AuthThirdLegPacket; sec_trailer * NewSecurityTrailer; OSF_SCALL *SCall = 0; RPC_STATUS Status; SECURITY_BUFFER_DESCRIPTOR InputBufferDescriptor; SECURITY_BUFFER InputBuffers[4]; BOOL fReceivePosted = 0; BOOL fDNE = 0; if (EventStatus) { LogEvent(SU_SCONN, EV_PKT_IN, this, LongToPtr(EventStatus)); } else { if (Packet->PTYPE == rpc_request) { LogEvent(SU_SCONN, EV_PKT_IN, this, 0, (((rpcconn_request *)Packet)->opnum << 24) | (Packet->PTYPE << 16) | Packet->frag_length); } else { LogEvent(SU_SCONN, EV_PKT_IN, this, 0, (Packet->PTYPE << 16) | Packet->frag_length); } } if (DebugCell) { DebugCell->LastReceiveTime = NtGetTickCount(); DebugCell->LastTransmitFragmentSize = (USHORT)BufferLength; } if (EventStatus != RPC_S_OK) { VALIDATE(EventStatus) { RPC_P_CONNECTION_CLOSED, RPC_P_RECEIVE_FAILED, RPC_P_CONNECTION_SHUTDOWN } END_VALIDATE; ConnectionClosedFlag = 1; if (fExclusive && !CachedSCallAvailable) CachedSCall->WakeUpPipeThreadIfNecessary(RPC_S_CALL_FAILED); TransFreeBuffer(Buffer); AbortConnection(); return; } ASSERT(EventStatus == 0); ASSERT(Buffer); GlobalRpcServer->PacketReceived(); // // Check and make sure that if this is the first packet on this // connection that it is a bind packet. // if ((Association == 0) && (Packet->PTYPE != rpc_bind)) { SendBindNak(protocol_version_not_supported, Packet->call_id); TransFreeBuffer(Packet); AbortConnection(); return; } // // A non bind or alter-context packet can't exceed MaxFrag bytes. // A bind packet will include the token and can get arbitrarily large. // if (Packet->PTYPE != rpc_bind && Packet->PTYPE != rpc_alter_context && ((DataConvertEndian(Packet->drep) == 0 && Packet->frag_length > TransMaximumSend()) || (DataConvertEndian(Packet->drep) != 0 && RpcpByteSwapShort(Packet->frag_length) > TransMaximumSend()) ) ) { CORRUPTION_ASSERT(0 && "Packet->frag_length exceeds TransMaximumSend()"); SC_CLEANUP(RPC_S_PROTOCOL_ERROR, 0); } switch (Packet->PTYPE) { case rpc_request: if (fExclusive) { if (Packet->pfc_flags & PFC_FIRST_FRAG && CachedSCallAvailable) { // // New call is about to be started // Add a reference on the connection // AddReference(); // CONN++ CachedSCallAvailable = 0; fReceivePosted = CachedSCall->BeginRpcCall(Packet, BufferLength); } // If we are not starting a new call, make sure that the call // for this connection is active. else if (CachedSCallAvailable == false) { fReceivePosted = CachedSCall->ProcessReceivedPDU(Packet, BufferLength); } // // Looks like we have received a request without the PFC_FIRST_FRAG, but // the call for this connection has been deleted and cached. The call can't // be expecting this packet. // The only scenario in which this is expected is when a call has failed and sent // back a failure packet but the client has not seen it yet and continues sending. // In this case we can ignore the packet. // else { TransFreeBuffer(Packet); fReceivePosted = 0; goto End; } } else { if ((long) Packet->call_id <= (long) CurrentCallId) { // // If it is a non-first fragment, or if it is a callback // SCall = FindCall(Packet->call_id); if (SCall == 0) { if ((long) Packet->call_id < (long) CurrentCallId || (Packet->pfc_flags & PFC_FIRST_FRAG) == 0) { // // Can't find the call. This could be because the pipe call // raised an exception and the call is now complete. // TransFreeBuffer(Packet); fReceivePosted = 0; goto End; } // // If the client is Win95, it will use the same call_id // for subsequent calls on the same connection // } } if (SCall == 0) { CurrentCallId = Packet->call_id; // // A new call is about to be started, create one // if (InterlockedCompareExchange( (LPLONG) &CachedSCallAvailable, 0, 1)) { SCall = CachedSCall; } else { Status = RPC_S_OK; // Create an SCALL object. // If the app verifier is enabled, we will create an object which // supports the verifier checks. if (gfRPCVerifierEnabled) { SCall = new (ServerInfo->SendContextSize) OSF_SCALL_AVRF(this, &Status); } else { SCall = new (ServerInfo->SendContextSize) OSF_SCALL(this, &Status); } if (SCall == 0 || Status != RPC_S_OK) { SendFault(RPC_S_OUT_OF_MEMORY, 1, Packet->call_id); if (SCall != 0) { delete SCall; } TransFreeBuffer(Packet); break; } } // // New call is about to be started // Add a reference on the connection // AddReference(); // CONN++ int DictKey; ASSERT(SCall); ConnMutex.Request(); // A new SCall/call_id will be inserted into the CallDict iff: // Packet->call_id <= CurrentCallId and FindCall does not find a call OR // Packet->call_id > CurrentCallId // In both of these cases there is no way that an existing call can be overwritten. DictKey = CallDict.Insert(ULongToPtr(Packet->call_id), SCall); ConnMutex.Clear(); if (DictKey == -1) { SendFault(RPC_S_OUT_OF_MEMORY, 1, Packet->call_id); FreeSCall(SCall); TransFreeBuffer(Packet); break; } ASSERT(SCall); // // We need this reference to prevent the call from going // away from under us when the client goes away // SCall->AddReference(); // CALL++ fReceivePosted = SCall->BeginRpcCall(Packet, BufferLength); SCall->OSF_SCALL::RemoveReference(); // CALL-- } else { ASSERT(SCall); // // The packet will be freed by the callee // fReceivePosted = SCall->ProcessReceivedPDU(Packet, BufferLength); // // Remove the reference added by the lookup // SCall->OSF_SCALL::RemoveReference(); // CALL-- } } break; case rpc_bind: case rpc_alter_context: // // Save the unbyteswapped header for the security related stuff // Especially if SECURITY is on. // For Bind and AlterContext we save entire packet [we can do better though] // if (Packet->auth_length != 0) { if (SavedHeaderSize < BufferLength) { if (SavedHeader != 0) { ASSERT(SavedHeaderSize != 0); RpcpFarFree(SavedHeader); } SavedHeader = RpcpFarAllocate(BufferLength); if (SavedHeader == 0) { if ( Association == 0 ) { SendBindNak( protocol_version_not_supported, Packet->call_id); TransFreeBuffer(Packet); AbortConnection(); return; } SC_CLEANUP(RPC_S_OUT_OF_MEMORY, 1); } SavedHeaderSize = BufferLength; RpcpMemoryCopy(SavedHeader, Packet, BufferLength); } else { RpcpMemoryCopy(SavedHeader, Packet, BufferLength); } } // // These things can take quite a while and could cause deadlocks // if we dont have any listening threads // Address->CreateThread(); Status = ValidatePacket(Packet, BufferLength); if (Status != RPC_S_OK) { ASSERT( Status == RPC_S_PROTOCOL_ERROR ); // // If this the first packet on the connection, it should be an // rpc_bind packet, and we want to send a rpc_bind_nak packet // rather than a fault. We can tell that this is the first packet // because the association is zero. // if ( Association == 0 ) { SendBindNak(protocol_version_not_supported, Packet->call_id); TransFreeBuffer(Packet); AbortConnection(); return; } // // It is not the first packet, so we need to send a fault instead, // and then we will blow the connection away. // SC_CLEANUP(RPC_S_PROTOCOL_ERROR, 1); } if (Packet->PTYPE == rpc_bind) { if (Association != 0) { SendBindNak(reason_not_specified_reject, Packet->call_id); TransFreeBuffer(Packet); AbortConnection(); return; } // // The packet will be freed by the callee // if (AssociationRequested( (rpcconn_bind *) Packet, BufferLength) != 0) { AbortConnection(); return; } } else { // Packet->PTYPE == rpc_alter_context per the case. if (Association == 0) { SendFault(RPC_S_PROTOCOL_ERROR, 1, Packet->call_id); } // // The packet will be freed by the callee // if (AlterContextRequested( (rpcconn_alter_context *) Packet, BufferLength) != 0 ) { AbortConnection(); return; } } break; case rpc_auth_3: // // This means that the client sent us back a third leg // AuthInfo.Authentication packet. // CORRUPTION_ASSERT(AuthContinueNeeded != 0); // Save the unbyteswapped header CORRUPTION_ASSERT(AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE); AuthThirdLegPacket = (rpcconn_auth3 *) Buffer; if (AuthContinueNeeded == 0) { CORRUPTION_ASSERT(0); SC_CLEANUP(RPC_S_PROTOCOL_ERROR, 1); } // Since we have just received the third leg auth packet, we // are not expecting one any longer. AuthContinueNeeded = 0; if (SavedHeaderSize < BufferLength) { if (SavedHeader != 0) { ASSERT(SavedHeaderSize != 0); RpcpFarFree(SavedHeader); } SavedHeader = RpcpFarAllocate(BufferLength); if (SavedHeader == 0) { SC_CLEANUP(RPC_S_OUT_OF_MEMORY, 1); } SavedHeaderSize = BufferLength; RpcpMemoryCopy(SavedHeader, AuthThirdLegPacket, BufferLength); } else { RpcpMemoryCopy(SavedHeader, AuthThirdLegPacket, BufferLength); } // // These things can take quite a while and could cause deadlocks // if we dont have any listening threads // Address->CreateThread(); Status = ValidatePacket( (rpcconn_common *) AuthThirdLegPacket, BufferLength); if ( Status != RPC_S_OK ) { SC_CLEANUP(RPC_S_OUT_OF_MEMORY, 1); } if ( AuthThirdLegPacket->common.PTYPE != rpc_auth_3 ) { SC_CLEANUP(RPC_S_OUT_OF_MEMORY, 1); } NewSecurityTrailer = (sec_trailer *) (((unsigned char *) AuthThirdLegPacket) + AuthThirdLegPacket->common.frag_length - sizeof(sec_trailer) - AuthThirdLegPacket->common.auth_length); if ( (NewSecurityTrailer->auth_type != AuthInfo.AuthenticationService) || (NewSecurityTrailer->auth_level != AuthInfo.AuthenticationLevel) ) { SC_CLEANUP(RPC_S_OUT_OF_MEMORY, 1); } InputBufferDescriptor.ulVersion = 0; InputBufferDescriptor.cBuffers = 4; InputBufferDescriptor.pBuffers = InputBuffers; InputBuffers[0].cbBuffer = sizeof(rpcconn_auth3); InputBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; InputBuffers[0].pvBuffer = SavedHeader; InputBuffers[1].cbBuffer = AuthThirdLegPacket->common.frag_length - sizeof(rpcconn_auth3) - AuthThirdLegPacket->common.auth_length; InputBuffers[1].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; InputBuffers[1].pvBuffer = (char *) SavedHeader + sizeof(rpcconn_auth3); InputBuffers[2].cbBuffer = AuthThirdLegPacket->common.auth_length; InputBuffers[2].BufferType = SECBUFFER_TOKEN; InputBuffers[2].pvBuffer = NewSecurityTrailer + 1; InputBuffers[3].cbBuffer = sizeof(DCE_INIT_SECURITY_INFO); InputBuffers[3].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY; InputBuffers[3].pvBuffer = &InitSecurityInfo; Status = AcceptThirdLeg( *((unsigned long *) AuthThirdLegPacket->common.drep), &InputBufferDescriptor, 0); LogEvent(SU_SCONN, EV_SEC_ACCEPT3, this, LongToPtr(Status), 0); if ( Status != RPC_S_OK ) { SC_CLEANUP(RPC_S_OUT_OF_MEMORY, 1); } TransFreeBuffer(AuthThirdLegPacket); DceSecurityInfo.ReceiveSequenceNumber += 1; // // We need to figure out how much space to reserve for security // information at the end of request and response packets. // In addition to saving space for the signature or header, // we need space to pad the packet to a multiple of the maximum // security block size as well as for the security trailer. // ASSERT(AdditionalSpaceForSecurity == 0); AdditionalSpaceForSecurity = CalculateAdditionalSpaceForSecurity ( AuthInfo.AuthenticationLevel, AuthInfo.AuthenticationService ); break; case rpc_response: case rpc_cancel : case rpc_orphaned : case rpc_fault : if (fExclusive) { // Check if there is an active call. if (!CachedSCallAvailable) { // Response and fault are only valid for callbacks. if (Packet->PTYPE == rpc_response || Packet->PTYPE == rpc_fault) { if (CachedSCall->CallStack == 0) { CORRUPTION_ASSERT(0 && "Unexpected packet type"); SC_CLEANUP(RPC_S_PROTOCOL_ERROR, 0); } } // // The packet will be freed by the callee // fReceivePosted = CachedSCall->ProcessReceivedPDU( Packet, BufferLength); } else { TransFreeBuffer(Packet); fReceivePosted = 0; goto End; } } else { SCall = FindCall(Packet->call_id); if (SCall == 0) { if (Packet->PTYPE == rpc_cancel || Packet->PTYPE == rpc_orphaned) { // // Too late, looks like the call is complete // TransFreeBuffer(Packet); } else { #if DBG PrintToDebugger( "RPC: Conn: 0x%lXNo SCall corresponding to the CallId: %d\n", this, Packet->call_id); CORRUPTION_ASSERT(0); #endif SC_CLEANUP(RPC_S_PROTOCOL_ERROR, 0); } break; } // Response and fault are only valid for callbacks. if (Packet->PTYPE == rpc_response || Packet->PTYPE == rpc_fault) { if (SCall->CallStack == 0) { CORRUPTION_ASSERT(0 && "Unexpected packet type"); SC_CLEANUP(RPC_S_PROTOCOL_ERROR, 0); } } // // The packet will be freed by the callee // fReceivePosted = SCall->ProcessReceivedPDU(Packet, BufferLength); // // Remove the reference added by the lookup // SCall->OSF_SCALL::RemoveReference(); // CALL-- } break; default: CORRUPTION_ASSERT(0); SC_CLEANUP(RPC_S_PROTOCOL_ERROR, 0); } End: // // Submit the receive for the next packet // if (!fReceivePosted) { TransAsyncReceive(); } // // Remove the reference held by the pending receive // OSF_SCONNECTION::RemoveReference(); // CONN-- return; Cleanup: SendFault(Status, fDNE, Packet->call_id); TransFreeBuffer(Packet); AbortConnection(); } RPC_STATUS OSF_SCONNECTION::ImpersonateClient ( ) /*++ Function Name:ImpersonateClient Parameters: Description: Returns: --*/ { RPC_STATUS Status; if ( !RpcSecurityBeingUsed ) { Status = SetThreadSecurityContext( (SECURITY_CONTEXT *) MAXUINT_PTR); if (RPC_S_OK != Status) { return Status; } return TransImpersonateClient(); } SECURITY_CONTEXT * SecurityContext = CurrentSecurityContext; if (!SecurityContext) { ASSERT(SecurityContextAltered); return RPC_S_NO_CONTEXT_AVAILABLE; } Status = SetThreadSecurityContext( SecurityContext); if (RPC_S_OK != Status) { return Status; } Status = SecurityContext->ImpersonateClient(); if (RPC_S_OK != Status) { ClearThreadSecurityContext(); } return Status; } RPC_STATUS OSF_SCONNECTION::RevertToSelf ( ) /*++ Function Name:RevertToSelf Parameters: Description: Returns: --*/ { SECURITY_CONTEXT * SecurityContext = ClearThreadSecurityContext(); if (!RpcSecurityBeingUsed) { if (SecurityContext) { ASSERT(SecurityContext == (SECURITY_CONTEXT *) MAXUINT_PTR); TransRevertToSelf(); } return RPC_S_OK; } if (SecurityContext) { SecurityContext->RevertToSelf(); } return(RPC_S_OK); } C_ASSERT(FaultSizeWithoutEEInfo == FIELD_OFFSET(rpcconn_fault, reserved2) + sizeof(long)); void OSF_SCONNECTION::SendFault ( IN RPC_STATUS Status, IN int DidNotExecute, IN unsigned long CallId, IN p_context_id_t p_cont_id ) /*++ Function Name:SendFault Parameters: Description: Returns: --*/ { rpcconn_fault *Fault; size_t FaultSize; BOOL fEEInfoPresent = FALSE; if (g_fSendEEInfo) { fEEInfoPresent = PickleEEInfoIntoPacket(FaultSizeWithoutEEInfo, (PVOID *)&Fault, &FaultSize); } if (fEEInfoPresent) { Fault->reserved = FaultEEInfoPresent; Fault->alloc_hint = FaultSize; } else { FaultSize = FaultSizeWithoutEEInfo; Fault = (rpcconn_fault *)_alloca(FaultSize); RpcpMemorySet(Fault, 0, FaultSize); Fault->alloc_hint = FaultSize; } ConstructPacket((rpcconn_common *)Fault, rpc_fault, FaultSize); if (DidNotExecute != 0) { DidNotExecute = PFC_DID_NOT_EXECUTE; } if (Status == ERROR_SHUTDOWN_IN_PROGRESS) { if (DidNotExecute) { Status = RPC_S_SERVER_UNAVAILABLE; } else { Status = ERROR_SERVER_SHUTDOWN_IN_PROGRESS; } } Fault->common.pfc_flags |= PFC_FIRST_FRAG | PFC_LAST_FRAG | DidNotExecute; Fault->status = MapToNcaStatusCode(Status); Fault->common.call_id = CallId; Fault->p_cont_id = p_cont_id; TransSend(Fault, FaultSize); if (fEEInfoPresent) delete Fault; } BOOL OSF_SCONNECTION::PickleEEInfoIntoPacket ( IN size_t PickleStartOffset, OUT PVOID *Packet, OUT size_t *PacketSize) /*++ Function Name: PickeEEInfoIntoPacket Parameters: PickleStartOffset - the offset in bytes where the pickling starts Packet - the allocated packet will be placed here on success. PacketSize - the size of the packet if success is returned. If failure is returned, this parameter is undefined Description: Checks for EEInfo on the thread, trims the EEInfo to MaxFrag, allocates the packet, zeroes it out, and pickles the EEInfo starting from PickleStartOffset. Returns: TRUE if EEInfo was pickled. FALSE if not. --*/ { unsigned char *CurrentPacket; BOOL fEEInfoPresent = FALSE; ExtendedErrorInfo *EEInfo; RPC_STATUS RpcStatus; size_t CurrentPacketSize; EEInfo = RpcpGetEEInfo(); if (EEInfo) { ASSERT(MaxFrag > 0); AddComputerNameToChain(EEInfo); TrimEEInfoToLength (MaxFrag, &CurrentPacketSize); if (CurrentPacketSize != 0) { CurrentPacketSize += PickleStartOffset; CurrentPacket = new unsigned char[CurrentPacketSize]; if (CurrentPacket) { ASSERT(IsBufferAligned(CurrentPacket + PickleStartOffset)); RpcpMemorySet(CurrentPacket, 0, CurrentPacketSize); RpcStatus = PickleEEInfo(EEInfo, CurrentPacket + PickleStartOffset, CurrentPacketSize - PickleStartOffset); if (RpcStatus == RPC_S_OK) { fEEInfoPresent = TRUE; *Packet = CurrentPacket; *PacketSize = CurrentPacketSize; } else { delete [] CurrentPacket; } } } } return fEEInfoPresent; } RPC_STATUS OSF_SCONNECTION::SendFragment( IN OUT rpcconn_common *pFragment, IN unsigned int LastFragmentFlag, IN unsigned int HeaderSize, IN unsigned int MaxSecuritySize, IN unsigned int DataLength, IN unsigned int MaximumFragmentLength, IN unsigned char *MyReservedForSec, IN BOOL fAsync, IN void *SendContext ) /*++ Function Name:SendFragment Parameters: Description: Returns: --*/ { sec_trailer * SecurityTrailer; unsigned int SecurityLength; unsigned int AuthPadLength; SECURITY_BUFFER_DESCRIPTOR BufferDescriptor; SECURITY_BUFFER SecurityBuffers[5]; DCE_MSG_SECURITY_INFO MsgSecurityInfo; RPC_STATUS Status; unsigned long AuthLevel; ULONG ReadOnlyFlag; long AuthLengthChange; AuthLevel = AuthInfo.AuthenticationLevel; if ( ((AuthLevel != RPC_C_AUTHN_LEVEL_NONE) && (AuthLevel != RPC_C_AUTHN_LEVEL_CONNECT)) || ((AuthLevel == RPC_C_AUTHN_LEVEL_CONNECT) &&(MaxSecuritySize != 0)) ) { if ( LastFragmentFlag == 0 ) { SecurityTrailer = (sec_trailer *) (((unsigned char *) pFragment) + MaximumFragmentLength - MaxSecuritySize); // It is not the last fragment, so we need to save away the // part of the buffer which could get overwritten with // authentication information. We can not use memcpy, // because the source and destination regions may overlap. RpcpMemoryMove(MyReservedForSec, SecurityTrailer, MaxSecuritySize); AuthPadLength = 0; } else { ASSERT( MAXIMUM_SECURITY_BLOCK_SIZE == 16 ); AuthPadLength = Pad16(HeaderSize+DataLength+sizeof(sec_trailer)); // Zero-out un-initialized pad. if (AuthPadLength != 0) { RpcpMemorySet(((unsigned char *) pFragment) + DataLength + HeaderSize, 0, AuthPadLength); } DataLength += AuthPadLength; ASSERT( ((DataLength + HeaderSize+sizeof(sec_trailer)) % MAXIMUM_SECURITY_BLOCK_SIZE) == 0 ); SecurityTrailer = (sec_trailer *) (((unsigned char *) pFragment) + DataLength + HeaderSize); pFragment->pfc_flags |= PFC_LAST_FRAG; } if (GetClientSupportsHeaderSigningFlag()) ReadOnlyFlag = SECBUFFER_READONLY_WITH_CHECKSUM; else ReadOnlyFlag = SECBUFFER_READONLY; SecurityTrailer->auth_type = (unsigned char) AuthInfo.AuthenticationService; SecurityTrailer->auth_level = (unsigned char) AuthLevel; SecurityTrailer->auth_pad_length = (unsigned char) AuthPadLength; SecurityTrailer->auth_reserved = 0; SecurityTrailer->auth_context_id = AuthContextId; BufferDescriptor.ulVersion = 0; BufferDescriptor.cBuffers = 5; BufferDescriptor.pBuffers = SecurityBuffers; SecurityBuffers[0].cbBuffer = HeaderSize; SecurityBuffers[0].BufferType = SECBUFFER_DATA | ReadOnlyFlag; SecurityBuffers[0].pvBuffer = ((unsigned char *) pFragment); SecurityBuffers[1].cbBuffer = (LastFragmentFlag != 0 ? DataLength : (MaximumFragmentLength - HeaderSize - MaxSecuritySize )); SecurityBuffers[1].BufferType = SECBUFFER_DATA; SecurityBuffers[1].pvBuffer = ((unsigned char *) pFragment) + HeaderSize; SecurityBuffers[2].cbBuffer = sizeof(sec_trailer); SecurityBuffers[2].BufferType = SECBUFFER_DATA | ReadOnlyFlag; SecurityBuffers[2].pvBuffer = SecurityTrailer; SecurityBuffers[3].cbBuffer = MaxSecuritySize - sizeof(sec_trailer); SecurityBuffers[3].BufferType = SECBUFFER_TOKEN; SecurityBuffers[3].pvBuffer = SecurityTrailer + 1; SecurityBuffers[4].cbBuffer = sizeof(DCE_MSG_SECURITY_INFO); SecurityBuffers[4].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY; SecurityBuffers[4].pvBuffer = &MsgSecurityInfo; MsgSecurityInfo.SendSequenceNumber = DceSecurityInfo.SendSequenceNumber; MsgSecurityInfo.ReceiveSequenceNumber = DceSecurityInfo.ReceiveSequenceNumber; MsgSecurityInfo.PacketType = pFragment->PTYPE; pFragment->auth_length = (unsigned short) SecurityBuffers[3].cbBuffer; SecurityLength = MaxSecuritySize; if ( LastFragmentFlag != 0 ) { pFragment->frag_length = HeaderSize + DataLength + SecurityLength; } else { pFragment->frag_length += SecurityLength - MaxSecuritySize; } Status = CurrentSecurityContext->SignOrSeal( MsgSecurityInfo.SendSequenceNumber, AuthLevel != RPC_C_AUTHN_LEVEL_PKT_PRIVACY, &BufferDescriptor); AuthLengthChange = pFragment->auth_length - SecurityBuffers[3].cbBuffer; ASSERT( AuthLengthChange >= 0); // if the token ended up shorter than the buffer we supplied, // sanitize the rest of the token buffer to avoid leaking // process data on the wire. if (AuthLengthChange > 0) { RpcpMemorySet(((unsigned char *)(SecurityBuffers[3].pvBuffer)) + SecurityBuffers[3].cbBuffer, 0, AuthLengthChange ); } if (Status != RPC_S_OK) { if ( LastFragmentFlag == 0 ) { RpcpMemoryCopy(SecurityTrailer, MyReservedForSec, MaxSecuritySize); } if (Status == ERROR_SHUTDOWN_IN_PROGRESS) { return Status; } if ( (Status == SEC_E_CONTEXT_EXPIRED) || (Status == SEC_E_QOP_NOT_SUPPORTED) ) { return (RPC_S_SEC_PKG_ERROR); } return (RPC_S_ACCESS_DENIED); } } else { SecurityLength = 0; } ASSERT(pFragment->frag_length <= MaxFrag); if ( LastFragmentFlag != 0 ) { pFragment->pfc_flags |= PFC_LAST_FRAG; ASSERT(pFragment->frag_length == DataLength+HeaderSize+SecurityLength); if (fAsync) { Status = TransAsyncSend( pFragment, pFragment->frag_length, SendContext); } else { Status = TransSend( pFragment, pFragment->frag_length); } if (Status != RPC_S_OK) { if ((Status == RPC_P_CONNECTION_CLOSED) || (Status == RPC_P_SEND_FAILED)) { return(RPC_S_CALL_FAILED_DNE); } if ( Status == RPC_P_RECEIVE_FAILED) { return(RPC_S_CALL_FAILED); } VALIDATE(Status) { RPC_S_OUT_OF_MEMORY, RPC_S_OUT_OF_RESOURCES } END_VALIDATE; return(Status); } return(RPC_S_OK); } ASSERT(pFragment->frag_length == MaximumFragmentLength - MaxSecuritySize + SecurityLength); if (fAsync) { Status = TransAsyncSend ( pFragment, pFragment->frag_length, SendContext); } else { Status = TransSend( pFragment, pFragment->frag_length); // // We need to restore the part of the buffer which we overwrote // with authentication information. // if ((AuthLevel != RPC_C_AUTHN_LEVEL_NONE) &&(MaxSecuritySize != 0)) { RpcpMemoryCopy(SecurityTrailer, MyReservedForSec, MaxSecuritySize); } } if ( Status != RPC_S_OK ) { if ( (Status == RPC_P_CONNECTION_CLOSED) || (Status == RPC_P_SEND_FAILED)) { return(RPC_S_CALL_FAILED_DNE); } VALIDATE(Status) { RPC_S_OUT_OF_MEMORY, RPC_S_OUT_OF_RESOURCES } END_VALIDATE; return(Status); } return Status ; } RPC_STATUS OSF_SCONNECTION::GetServerPrincipalName ( IN unsigned long Flags, OUT RPC_CHAR **ServerPrincipalName OPTIONAL ) /*++ Routine Description: Obtains the server principal name. Arguments: ServerPrincipalName - Returns the server principal name which the client specified. Return Value: RPC_S_OK or RPC_S_* / Win32 error --*/ { RPC_STATUS Status; SECURITY_CONTEXT * SecurityContext; SecurityContext = CurrentSecurityContext; if ( ARGUMENT_PRESENT(ServerPrincipalName) ) { if (AuthInfo.AuthenticationService == RPC_C_AUTHN_GSS_SCHANNEL) { if (AuthInfo.PacHandle == 0) { Status = SecurityContext->GetDceInfo( &AuthInfo.PacHandle, &AuthInfo.AuthorizationService ); if (Status != RPC_S_OK) { return Status; } } Status = RpcCertGeneratePrincipalName( (PCCERT_CONTEXT) AuthInfo.PacHandle, Flags, ServerPrincipalName ); return Status; } else { Status = Address->Server->InquirePrincipalName( SecurityContext->AuthenticationService, ServerPrincipalName); VALIDATE(Status) { RPC_S_OK, RPC_S_OUT_OF_MEMORY } END_VALIDATE; return(Status); } } return RPC_S_OK; } UINT OSF_SCONNECTION::CalculateAdditionalSpaceForSecurity ( IN ULONG AuthenticationLevel, IN ULONG AuthenticationService ) /*++ Routine Description: Calculates how much additional space for security needs to be set aside. Arguments: AuthenticationLevel - the authentication level for the connection. This parameter will be checked for validity. AuthenticationService - the authentication service for the connection. This parameter is not checked for vailidity. Return Value: The additional space we need to set aside for security. --*/ { UINT NewAdditionalSpaceForSecurity; switch (AuthenticationLevel) { case RPC_C_AUTHN_LEVEL_NONE: NewAdditionalSpaceForSecurity = 0; break; case RPC_C_AUTHN_LEVEL_CONNECT: ASSERT(CurrentSecurityContext); NewAdditionalSpaceForSecurity = MAXIMUM_SECURITY_BLOCK_SIZE + sizeof(sec_trailer); // we know the MS providers will not put anything meaningful in the token // for connect level security. Therefore we can skip both allocating space and // putting anything in the token for them. if (AuthenticationService != RPC_C_AUTHN_WINNT && AuthenticationService != RPC_C_AUTHN_GSS_KERBEROS && AuthenticationService != RPC_C_AUTHN_GSS_NEGOTIATE && AuthenticationService != RPC_C_AUTHN_GSS_SCHANNEL ) { NewAdditionalSpaceForSecurity += CurrentSecurityContext->MaximumSignatureLength(); } break; case RPC_C_AUTHN_LEVEL_CALL: case RPC_C_AUTHN_LEVEL_PKT: case RPC_C_AUTHN_LEVEL_PKT_INTEGRITY: ASSERT(CurrentSecurityContext); NewAdditionalSpaceForSecurity = MAXIMUM_SECURITY_BLOCK_SIZE + CurrentSecurityContext->MaximumSignatureLength() + sizeof(sec_trailer); break; case RPC_C_AUTHN_LEVEL_PKT_PRIVACY: ASSERT(CurrentSecurityContext); NewAdditionalSpaceForSecurity = MAXIMUM_SECURITY_BLOCK_SIZE + CurrentSecurityContext->MaximumHeaderLength() + sizeof(sec_trailer); break; default: ASSERT(!"Unknown Security Level\n"); NewAdditionalSpaceForSecurity = 0; } return NewAdditionalSpaceForSecurity; } RPC_STATUS OSF_SCONNECTION::EnsureSecur32DllLoaded ( void ) /*++ Routine Description: Ensures that the secur32 dll is loaded and ready in Secur32Dll and that SecpSetIPAddressFnPtr is initialized. Arguments: Return Value: RPC_S_OK or RPC_S_* error --*/ { RPC_STATUS RpcStatus; if (SecpSetIPAddressFnPtr != NULL) return RPC_S_OK; GlobalMutexRequest(); if (SecpSetIPAddressFnPtr != NULL) { GlobalMutexClear(); return RPC_S_OK; } RpcStatus = RPC_S_OK; // it is possible that this is loaded from a previous attempt ASSERT (Secur32Dll == NULL); Secur32Dll = new DLL (L"secur32.dll", &RpcStatus); if (Secur32Dll == NULL) RpcStatus = RPC_S_OUT_OF_MEMORY; else if (RpcStatus != RPC_S_OK) { delete Secur32Dll; Secur32Dll = NULL; // fall through with the RpcStatus } else { SecpSetIPAddressFnPtr = (SecpSetIPAddressFn) Secur32Dll->GetEntryPoint("SecpSetIPAddress"); // we know GetEntryPoint can't fail if security exports this function. The // only way it can fail is if security doesn't export this function, // which can only be due to major config issue ASSERT(SecpSetIPAddressFnPtr != NULL); if (SecpSetIPAddressFnPtr == NULL) { delete Secur32Dll; Secur32Dll = NULL; RpcStatus = RPC_S_INTERNAL_ERROR; } // fall through with the RpcStatus } GlobalMutexClear(); return RpcStatus; } RPC_STATUS OSF_SCONNECTION::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 - The operation completed successfully. RPC_S_BINDING_HAS_NO_AUTH - The remote procedure call represented by this binding is not authenticated. RPC_S_OUT_OF_MEMORY - Insufficient memory is available to inquire the server principal name. --*/ { RPC_STATUS Status = RPC_S_OK; SECURITY_CONTEXT * SecurityContext; SecurityContext = CurrentSecurityContext; if ( !SecurityContext ) { return(RPC_S_BINDING_HAS_NO_AUTH); } if (AuthenticationLevel) { *AuthenticationLevel = SecurityContext->AuthenticationLevel; } if (AuthenticationService) { *AuthenticationService = SecurityContext->AuthenticationService; } if (Privileges || AuthorizationService) { if (AuthInfo.PacHandle == 0) { Status = SecurityContext->GetDceInfo(&AuthInfo.PacHandle, &AuthInfo.AuthorizationService); } if ( Privileges ) { *Privileges = AuthInfo.PacHandle; } if ( AuthorizationService ) { *AuthorizationService = AuthInfo.AuthorizationService; } if (Status != RPC_S_OK) { return Status; } } Status = GetServerPrincipalName(Flags, ServerPrincipalName); return(Status); } RPC_STATUS OSF_SCONNECTION::InquireCallAttributes ( IN OUT void *RpcCallAttributes ) /*++ Routine Description: Inquire the security context attributes for the OSF 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; SECURITY_CONTEXT * SecurityContext; RPC_CHAR *ServerPrincipalName = NULL; ULONG ServerPrincipalNameLength; // in bytes, including terminating NULL SecurityContext = CurrentSecurityContext; if ( !SecurityContext ) { return(RPC_S_BINDING_HAS_NO_AUTH); } CallAttributes = (RPC_CALL_ATTRIBUTES_V1 *)RpcCallAttributes; CallAttributes->AuthenticationLevel = SecurityContext->AuthenticationLevel; CallAttributes->AuthenticationService = SecurityContext->AuthenticationService; CallAttributes->NullSession = SecurityContext->ContextAttributes & ASC_RET_NULL_SESSION; if (CallAttributes->Flags & RPC_QUERY_CLIENT_PRINCIPAL_NAME) { CallAttributes->ClientPrincipalNameBufferLength = 0; } if (CallAttributes->Flags & RPC_QUERY_SERVER_PRINCIPAL_NAME) { Status = GetServerPrincipalName(CallAttributes->Flags, &ServerPrincipalName); if (Status != RPC_S_OK) return Status; if (ServerPrincipalName) { ServerPrincipalNameLength = (RpcpStringLength(ServerPrincipalName) + 1) * sizeof(RPC_CHAR); // now, see whether the user supplied memory is big enough if (CallAttributes->ServerPrincipalNameBufferLength < ServerPrincipalNameLength) { Status = ERROR_MORE_DATA; } else { // a buffer is specified, and it is large enough RpcpMemoryCopy(CallAttributes->ServerPrincipalName, ServerPrincipalName, ServerPrincipalNameLength); Status = RPC_S_OK; } // in both cases store the resulting length CallAttributes->ServerPrincipalNameBufferLength = ServerPrincipalNameLength; RpcStringFree(&ServerPrincipalName); } else { CallAttributes->ServerPrincipalNameBufferLength = 0; } return Status; } else { return RPC_S_OK; } } OSF_SBINDING * OSF_SCONNECTION::LookupBinding ( IN p_context_id_t PresentContextId ) /*++ Function Name:LookupBinding Parameters: Description: Returns: --*/ { OSF_SBINDING *CurBinding; DictionaryCursor cursor; Bindings.Reset(cursor); while ((CurBinding = Bindings.Next(cursor))) { if (CurBinding->GetPresentationContext() == PresentContextId) { return CurBinding; } } return NULL; } RPC_STATUS OSF_SCONNECTION::GetAssociationContextCollection ( OUT ContextCollection **CtxCollection ) { return Association->GetAssociationContextCollection(CtxCollection); } RPC_STATUS OSF_SCONNECTION::IsClientLocal ( OUT unsigned int * ClientLocalFlag ) /*++ Routine Description: We just need to inquire the client process identifier for this connection; if the first part is zero, then the client is local. Arguments: ClientLocalFlag - Returns an indication of whether or not the client is local (ie. on the same machine as the server). This field will be set to a non-zero value to indicate that the client is local; otherwise, the client is remote. Return Value: RPC_S_OK - This will always be used. --*/ { RPC_CLIENT_PROCESS_IDENTIFIER ClientProcess; int i; TransQueryClientProcess(&ClientProcess); if ( ClientProcess.IsLocal() == FALSE ) { if (ClientProcess.IsNull()) return RPC_S_CANNOT_SUPPORT; *ClientLocalFlag = 0; } else { *ClientLocalFlag = 1; } return(RPC_S_OK); } int OSF_SCONNECTION::SendBindNak ( IN p_reject_reason_t reject_reason, IN unsigned long CallId ) { rpcconn_bind_nak *BindNak; size_t BindNakSize; BOOL fEEInfoPresent = FALSE; int RetVal; if (g_fSendEEInfo) { fEEInfoPresent = PickleEEInfoIntoPacket(BindNakSizeWithoutEEInfo, (PVOID *) &BindNak, &BindNakSize); } if (fEEInfoPresent == FALSE) { BindNakSize = BindNakSizeWithoutEEInfoAndSignature; BindNak = (rpcconn_bind_nak *)_alloca(BindNakSize); RpcpMemorySet(BindNak, 0, BindNakSize); } else { RpcpMemoryCopy (&BindNak->Signature, BindNakEEInfoSignature, sizeof (UUID)); } ConstructPacket((rpcconn_common *) BindNak, rpc_bind_nak, BindNakSize); BindNak->provider_reject_reason = reject_reason; BindNak->versions.n_protocols = 1; BindNak->versions.p_protocols[0].major = OSF_RPC_V20_VERS; BindNak->versions.p_protocols[0].minor = 0; BindNak->common.call_id = CallId; BindNak->common.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG ; if (TransSend(BindNak,BindNakSize)) { RetVal = -1; } else { RetVal = 0; } if (fEEInfoPresent) { delete BindNak; } return RetVal; } typedef struct tagSelectedInterfaceAndTransferSyntaxInfo { RPC_INTERFACE *Interface; int SelectedAvailableTransferSyntaxIndex; } SelectedInterfaceAndTransferSyntaxInfo; int OSF_SCONNECTION::ProcessPContextList ( IN OSF_ADDRESS * Address, IN p_cont_list_t *PContextList, IN OUT unsigned int * PContextListLength, OUT p_result_list_t *ResultList ) /*++ Routine Description: Arguments: Address - Supplies the address which owns this connection. We need this information so that we can try to find the interface (and transfer syntax) the client requested. PContextList - Supplies a pointer to the presentation context list which the client passed in the rpc_bind packet. It has not yet had data conversion performed on it. PContextListLength - Supplies the maximum possible length of the presentation context list, and returns its actual length. The lengths are in bytes as usual. ResultList - Returns the result list corresponding to the presentation context list. Return Value: A non-zero value will be returned if we are unable to process the presentation context list. The caller should send an rpc_bind_nak packet to the client, and then close the connection. --*/ { p_cont_elem_t *PContextElem; unsigned int PContextListIndex; unsigned int TransferSyntaxIndex; SelectedInterfaceAndTransferSyntaxInfo *SelectionInfo; OSF_SBINDING * SBinding; RPC_STATUS Status; BOOL fInterfaceTransferIsPreferred; p_result_t *PResultElem; int PreferredPContextIndex; BOOL fRejectCurrentContext; BOOL fPContextAlreadyAccepted; unsigned int NumberOfPContextElements; int fIgnored; if (*PContextListLength < sizeof(p_cont_list_t)) { return(1); } NumberOfPContextElements = (unsigned int) PContextList->n_context_elem; // make sure the client doesn't offer a gaziliion pcontexts if (NumberOfPContextElements > MAX_N_CONTEXT_ELEM) { ASSERT(NumberOfPContextElements < MAX_N_CONTEXT_ELEM); return 1; } // // The function is called on a bind and when an alter context is received. // There is a bound on the number of times the client can alter context. The // bound is num_interfaces * MaximumNumberOfTransferSyntaxes(2) // * MAX_NUM_INTERFACE_VERSIONS(20) * MAX_NUM_IDENTICAL_ALTER_CONTEXTS (100) // We should check to make sure the number of alter contexts on this connection does // not exceed this. The number of alter contexts received is just the size of the dict. // if (Bindings.Size() > GlobalRpcServer->InquireInterfaceCount() * MaximumNumberOfTransferSyntaxes * MAX_NUM_INTERFACE_VERSIONS * MAX_NUM_IDENTICAL_ALTER_CONTEXTS) { ASSERT(Bindings.Size() <= GlobalRpcServer->InquireInterfaceCount() * MaximumNumberOfTransferSyntaxes * MAX_NUM_INTERFACE_VERSIONS * MAX_NUM_IDENTICAL_ALTER_CONTEXTS); return 1; } SelectionInfo = (SelectedInterfaceAndTransferSyntaxInfo *) _alloca(sizeof(SelectedInterfaceAndTransferSyntaxInfo) * NumberOfPContextElements); *PContextListLength -= (sizeof(p_cont_list_t) - sizeof(p_cont_elem_t)); ResultList->n_results = PContextList->n_context_elem; ResultList->reserved = 0; ResultList->reserved2 = 0; ASSERT(NumberOfPContextElements > 0); // We do not need to bother with un-initialized fields since we always return // at least 1 element. PreferredPContextIndex = -1; for (PContextListIndex = 0, PContextElem = PContextList->p_cont_elem; PContextListIndex < NumberOfPContextElements; PContextListIndex ++) { if (*PContextListLength < sizeof(p_cont_elem_t)) { return(1); } if (*PContextListLength < (sizeof(p_cont_elem_t) + sizeof(p_syntax_id_t) * (PContextElem->n_transfer_syn - 1))) { return(1); } *PContextListLength -= (sizeof(p_cont_elem_t) + sizeof(p_syntax_id_t) * (PContextElem->n_transfer_syn - 1)); if ( DataConvertEndian(((unsigned char *) &DataRep)) != 0 ) { PContextElem->p_cont_id = RpcpByteSwapShort(PContextElem->p_cont_id); ByteSwapSyntaxId(&PContextElem->abstract_syntax); for ( TransferSyntaxIndex = 0; TransferSyntaxIndex < PContextElem->n_transfer_syn; TransferSyntaxIndex++ ) { ByteSwapSyntaxId(&(PContextElem->transfer_syntaxes[ TransferSyntaxIndex])); } } Status = Address->FindInterfaceTransfer( (PRPC_SYNTAX_IDENTIFIER) &PContextElem->abstract_syntax.if_uuid, (PRPC_SYNTAX_IDENTIFIER) PContextElem->transfer_syntaxes, PContextElem->n_transfer_syn, (PRPC_SYNTAX_IDENTIFIER) &(ResultList->p_results[PContextListIndex].transfer_syntax), &SelectionInfo[PContextListIndex].Interface, &fInterfaceTransferIsPreferred, &fIgnored, &SelectionInfo[PContextListIndex].SelectedAvailableTransferSyntaxIndex); if (Status == RPC_S_OK) { ResultList->p_results[PContextListIndex].result = acceptance; ResultList->p_results[PContextListIndex].reason = 0; if (fInterfaceTransferIsPreferred) { // only one pcontext can be preferred. If not, there is // error in the stubs ASSERT(PreferredPContextIndex == -1); PreferredPContextIndex = PContextListIndex; } // for all accepted we will make a second pass once we know // which transfer syntax will be selected } else { ResultList->p_results[PContextListIndex].result = provider_rejection; if (Status == RPC_S_UNSUPPORTED_TRANS_SYN) { ResultList->p_results[PContextListIndex].reason = proposed_transfer_syntaxes_not_supported; } else { ASSERT(Status == RPC_S_UNKNOWN_IF); ResultList->p_results[PContextListIndex].reason = abstract_syntax_not_supported; } memset(&(ResultList->p_results[PContextListIndex]. transfer_syntax.if_uuid.Data1),0,sizeof(GUID)); ResultList->p_results[PContextListIndex]. transfer_syntax.if_version = 0; } PContextElem = (p_cont_elem_t *) ((unsigned char *)PContextElem + sizeof(p_cont_elem_t) + sizeof(p_syntax_id_t) * (PContextElem->n_transfer_syn - 1)); } fPContextAlreadyAccepted = FALSE; for (PContextListIndex = 0, PResultElem = ResultList->p_results, PContextElem = PContextList->p_cont_elem; PContextListIndex < NumberOfPContextElements; PContextListIndex ++, PResultElem = &(ResultList->p_results[PContextListIndex])) { fRejectCurrentContext = TRUE; // if there is a preferred context ... if (PreferredPContextIndex >= 0) { // ... and this is the one, don't reject it if ((unsigned int)PreferredPContextIndex == PContextListIndex) { ASSERT(PResultElem->result == acceptance); fRejectCurrentContext = FALSE; } else { // else nothing - this is not the preferred one, and the // default action is reject it } } else if (PResultElem->result == acceptance) { // if we haven't already accepted one, accept the current if (!fPContextAlreadyAccepted) { fRejectCurrentContext = FALSE; fPContextAlreadyAccepted = TRUE; } else { // else nothing - we have already accepted one and // we will reject this one } } if (!fRejectCurrentContext) { SBinding = new OSF_SBINDING(SelectionInfo[PContextListIndex].Interface, PContextElem->p_cont_id, SelectionInfo[PContextListIndex].SelectedAvailableTransferSyntaxIndex); if ( (SBinding == 0) || (Bindings.Insert(SBinding) == -1)) { if (SBinding != 0) delete SBinding; PResultElem->result = provider_rejection; PResultElem->reason = local_limit_exceeded; memset(&(PResultElem->transfer_syntax.if_uuid.Data1), 0, sizeof(p_syntax_id_t)); } } else if (PResultElem->result == acceptance) { // apparently we have already accepted somebody, and this is not the // lucky one PResultElem->result = provider_rejection; PResultElem->reason = proposed_transfer_syntaxes_not_supported; memset(&(PResultElem->transfer_syntax.if_uuid.Data1), 0, sizeof(p_syntax_id_t)); } else { // nothing - we have to reject the current one, and it has already // been rejected } PContextElem = (p_cont_elem_t *) ((unsigned char *)PContextElem + sizeof(p_cont_elem_t) + sizeof(p_syntax_id_t) * (PContextElem->n_transfer_syn - 1)); } return(0); } unsigned short // Return the minimum of the three arguments. MinOf ( IN unsigned short Arg1, IN unsigned short Arg2, IN unsigned short Arg3 ) { unsigned short Min = 0xFFFF; if (Arg1 < Min) Min = Arg1; if (Arg2 < Min) Min = Arg2; if (Arg3 < Min) Min = Arg3; return(Min); } int OSF_SCONNECTION::AssociationRequested ( IN rpcconn_bind * BindPacket, IN unsigned int BindPacketLength ) /*++ Routine Description: Arguments: Address - Supplies the address which owns this connection. BindPacket - Supplies the buffer containing the rpc_bind packet received from the client. BindPacketLength - Supplies the length of the buffer in bytes. Return Value: A non-zero return value indicates that the connection needs to be deleted by the caller. --*/ { p_cont_list_t * PContextList; unsigned int SecondaryAddressLength; unsigned int BindAckLength, TokenLength = 0, NewSecurityTrailerOffset; rpcconn_bind_ack * BindAck; RPC_STATUS Status; sec_trailer * SecurityTrailer, * NewSecurityTrailer; SECURITY_CREDENTIALS * SecurityCredentials = 0; RPC_CLIENT_PROCESS_IDENTIFIER ClientProcess; unsigned int CompleteNeeded = 0; SECURITY_BUFFER_DESCRIPTOR InputBufferDescriptor; SECURITY_BUFFER_DESCRIPTOR OutputBufferDescriptor; SECURITY_BUFFER InputBuffers[4]; SECURITY_BUFFER OutputBuffers[4]; unsigned long CallId = BindPacket->common.call_id; ULONG CalculatedSize; PContextList = (p_cont_list_t *) (BindPacket + 1); CalculatedSize = sizeof(rpcconn_bind)+sizeof(p_cont_list_t) + (PContextList->n_context_elem-1)*sizeof(p_cont_elem_t); // Sanity-check the bind packet. if ( BindPacketLength < CalculatedSize || PContextList->n_context_elem < 1 ) { TransFreeBuffer(BindPacket); SendBindNak(reason_not_specified_reject, CallId); return(1); } DataRep = * (unsigned long *) BindPacket->common.drep; if ( DataConvertEndian(BindPacket->common.drep) != 0 ) { BindPacket->max_xmit_frag = RpcpByteSwapShort(BindPacket->max_xmit_frag); BindPacket->max_recv_frag = RpcpByteSwapShort(BindPacket->max_recv_frag); BindPacket->assoc_group_id = RpcpByteSwapLong(BindPacket->assoc_group_id); } ASSERT(TransMaximumSend() % 8 == 0); MaxFrag = MinOf(BindPacket->max_xmit_frag, BindPacket->max_recv_frag, (unsigned short) TransMaximumSend()) & 0xFFFFFFF8; if ( MaxFrag < MUST_RECV_FRAG_SIZE ) MaxFrag = MUST_RECV_FRAG_SIZE; ASSERT(MaxFrag % 8 == 0); // Now we need to check to see if we should be performing authentication // at the rpc protocol level. This will be the case if there is // authentication information in the packet. if ( BindPacket->common.auth_length != 0 ) { // Ok, we have got authentication information in the packet. We // will save away the information, and then check it. SecurityTrailer = (sec_trailer *) (((unsigned char *) BindPacket) + BindPacketLength - BindPacket->common.auth_length - sizeof(sec_trailer)); AuthInfo.AuthenticationLevel = SecurityTrailer->auth_level; //Hack for OSF Clients //If Level is CALL .. bump it ip to CONNECT if (AuthInfo.AuthenticationLevel == RPC_C_AUTHN_LEVEL_CALL) { AuthInfo.AuthenticationLevel = RPC_C_AUTHN_LEVEL_PKT; } if ( (AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_CONNECT) && (AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_PKT) && (AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_PKT_INTEGRITY) && (AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_PKT_PRIVACY) ) { TransFreeBuffer(BindPacket); SendBindNak(reason_not_specified_reject, CallId); return(1); } AuthInfo.AuthenticationService = SecurityTrailer->auth_type; AuthContextId = SecurityTrailer->auth_context_id; if ( DataConvertEndian(BindPacket->common.drep) != 0 ) { AuthContextId = RpcpByteSwapLong(AuthContextId); } RPC_STATUS Status = RPC_S_OK; CurrentSecurityContext = new SECURITY_CONTEXT( &AuthInfo, AuthContextId, FALSE, &Status ); if ( (CurrentSecurityContext == 0) || RPC_S_OK != Status) { if (CurrentSecurityContext != 0) { delete CurrentSecurityContext; } TransFreeBuffer(BindPacket); SendBindNak(local_limit_exceeded_reject, CallId); return(1); } if (SecurityContextDict.Insert(CurrentSecurityContext) == -1) { delete CurrentSecurityContext; TransFreeBuffer(BindPacket); SendBindNak(local_limit_exceeded_reject, CallId); return(1); } CallTestHook( TH_RPC_SECURITY_SERVER_CONTEXT_CREATED, CurrentSecurityContext, this ); RpcSecurityBeingUsed = 1; Status = Address->Server->AcquireCredentials( AuthInfo.AuthenticationService, AuthInfo.AuthenticationLevel, &SecurityCredentials); if ( Status == RPC_S_OUT_OF_MEMORY ) { TransFreeBuffer(BindPacket); SendBindNak(local_limit_exceeded_reject, CallId); return(1); } if ( Status != RPC_S_OK ) { TransFreeBuffer(BindPacket); RpcpErrorAddRecord(EEInfoGCRuntime, Status, EEInfoDLAssociationRequested30, AuthInfo.AuthenticationService, AuthInfo.AuthenticationLevel); SendBindNak(authentication_type_not_recognized, CallId); return(1); } ASSERT( SecurityCredentials != 0 ); } // Calculate the size of the rpc_bind_ack packet. // sizeof(rpcconn_bind_ack) = 26, so we may need to add 2 bytes to get it aligned // on a 4-byte bound. SecondaryAddressLength = Address->TransSecondarySize(); BindAckLength = sizeof(rpcconn_bind_ack) + SecondaryAddressLength + Pad4(SecondaryAddressLength + 2) + sizeof(p_result_list_t) + sizeof(p_result_t) * (PContextList->n_context_elem - 1); // BindAckLength should be 4-byte aligned: // (mod 4) BindAckLength = 2 + x - (x+2) + 0 + 0 * y = 2 + x - x - 2 = 0 ASSERT(Pad4(BindAckLength) == 0); // We need to save some space for authentication information if // necessary. This includes space for the token, the security trailer, // and alignment if necessary. if ( SecurityCredentials != 0 ) { NewSecurityTrailerOffset = BindAckLength; BindAckLength += SecurityCredentials->MaximumTokenLength() + sizeof(sec_trailer); } // BindAckLength may exceed MaxFrag negotiated if the token is // large enough. // Allocate the rpc_bind_ack packet. If that fails, send a rpc_bind_nak // to the client indicating that the server is out of resources; // whoever called AssociationRequested will take care of cleaning up // the connection. Status = TransGetBuffer((void **) &BindAck, BindAckLength); if ( Status != RPC_S_OK ) { ASSERT( Status == RPC_S_OUT_OF_MEMORY ); if ( SecurityCredentials != 0 ) { SecurityCredentials->DereferenceCredentials(); } TransFreeBuffer(BindPacket); SendBindNak(local_limit_exceeded_reject, CallId); return(1); } // Finally we get to do something about that authentication that the // client sent us. if ( SecurityCredentials != 0 ) { NewSecurityTrailer = (sec_trailer *) (((unsigned char *) BindAck) + NewSecurityTrailerOffset); InitSecurityInfo.DceSecurityInfo = DceSecurityInfo; InitSecurityInfo.PacketType = BindPacket->common.PTYPE; InputBufferDescriptor.ulVersion = 0; InputBufferDescriptor.cBuffers = 4; InputBufferDescriptor.pBuffers = InputBuffers; InputBuffers[0].cbBuffer = sizeof(rpcconn_bind); InputBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; InputBuffers[0].pvBuffer = SavedHeader; InputBuffers[1].cbBuffer = BindPacket->common.frag_length - sizeof(rpcconn_bind) - BindPacket->common.auth_length; InputBuffers[1].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; InputBuffers[1].pvBuffer = (char *) SavedHeader + sizeof(rpcconn_bind); InputBuffers[2].cbBuffer = BindPacket->common.auth_length; InputBuffers[2].BufferType = SECBUFFER_TOKEN; InputBuffers[2].pvBuffer = SecurityTrailer + 1; InputBuffers[3].cbBuffer = sizeof(DCE_INIT_SECURITY_INFO); InputBuffers[3].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY; InputBuffers[3].pvBuffer = &InitSecurityInfo; OutputBufferDescriptor.ulVersion = 0; OutputBufferDescriptor.cBuffers = 4; OutputBufferDescriptor.pBuffers = OutputBuffers; OutputBuffers[0].cbBuffer = sizeof(rpcconn_bind_ack) - sizeof(unsigned short); OutputBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; OutputBuffers[0].pvBuffer = BindAck; OutputBuffers[1].cbBuffer = BindAckLength - SecurityCredentials->MaximumTokenLength() - (sizeof(rpcconn_bind_ack) - sizeof(unsigned short)); OutputBuffers[1].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; OutputBuffers[1].pvBuffer = ((unsigned char *) BindAck) + sizeof(rpcconn_bind_ack) - sizeof(unsigned short); OutputBuffers[2].cbBuffer = SecurityCredentials->MaximumTokenLength(); OutputBuffers[2].BufferType = SECBUFFER_TOKEN; OutputBuffers[2].pvBuffer = NewSecurityTrailer + 1; OutputBuffers[3].cbBuffer = sizeof(DCE_INIT_SECURITY_INFO); OutputBuffers[3].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY; OutputBuffers[3].pvBuffer = &InitSecurityInfo; Status = AcceptFirstTime( SecurityCredentials, &InputBufferDescriptor, &OutputBufferDescriptor, SecurityTrailer->auth_level, *((unsigned long *) BindPacket->common.drep), 0); LogEvent(SU_SCONN, EV_SEC_ACCEPT1, this, LongToPtr(Status), OutputBuffers[2].cbBuffer); #if 0 if (Status == SEC_E_BUFFER_TOO_SMALL) { unsigned long NewTokenLength = OutputBuffers[2].cbBuffer; TransFreeBuffer( BindAck ); BindAckLength = sizeof(rpcconn_bind_ack) + SecondaryAddressLength + Pad4(SecondaryAddressLength + 2) + sizeof(p_result_list_t) + sizeof(p_result_t) * (PContextList->n_context_elem - 1); // BindAckLength should be 4-byte aligned. ASSERT(Pad4(BindAckLength) == 0); NewSecurityTrailerOffset = BindAckLength; BindAckLength += NewTokenLength + sizeof(sec_trailer); // BindAckLength may exceed MaxFrag negotiated if the token // is large enough. Status = TransGetBuffer((void **) &BindAck, BindAckLength); if ( Status != RPC_S_OK ) { ASSERT( Status == RPC_S_OUT_OF_MEMORY ); SecurityCredentials->DereferenceCredentials(); TransFreeBuffer(BindPacket); SendBindNak(local_limit_exceeded_reject, CallId); return(1); } NewSecurityTrailer = (sec_trailer *) (((unsigned char *) BindAck) + NewSecurityTrailerOffset); OutputBufferDescriptor.ulVersion = 0; OutputBufferDescriptor.cBuffers = 4; OutputBufferDescriptor.pBuffers = OutputBuffers; OutputBuffers[0].cbBuffer = sizeof(rpcconn_bind_ack) - sizeof(unsigned short); OutputBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; OutputBuffers[0].pvBuffer = BindAck; OutputBuffers[1].cbBuffer = BindAckLength - NewTokenLength - (sizeof(rpcconn_bind_ack) - sizeof(unsigned short)); OutputBuffers[1].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; OutputBuffers[1].pvBuffer = ((unsigned char *) BindAck) + sizeof(rpcconn_bind_ack) - sizeof(unsigned short); OutputBuffers[2].cbBuffer = NewTokenLength; OutputBuffers[2].BufferType = SECBUFFER_TOKEN; OutputBuffers[2].pvBuffer = NewSecurityTrailer + 1; OutputBuffers[3].cbBuffer = sizeof(DCE_INIT_SECURITY_INFO); OutputBuffers[3].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY; OutputBuffers[3].pvBuffer = &InitSecurityInfo; Status = AcceptFirstTime( SecurityCredentials, &InputBufferDescriptor, &OutputBufferDescriptor, SecurityTrailer->auth_level, *((unsigned long *) BindPacket->common.drep), 0); LogEvent(SU_SCONN, EV_SEC_ACCEPT1, this, (void *) Status, OutputBuffers[2].cbBuffer); } #endif TokenLength = (unsigned int) OutputBuffers[2].cbBuffer; if ( ( Status == RPC_P_CONTINUE_NEEDED ) || ( Status == RPC_S_OK ) || ( Status == RPC_P_COMPLETE_NEEDED ) || ( Status == RPC_P_COMPLETE_AND_CONTINUE ) ) { if ( Status == RPC_P_CONTINUE_NEEDED ) { AuthContinueNeeded = 1; } else if ( Status == RPC_P_COMPLETE_AND_CONTINUE ) { AuthContinueNeeded = 1; CompleteNeeded = 1; } else if ( Status == RPC_P_COMPLETE_NEEDED ) { CompleteNeeded = 1; } BindAckLength = BindAckLength + TokenLength - SecurityCredentials->MaximumTokenLength(); NewSecurityTrailer->auth_type = SecurityTrailer->auth_type; NewSecurityTrailer->auth_level = SecurityTrailer->auth_level; NewSecurityTrailer->auth_pad_length = 0; NewSecurityTrailer->auth_reserved = 0; NewSecurityTrailer->auth_context_id = AuthContextId; SecurityCredentials->DereferenceCredentials(); } else { VALIDATE(Status) { RPC_S_OUT_OF_MEMORY, RPC_S_ACCESS_DENIED, ERROR_SHUTDOWN_IN_PROGRESS, RPC_S_UNKNOWN_AUTHN_SERVICE } END_VALIDATE; TransFreeBuffer(BindPacket); TransFreeBuffer(BindAck); SecurityCredentials->DereferenceCredentials(); if (Status == RPC_S_OUT_OF_MEMORY) { SendBindNak(local_limit_exceeded_reject, CallId); } else if (Status == RPC_S_UNKNOWN_AUTHN_SERVICE || Status == ERROR_SHUTDOWN_IN_PROGRESS ) { SendBindNak(authentication_type_not_recognized, CallId); } else { SendBindNak(invalid_checksum, CallId); } return(1); } } TransQueryClientProcess(&ClientProcess); if ( BindPacket->assoc_group_id != 0 ) { // This means this is a connection on an existing association. Association = Address->FindAssociation( (int) BindPacket->assoc_group_id, &ClientProcess); if ( Association == 0 ) { RpcpErrorAddRecord (EEInfoGCRuntime, RPC_S_ENTRY_NOT_FOUND, EEInfoDLAssociationRequested10, BindPacket->assoc_group_id, ClientProcess.GetDebugULongLong1(), ClientProcess.GetDebugULongLong2()); TransFreeBuffer(BindPacket); TransFreeBuffer(BindAck); SendBindNak(reason_not_specified_reject, CallId); return(1); } } if ( Association == 0 ) { Association = new OSF_ASSOCIATION(Address, &ClientProcess, &Status); if ( (Association == 0) || (Status != RPC_S_OK) ) { if (Association != 0) { delete Association; Association = NULL; } TransFreeBuffer(BindPacket); TransFreeBuffer(BindAck); RpcpErrorAddRecord (EEInfoGCRuntime, Status, EEInfoDLAssociationRequested20, sizeof(OSF_ASSOCIATION)); SendBindNak(local_limit_exceeded_reject, CallId); return(1); } } BindPacketLength -= sizeof(rpcconn_bind); if ( ProcessPContextList(Address, PContextList, &BindPacketLength, (p_result_list_t *) (((unsigned char *) BindAck) + sizeof(rpcconn_bind_ack) + SecondaryAddressLength + Pad4(SecondaryAddressLength + 2))) != 0 ) { TransFreeBuffer(BindPacket); TransFreeBuffer(BindAck); SendBindNak(reason_not_specified_reject, CallId); return(1); } // Fill in the header of the rpc_bind_ack packet. ConstructPacket((rpcconn_common *) BindAck, rpc_bind_ack, BindAckLength); BindAck->max_xmit_frag = BindAck->max_recv_frag = MaxFrag; BindAck->assoc_group_id = Association->AssocGroupId(); BindAck->sec_addr_length = (unsigned short) SecondaryAddressLength; BindAck->common.call_id = CallId; if (PFC_CONC_MPX & BindPacket->common.pfc_flags) { ((rpcconn_common *) BindAck)->pfc_flags |= (PFC_FIRST_FRAG | PFC_LAST_FRAG | PFC_CONC_MPX) ; } else { fExclusive = 1; ((rpcconn_common *) BindAck)->pfc_flags |= (PFC_FIRST_FRAG | PFC_LAST_FRAG) ; } // if caller supports header signing, tell it that we also support it. if (BindPacket->common.pfc_flags & PFC_SUPPORT_HEADER_SIGN) { ((rpcconn_common *) BindAck)->pfc_flags |= PFC_SUPPORT_HEADER_SIGN; SetClientSupportsHeaderSigningFlag(); } DceSecurityInfo.ReceiveSequenceNumber += 1; if ( SecondaryAddressLength != 0 ) { Status = Address->TransSecondary((unsigned char *) (BindAck + 1), SecondaryAddressLength); if (Status != RPC_S_OK) { ASSERT(Status == RPC_S_OUT_OF_MEMORY); TransFreeBuffer(BindPacket); TransFreeBuffer(BindAck); SendBindNak(reason_not_specified_reject, CallId); return(1); } } // The result list has already been filled in by ProcessPContextList. // All that is left to do, is fill in the authentication information. BindAck->common.auth_length = (unsigned short) TokenLength; // Send the rpc_bind_ack packet back to the client. TransFreeBuffer(BindPacket); if ( CompleteNeeded != 0 ) { Status = CurrentSecurityContext->CompleteSecurityToken( &OutputBufferDescriptor); if (Status != RPC_S_OK) { TransFreeBuffer(BindAck); SendBindNak(invalid_checksum, CallId); return(1); } } // // We may need to do third leg AuthInfo.Authentication. // we will do that when we receive the third leg packet // if ( AuthContinueNeeded == 0 ) { // // We need to figure out how much space to reserve for security // information at the end of request and response packets. // In addition to saving space for the signature or header, // we need space to pad the packet to a multiple of the maximum // security block size as well as for the security trailer. // // // In the case where we need a third leg, this information will be obtained // after we process the third leg packet. // ASSERT(AdditionalSpaceForSecurity == 0); AdditionalSpaceForSecurity = CalculateAdditionalSpaceForSecurity ( AuthInfo.AuthenticationLevel, AuthInfo.AuthenticationService ); } // // Sending the bind ack should be the last thing we do // in this function. The action will continue in the processing // of the third leg. // Status = TransSend(BindAck, BindAckLength); TransFreeBuffer(BindAck); if ( Status != RPC_S_OK ) { return(1); } if (DebugCell) { DWORD LocalFlags = 0; if (AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE) { LocalFlags = AuthInfo.AuthenticationLevel << 1; switch (AuthInfo.AuthenticationService) { case RPC_C_AUTHN_WINNT: LocalFlags |= DBGCELL_AUTH_SVC_NTLM; break; case RPC_C_AUTHN_GSS_KERBEROS: case RPC_C_AUTHN_GSS_NEGOTIATE: LocalFlags |= DBGCELL_AUTH_SVC_KERB; break; default: ASSERT(AuthInfo.AuthenticationService); LocalFlags |= DBGCELL_AUTH_SVC_OTHER; } } if (fExclusive) LocalFlags |= 1; DebugCell->ConnectionID[0] = ULongToPtr(ClientProcess.GetDebugULong1()); DebugCell->ConnectionID[1] = ULongToPtr(ClientProcess.GetDebugULong2()); DebugCell->Flags = (unsigned char)LocalFlags; } return(0); } int OSF_SCONNECTION::AlterContextRequested ( IN rpcconn_alter_context * AlterContext, IN unsigned int AlterContextLength ) /*++ Routine Description: Arguments: AlterContext - Supplies the buffer containing the rpc_alter_context packet received from the client. AlterContextLength - Supplies the length of the buffer in bytes. Return Value: A non-zero return value indicates that the connection needs to be deleted by the caller. --*/ { p_cont_list_t *PContextList; rpcconn_alter_context_resp * AlterContextResp = 0; unsigned int AlterContextRespLength = 0; unsigned int TokenLength = 0; unsigned int CompleteNeeded = 0; RPC_STATUS Status; sec_trailer * SecurityTrailer, * NewSecurityTrailer; SECURITY_BUFFER_DESCRIPTOR InputBufferDescriptor; SECURITY_BUFFER_DESCRIPTOR OutputBufferDescriptor; SECURITY_BUFFER InputBuffers[4]; SECURITY_BUFFER OutputBuffers[4]; DCE_INIT_SECURITY_INFO InitSecurityInfo; SECURITY_CREDENTIALS * SecurityCredentials = 0; unsigned long SecureAlterContext = 0; unsigned long NewContextRequired = 0; CLIENT_AUTH_INFO NewClientInfo; unsigned NewId; SECURITY_CONTEXT * SecId; unsigned long CallId = AlterContext->common.call_id; ULONG CalculatedSize; // // The packet has already been validate by whoever called this method. // Data conversion of the common part of the header was performed at // that time as well. We do not use the max_xmit_frag, max_recv_frag, // or assoc_group_id fields of the packet, so we will not bother to // data convert them. // // make sure PContextList is there if ( AlterContextLength < sizeof(rpcconn_alter_context) + sizeof(p_cont_list_t)) { SendFault(RPC_S_ACCESS_DENIED, 1, CallId); TransFreeBuffer(AlterContext); return(1); } PContextList = (p_cont_list_t *) (AlterContext + 1); CalculatedSize = sizeof(rpcconn_alter_context)+sizeof(p_cont_list_t) + (PContextList->n_context_elem-1)*sizeof(p_cont_elem_t); // Sanity-check the packet. if ( AlterContextLength < CalculatedSize || PContextList->n_context_elem < 1 ) { SendFault(RPC_S_ACCESS_DENIED, 1, CallId); TransFreeBuffer(AlterContext); return(1); } DataRep = * (unsigned long *) AlterContext->common.drep; if ( AlterContext->common.auth_length != 0 ) { // // We are dealing with a secure alter context // it may be adding a presentation context // or a new security context // SecureAlterContext = 1; SecurityTrailer = (sec_trailer *) (((unsigned char *) AlterContext) + AlterContextLength - AlterContext->common.auth_length - sizeof(sec_trailer)); NewId = SecurityTrailer->auth_context_id; NewClientInfo.AuthenticationLevel = SecurityTrailer->auth_level; NewClientInfo.AuthenticationService = SecurityTrailer->auth_type; if (DataConvertEndian(((unsigned char *)&DataRep)) != 0) { NewId = RpcpByteSwapLong(NewId); } if (NewClientInfo.AuthenticationLevel == RPC_C_AUTHN_LEVEL_CALL) { NewClientInfo.AuthenticationLevel = RPC_C_AUTHN_LEVEL_PKT; } // // Check to see if a new context is being added.. // SecId = FindSecurityContext(NewId, NewClientInfo.AuthenticationLevel, NewClientInfo.AuthenticationService ); if (SecId == 0) { RPC_STATUS Status = RPC_S_OK; SecId = new SECURITY_CONTEXT(&NewClientInfo, NewId, FALSE, &Status); if ( (SecId == 0) || RPC_S_OK != Status) { if (SecId != 0) { delete SecId; } SendFault(RPC_S_OUT_OF_MEMORY, 1, CallId); TransFreeBuffer(AlterContext); return (1); } if (SecurityContextDict.Insert(SecId) == -1) { delete SecId; SendFault(RPC_S_OUT_OF_MEMORY, 1, CallId); TransFreeBuffer(AlterContext); return (1); } NewContextRequired = 1; // // If previously no secure rpc had taken place // set original sec. context // else, mark this connection to indicate // security context is altered .. // if (RpcSecurityBeingUsed) { SecurityContextAltered = 1; } } AuthInfo = NewClientInfo; AuthInfo.ReferenceCredentials(); AuthContextId = NewId; CurrentSecurityContext = SecId; RpcSecurityBeingUsed = 1; if ( (AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_CONNECT) && (AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_PKT) && (AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_PKT_INTEGRITY) && (AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_PKT_PRIVACY) ) { SendFault(RPC_S_ACCESS_DENIED, 1, CallId); TransFreeBuffer(AlterContext); return(1); } Status = Address->Server->AcquireCredentials( AuthInfo.AuthenticationService, AuthInfo.AuthenticationLevel, &SecurityCredentials ); if ( Status == RPC_S_OUT_OF_MEMORY || Status == ERROR_SHUTDOWN_IN_PROGRESS) { SendFault(Status, 1, CallId); TransFreeBuffer(AlterContext); return(1); } if ( Status != RPC_S_OK ) { if (SecurityCredentials != 0) { SecurityCredentials->DereferenceCredentials(); } SendFault(RPC_S_ACCESS_DENIED, 1, CallId); TransFreeBuffer(AlterContext); return(1); } ASSERT( SecurityCredentials != 0 ); } //if secure alter context AlterContextRespLength = sizeof(rpcconn_alter_context_resp) + sizeof(p_result_list_t) + sizeof(p_result_t) * (PContextList->n_context_elem - 1); // The packet length should be 4-byte aligned: // AlterContextRespLength = 0x1c + 0x1c + 0x18 * x = 0 (mod 4) ASSERT(Pad4(AlterContextRespLength) == 0); if (SecureAlterContext != 0) { ASSERT(SecurityCredentials != 0); AlterContextRespLength += SecurityCredentials->MaximumTokenLength() + sizeof(sec_trailer); } Status = TransGetBuffer((void **) &AlterContextResp, AlterContextRespLength); if ( Status != RPC_S_OK ) { ASSERT( Status == RPC_S_OUT_OF_MEMORY ); if (SecurityCredentials != 0) { SecurityCredentials->DereferenceCredentials(); } SendFault(RPC_S_OUT_OF_MEMORY, 1, CallId); TransFreeBuffer(AlterContext); return(1); } AlterContextLength -= sizeof(rpcconn_alter_context); if ( ProcessPContextList(Address, PContextList, &AlterContextLength, (p_result_list_t *) (AlterContextResp + 1)) != 0 ) { TransFreeBuffer(AlterContext); TransFreeBuffer(AlterContextResp); if (SecurityCredentials != 0) { SecurityCredentials->DereferenceCredentials(); } SendFault(RPC_S_PROTOCOL_ERROR, 1, CallId); return(1); } if ( SecureAlterContext != 0 ) { ASSERT(SecurityCredentials != 0); NewSecurityTrailer = (sec_trailer *) (((unsigned char *) AlterContextResp) + AlterContextRespLength - SecurityCredentials->MaximumTokenLength() - sizeof(sec_trailer)); InitSecurityInfo.DceSecurityInfo = DceSecurityInfo; InitSecurityInfo.PacketType = AlterContext->common.PTYPE; InputBufferDescriptor.ulVersion = 0; InputBufferDescriptor.cBuffers = 4; InputBufferDescriptor.pBuffers = InputBuffers; InputBuffers[0].cbBuffer = sizeof(rpcconn_alter_context); InputBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; InputBuffers[0].pvBuffer = SavedHeader; InputBuffers[1].cbBuffer = AlterContext->common.frag_length - sizeof(rpcconn_alter_context) - AlterContext->common.auth_length; InputBuffers[1].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; InputBuffers[1].pvBuffer = (char *) SavedHeader + sizeof(rpcconn_alter_context); InputBuffers[2].cbBuffer = AlterContext->common.auth_length; InputBuffers[2].BufferType = SECBUFFER_TOKEN; InputBuffers[2].pvBuffer = SecurityTrailer + 1; InputBuffers[3].cbBuffer = sizeof(DCE_INIT_SECURITY_INFO); InputBuffers[3].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY; InputBuffers[3].pvBuffer = &InitSecurityInfo; OutputBufferDescriptor.ulVersion = 0; OutputBufferDescriptor.cBuffers = 4; OutputBufferDescriptor.pBuffers = OutputBuffers; OutputBuffers[0].cbBuffer = sizeof(rpcconn_alter_context_resp); OutputBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; OutputBuffers[0].pvBuffer = AlterContextResp; OutputBuffers[1].cbBuffer = AlterContextRespLength - SecurityCredentials->MaximumTokenLength() - sizeof(rpcconn_alter_context_resp); OutputBuffers[1].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; OutputBuffers[1].pvBuffer = ((unsigned char *) AlterContextResp) + sizeof(rpcconn_alter_context_resp); OutputBuffers[2].cbBuffer = SecurityCredentials->MaximumTokenLength(); OutputBuffers[2].BufferType = SECBUFFER_TOKEN; OutputBuffers[2].pvBuffer = NewSecurityTrailer + 1; OutputBuffers[3].cbBuffer = sizeof(DCE_INIT_SECURITY_INFO); OutputBuffers[3].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY; OutputBuffers[3].pvBuffer = &InitSecurityInfo; if ( NewContextRequired != 0 ) { Status = AcceptFirstTime( SecurityCredentials, &InputBufferDescriptor, &OutputBufferDescriptor, SecurityTrailer->auth_level, *((unsigned long *) AlterContext->common.drep), NewContextRequired ); LogEvent(SU_SCONN, EV_SEC_ACCEPT1, this, LongToPtr(Status), OutputBuffers[2].cbBuffer); // // Since we have (potentially) a new security context we // need to figure out // additional security related information at this stage.. // if ( Status == RPC_S_OK || Status == RPC_P_COMPLETE_NEEDED || Status == RPC_P_CONTINUE_NEEDED || Status == RPC_P_COMPLETE_AND_CONTINUE ) { ASSERT(SecurityTrailer->auth_level != RPC_C_AUTHN_LEVEL_NONE); AdditionalSpaceForSecurity = CalculateAdditionalSpaceForSecurity ( SecurityTrailer->auth_level, SecurityTrailer->auth_type ); } } else { // We can't be sure that the third leg packet is expected. // It is possible that the client will alter-context and existing // context rather then add a new one. In that case, AcceptThirdLeg // will not be preceeded by AcceptFirstTime and AuthContinueNeeded == 0. // Since we have just received the third leg auth packet, we // are not expecting one any longer. AuthContinueNeeded = 0; Status = AcceptThirdLeg( *((unsigned long *) AlterContext->common.drep), &InputBufferDescriptor, &OutputBufferDescriptor ); LogEvent(SU_SCONN, EV_SEC_ACCEPT3, this, LongToPtr(Status), OutputBuffers[2].cbBuffer); } TokenLength = (unsigned int) OutputBuffers[2].cbBuffer; if ( ( Status == RPC_P_CONTINUE_NEEDED ) || ( Status == RPC_S_OK ) || ( Status == RPC_P_COMPLETE_NEEDED ) || ( Status == RPC_P_COMPLETE_AND_CONTINUE ) ) { if ( Status == RPC_P_CONTINUE_NEEDED ) { AuthContinueNeeded = 1; } else if ( Status == RPC_P_COMPLETE_AND_CONTINUE ) { AuthContinueNeeded = 1; CompleteNeeded = 1; } else if ( Status == RPC_P_COMPLETE_NEEDED ) { CompleteNeeded = 1; } if ( Status == RPC_S_OK || Status == RPC_P_COMPLETE_NEEDED ) { ASSERT(SecurityTrailer->auth_level != RPC_C_AUTHN_LEVEL_NONE); AdditionalSpaceForSecurity = CalculateAdditionalSpaceForSecurity ( SecurityTrailer->auth_level, SecurityTrailer->auth_type ); } AlterContextRespLength = AlterContextRespLength + TokenLength - SecurityCredentials->MaximumTokenLength(); NewSecurityTrailer->auth_type = SecurityTrailer->auth_type; NewSecurityTrailer->auth_level = SecurityTrailer->auth_level; NewSecurityTrailer->auth_pad_length = 0; NewSecurityTrailer->auth_reserved = 0; NewSecurityTrailer->auth_context_id = AuthContextId; SecurityCredentials->DereferenceCredentials(); SecurityCredentials = 0; Status = RPC_S_OK; } if (Status) { TransFreeBuffer(AlterContext); TransFreeBuffer(AlterContextResp); SecurityCredentials->DereferenceCredentials(); SendFault(RPC_S_ACCESS_DENIED, 1, CallId); return(1); } } DceSecurityInfo.ReceiveSequenceNumber++; ConstructPacket((rpcconn_common *) AlterContextResp, rpc_alter_context_resp, AlterContextRespLength); TransFreeBuffer(AlterContext); if ( Association == 0 ) { TransFreeBuffer(AlterContextResp); SendFault(RPC_S_PROTOCOL_ERROR, 1, CallId); return(1); } AlterContextResp->assoc_group_id = Association->AssocGroupId(); AlterContextResp->sec_addr_length = 0; AlterContextResp->max_xmit_frag = AlterContextResp->max_recv_frag = MaxFrag; AlterContextResp->common.call_id = CallId; AlterContextResp->common.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; AlterContextResp->common.auth_length = (unsigned short) TokenLength; if (CompleteNeeded != 0) { CurrentSecurityContext->CompleteSecurityToken(&OutputBufferDescriptor); } Status= TransSend(AlterContextResp, AlterContextRespLength); TransFreeBuffer(AlterContextResp); if ( Status != RPC_S_OK ) { return(1); } return(0); } RPC_STATUS OSF_SCONNECTION::EatAuthInfoFromPacket ( IN rpcconn_request * Request, IN OUT int * RequestLength, IN OUT void * *SavedHeader, IN OUT unsigned long *SavedHeaderSize ) /*++ Routine Description: If there is authentication information in the packet, this routine will check it, and perform security as necessary. This may include calls to the security support package. Arguments: Request - Supplies the packet which may contain authentication information. RequestLength - Supplies the length of the packet in bytes, and returns the length of the packet without authentication information. Return Value: RPC_S_OK - Everything went just fine. RPC_S_ACCESS_DENIED - A security failure of some sort occured. RPC_S_PROTOCOL_ERROR - This will occur if no authentication information is in the packet, and some was expected, or visa versa. --*/ { sec_trailer * SecurityTrailer; RPC_STATUS Status; SECURITY_BUFFER_DESCRIPTOR BufferDescriptor; SECURITY_BUFFER SecurityBuffers[5]; DCE_MSG_SECURITY_INFO MsgSecurityInfo; unsigned long Id, Level, Service; SECURITY_CONTEXT * SecId; unsigned int HeaderSize = sizeof(rpcconn_request); unsigned long DataRep = * (unsigned long *) Request->common.drep; ULONG ReadOnlyFlag; if ( (Request->common.pfc_flags & PFC_OBJECT_UUID) != 0 ) { HeaderSize += sizeof(UUID); } if ( Request->common.auth_length != 0 ) { SecurityTrailer = (sec_trailer *) (((unsigned char *) Request) + Request->common.frag_length - Request->common.auth_length - sizeof(sec_trailer)); if (!IsBufferAlignedOnStructBoundary(SecurityTrailer)) { CORRUPTION_ASSERT(0 && "SecurityTrailer is unaligned"); return(RPC_S_PROTOCOL_ERROR); } if (RpcSecurityBeingUsed == 0) { return(RPC_S_PROTOCOL_ERROR); } // // Find the appropriate security context.. // Id = SecurityTrailer->auth_context_id; Level = SecurityTrailer->auth_level; Service = SecurityTrailer->auth_type; if (DataConvertEndian(((unsigned char *)&DataRep)) != 0) { Id = RpcpByteSwapLong(Id); } // // Osf Hack // if (Level == RPC_C_AUTHN_LEVEL_CALL) { Level = RPC_C_AUTHN_LEVEL_PKT; } if ( (CurrentSecurityContext == 0) ||(CurrentSecurityContext->AuthContextId != Id) ||(CurrentSecurityContext->AuthenticationLevel != Level) ||(CurrentSecurityContext->AuthenticationService != Service) ) { SecId = FindSecurityContext(Id, Level, Service); if (SecId == 0) { return (RPC_S_PROTOCOL_ERROR); } CurrentSecurityContext = SecId; AuthInfo.AuthenticationLevel = Level; AuthInfo.AuthenticationService = Service; AuthContextId = Id; ASSERT(Level != RPC_C_AUTHN_LEVEL_NONE); AdditionalSpaceForSecurity = CalculateAdditionalSpaceForSecurity ( Level, Service ); } if (GetClientSupportsHeaderSigningFlag()) ReadOnlyFlag = SECBUFFER_READONLY_WITH_CHECKSUM; else ReadOnlyFlag = SECBUFFER_READONLY; *RequestLength -= (Request->common.auth_length +HeaderSize + sizeof(sec_trailer) + SecurityTrailer->auth_pad_length); ASSERT(*RequestLength >= 0); MsgSecurityInfo.SendSequenceNumber = DceSecurityInfo.SendSequenceNumber; MsgSecurityInfo.ReceiveSequenceNumber = DceSecurityInfo.ReceiveSequenceNumber; MsgSecurityInfo.PacketType = Request->common.PTYPE; BufferDescriptor.ulVersion = 0; BufferDescriptor.cBuffers = 5; BufferDescriptor.pBuffers = SecurityBuffers; SecurityBuffers[0].cbBuffer = HeaderSize; SecurityBuffers[0].BufferType = SECBUFFER_DATA | ReadOnlyFlag; SecurityBuffers[0].pvBuffer = (unsigned char *) *SavedHeader; SecurityBuffers[1].cbBuffer = *RequestLength + SecurityTrailer->auth_pad_length; SecurityBuffers[1].BufferType = SECBUFFER_DATA; SecurityBuffers[1].pvBuffer = ((unsigned char *) Request) + HeaderSize; SecurityBuffers[2].cbBuffer = sizeof(sec_trailer); SecurityBuffers[2].BufferType = SECBUFFER_DATA | ReadOnlyFlag; SecurityBuffers[2].pvBuffer = SecurityTrailer; SecurityBuffers[3].cbBuffer = Request->common.auth_length; SecurityBuffers[3].BufferType = SECBUFFER_TOKEN; SecurityBuffers[3].pvBuffer = SecurityTrailer + 1; SecurityBuffers[4].cbBuffer = sizeof(DCE_MSG_SECURITY_INFO); SecurityBuffers[4].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY; SecurityBuffers[4].pvBuffer = &MsgSecurityInfo; Status = CurrentSecurityContext->VerifyOrUnseal( MsgSecurityInfo.ReceiveSequenceNumber, AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_PKT_PRIVACY, &BufferDescriptor); if ( Status != RPC_S_OK ) { ASSERT( Status == RPC_S_ACCESS_DENIED || Status == ERROR_PASSWORD_MUST_CHANGE || Status == ERROR_PASSWORD_EXPIRED || Status == ERROR_ACCOUNT_DISABLED || Status == ERROR_INVALID_LOGON_HOURS || Status == RPC_S_OUT_OF_MEMORY); return(Status); } } else if (CurrentSecurityContext == 0) { // This is a non-secure connection. There is nothing to be done. ASSERT(AuthInfo.AuthenticationLevel == RPC_C_AUTHN_LEVEL_NONE); // Corrupt data could have lead us to previously allocate a SavedHeader. // This is fine because there is at most one such allocation per call. CORRUPTION_ASSERT(*SavedHeader == 0); *RequestLength -= HeaderSize; ASSERT(*RequestLength >= 0); } else { // // We are processing a fragment with zero auth length on a secure // connection. This should only happen with connect or call level security. // if (CurrentSecurityContext->AuthenticationLevel != RPC_C_AUTHN_LEVEL_CONNECT && CurrentSecurityContext->AuthenticationLevel != RPC_C_AUTHN_LEVEL_CALL) { CORRUPTION_ASSERT(0 && "Unsecure RPC on a secure connection."); return RPC_S_ACCESS_DENIED; } else { if (*SavedHeader != 0) { ASSERT(*SavedHeaderSize != 0); RpcpFarFree(*SavedHeader); *SavedHeader = 0; *SavedHeaderSize = 0; } *RequestLength -= HeaderSize; } } DceSecurityInfo.ReceiveSequenceNumber += 1; if (*RequestLength < 0) { return RPC_S_ACCESS_DENIED; } return(RPC_S_OK); } BOOL OSF_SCONNECTION::MaybeQueueThisCall ( IN OSF_SCALL *ThisCall ) { BOOL fCallQueued = 0; ConnMutex.Request(); if (fCurrentlyDispatched) { if (CallQueue.PutOnQueue(ThisCall, 0)) { ThisCall->SendFault(RPC_S_OUT_OF_MEMORY, 1); // // Remove the reply reference // ThisCall->RemoveReference(); // CALL-- // // Remove the dispatch reference(); // ThisCall->RemoveReference(); // CALL-- } fCallQueued = 1; } else { fCurrentlyDispatched = 1; } ConnMutex.Clear(); return fCallQueued; } void OSF_SCONNECTION::AbortQueuedCalls ( ) { OSF_SCALL *NextCall; unsigned int ignore; while (1) { ConnMutex.Request(); NextCall = (OSF_SCALL *) CallQueue.TakeOffQueue(&ignore); if (NextCall == 0) { fCurrentlyDispatched = 0; ConnMutex.Clear(); break; } ConnMutex.Clear(); // // Remove the reply reference // NextCall->RemoveReference(); // CALL-- // // Remove the dispatch reference on the call // NextCall->RemoveReference(); // CALL-- } } void OSF_SCONNECTION::DispatchQueuedCalls ( ) { OSF_SCALL *NextCall; unsigned int ignore; while (1) { ConnMutex.Request(); NextCall = (OSF_SCALL *) CallQueue.TakeOffQueue(&ignore); if (NextCall == 0) { fCurrentlyDispatched = 0; ConnMutex.Clear(); break; } ConnMutex.Clear(); NextCall->DispatchHelper(); // // Remove the dispatch reference on the call // NextCall->RemoveReference(); // CALL-- } } RPC_STATUS OSF_SCONNECTION::TurnOnOffKeepAlives ( IN BOOL TurnOn, IN ULONG Time, IN ULONG Interval ) /*++ Routine Description: Turns on or off keepalives for the given connection Arguments: TurnOn - if non-zero, keep alives will be turned on with the timeout specified. If zero, keepalives will be turned off Timeout - The time in milliseconds between the last keep alive response and the next probe. Interval - The timeout in milliseconds for the subsequent keepalive packets. The time between two consequitive keep alive probes if the first one did not receive a response. Return Value: RPC_S_OK if the transport supports keep alives RPC_S_CANNOT_SUPPORT otherwise --*/ { KEEPALIVE_TIMEOUT uTime; uTime.Milliseconds = Time; if (TurnOn && fKeepalivesTurnedOn) { return RPC_S_OK; } if (ServerInfo->TurnOnOffKeepAlives) { if (ServerInfo->TurnOnOffKeepAlives(TransConnection, TurnOn, TRUE, tuMilliseconds, uTime, Interval) != RPC_S_OK) { return RPC_S_CANNOT_SUPPORT; } else { fKeepalivesTurnedOn = TRUE; return RPC_S_OK; } } else { return RPC_S_CANNOT_SUPPORT; } } DLL *OSF_SCONNECTION::Secur32Dll = NULL; SecpSetIPAddressFn OSF_SCONNECTION::SecpSetIPAddressFnPtr = NULL; RPC_STATUS OSF_SCONNECTION::AcceptFirstTime ( IN SECURITY_CREDENTIALS * NewCredentials, IN SECURITY_BUFFER_DESCRIPTOR PAPI * InputBufferDescriptor, IN OUT SECURITY_BUFFER_DESCRIPTOR PAPI * OutputBufferDescriptor, IN unsigned long AuthenticationLevel, IN unsigned long DataRepresentation, IN unsigned long NewContextNeededFlag ) /*++ Routine Description: Accepts the first leg of a security context negotiation. Little more than a wrapper for the respective SECURITY_CONTEXT function. Arguments: NewCredentials - the new credentials to assign to the security context. InputBufferDescriptor - a structure describing the buffers with data that we received from the client. OutputBufferDescriptor - a structure describing the buffers with data to return to the client. AuthenticationLevel - the authentication level requested by the client. DataRepresentation - the data representation for this call. NewContextNeededFlag - true if we have to establish a new security context. Return Value: RPC_S_OK or RPC_S_* error. --*/ { RPC_CLIENT_IP_ADDRESS ClientIpAddress; SECURITY_STATUS SecurityStatus; RPC_STATUS RpcStatus; // secur32 doesn't export this function on wow #if !defined(BUILD_WOW6432) TransQueryClientIpAddress (&ClientIpAddress); RpcStatus = EnsureSecur32DllLoaded(); if (RpcStatus != RPC_S_OK) return RpcStatus; SecurityStatus = SecpSetIPAddressFnPtr (ClientIpAddress.Data, ClientIpAddress.DataSize); if (SecurityStatus != SEC_E_OK) { RpcpErrorAddRecord(EEInfoGCRuntime, RPC_S_OUT_OF_MEMORY, EEInfoDLOSF_SCONNECTION__AcceptFirstTime, SecurityStatus); return RPC_S_OUT_OF_MEMORY; } #endif // BUILD_WOW6432 return CurrentSecurityContext->AcceptFirstTime ( NewCredentials, InputBufferDescriptor, OutputBufferDescriptor, AuthenticationLevel, DataRepresentation, NewContextNeededFlag ); } RPC_STATUS OSF_SCONNECTION::AcceptThirdLeg ( IN unsigned long DataRepresentation, IN SECURITY_BUFFER_DESCRIPTOR PAPI * BufferDescriptor, OUT SECURITY_BUFFER_DESCRIPTOR PAPI * OutBufferDescriptor ) /*++ Routine Description: Accepts a non-first (second or higher) leg of a security context negotiation. Little more than a wrapper for the respective SECURITY_CONTEXT function. Arguments: DataRepresentation - the data representation for this call. InputBufferDescriptor - a structure describing the buffers with data that we received from the client. OutputBufferDescriptor - a structure describing the buffers with data to return to the client. Return Value: RPC_S_OK or RPC_S_* error. --*/ { RPC_CLIENT_IP_ADDRESS ClientIpAddress; SECURITY_STATUS SecurityStatus; RPC_STATUS RpcStatus; // secur32 doesn't export this function on wow #if !defined(BUILD_WOW6432) TransQueryClientIpAddress (&ClientIpAddress); RpcStatus = EnsureSecur32DllLoaded(); if (RpcStatus != RPC_S_OK) return RpcStatus; SecurityStatus = SecpSetIPAddressFnPtr (ClientIpAddress.Data, ClientIpAddress.DataSize); if (SecurityStatus != SEC_E_OK) { RpcpErrorAddRecord(EEInfoGCRuntime, RPC_S_OUT_OF_MEMORY, EEInfoDLOSF_SCONNECTION__AcceptThirdLeg, SecurityStatus); return RPC_S_OUT_OF_MEMORY; } #endif // BUILD_WOW6432 return CurrentSecurityContext->AcceptThirdLeg ( DataRepresentation, BufferDescriptor, OutBufferDescriptor ); } OSF_ASSOCIATION::OSF_ASSOCIATION ( IN OSF_ADDRESS *TheAddress, IN RPC_CLIENT_PROCESS_IDENTIFIER * ClientProcess, OUT RPC_STATUS * Status ) { ObjectType = OSF_ASSOCIATION_TYPE; *Status = RPC_S_OK; ConnectionCount = 1; Address = TheAddress; this->ClientProcess.Set(ClientProcess); AssociationGroupId = InterlockedExchangeAdd(&GroupIdCounter, 1); AssociationDictKey = Address->AddAssociation(this); if (AssociationDictKey == -1) { *Status = RPC_S_OUT_OF_MEMORY; } } OSF_ASSOCIATION::~OSF_ASSOCIATION ( ) { if (AssociationDictKey != -1) { int HashBucketNumber; HashBucketNumber = Address->GetHashBucketForAssociation(AssocGroupId()); // lock the bucket Address->GetAssociationBucketMutex(HashBucketNumber)->Request(); Address->RemoveAssociation(AssociationDictKey, this); // unlock the bucket Address->GetAssociationBucketMutex(HashBucketNumber)->Clear(); } } BOOL OSF_ASSOCIATION::RemoveConnectionUnsafe ( void ) { int HashBucketNumber; // get the hashed bucket HashBucketNumber = Address->GetHashBucketForAssociation(AssociationGroupId); // verify the bucket is locked Address->GetAssociationBucketMutex(HashBucketNumber)->VerifyOwned(); ConnectionCount --; if (ConnectionCount == 0) { Address->RemoveAssociation(AssociationDictKey, this); AssociationDictKey = -1; return AssociationDictKey; // AssociationDictKey is a quick non-zero value } else return FALSE; } void OSF_ASSOCIATION::RemoveConnection ( ) { int HashBucketNumber; BOOL Res; // get the hashed bucket HashBucketNumber = Address->GetHashBucketForAssociation(AssociationGroupId); // lock the bucket Address->GetAssociationBucketMutex(HashBucketNumber)->Request(); Res = RemoveConnectionUnsafe(); // unlock the bucket Address->GetAssociationBucketMutex(HashBucketNumber)->Clear(); if (Res) delete this; } RPC_STATUS OSF_ASSOCIATION::CreateThread(void) { return Address->CreateThread(); } RPC_ADDRESS * OsfCreateRpcAddress ( IN TRANS_INFO *TransportInfo ) /*++ Routine Description: This routine will be called to create an object representing an rpc address. That is all it has got to do. Arguments: A new rpc address will be returned, unless insufficient memory is available to create the new rpc address, in which case zero will be returned. --*/ { RPC_ADDRESS * RpcAddress; RPC_STATUS Status = RPC_S_OK; RPC_CONNECTION_TRANSPORT *ServerInfo = (RPC_CONNECTION_TRANSPORT *) TransportInfo->InqTransInfo(); RpcAddress = new (ServerInfo->AddressSize) OSF_ADDRESS(TransportInfo, &Status); if ( Status != RPC_S_OK ) { delete RpcAddress; return(0); } return(RpcAddress); } RPC_STATUS OSF_SCALL::Cancel( void * ThreadHandle ) { InterlockedIncrement(&CancelPending); return RPC_S_OK; } unsigned OSF_SCALL::TestCancel( ) { return InterlockedExchange(&CancelPending, 0); } RPC_STATUS OSF_SCALL::ToStringBinding ( OUT RPC_CHAR * * StringBinding ) /*++ Routine Description: We need to convert this connection 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 representation of the binding handle. Return Value: RPC_S_OK - The operation completed successfully. RPC_S_OUT_OF_MEMORY - We do not have enough memory available to allocate space for the string binding. --*/ { BINDING_HANDLE * BindingHandle; RPC_STATUS Status = RPC_S_OK; BindingHandle = Address->InquireBinding(); if (BindingHandle == 0) return(RPC_S_OUT_OF_MEMORY); if ( ObjectUuidSpecified != 0) { Status = RpcBindingSetObject(BindingHandle, (UUID *) &ObjectUuid); } if (Status == RPC_S_OK) { Status = BindingHandle->ToStringBinding(StringBinding); } BindingHandle->BindingFree(); return(Status); } #if DBG void OSF_SCALL::InterfaceForCallDoesNotUseStrict ( void ) { CurrentBinding->InterfaceForCallDoesNotUseStrict(); } #endif RPC_STATUS OSF_SCALL::InqLocalConnAddress ( IN OUT void *Buffer, IN OUT unsigned long *BufferSize, OUT unsigned long *AddressFormat ) /*++ Routine Description: This routine is used by a server application to inquire about the local address on which a call is made. Arguments: Buffer - The buffer that will receive the output address BufferSize - the size of the supplied Buffer on input. On output the number of bytes written to the buffer. If the buffer is too small to receive all the output data, ERROR_MORE_DATA is returned, nothing is written to the buffer, and BufferSize is set to the size of the buffer needed to return all the data. AddressFormat - a constant indicating the format of the returned address. Currently supported are RPC_P_ADDR_FORMAT_TCP_IPV4 and RPC_P_ADDR_FORMAT_TCP_IPV6. Return Values: RPC_S_OK - success. RPC_S_OUT_OF_MEMORY - Insufficient memory is available to complete this operation. RPC_S_INVALID_BINDING - The supplied client binding is invalid. RPC_S_CANNOT_SUPPORT - The local address was inquired for a protocol sequence that doesn't support this type of functionality. Currently only ncacn_ip_tcp supports it. RPC_S_* or Win32 error for other errors --*/ { return Connection->InqLocalConnAddress( Buffer, BufferSize, AddressFormat); } void OSF_SCALL::WakeUpPipeThreadIfNecessary ( IN RPC_STATUS Status ) /*++ Routine Description: If a pipe thread is being stuck on wait, it fires the event to wake it up. Arguments: Status - the status with which the call failed. Return Values: --*/ { if (pAsync == 0) { if (fPipeCall) { CallMutex.Request(); CurrentState = CallAborted; AsyncStatus = Status; // wake up the thread that was flow controlled, if any fChoked = 0; CallMutex.Clear(); LogEvent(SU_SCALL, EV_STATUS, this, SyncEvent.EventHandle, 0, 1, 0); SyncEvent.Raise(); } } else { if (fAsyncPipeCall) { CallMutex.Request(); CurrentState = CallAborted; AsyncStatus = Status; CallMutex.Clear(); // For async pipes, this path races with OSF_SCALL::ProcessReceivedPDU. // If the notification has been issued there, then NeededLength == 0. // We want to issue this notification iff NeededLength != 0, since that // means that there will be no thread attempting a pull and clening up // the call syncronously. if (NeededLength > 0) { IssueNotification(); } } } } BOOL OSF_SCALL::ProcessVerificationTrailer ( IN rpc_sec_verification_trailer_command *FirstCommand, IN unsigned char *BufferEnd, IN RPC_MESSAGE *RpcMessage ) /*++ Routine Description: Processes the commands in the verification trailer. Arguments: FirstCommand - pointer to the first verification trailer command. At least one command must be present. BufferEnd - the first invalid position in the buffer (i.e. invalid in the sense beyond the end of the buffer). FirstCommand is NOT guaranteeed to be smaller than BufferEnd RpcMessage - current RPC Message. Return Values: non-zero - all commands passed verification check 0 - one or more commands failed verification check --*/ { rpc_sec_verification_trailer_command *CurrentCommand; rpc_sec_verification_trailer_command *NextCommand; rpc_sec_vt_bitmask *BitmaskCommand; rpc_sec_vt_pcontext *PContextCommand; rpc_sec_vt_header2 *Header2; ULONG CommandCode; BOOL LastCommand; CurrentCommand = FirstCommand; while (TRUE) { // check that we can read the command code and length if ((unsigned char *)(CurrentCommand + 1) > BufferEnd) return FALSE; CommandCode = CurrentCommand->command & SEC_VT_COMMAND_CODE_MASK; LastCommand = CurrentCommand->command & SEC_VT_COMMAND_END; NextCommand = (rpc_sec_verification_trailer_command *)(((unsigned char *)CurrentCommand) + sizeof(rpc_sec_verification_trailer_command) + CurrentCommand->length) // length is only USHORT - cannot overflow in user mode ; // can we read the entire command up to the next command? if ((unsigned char *)NextCommand > BufferEnd) return FALSE; // from here, command is verified to be readable up to the declared length. // individual commands may only verify the length is what is expected switch (CommandCode) { case SEC_VT_COMMAND_BITMASK_1: if (CurrentCommand->length != (sizeof(rpc_sec_vt_bitmask) - sizeof(rpc_sec_verification_trailer_command))) return FALSE; BitmaskCommand = (rpc_sec_vt_bitmask *)CurrentCommand; // if client supports header signing, but the cleartext data did not indicate so, // there must be someone in the middle tampering with data. if ((BitmaskCommand->bits & CLIENT_SUPPORT_HEADER_SIGNING) && (Connection->GetClientSupportsHeaderSigningFlag() == FALSE)) { return FALSE; } break; case SEC_VT_COMMAND_PCONTEXT: if (CurrentCommand->length != (sizeof(rpc_sec_vt_pcontext) - sizeof(rpc_sec_verification_trailer_command))) return FALSE; PContextCommand = (rpc_sec_vt_pcontext *)CurrentCommand; // check that the binding information corresponds to what we have in // the call pcontext if (RpcpMemoryCompare(&PContextCommand->InterfaceId, CurrentBinding->GetInterfaceId(), sizeof(RPC_SYNTAX_IDENTIFIER) )) { return FALSE; } if (RpcpMemoryCompare(&PContextCommand->TransferSyntax, CurrentBinding->GetTransferSyntaxId(), sizeof(RPC_SYNTAX_IDENTIFIER) )) { return FALSE; } break; case SEC_VT_COMMAND_HEADER2: if (CurrentCommand->length != (sizeof(rpc_sec_vt_header2) - sizeof(rpc_sec_verification_trailer_command))) return FALSE; Header2 = (rpc_sec_vt_header2 *)CurrentCommand; // check that the information in the two headers matches if (Header2->call_id != CallId) return FALSE; if ((*(ULONG *)Header2->drep) != RpcMessage->DataRepresentation) return FALSE; if (Header2->opnum != (USHORT)RpcMessage->ProcNum) return FALSE; if (Header2->p_cont_id != (USHORT)CurrentBinding->GetPresentationContext()) return FALSE; if (Header2->PTYPE != rpc_request) return FALSE; break; default: // unknown command - skip to next unless current command // requires processing if (CurrentCommand->command & SEC_VT_MUST_PROCESS_COMMAND) return FALSE; } if (LastCommand) break; CurrentCommand = NextCommand; } return TRUE; } BOOL OSF_SCALL::CheckVerificationTrailer ( IN unsigned char *BufferStart, IN unsigned char *BufferEnd, IN RPC_MESSAGE *RpcMessage ) /*++ Routine Description: Checks if the just unmarshalled fragment should be checked for verification trailer. Arguments: BufferStart - the current position in the buffer. BufferEnd - the first invalid position in the buffer (i.e. invalid in the sense beyond the end of the buffer). BufferStart is guaranteeed to be at least sizeof(rpc_sec_verification_trailer) bytes before BufferEnd by caller. RpcMessage - the current RPC Message Return Values: non-zero - verification trailer passes check or is not present (which is an implicit pass) 0 - verification trailer is present and failed the check --*/ { ULONG AuthnLevel; rpc_sec_verification_trailer *VTrailer; if (Connection->CurrentSecurityContext) { AuthnLevel = Connection->CurrentSecurityContext->AuthenticationLevel; // if this is a secure connection and the current check is in the dispatch // buffer, then it may be a verification trailer if (AuthnLevel >= RPC_C_AUTHN_LEVEL_PKT_INTEGRITY) { if ((BufferStart >= DispatchBuffer) && (BufferStart < (unsigned char *)DispatchBuffer + DispatchBufferOffset) ) { VTrailer = (rpc_sec_verification_trailer *) AlignPtr4(BufferStart); if ((unsigned char *)(VTrailer + 1) > BufferEnd) return TRUE; if (RpcpMemoryCompare(VTrailer, SecVerificationTrailerSignature, sizeof(SecVerificationTrailerSignature))) return TRUE; // now we know we have a trailer. Verify it. return ProcessVerificationTrailer ((rpc_sec_verification_trailer_command *)(VTrailer + 1), BufferEnd, RpcMessage ); } else { #if DBG DbgPrint("RPCRT4: Current position not in dispatch buffer - ignored\n"); #endif } } } return TRUE; } RPC_STATUS RPC_ENTRY I_RpcTransServerReallocPacket ( IN RPC_TRANSPORT_CONNECTION ThisConnection, IN OUT void * * Buffer, IN unsigned int OldBufferLength, IN unsigned int NewBufferLength ) /*++ Routine Description: The server side transport interface modules will use this routine to increase the size of a buffer so that the entire packet to be received will fit into it, or to allocate a new buffer. If the buffer is to be reallocated, the data from the old buffer is copied into the beginning of the new buffer. The old buffer will be freed. Arguments: ThisConnection - Supplies the connection for which we are reallocating a transport buffer. Buffer - Supplies the buffer which we want to reallocate to be larger. If no buffer is supplied, then a new one is allocated anyway. The new buffer is returned via this argument. OldBufferLength - Supplies the current length of the buffer in bytes. This information is necessary so we know how much of the buffer needs to be copied into the new buffer. NewBufferLength - Supplies the required length of the buffer in bytes. Return Value: RPC_S_OK - The requested larger buffer has successfully been allocated. RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate the buffer. --*/ { ASSERT(0); return(RPC_S_INTERNAL_ERROR); } BUFFER RPC_ENTRY I_RpcTransServerAllocatePacket ( IN RPC_TRANSPORT_CONNECTION ThisConnection, IN UINT Size ) /*++ Routine Description: The server side transport interface modules will use this routine to increase the size of a buffer so that the entire packet to be received will fit into it, or to allocate a new buffer. If the buffer is to be reallocated, the data from the old buffer is copied into the beginning of the new buffer. The old buffer will be freed. Arguments: ThisConnection - Supplies the connection for which we are reallocating a transport buffer. Buffer - Supplies the buffer which we want to reallocate to be larger. If no buffer is supplied, then a new one is allocated anyway. The new buffer is returned via this argument. OldBufferLength - Supplies the current length of the buffer in bytes. This information is necessary so we know how much of the buffer needs to be copied into the new buffer. NewBufferLength - Supplies the required length of the buffer in bytes. Return Value: RPC_S_OK - The requested larger buffer has successfully been allocated. RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate the buffer. --*/ { ASSERT(0); return(0); } unsigned short RPC_ENTRY I_RpcTransServerMaxFrag ( IN RPC_TRANSPORT_CONNECTION ThisConnection ) /*++ Routine Description: The server side transport interface modules will use this routine to determine the negotiated maximum fragment size. Arguments: ThisConnection - Supplies the connection for which we are returning the maximum fragment size. --*/ { ASSERT(0); return(0); } RPC_TRANSPORT_CONNECTION RPC_ENTRY I_RpcTransServerNewConnection ( IN RPC_TRANSPORT_ADDRESS ThisAddress ) { OSF_SCONNECTION * SConnection; OSF_ADDRESS *Address ; Address = InqTransAddress(ThisAddress) ; SConnection = Address->NewConnection(); if ( SConnection == 0 ) { return(0); } return(SConnection->TransConnection); } void RPC_ENTRY I_RpcTransServerFreePacket ( IN RPC_TRANSPORT_CONNECTION ThisConnection, IN void * Buffer ) /*++ Routine Description: We need to free a transport buffer for a transport connection; this will typically occur when the connection is being closed. Arguments: ThisConnection - Supplies the transport connection which owns the buffer. Buffer - Supplies the buffer to be freed. --*/ { ASSERT(0); } void * RPC_ENTRY I_RpcTransProtectThread ( void ) /*++ Routine Description: In some cases, if an asyncronous io operation has been started by a thread, the thread can not be deleted because the io operation will be cancelled. This routine will be called by a transport to indicate that the current thread can not be deleted. Return Value: A pointer to the thread will be returned. This is necessary, so that later the thread can be unprotected. --*/ { #ifdef RPC_OLD_IO_PROTECTION THREAD * Thread = RpcpGetThreadPointer(); Thread->ProtectThread(); return((void *) Thread); #endif return 0; } void RPC_ENTRY I_RpcTransUnprotectThread ( IN void * Thread ) /*++ Routine Description: When a thread no longer needs to be protected from deletion, this routine must be called. Arguments: Thread - Supplies the thread which no longer needs to be protected from deletion. --*/ { #ifdef RPC_OLD_IO_PROTECTION ((THREAD *) Thread)->UnprotectThread(); #endif } void I_RpcTransVerifyServerRuntimeCallFromContext( void *SendContext ) /*++ Routine Description: Verifies that the supplied context follows a valid runtime server call object. Arguments: SendContext - the context as seen by the transport Return Value: --*/ { ASSERT(InqTransSCall(SendContext)->InvalidHandle(OSF_SCALL_TYPE) == 0); } const UUID BindNakEEInfoSignatureData = { /* 90740320-fad0-11d3-82d7-009027b130ab */ 0x90740320, 0xfad0, 0x11d3, {0x82, 0xd7, 0x00, 0x90, 0x27, 0xb1, 0x30, 0xab} }; const UUID *BindNakEEInfoSignature = &BindNakEEInfoSignatureData;