Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

8751 lines
257 KiB

/*++
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 <precomp.hxx>
#include <wincrypt.h>
#include <rpcssl.h>
#include <thrdctx.hxx>
#include <hndlsvr.hxx>
#include <osfpcket.hxx>
#include <osfsvr.hxx>
#include <sdict2.hxx>
#include <rpccfg.h>
#include <schnlsp.h> // for UNISP_RPC_ID
#include <charconv.hxx>
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;