2397 lines
72 KiB
C
2397 lines
72 KiB
C
/*++
|
||
|
||
Copyright (c) 1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
ccmp.c
|
||
|
||
Abstract:
|
||
|
||
Cluster Control Message Protocol code.
|
||
|
||
Author:
|
||
|
||
Mike Massa (mikemas) January 24, 1997
|
||
|
||
Revision History:
|
||
|
||
Who When What
|
||
-------- -------- ----------------------------------------------
|
||
mikemas 01-24-97 created
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
#include "ccmp.tmh"
|
||
|
||
#include <sspi.h>
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
|
||
#pragma alloc_text(INIT, CcmpLoad)
|
||
#pragma alloc_text(PAGE, CcmpUnload)
|
||
|
||
#endif // ALLOC_PRAGMA
|
||
|
||
//
|
||
// Local Data
|
||
//
|
||
PCN_RESOURCE_POOL CcmpSendRequestPool = NULL;
|
||
PCN_RESOURCE_POOL CcmpMcastHBSendRequestPool = NULL;
|
||
PCN_RESOURCE_POOL CcmpReceiveRequestPool = NULL;
|
||
|
||
|
||
#define CCMP_SEND_REQUEST_POOL_DEPTH 5
|
||
#define CCMP_RECEIVE_REQUEST_POOL_DEPTH 2
|
||
|
||
typedef enum {
|
||
CcmpInvalidMsgCode = 0
|
||
} CCMP_MSG_CODE;
|
||
|
||
//
|
||
// Packet header structures must be packed.
|
||
//
|
||
#include <packon.h>
|
||
|
||
typedef struct {
|
||
ULONG SeqNumber;
|
||
ULONG AckNumber;
|
||
} CCMP_HEARTBEAT_MSG, *PCCMP_HEARTBEAT_MSG;
|
||
|
||
typedef struct {
|
||
ULONG SeqNumber;
|
||
} CCMP_POISON_MSG, *PCCMP_POISON_MSG;
|
||
|
||
typedef struct {
|
||
ULONG NodeCount;
|
||
CX_CLUSTERSCREEN McastTargetNodes;
|
||
} CCMP_MCAST_HEARTBEAT_HEADER, *PCCMP_MCAST_HEARTBEAT_MSG;
|
||
|
||
typedef struct {
|
||
UCHAR Type;
|
||
UCHAR Code;
|
||
USHORT Checksum;
|
||
|
||
union {
|
||
CCMP_HEARTBEAT_MSG Heartbeat;
|
||
CCMP_POISON_MSG Poison;
|
||
CCMP_MCAST_HEARTBEAT_HEADER McastHeartbeat;
|
||
} Message;
|
||
|
||
} CCMP_HEADER, *PCCMP_HEADER;
|
||
|
||
#include <packoff.h>
|
||
|
||
|
||
typedef struct {
|
||
PCX_SEND_COMPLETE_ROUTINE CompletionRoutine;
|
||
PVOID CompletionContext;
|
||
PVOID MessageData;
|
||
} CCMP_SEND_CONTEXT, *PCCMP_SEND_CONTEXT;
|
||
|
||
typedef struct {
|
||
PCNP_NETWORK Network;
|
||
CL_NODE_ID SourceNodeId;
|
||
ULONG TsduSize;
|
||
ULONG CnpReceiveFlags;
|
||
} CCMP_RECEIVE_CONTEXT, *PCCMP_RECEIVE_CONTEXT;
|
||
|
||
//
|
||
// Size of pre-allocated buffers for CCMP multicast heartbeats.
|
||
//
|
||
#define CCMP_MCAST_HEARTBEAT_PAYLOAD_PREALLOC(_NodeCount) \
|
||
((_NodeCount) * sizeof(CX_HB_NODE_INFO))
|
||
|
||
#define CCMP_MCAST_HEARTBEAT_PREALLOC(_NodeCount) \
|
||
(sizeof(CCMP_HEADER) \
|
||
+ CCMP_MCAST_HEARTBEAT_PAYLOAD_PREALLOC(_NodeCount) \
|
||
)
|
||
|
||
|
||
//
|
||
// Security contexts.
|
||
//
|
||
// The heartbeat and poison packets are signed to detect tampering or
|
||
// spoofing. The context is first established in user mode, then passed to
|
||
// clusnet and imported into the kernel security package.
|
||
//
|
||
// A node maintains an inbound and outbound based context with each node in
|
||
// the cluster. Hence, an array, indexed by Node Id, holds the data used to
|
||
// represent a context between this node and the specified node.
|
||
//
|
||
// The use of multiple, simultaneous security packages is supported on NT5. As
|
||
// of right now, the signature size can't be determined until the context has
|
||
// been generated. It's possible for the signature buffer size for the initial
|
||
// context to be smaller than the buffer size for subsequent
|
||
// contexts. RichardW is going to provide the ability to determine the
|
||
// signature size for a given package without having to generate a context.
|
||
//
|
||
// There are two scenarios where changing signature buffer size has an effect:
|
||
// 1) a mixed mode (SP4/NT5), 2 node cluster is using NTLM with a signature
|
||
// buffer size of 16 bytes. The SP4 node is upgraded to NT5. When the two
|
||
// nodes join, they will use kerberos which has a larger signature buffer size
|
||
// than NTLM but the 1st node has already allocated 16 b. signature
|
||
// buffers. This could be fixed by noting the change in buffer size and
|
||
// reallocating the lookaside list for the new size. This doesn't solve the
|
||
// problem with more than 2 nodes: 2) with > 2 node, mixed mode clusters, it's
|
||
// possible to have some nodes using NTLM and others using kerberos. If the
|
||
// max signature buffer can be determined before any contexts are generated
|
||
// then we'll allocated the largest buffer needed. If not, either multiple
|
||
// sets of signature buffers have to be maintained or the old, smaller buffer
|
||
// list is deallocated while a new, larger list is generated (in a
|
||
// synchronized fashion of course).
|
||
//
|
||
|
||
typedef struct _CLUSNET_SECURITY_DATA {
|
||
CtxtHandle Inbound;
|
||
CtxtHandle Outbound;
|
||
ULONG SignatureBufferSize;
|
||
} CLUSNET_SECURITY_DATA, * PCLUSNET_SECURITY_DATA;
|
||
|
||
//
|
||
// this array of structs holds the in/outbound contexts and the signature
|
||
// buffer size needed for communicating with the node indexed at this
|
||
// location. The index is based on internal (zero based) numbering.
|
||
//
|
||
CLUSNET_SECURITY_DATA SecurityContexts[ ClusterMinNodeId + ClusterDefaultMaxNodes ];
|
||
|
||
//
|
||
// the size of the signature buffers in the sig buffer lookaside list
|
||
//
|
||
ULONG AllocatedSignatureBufferSize = 0;
|
||
|
||
//
|
||
// the largest size of the signature buffers imported
|
||
//
|
||
ULONG MaxSignatureSize = 0;
|
||
|
||
CN_LOCK SecCtxtLock;
|
||
|
||
#define VALID_SSPI_HANDLE( _x ) ((_x).dwUpper != (ULONG_PTR)-1 && \
|
||
(_x).dwLower != (ULONG_PTR)-1 )
|
||
|
||
#define INVALIDATE_SSPI_HANDLE( _x ) { \
|
||
(_x).dwUpper = (ULONG_PTR)-1; \
|
||
(_x).dwLower = (ULONG_PTR)-1; \
|
||
}
|
||
|
||
//
|
||
// Lookaside list of signature data and its MDL
|
||
//
|
||
|
||
typedef struct _SIGNATURE_DATA {
|
||
SINGLE_LIST_ENTRY Next;
|
||
CN_SIGNATURE_FIELD
|
||
PMDL SigMDL;
|
||
UCHAR PacketSignature[0];
|
||
} SIGNATURE_DATA, *PSIGNATURE_DATA;
|
||
|
||
PNPAGED_LOOKASIDE_LIST SignatureLL;
|
||
#define CN_SIGNATURE_TAG CN_POOL_TAG
|
||
|
||
//
|
||
// Routines exported within the Cluster Transport.
|
||
//
|
||
NTSTATUS
|
||
CcmpLoad(
|
||
VOID
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
ULONG i;
|
||
|
||
IF_CNDBG(CN_DEBUG_INIT) {
|
||
CNPRINT(("[CCMP] Loading...\n"));
|
||
}
|
||
|
||
CcmpSendRequestPool = CnpCreateSendRequestPool(
|
||
CNP_VERSION_UNICAST,
|
||
PROTOCOL_CCMP,
|
||
sizeof(CCMP_HEADER),
|
||
sizeof(CCMP_SEND_CONTEXT),
|
||
CCMP_SEND_REQUEST_POOL_DEPTH
|
||
);
|
||
|
||
if (CcmpSendRequestPool == NULL) {
|
||
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
CcmpReceiveRequestPool = CnpCreateReceiveRequestPool(
|
||
sizeof(CCMP_RECEIVE_CONTEXT),
|
||
CCMP_RECEIVE_REQUEST_POOL_DEPTH
|
||
);
|
||
|
||
if (CcmpSendRequestPool == NULL) {
|
||
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
CcmpMcastHBSendRequestPool =
|
||
CnpCreateSendRequestPool(
|
||
CNP_VERSION_MULTICAST,
|
||
PROTOCOL_CCMP,
|
||
(USHORT)CCMP_MCAST_HEARTBEAT_PREALLOC(ClusterDefaultMaxNodes),
|
||
(USHORT)sizeof(CCMP_SEND_CONTEXT),
|
||
CCMP_SEND_REQUEST_POOL_DEPTH
|
||
);
|
||
if (CcmpMcastHBSendRequestPool == NULL) {
|
||
IF_CNDBG( CN_DEBUG_INIT )
|
||
CNPRINT(("[CCMP]: no memory for mcast heartbeat "
|
||
"send request pool\n"));
|
||
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
//
|
||
// initialize the individual client and server side security contexts
|
||
//
|
||
|
||
for ( i = ClusterMinNodeId; i <= ClusterDefaultMaxNodes; ++i ) {
|
||
INVALIDATE_SSPI_HANDLE( SecurityContexts[ i ].Outbound );
|
||
INVALIDATE_SSPI_HANDLE( SecurityContexts[ i ].Inbound );
|
||
SecurityContexts[ i ].SignatureBufferSize = 0;
|
||
}
|
||
|
||
CnInitializeLock( &SecCtxtLock, CNP_SEC_CTXT_LOCK );
|
||
|
||
SignatureLL = NULL;
|
||
|
||
IF_CNDBG(CN_DEBUG_INIT) {
|
||
CNPRINT(("[CCMP] Loaded.\n"));
|
||
}
|
||
|
||
return(STATUS_SUCCESS);
|
||
|
||
} // CcmpLoad
|
||
|
||
|
||
VOID
|
||
CcmpUnload(
|
||
VOID
|
||
)
|
||
{
|
||
ULONG i;
|
||
|
||
PAGED_CODE();
|
||
|
||
|
||
IF_CNDBG(CN_DEBUG_INIT) {
|
||
CNPRINT(("[CCMP] Unloading...\n"));
|
||
}
|
||
|
||
if (CcmpSendRequestPool != NULL) {
|
||
CnpDeleteSendRequestPool(CcmpSendRequestPool);
|
||
CcmpSendRequestPool = NULL;
|
||
}
|
||
|
||
if (CcmpMcastHBSendRequestPool != NULL) {
|
||
CnpDeleteSendRequestPool(CcmpMcastHBSendRequestPool);
|
||
CcmpMcastHBSendRequestPool = NULL;
|
||
}
|
||
|
||
if (CcmpReceiveRequestPool != NULL) {
|
||
CnpDeleteReceiveRequestPool(CcmpReceiveRequestPool);
|
||
CcmpReceiveRequestPool = NULL;
|
||
}
|
||
|
||
//
|
||
// free Signature buffers and delete security contexts
|
||
//
|
||
|
||
if ( SignatureLL != NULL ) {
|
||
|
||
ExDeleteNPagedLookasideList( SignatureLL );
|
||
CnFreePool( SignatureLL );
|
||
SignatureLL = NULL;
|
||
AllocatedSignatureBufferSize = 0;
|
||
}
|
||
|
||
for ( i = ClusterMinNodeId; i <= ClusterDefaultMaxNodes; ++i ) {
|
||
|
||
CxDeleteSecurityContext( i );
|
||
}
|
||
|
||
IF_CNDBG(CN_DEBUG_INIT) {
|
||
CNPRINT(("[CCMP] Unload complete.\n"));
|
||
}
|
||
|
||
return;
|
||
|
||
} // CcmpUnload
|
||
|
||
#ifdef MM_IN_CLUSNET
|
||
VOID
|
||
CcmpCompleteSendMembershipMsg(
|
||
IN NTSTATUS Status,
|
||
IN ULONG BytesSent,
|
||
IN PCNP_SEND_REQUEST SendRequest,
|
||
IN PMDL DataMdl,
|
||
IN PIRP Irp
|
||
)
|
||
{
|
||
PCCMP_SEND_CONTEXT sendContext = SendRequest->UpperProtocolContext;
|
||
|
||
CnAssert(DataMdl != NULL);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
if (BytesSent >= sizeof(CCMP_HEADER)) {
|
||
BytesSent -= sizeof(CCMP_HEADER);
|
||
}
|
||
else {
|
||
BytesSent = 0;
|
||
CnAssert(FALSE);
|
||
}
|
||
|
||
//
|
||
// Update the Information field of the completed IRP to
|
||
// reflect the actual bytes sent (adjusted for the CCMP
|
||
// header).
|
||
//
|
||
Irp->IoStatus.Information = BytesSent;
|
||
}
|
||
else {
|
||
CnAssert(BytesSent == 0);
|
||
}
|
||
|
||
//
|
||
// Call the completion routine.
|
||
//
|
||
(*(sendContext->CompletionRoutine))(
|
||
Status,
|
||
BytesSent,
|
||
sendContext->CompletionContext,
|
||
sendContext->MessageData
|
||
);
|
||
|
||
//
|
||
// Free the stuff we allocated.
|
||
//
|
||
IoFreeMdl(DataMdl);
|
||
|
||
CnFreeResource((PCN_RESOURCE) SendRequest);
|
||
|
||
return;
|
||
|
||
} // CcmpCompleteSendMembershipMsg
|
||
|
||
|
||
NTSTATUS
|
||
CxSendMembershipMessage(
|
||
IN CL_NODE_ID DestinationNodeId,
|
||
IN PVOID MessageData,
|
||
IN USHORT MessageDataLength,
|
||
IN PCX_SEND_COMPLETE_ROUTINE CompletionRoutine,
|
||
IN PVOID CompletionContext OPTIONAL
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
PCNP_SEND_REQUEST sendRequest;
|
||
PCCMP_HEADER ccmpHeader;
|
||
PMDL dataMdl;
|
||
PCCMP_SEND_CONTEXT sendContext;
|
||
|
||
|
||
CnAssert(MessageData != NULL);
|
||
CnAssert(MessageDataLength > 0);
|
||
|
||
dataMdl = IoAllocateMdl(
|
||
MessageData,
|
||
MessageDataLength,
|
||
FALSE,
|
||
FALSE,
|
||
NULL
|
||
);
|
||
|
||
if (dataMdl != NULL) {
|
||
MmBuildMdlForNonPagedPool(dataMdl);
|
||
|
||
sendRequest = (PCNP_SEND_REQUEST) CnAllocateResource(
|
||
CcmpSendRequestPool
|
||
);
|
||
|
||
if (sendRequest != NULL) {
|
||
|
||
//
|
||
// Fill in the CCMP header.
|
||
//
|
||
ccmpHeader = sendRequest->UpperProtocolHeader;
|
||
RtlZeroMemory(ccmpHeader, sizeof(CCMP_HEADER));
|
||
ccmpHeader->Type = CcmpMembershipMsgType;
|
||
|
||
//
|
||
// Fill in the caller portion of the CNP send request.
|
||
//
|
||
sendRequest->UpperProtocolIrp = NULL;
|
||
sendRequest->CompletionRoutine = CcmpCompleteSendMembershipMsg;
|
||
|
||
//
|
||
// Fill in our own send context.
|
||
//
|
||
sendContext = sendRequest->UpperProtocolContext;
|
||
sendContext->CompletionRoutine = CompletionRoutine;
|
||
sendContext->CompletionContext = CompletionContext;
|
||
sendContext->MessageData = MessageData;
|
||
|
||
//
|
||
// Send the message.
|
||
//
|
||
status = CnpSendPacket(
|
||
sendRequest,
|
||
DestinationNodeId,
|
||
dataMdl,
|
||
MessageDataLength,
|
||
FALSE,
|
||
ClusterAnyNetworkId
|
||
);
|
||
|
||
return(status);
|
||
}
|
||
|
||
IoFreeMdl(dataMdl);
|
||
}
|
||
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
return(status);
|
||
|
||
} // CxSendMembershipMessage
|
||
#endif // MM_IN_CLUSNET
|
||
|
||
VOID
|
||
CcmpCompleteSendHeartbeatMsg(
|
||
IN NTSTATUS Status,
|
||
IN OUT PULONG BytesSent,
|
||
IN PCNP_SEND_REQUEST SendRequest,
|
||
IN PMDL DataMdl
|
||
)
|
||
{
|
||
PCCMP_HEADER ccmpHeader = SendRequest->UpperProtocolHeader;
|
||
PCNP_HEADER cnpHeader = SendRequest->CnpHeader;
|
||
PSIGNATURE_DATA SigData;
|
||
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
MEMLOG(MemLogHBPacketSendComplete,
|
||
CcmpHeartbeatMsgType,
|
||
ccmpHeader->Message.Heartbeat.SeqNumber);
|
||
|
||
CnTrace(CCMP_SEND_DETAIL, CcmpTraceSendHBComplete,
|
||
"[CCMP] Send of heartbeat to node %u completed, seqno %u.",
|
||
cnpHeader->DestinationAddress, // LOGULONG
|
||
ccmpHeader->Message.Heartbeat.SeqNumber // LOGULONG
|
||
);
|
||
|
||
//
|
||
// Strip the CCMP header off of the byte count
|
||
//
|
||
if (*BytesSent >= sizeof(CCMP_HEADER)) {
|
||
*BytesSent -= sizeof(CCMP_HEADER);
|
||
}
|
||
else {
|
||
*BytesSent = 0;
|
||
CnAssert(FALSE);
|
||
}
|
||
}
|
||
else {
|
||
MEMLOG(MemLogPacketSendFailed,
|
||
cnpHeader->DestinationAddress,
|
||
Status);
|
||
|
||
CnTrace(CCMP_SEND_ERROR, CcmpTraceSendHBFailedBelow,
|
||
"[CCMP] Transport failed to send heartbeat to node %u, "
|
||
"seqno %u, status %!status!.",
|
||
cnpHeader->DestinationAddress, // LOGULONG
|
||
ccmpHeader->Message.Heartbeat.SeqNumber, // LOGULONG
|
||
Status // LOGSTATUS
|
||
);
|
||
|
||
CnAssert(*BytesSent == 0);
|
||
}
|
||
|
||
//
|
||
// Strip the sig data off of the byte count and free it
|
||
//
|
||
CnAssert(DataMdl != NULL);
|
||
|
||
SigData = CONTAINING_RECORD(
|
||
DataMdl->MappedSystemVa,
|
||
SIGNATURE_DATA,
|
||
PacketSignature
|
||
);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
if (*BytesSent >= SigData->SigMDL->ByteCount) {
|
||
*BytesSent -= SigData->SigMDL->ByteCount;
|
||
} else {
|
||
*BytesSent = 0;
|
||
CnAssert(FALSE);
|
||
}
|
||
}
|
||
|
||
// XXX: restore the original buffer size
|
||
SigData->SigMDL->ByteCount = AllocatedSignatureBufferSize;
|
||
|
||
ExFreeToNPagedLookasideList( SignatureLL, SigData );
|
||
|
||
//
|
||
// At this point BytesSent should be zero.
|
||
//
|
||
CnAssert(*BytesSent == 0);
|
||
|
||
//
|
||
// Free the send request.
|
||
//
|
||
CnFreeResource((PCN_RESOURCE) SendRequest);
|
||
|
||
return;
|
||
|
||
} // CcmpCompleteSendHeartbeatMsg
|
||
|
||
|
||
NTSTATUS
|
||
CxSendHeartBeatMessage(
|
||
IN CL_NODE_ID DestinationNodeId,
|
||
IN ULONG SeqNumber,
|
||
IN ULONG AckNumber,
|
||
IN CL_NETWORK_ID NetworkId
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
PCNP_SEND_REQUEST sendRequest;
|
||
PCCMP_HEADER ccmpHeader;
|
||
SecBufferDesc SignatureDescriptor;
|
||
SecBuffer SignatureSecBuffer[2];
|
||
PSIGNATURE_DATA SigData;
|
||
CN_IRQL SecContextIrql;
|
||
PCLUSNET_SECURITY_DATA contextData = &SecurityContexts[ DestinationNodeId ];
|
||
|
||
|
||
sendRequest = (PCNP_SEND_REQUEST) CnAllocateResource( CcmpSendRequestPool );
|
||
|
||
if (sendRequest != NULL) {
|
||
|
||
//
|
||
// Fill in the CCMP header.
|
||
//
|
||
ccmpHeader = sendRequest->UpperProtocolHeader;
|
||
RtlZeroMemory(ccmpHeader, sizeof(CCMP_HEADER));
|
||
ccmpHeader->Type = CcmpHeartbeatMsgType;
|
||
ccmpHeader->Message.Heartbeat.SeqNumber = SeqNumber;
|
||
ccmpHeader->Message.Heartbeat.AckNumber = AckNumber;
|
||
|
||
//
|
||
// allocate a buffer and generate a signature. SignatureLL
|
||
// will be NULL if security contexts have not yet been
|
||
// imported.
|
||
//
|
||
|
||
if (SignatureLL != NULL) {
|
||
|
||
SigData = ExAllocateFromNPagedLookasideList( SignatureLL );
|
||
|
||
if (SigData != NULL) {
|
||
|
||
//
|
||
// acquire the lock on the security contexts and see if
|
||
// we have a valid one with which to send this packet
|
||
//
|
||
|
||
CnAcquireLock( &SecCtxtLock, &SecContextIrql );
|
||
|
||
if ( VALID_SSPI_HANDLE( contextData->Outbound )) {
|
||
|
||
//
|
||
// build a descriptor for the message and signature
|
||
//
|
||
|
||
SignatureDescriptor.cBuffers = 2;
|
||
SignatureDescriptor.pBuffers = SignatureSecBuffer;
|
||
SignatureDescriptor.ulVersion = SECBUFFER_VERSION;
|
||
|
||
SignatureSecBuffer[0].BufferType = SECBUFFER_DATA;
|
||
SignatureSecBuffer[0].cbBuffer = sizeof(CCMP_HEADER);
|
||
SignatureSecBuffer[0].pvBuffer = (PVOID)ccmpHeader;
|
||
|
||
SignatureSecBuffer[1].BufferType = SECBUFFER_TOKEN;
|
||
SignatureSecBuffer[1].cbBuffer =
|
||
contextData->SignatureBufferSize;
|
||
SignatureSecBuffer[1].pvBuffer =
|
||
SigData->PacketSignature;
|
||
|
||
status = MakeSignature(&contextData->Outbound,
|
||
0,
|
||
&SignatureDescriptor,
|
||
0);
|
||
CnAssert( status == STATUS_SUCCESS );
|
||
|
||
CnReleaseLock( &SecCtxtLock, SecContextIrql );
|
||
|
||
if ( status == STATUS_SUCCESS ) {
|
||
|
||
//
|
||
// Fill in the caller portion of the CNP send request.
|
||
//
|
||
sendRequest->UpperProtocolIrp = NULL;
|
||
sendRequest->CompletionRoutine =
|
||
CcmpCompleteSendHeartbeatMsg;
|
||
|
||
//
|
||
// Send the message.
|
||
//
|
||
|
||
MEMLOG(
|
||
MemLogHBPacketSend,
|
||
CcmpHeartbeatMsgType,
|
||
SeqNumber
|
||
);
|
||
|
||
CnTrace(CCMP_SEND_DETAIL, CcmpTraceSendHB,
|
||
"[CCMP] Sending heartbeat to node %u "
|
||
"on network %u, seqno %u, ackno %u.",
|
||
DestinationNodeId, // LOGULONG
|
||
NetworkId, // LOGULONG
|
||
SeqNumber, // LOGULONG
|
||
AckNumber // LOGULONG
|
||
);
|
||
|
||
//
|
||
// XXX: adjust the MDL to reflect the true
|
||
// number of bytes in the signature buffer. This
|
||
// will go away when the max sig buffer size can
|
||
// be determined in user mode
|
||
//
|
||
SigData->SigMDL->ByteCount =
|
||
contextData->SignatureBufferSize;
|
||
|
||
status = CnpSendPacket(
|
||
sendRequest,
|
||
DestinationNodeId,
|
||
SigData->SigMDL,
|
||
(USHORT)contextData->SignatureBufferSize,
|
||
FALSE,
|
||
NetworkId);
|
||
|
||
//
|
||
// CnpSendPacket is responsible for ensuring
|
||
// that CcmpCompleteSendHeartbeatMsg is called (it
|
||
// is stored in the send request data structure).
|
||
//
|
||
}
|
||
} else {
|
||
|
||
CnReleaseLock( &SecCtxtLock, SecContextIrql );
|
||
ExFreeToNPagedLookasideList( SignatureLL, SigData );
|
||
CnFreeResource((PCN_RESOURCE) sendRequest);
|
||
|
||
status = STATUS_CLUSTER_NO_SECURITY_CONTEXT;
|
||
}
|
||
} else {
|
||
|
||
CnFreeResource((PCN_RESOURCE) sendRequest);
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
} else {
|
||
|
||
CnFreeResource((PCN_RESOURCE) sendRequest);
|
||
status = STATUS_CLUSTER_NO_SECURITY_CONTEXT;
|
||
}
|
||
|
||
} else {
|
||
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
CnTrace(CCMP_SEND_ERROR, CcmpTraceSendHBFailedInternal,
|
||
"[CCMP] Failed to send heartbeat to node %u on net %u, "
|
||
"seqno %u, status %!status!.",
|
||
DestinationNodeId, // LOGULONG
|
||
NetworkId, // LOGULONG
|
||
SeqNumber, // LOGULONG
|
||
status // LOGSTATUS
|
||
);
|
||
}
|
||
|
||
return(status);
|
||
|
||
} // CxSendHeartbeatMessage
|
||
|
||
|
||
VOID
|
||
CcmpCompleteSendMcastHeartbeatMsg(
|
||
IN NTSTATUS Status,
|
||
IN OUT PULONG BytesSent,
|
||
IN PCNP_SEND_REQUEST SendRequest,
|
||
IN PMDL DataMdl
|
||
)
|
||
{
|
||
PCCMP_HEADER ccmpHeader = SendRequest->UpperProtocolHeader;
|
||
PCNP_HEADER cnpHeader = SendRequest->CnpHeader;
|
||
PCCMP_SEND_CONTEXT sendContext = SendRequest->UpperProtocolContext;
|
||
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
MEMLOG(MemLogHBPacketSendComplete,
|
||
CcmpMcastHeartbeatMsgType,
|
||
0xFFFFFFFF);
|
||
|
||
CnTrace(
|
||
CCMP_SEND_DETAIL, CcmpTraceSendMcastHBComplete,
|
||
"[CCMP] Send of multicast heartbeat "
|
||
"on network id %u completed.",
|
||
SendRequest->Network->Id // LOGULONG
|
||
);
|
||
|
||
//
|
||
// Strip the CCMP header and multicast heartbeat payload
|
||
// off of the byte count. The size of the message sent was
|
||
// saved in the send request data structure.
|
||
//
|
||
if (*BytesSent >= SendRequest->UpperProtocolHeaderLength) {
|
||
*BytesSent -= SendRequest->UpperProtocolHeaderLength;
|
||
}
|
||
else {
|
||
*BytesSent = 0;
|
||
CnAssert(FALSE);
|
||
}
|
||
}
|
||
else {
|
||
MEMLOG(MemLogPacketSendFailed,
|
||
cnpHeader->DestinationAddress,
|
||
Status);
|
||
|
||
CnTrace(
|
||
CCMP_SEND_ERROR, CcmpTraceSendHBFailedBelow,
|
||
"[CCMP] Transport failed to send multicast "
|
||
"heartbeat on network id %u, status %!status!.",
|
||
SendRequest->Network->Id, // LOGULONG
|
||
Status // LOGSTATUS
|
||
);
|
||
|
||
CnAssert(*BytesSent == 0);
|
||
}
|
||
|
||
//
|
||
// At this point BytesSent should be zero.
|
||
//
|
||
CnAssert(*BytesSent == 0);
|
||
|
||
//
|
||
// Call the completion routine if one was specified
|
||
//
|
||
if (sendContext->CompletionRoutine) {
|
||
(*(sendContext->CompletionRoutine))(
|
||
Status,
|
||
*BytesSent,
|
||
sendContext->CompletionContext,
|
||
NULL
|
||
);
|
||
}
|
||
|
||
//
|
||
// Free the send request.
|
||
//
|
||
CnFreeResource((PCN_RESOURCE) SendRequest);
|
||
|
||
return;
|
||
|
||
} // CcmpCompleteSendHeartbeatMsg
|
||
|
||
|
||
NTSTATUS
|
||
CxSendMcastHeartBeatMessage(
|
||
IN CL_NETWORK_ID NetworkId,
|
||
IN PVOID McastGroup,
|
||
IN CX_CLUSTERSCREEN McastTargetNodes,
|
||
IN CX_HB_NODE_INFO NodeInfo[],
|
||
IN PCX_SEND_COMPLETE_ROUTINE CompletionRoutine, OPTIONAL
|
||
IN PVOID CompletionContext OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Send a multicast heartbeat message. The mcast heartbeat is
|
||
structured as follows:
|
||
|
||
CCMP_HEADER
|
||
|
||
CNP_MCAST_SIGNATURE (including signature buffer)
|
||
|
||
CCMP_MCAST_HEARTBEAT_MESSAGE
|
||
|
||
Arguments:
|
||
|
||
NetworkId - network to send mcast heartbeat
|
||
|
||
McastGroup - contains data for the multicast group to
|
||
which the message is to be sent
|
||
|
||
McastTargetNodes - screen that indicates whether the
|
||
(internal) node id is a target of this multicast heartbeat.
|
||
|
||
NodeInfo - vector, of size ClusterDefaultMaxNodes+ClusterMinNodeId,
|
||
of node info data structures indexed by dest node id
|
||
|
||
CompletionRoutine - called in this routine if the request is
|
||
not passed down to a lower level (in which case it will be
|
||
called by this routine's completion routine)
|
||
|
||
CompletionContext - context for CompletionRoutine
|
||
|
||
Return value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_HOST_UNREACHABLE;
|
||
PCNP_SEND_REQUEST sendRequest;
|
||
PCCMP_HEADER ccmpHeader;
|
||
PCCMP_SEND_CONTEXT sendContext;
|
||
CX_HB_NODE_INFO UNALIGNED * payload;
|
||
PVOID signHeaders[2];
|
||
ULONG signHeaderLengths[2];
|
||
ULONG sigLen;
|
||
PCNP_MULTICAST_GROUP mcastGroup;
|
||
BOOLEAN pushedPacket = FALSE;
|
||
|
||
|
||
mcastGroup = (PCNP_MULTICAST_GROUP) McastGroup;
|
||
CnAssert(mcastGroup != NULL);
|
||
|
||
sendRequest = (PCNP_SEND_REQUEST) CnAllocateResource(
|
||
CcmpMcastHBSendRequestPool
|
||
);
|
||
|
||
if (sendRequest != NULL) {
|
||
|
||
//
|
||
// Fill in the caller portion of the CNP send request.
|
||
//
|
||
sendRequest->UpperProtocolIrp = NULL;
|
||
sendRequest->CompletionRoutine = CcmpCompleteSendMcastHeartbeatMsg;
|
||
sendRequest->McastGroup = mcastGroup;
|
||
|
||
//
|
||
// Fill in our own send context.
|
||
//
|
||
sendContext = sendRequest->UpperProtocolContext;
|
||
sendContext->CompletionRoutine = CompletionRoutine;
|
||
sendContext->CompletionContext = CompletionContext;
|
||
|
||
//
|
||
// Fill in the CCMP header.
|
||
//
|
||
ccmpHeader = sendRequest->UpperProtocolHeader;
|
||
RtlZeroMemory(ccmpHeader, sizeof(CCMP_HEADER));
|
||
ccmpHeader->Type = CcmpMcastHeartbeatMsgType;
|
||
ccmpHeader->Message.McastHeartbeat.NodeCount = ClusterDefaultMaxNodes;
|
||
ccmpHeader->Message.McastHeartbeat.McastTargetNodes = McastTargetNodes;
|
||
|
||
//
|
||
// Fill in the heartbeat data.
|
||
//
|
||
payload = (CX_HB_NODE_INFO UNALIGNED *)(ccmpHeader + 1);
|
||
RtlCopyMemory(
|
||
payload,
|
||
&(NodeInfo[ClusterMinNodeId]),
|
||
sizeof(*NodeInfo) * ClusterDefaultMaxNodes
|
||
);
|
||
|
||
//
|
||
// Send the message.
|
||
//
|
||
|
||
MEMLOG(
|
||
MemLogHBPacketSend,
|
||
CcmpMcastHeartbeatMsgType,
|
||
0xFFFFFFFF
|
||
);
|
||
|
||
CnTrace(
|
||
CCMP_SEND_DETAIL, CcmpTraceSendMcastHB,
|
||
"[CCMP] Sending multicast heartbeat on network %u, "
|
||
"node count %u, target mask %04X",
|
||
NetworkId, // LOGULONG
|
||
ClusterDefaultMaxNodes, // LOGUSHORT
|
||
McastTargetNodes.UlongScreen
|
||
);
|
||
|
||
status = CnpSendPacket(
|
||
sendRequest,
|
||
ClusterAnyNodeId,
|
||
NULL,
|
||
0,
|
||
FALSE,
|
||
NetworkId
|
||
);
|
||
|
||
//
|
||
// CnpSendPacket is responsible for ensuring
|
||
// that CcmpCompleteSendMcastHeartbeatMsg is called
|
||
// (it is stored in the send request data structure).
|
||
//
|
||
|
||
pushedPacket = TRUE;
|
||
|
||
|
||
} else {
|
||
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
CnTrace(CCMP_SEND_ERROR, CcmpTraceSendMcastHBFailedInternal,
|
||
"[CCMP] Failed to send multicast heartbeat on net %u, "
|
||
"status %!status!, pushedPacket = %!bool!.",
|
||
NetworkId, // LOGULONG
|
||
status, // LOGSTATUS
|
||
pushedPacket
|
||
);
|
||
}
|
||
|
||
//
|
||
// If the request wasn't submitted to the next lower layer and
|
||
// a completion routine was provided, call the completion
|
||
// routine.
|
||
//
|
||
if (!pushedPacket && CompletionRoutine) {
|
||
(*CompletionRoutine)(
|
||
status,
|
||
0,
|
||
CompletionContext,
|
||
NULL
|
||
);
|
||
}
|
||
|
||
return(status);
|
||
|
||
} // CxSendMcastHeartBeatMessage
|
||
|
||
|
||
VOID
|
||
CcmpCompleteSendPoisonPkt(
|
||
IN NTSTATUS Status,
|
||
IN OUT PULONG BytesSent,
|
||
IN PCNP_SEND_REQUEST SendRequest,
|
||
IN PMDL DataMdl
|
||
)
|
||
{
|
||
PCCMP_SEND_CONTEXT sendContext = SendRequest->UpperProtocolContext;
|
||
PSIGNATURE_DATA SigData;
|
||
PCNP_HEADER cnpHeader = (PCNP_HEADER) SendRequest->CnpHeader;
|
||
|
||
|
||
MEMLOG(MemLogHBPacketSendComplete,
|
||
CcmpPoisonMsgType,
|
||
( sendContext->CompletionRoutine == NULL ));
|
||
|
||
IF_CNDBG( CN_DEBUG_POISON | CN_DEBUG_CCMPSEND )
|
||
CNPRINT(("[CCMP] Send of poison packet to node %u completed "
|
||
"with status %08x\n",
|
||
cnpHeader->DestinationAddress, Status));
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
CnTrace(CCMP_SEND_DETAIL, CcmpTraceSendPoisonComplete,
|
||
"[CCMP] Send of poison packet to node %u completed.",
|
||
cnpHeader->DestinationAddress // LOGULONG
|
||
);
|
||
|
||
//
|
||
// Strip the CCMP header off of the byte count
|
||
//
|
||
if (*BytesSent >= sizeof(CCMP_HEADER)) {
|
||
*BytesSent -= sizeof(CCMP_HEADER);
|
||
}
|
||
else {
|
||
*BytesSent = 0;
|
||
CnAssert(FALSE);
|
||
}
|
||
|
||
} else {
|
||
CnTrace(CCMP_SEND_ERROR, CcmpTraceSendPoisonFailedBelow,
|
||
"[CCMP] Transport failed to send poison packet to node %u, "
|
||
"status %!status!.",
|
||
cnpHeader->DestinationAddress, // LOGULONG
|
||
Status // LOGSTATUS
|
||
);
|
||
|
||
CnAssert(*BytesSent == 0);
|
||
}
|
||
|
||
//
|
||
// Strip the sig data off of the byte count and free it
|
||
//
|
||
CnAssert(DataMdl != NULL);
|
||
|
||
SigData = CONTAINING_RECORD(
|
||
DataMdl->MappedSystemVa,
|
||
SIGNATURE_DATA,
|
||
PacketSignature
|
||
);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
if (*BytesSent >= SigData->SigMDL->ByteCount) {
|
||
*BytesSent -= SigData->SigMDL->ByteCount;
|
||
} else {
|
||
*BytesSent = 0;
|
||
CnAssert(FALSE);
|
||
}
|
||
}
|
||
|
||
// XXX: restore the original buffer size
|
||
SigData->SigMDL->ByteCount = AllocatedSignatureBufferSize;
|
||
|
||
ExFreeToNPagedLookasideList( SignatureLL, SigData );
|
||
|
||
//
|
||
// At this point BytesSent should be zero.
|
||
//
|
||
CnAssert(*BytesSent == 0);
|
||
|
||
//
|
||
// Call the completion routine if one was specified
|
||
//
|
||
if (sendContext->CompletionRoutine) {
|
||
(*(sendContext->CompletionRoutine))(
|
||
Status,
|
||
*BytesSent,
|
||
sendContext->CompletionContext,
|
||
sendContext->MessageData
|
||
);
|
||
}
|
||
|
||
//
|
||
// Free the send request.
|
||
//
|
||
CnFreeResource((PCN_RESOURCE) SendRequest);
|
||
|
||
return;
|
||
|
||
} // CcmpCompleteSendPoisonPkt
|
||
|
||
|
||
VOID
|
||
CxSendPoisonPacket(
|
||
IN CL_NODE_ID DestinationNodeId,
|
||
IN PCX_SEND_COMPLETE_ROUTINE CompletionRoutine, OPTIONAL
|
||
IN PVOID CompletionContext, OPTIONAL
|
||
IN PIRP Irp OPTIONAL
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
PCNP_NODE node;
|
||
|
||
|
||
node = CnpFindNode(DestinationNodeId);
|
||
|
||
if (node == NULL) {
|
||
if (CompletionRoutine) {
|
||
(*CompletionRoutine)(
|
||
STATUS_CLUSTER_NODE_NOT_FOUND,
|
||
0,
|
||
CompletionContext,
|
||
NULL
|
||
);
|
||
}
|
||
|
||
if (Irp) {
|
||
Irp->IoStatus.Status = STATUS_CLUSTER_NODE_NOT_FOUND;
|
||
Irp->IoStatus.Information = 0;
|
||
|
||
IF_CNDBG( CN_DEBUG_POISON | CN_DEBUG_CCMPSEND )
|
||
CNPRINT(("[CCMP] CxSendPoisonPacket completing IRP "
|
||
"%p with status %08x\n",
|
||
Irp, Irp->IoStatus.Status));
|
||
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
}
|
||
}
|
||
else {
|
||
CcmpSendPoisonPacket(
|
||
node,
|
||
CompletionRoutine,
|
||
CompletionContext,
|
||
NULL,
|
||
Irp
|
||
);
|
||
}
|
||
|
||
return;
|
||
|
||
} // CxSendPoisonPacket
|
||
|
||
|
||
VOID
|
||
CcmpSendPoisonPacket(
|
||
IN PCNP_NODE Node,
|
||
IN PCX_SEND_COMPLETE_ROUTINE CompletionRoutine, OPTIONAL
|
||
IN PVOID CompletionContext, OPTIONAL
|
||
IN PCNP_NETWORK Network, OPTIONAL
|
||
IN PIRP Irp OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Notes:
|
||
|
||
Called with the node lock held. Returns with the node lock released.
|
||
|
||
If this send request is not submitted to the next lower layer,
|
||
CompletionRoutine must be called (if it is not NULL).
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PCNP_SEND_REQUEST sendRequest;
|
||
PCCMP_HEADER ccmpHeader;
|
||
PCCMP_SEND_CONTEXT sendContext;
|
||
SecBufferDesc SignatureDescriptor;
|
||
SecBuffer SignatureSecBuffer[2];
|
||
PSIGNATURE_DATA SigData;
|
||
CN_IRQL SecContextIrql;
|
||
SECURITY_STATUS secStatus;
|
||
PCNP_INTERFACE interface;
|
||
PCLUSNET_SECURITY_DATA contextData = &SecurityContexts[Node->Id];
|
||
CL_NETWORK_ID networkId;
|
||
CL_NODE_ID nodeId = Node->Id;
|
||
|
||
|
||
sendRequest = (PCNP_SEND_REQUEST) CnAllocateResource(CcmpSendRequestPool);
|
||
|
||
if (sendRequest != NULL) {
|
||
//
|
||
// make sure we have an interface to send this on. We
|
||
// could be shutting down and have dropped info out of
|
||
// the database
|
||
//
|
||
if ( Network != NULL ) {
|
||
PLIST_ENTRY entry;
|
||
|
||
//
|
||
// we really want to send this packet over the indicated
|
||
// network. walk the node's interface list matching the
|
||
// supplied network id to the interface's network ID and
|
||
// send the packet on that interface
|
||
//
|
||
|
||
for (entry = Node->InterfaceList.Flink;
|
||
entry != &(Node->InterfaceList);
|
||
entry = entry->Flink
|
||
)
|
||
{
|
||
interface = CONTAINING_RECORD(entry,
|
||
CNP_INTERFACE,
|
||
NodeLinkage);
|
||
|
||
if ( interface->Network == Network ) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ( entry == &Node->InterfaceList ) {
|
||
interface = Node->CurrentInterface;
|
||
}
|
||
}
|
||
else {
|
||
interface = Node->CurrentInterface;
|
||
}
|
||
|
||
if ( interface != NULL ) {
|
||
networkId = interface->Network->Id;
|
||
|
||
//
|
||
// Fill in the CCMP header.
|
||
//
|
||
ccmpHeader = sendRequest->UpperProtocolHeader;
|
||
RtlZeroMemory(ccmpHeader, sizeof(CCMP_HEADER));
|
||
ccmpHeader->Type = CcmpPoisonMsgType;
|
||
ccmpHeader->Message.Poison.SeqNumber =
|
||
++(interface->SequenceToSend);
|
||
|
||
CnReleaseLock( &Node->Lock, Node->Irql );
|
||
|
||
//
|
||
// Fill in the caller portion of the CNP send request.
|
||
//
|
||
sendRequest->UpperProtocolIrp = Irp;
|
||
sendRequest->CompletionRoutine = CcmpCompleteSendPoisonPkt;
|
||
|
||
//
|
||
// Fill in our own send context.
|
||
//
|
||
sendContext = sendRequest->UpperProtocolContext;
|
||
sendContext->CompletionRoutine = CompletionRoutine;
|
||
sendContext->CompletionContext = CompletionContext;
|
||
|
||
//
|
||
// allocate a signature buffer and generate one. SignatureLL
|
||
// will be NULL if security contexts have not yet been
|
||
// imported.
|
||
//
|
||
|
||
if (SignatureLL != NULL) {
|
||
|
||
SigData = ExAllocateFromNPagedLookasideList( SignatureLL );
|
||
|
||
if (SigData != NULL) {
|
||
|
||
//
|
||
// acquire the lock on the security contexts and see if
|
||
// we have a valid one with which to send this packet
|
||
//
|
||
|
||
CnAcquireLock( &SecCtxtLock, &SecContextIrql );
|
||
|
||
if ( VALID_SSPI_HANDLE( contextData->Outbound )) {
|
||
|
||
//
|
||
// build a descriptor for the message and signature
|
||
//
|
||
|
||
SignatureDescriptor.cBuffers = 2;
|
||
SignatureDescriptor.pBuffers = SignatureSecBuffer;
|
||
SignatureDescriptor.ulVersion = SECBUFFER_VERSION;
|
||
|
||
SignatureSecBuffer[0].BufferType = SECBUFFER_DATA;
|
||
SignatureSecBuffer[0].cbBuffer = sizeof(CCMP_HEADER);
|
||
SignatureSecBuffer[0].pvBuffer = (PVOID)ccmpHeader;
|
||
|
||
SignatureSecBuffer[1].BufferType = SECBUFFER_TOKEN;
|
||
SignatureSecBuffer[1].cbBuffer =
|
||
contextData->SignatureBufferSize;
|
||
SignatureSecBuffer[1].pvBuffer =
|
||
SigData->PacketSignature;
|
||
|
||
secStatus = MakeSignature(
|
||
&contextData->Outbound,
|
||
0,
|
||
&SignatureDescriptor,
|
||
0);
|
||
CnAssert( secStatus == STATUS_SUCCESS );
|
||
|
||
CnReleaseLock( &SecCtxtLock, SecContextIrql );
|
||
|
||
//
|
||
// no completion routine means this routine was called
|
||
// from the heartbeat dpc. We'll use that to
|
||
// distinguish between that and clussvc calling for a
|
||
// poison packet to be sent.
|
||
//
|
||
|
||
//
|
||
// WMI tracing prints the thread id,
|
||
// can figure out DPC or not on our own
|
||
//
|
||
CnTrace(CCMP_SEND_DETAIL, CcmpTraceSendPoison,
|
||
"[CCMP] Sending poison packet to node %u "
|
||
"on net %u.",
|
||
nodeId, // LOGULONG
|
||
networkId // LOGULONG
|
||
);
|
||
|
||
MEMLOG(MemLogHBPacketSend,
|
||
CcmpPoisonMsgType,
|
||
( CompletionRoutine == NULL ));
|
||
|
||
//
|
||
// Send the message.
|
||
//
|
||
//
|
||
// XXX: adjust the MDL to reflect the true number of
|
||
// bytes in the signature buffer. This will go away
|
||
// when the max sig buffer size can be determined in
|
||
// user mode
|
||
//
|
||
SigData->SigMDL->ByteCount =
|
||
contextData->SignatureBufferSize;
|
||
|
||
CnpSendPacket(
|
||
sendRequest,
|
||
nodeId,
|
||
SigData->SigMDL,
|
||
(USHORT)contextData->SignatureBufferSize,
|
||
FALSE,
|
||
networkId
|
||
);
|
||
|
||
//
|
||
// CnpSendPacket is responsible for ensuring
|
||
// that CcmpCompleteSendPoisonPkt is called.
|
||
// CcmpCompleteSendPoisonPkt calls CompletionRoutine,
|
||
// which was a parameter to this routine.
|
||
//
|
||
return;
|
||
|
||
} else {
|
||
|
||
CnReleaseLock( &SecCtxtLock, SecContextIrql );
|
||
ExFreeToNPagedLookasideList( SignatureLL, SigData );
|
||
CnFreeResource((PCN_RESOURCE) sendRequest);
|
||
|
||
status = STATUS_CLUSTER_NO_SECURITY_CONTEXT;
|
||
}
|
||
|
||
} else {
|
||
|
||
CnFreeResource((PCN_RESOURCE) sendRequest);
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
} else {
|
||
|
||
CnFreeResource((PCN_RESOURCE) sendRequest);
|
||
status = STATUS_CLUSTER_NO_SECURITY_CONTEXT;
|
||
}
|
||
} else {
|
||
CnReleaseLock( &Node->Lock, Node->Irql );
|
||
CnFreeResource((PCN_RESOURCE) sendRequest);
|
||
status = STATUS_CLUSTER_NETINTERFACE_NOT_FOUND;
|
||
}
|
||
} else {
|
||
CnReleaseLock( &Node->Lock, Node->Irql );
|
||
IF_CNDBG( CN_DEBUG_POISON )
|
||
CNPRINT(("[CCMP] No send resources for SendPoisonPacket\n"));
|
||
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
CnTrace(CCMP_SEND_ERROR, CcmpTraceSendPoisonFailedInternal,
|
||
"[CCMP] Failed to send poison packet to node %u, status %!status!.",
|
||
nodeId, // LOGULONG
|
||
status // LOGSTATUS
|
||
);
|
||
|
||
//
|
||
// The request to send a poison packet did not make it to the
|
||
// next lower layer. If a completion routine was provided,
|
||
// call it now.
|
||
//
|
||
if (CompletionRoutine) {
|
||
|
||
(*CompletionRoutine)(
|
||
status,
|
||
0,
|
||
CompletionContext,
|
||
NULL
|
||
);
|
||
}
|
||
|
||
//
|
||
// If an upper protocol IRP was provided, complete it now.
|
||
//
|
||
if (Irp) {
|
||
|
||
IF_CNDBG( CN_DEBUG_POISON | CN_DEBUG_CCMPSEND )
|
||
CNPRINT(("[CCMP] CcmpSendPoisonPacket completing IRP "
|
||
"%p with status %08x\n",
|
||
Irp, status));
|
||
|
||
Irp->IoStatus.Status = status;
|
||
Irp->IoStatus.Information = 0;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
}
|
||
|
||
return;
|
||
|
||
} // CcmpSendPoisonPacket
|
||
|
||
|
||
VOID
|
||
CcmpProcessReceivePacket(
|
||
IN PCNP_NETWORK Network,
|
||
IN CL_NODE_ID SourceNodeId,
|
||
IN ULONG CnpReceiveFlags,
|
||
IN ULONG TsduSize,
|
||
IN PVOID Tsdu
|
||
)
|
||
{
|
||
CCMP_HEADER UNALIGNED * header = Tsdu;
|
||
SECURITY_STATUS SecStatus;
|
||
CX_HB_NODE_INFO UNALIGNED * nodeInfo;
|
||
|
||
|
||
CnVerifyCpuLockMask(
|
||
0, // Required
|
||
0xFFFFFFFF, // Forbidden
|
||
0 // Maximum
|
||
);
|
||
|
||
CnAssert(TsduSize >= sizeof(CCMP_HEADER));
|
||
|
||
//
|
||
// adjust to point past CCMP header to message payload.
|
||
//
|
||
// For unicasts, the message payload is the Signature data.
|
||
//
|
||
// For multicasts, the signature was verified at the CNP level.
|
||
//
|
||
|
||
if (header->Type == CcmpMcastHeartbeatMsgType) {
|
||
|
||
IF_CNDBG(CN_DEBUG_CCMPRECV) {
|
||
CNPRINT(("[CCMP] Recv'd mcast packet from node %u "
|
||
"on network %u, node count %u, target "
|
||
"mask %04x, CNP flags %x.\n",
|
||
SourceNodeId,
|
||
Network->Id,
|
||
header->Message.McastHeartbeat.NodeCount,
|
||
header->Message.McastHeartbeat.McastTargetNodes.UlongScreen,
|
||
CnpReceiveFlags
|
||
));
|
||
}
|
||
|
||
//
|
||
// Verify that the message was identified as a CNP multicast
|
||
// and that the signature was verified.
|
||
//
|
||
if ((CnpReceiveFlags &
|
||
(CNP_RECV_FLAG_MULTICAST | CNP_RECV_FLAG_SIGNATURE_VERIFIED)
|
||
) !=
|
||
(CNP_RECV_FLAG_MULTICAST | CNP_RECV_FLAG_SIGNATURE_VERIFIED)
|
||
) {
|
||
|
||
IF_CNDBG(CN_DEBUG_CCMPRECV) {
|
||
CNPRINT(("[CCMP] Dropping mcast packet from node %u "
|
||
"that was not identified as CNP multicast, "
|
||
"CNP flags %x.\n",
|
||
SourceNodeId, CnpReceiveFlags
|
||
));
|
||
}
|
||
|
||
CnTrace(CCMP_RECV_ERROR, CcmpTraceReceiveNotVerified,
|
||
"[CCMP] Dropping mcast packet from node %u "
|
||
"that was not identified as CNP multicast, "
|
||
"CNP flags %x.",
|
||
SourceNodeId, CnpReceiveFlags
|
||
);
|
||
|
||
//
|
||
// Drop it.
|
||
//
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// Verify that the node count reported in the header is reasonable.
|
||
// It must be compatible with our assumption that the entire
|
||
// cluster screen fits in one ULONG.
|
||
//
|
||
if (header->Message.McastHeartbeat.NodeCount >
|
||
(sizeof(header->Message.McastHeartbeat.McastTargetNodes) * BYTEL)
|
||
) {
|
||
|
||
IF_CNDBG(CN_DEBUG_CCMPRECV) {
|
||
CNPRINT(("[CCMP] Recv'd mcast packet from node %u "
|
||
"with invalid node count %u, CNP flags %x.\n",
|
||
SourceNodeId,
|
||
header->Message.McastHeartbeat.NodeCount,
|
||
CnpReceiveFlags
|
||
));
|
||
}
|
||
|
||
CnTrace(CCMP_RECV_ERROR, CcmpTraceReceiveNotTarget,
|
||
"[CCMP] Recv'd mcast packet from node %u "
|
||
"with invalid node count %u, CNP flags %x.",
|
||
SourceNodeId,
|
||
header->Message.McastHeartbeat.NodeCount,
|
||
CnpReceiveFlags
|
||
);
|
||
|
||
//
|
||
// Drop it.
|
||
//
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// Verify that the packet contains data for this node.
|
||
//
|
||
if (!CnpClusterScreenMember(
|
||
header->Message.McastHeartbeat.McastTargetNodes.ClusterScreen,
|
||
INT_NODE(CnLocalNodeId)
|
||
)) {
|
||
|
||
IF_CNDBG(CN_DEBUG_CCMPRECV) {
|
||
CNPRINT(("[CCMP] Recv'd mcast packet from node %u "
|
||
"but node %u is not a target, CNP flags %x.\n",
|
||
SourceNodeId, CnLocalNodeId, CnpReceiveFlags
|
||
));
|
||
}
|
||
|
||
CnTrace(CCMP_RECV_ERROR, CcmpTraceReceiveNotTarget,
|
||
"[CCMP] Recv'd mcast packet from node %u "
|
||
"but node %u is not a target, CNP flags %x.",
|
||
SourceNodeId, CnLocalNodeId, CnpReceiveFlags
|
||
);
|
||
|
||
//
|
||
// Drop it.
|
||
//
|
||
goto error_exit;
|
||
}
|
||
|
||
nodeInfo = (CX_HB_NODE_INFO UNALIGNED *)((PUCHAR)Tsdu +
|
||
sizeof(CCMP_HEADER));
|
||
|
||
SecStatus = SEC_E_OK;
|
||
|
||
} else {
|
||
|
||
SecBufferDesc PacketDataDescriptor;
|
||
SecBuffer PacketData[3];
|
||
ULONG fQOP;
|
||
CN_IRQL SecContextIrql;
|
||
PCLUSNET_SECURITY_DATA contextData = &SecurityContexts[SourceNodeId];
|
||
|
||
CnAssert(!(CnpReceiveFlags & CNP_RECV_FLAG_MULTICAST));
|
||
CnAssert(!(CnpReceiveFlags & CNP_RECV_FLAG_SIGNATURE_VERIFIED));
|
||
|
||
Tsdu = header + 1;
|
||
TsduSize -= sizeof(CCMP_HEADER);
|
||
|
||
//
|
||
// Acquire the security context lock.
|
||
//
|
||
CnAcquireLock( &SecCtxtLock, &SecContextIrql );
|
||
|
||
//
|
||
// Verify that we have a valid context data.
|
||
//
|
||
if ( !VALID_SSPI_HANDLE( contextData->Inbound )) {
|
||
|
||
CnReleaseLock( &SecCtxtLock, SecContextIrql );
|
||
|
||
IF_CNDBG(CN_DEBUG_CCMPRECV) {
|
||
CNPRINT(("[CCMP] Dropping packet - no security context "
|
||
"available for src node %u.\n",
|
||
SourceNodeId // LOGULONG
|
||
));
|
||
}
|
||
|
||
CnTrace(CCMP_RECV_ERROR, CcmpTraceReceiveNoSecurityContext,
|
||
"[CCMP] Dropping packet - no security context available for "
|
||
"src node %u.",
|
||
SourceNodeId // LOGULONG
|
||
);
|
||
|
||
MEMLOG( MemLogNoSecurityContext, SourceNodeId, 0 );
|
||
|
||
//
|
||
// Drop it.
|
||
//
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// Validate that the received signature size is expected.
|
||
//
|
||
if ( TsduSize < contextData->SignatureBufferSize ) {
|
||
|
||
IF_CNDBG(CN_DEBUG_CCMPRECV) {
|
||
CNPRINT(("[CCMP] Recv'd packet from node %u with "
|
||
"invalid signature buffer size %u.\n",
|
||
SourceNodeId,
|
||
TsduSize
|
||
));
|
||
}
|
||
|
||
CnTrace(CCMP_RECV_ERROR, CcmpTraceReceiveBadSignatureSize,
|
||
"[CCMP] Recv'd packet from node %u with invalid signature "
|
||
"buffer size %u.",
|
||
SourceNodeId, // LOGULONG
|
||
TsduSize // LOGULONG
|
||
);
|
||
|
||
MEMLOG( MemLogSignatureSize, SourceNodeId, TsduSize );
|
||
|
||
CnReleaseLock( &SecCtxtLock, SecContextIrql );
|
||
|
||
//
|
||
// Drop it.
|
||
//
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// Build the descriptors for the message and the
|
||
// signature buffer
|
||
//
|
||
PacketDataDescriptor.cBuffers = 2;
|
||
PacketDataDescriptor.pBuffers = PacketData;
|
||
PacketDataDescriptor.ulVersion = SECBUFFER_VERSION;
|
||
|
||
PacketData[0].BufferType = SECBUFFER_DATA;
|
||
PacketData[0].cbBuffer = sizeof(CCMP_HEADER);
|
||
PacketData[0].pvBuffer = (PVOID)header;
|
||
|
||
PacketData[1].BufferType = SECBUFFER_TOKEN;
|
||
PacketData[1].cbBuffer = contextData->SignatureBufferSize;
|
||
PacketData[1].pvBuffer = (PVOID)Tsdu;
|
||
|
||
//
|
||
// Verify the signature of the packet.
|
||
//
|
||
SecStatus = VerifySignature(&contextData->Inbound,
|
||
&PacketDataDescriptor,
|
||
0, // no sequence number
|
||
&fQOP); // Quality of protection
|
||
|
||
//
|
||
// Release the security context lock.
|
||
//
|
||
CnReleaseLock( &SecCtxtLock, SecContextIrql );
|
||
}
|
||
|
||
//
|
||
// If the signature was verified, deliver the message.
|
||
//
|
||
if ( SecStatus == SEC_E_OK ) {
|
||
|
||
if (header->Type == CcmpHeartbeatMsgType) {
|
||
CnpReceiveHeartBeatMessage(Network,
|
||
SourceNodeId,
|
||
header->Message.Heartbeat.SeqNumber,
|
||
header->Message.Heartbeat.AckNumber,
|
||
FALSE);
|
||
}
|
||
else if (header->Type == CcmpMcastHeartbeatMsgType) {
|
||
CnpReceiveHeartBeatMessage(
|
||
Network,
|
||
SourceNodeId,
|
||
nodeInfo[INT_NODE(CnLocalNodeId)].SeqNumber,
|
||
nodeInfo[INT_NODE(CnLocalNodeId)].AckNumber,
|
||
(BOOLEAN)(CnpReceiveFlags & CNP_RECV_FLAG_CURRENT_MULTICAST_GROUP)
|
||
);
|
||
}
|
||
else if (header->Type == CcmpPoisonMsgType) {
|
||
CnpReceivePoisonPacket(Network,
|
||
SourceNodeId,
|
||
header->Message.Heartbeat.SeqNumber);
|
||
}
|
||
#ifdef MM_IN_CLUSNET
|
||
else if (header->Type == CcmpMembershipMsgType) {
|
||
if (TsduSize > 0) {
|
||
PVOID messageBuffer = Tsdu;
|
||
|
||
//
|
||
// Copy the data if it is unaligned.
|
||
//
|
||
if ( (((ULONG) Tsdu) & 0x3) != 0 ) {
|
||
IF_CNDBG(CN_DEBUG_CCMPRECV) {
|
||
CNPRINT(("[CCMP] Copying misaligned membership packet\n"));
|
||
}
|
||
|
||
messageBuffer = CnAllocatePool(TsduSize);
|
||
|
||
if (messageBuffer != NULL) {
|
||
RtlMoveMemory(messageBuffer, Tsdu, TsduSize);
|
||
}
|
||
}
|
||
|
||
if (messageBuffer != NULL) {
|
||
|
||
CmmReceiveMessageHandler(SourceNodeId,
|
||
messageBuffer,
|
||
TsduSize);
|
||
}
|
||
|
||
if (messageBuffer != Tsdu) {
|
||
CnFreePool(messageBuffer);
|
||
}
|
||
}
|
||
}
|
||
#endif // MM_IN_CLUSNET
|
||
else {
|
||
IF_CNDBG(CN_DEBUG_CCMPRECV) {
|
||
CNPRINT(("[CCMP] Received packet with unknown "
|
||
"type %u from node %u, CNP flags %x.\n",
|
||
header->Type,
|
||
SourceNodeId,
|
||
CnpReceiveFlags
|
||
));
|
||
}
|
||
|
||
CnTrace(CCMP_RECV_ERROR, CcmpTraceReceiveInvalidType,
|
||
"[CCMP] Received packet with unknown type %u from "
|
||
"node %u, CNP flags %x.",
|
||
header->Type, // LOGUCHAR
|
||
SourceNodeId, // LOGULONG
|
||
CnpReceiveFlags // LOGXLONG
|
||
);
|
||
CnAssert(FALSE);
|
||
}
|
||
} else {
|
||
IF_CNDBG(CN_DEBUG_CCMPRECV) {
|
||
CNPRINT(("[CCMP] Recv'd packet type %u with bad "
|
||
"signature from node %d, security status %08x, "
|
||
"CNP flags %x.\n",
|
||
header->Type,
|
||
SourceNodeId,
|
||
SecStatus,
|
||
CnpReceiveFlags
|
||
));
|
||
}
|
||
|
||
CnTrace(CCMP_RECV_ERROR, CcmpTraceReceiveInvalidSignature,
|
||
"[CCMP] Recv'd %!msgtype! packet with bad signature from node %d, "
|
||
"security status %08x, CNP flags %x.",
|
||
header->Type, // LOGMsgType
|
||
SourceNodeId, // LOGULONG
|
||
SecStatus, // LOGXLONG
|
||
CnpReceiveFlags // LOGXLONG
|
||
);
|
||
|
||
MEMLOG( MemLogInvalidSignature, SourceNodeId, header->Type );
|
||
}
|
||
|
||
error_exit:
|
||
|
||
CnVerifyCpuLockMask(
|
||
0, // Required
|
||
0xFFFFFFFF, // Forbidden
|
||
0 // Maximum
|
||
);
|
||
|
||
return;
|
||
|
||
} // CcmpProcessReceivePacket
|
||
|
||
|
||
NTSTATUS
|
||
CcmpCompleteReceivePacket(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
{
|
||
PCNP_RECEIVE_REQUEST request = Context;
|
||
PCCMP_RECEIVE_CONTEXT context = request->UpperProtocolContext;
|
||
|
||
|
||
if (Irp->IoStatus.Status == STATUS_SUCCESS) {
|
||
CnAssert(Irp->IoStatus.Information == context->TsduSize);
|
||
|
||
CcmpProcessReceivePacket(
|
||
context->Network,
|
||
context->SourceNodeId,
|
||
context->CnpReceiveFlags,
|
||
(ULONG)Irp->IoStatus.Information,
|
||
request->DataBuffer
|
||
);
|
||
}
|
||
else {
|
||
CnTrace(CCMP_RECV_ERROR, CcmpTraceCompleteReceiveFailed,
|
||
"[CDP] Failed to fetch packet data, src node %u, "
|
||
"CNP flags %x, status %!status!.",
|
||
context->SourceNodeId, // LOGULONG
|
||
context->CnpReceiveFlags, // LOGXLONG
|
||
Irp->IoStatus.Status // LOGSTATUS
|
||
);
|
||
}
|
||
|
||
CnpFreeReceiveRequest(request);
|
||
|
||
CnVerifyCpuLockMask(
|
||
0, // Required
|
||
0xFFFFFFFF, // Forbidden
|
||
0 // Maximum
|
||
);
|
||
|
||
return(STATUS_MORE_PROCESSING_REQUIRED);
|
||
|
||
} // CcmpCompleteReceivePacket
|
||
|
||
|
||
NTSTATUS
|
||
CcmpReceivePacketHandler(
|
||
IN PCNP_NETWORK Network,
|
||
IN CL_NODE_ID SourceNodeId,
|
||
IN ULONG CnpReceiveFlags,
|
||
IN ULONG TdiReceiveDatagramFlags,
|
||
IN ULONG BytesIndicated,
|
||
IN ULONG BytesAvailable,
|
||
OUT PULONG BytesTaken,
|
||
IN PVOID Tsdu,
|
||
OUT PIRP * Irp
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
CCMP_HEADER UNALIGNED * header = Tsdu;
|
||
PCNP_RECEIVE_REQUEST request;
|
||
|
||
|
||
CnAssert(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
||
|
||
if (BytesIndicated >= sizeof(CCMP_HEADER)) {
|
||
if (BytesIndicated == BytesAvailable) {
|
||
|
||
CcmpProcessReceivePacket(
|
||
Network,
|
||
SourceNodeId,
|
||
CnpReceiveFlags,
|
||
BytesAvailable,
|
||
Tsdu
|
||
);
|
||
|
||
*BytesTaken += BytesAvailable;
|
||
*Irp = NULL;
|
||
|
||
CnVerifyCpuLockMask(
|
||
0, // Required
|
||
0xFFFFFFFF, // Forbidden
|
||
0 // Maximum
|
||
);
|
||
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
//
|
||
// We need to fetch the rest of the packet before we
|
||
// can process it.
|
||
//
|
||
// This message cannot be a CNP multicast, because
|
||
// the CNP layer could not have verified an incomplete
|
||
// message.
|
||
//
|
||
CnAssert(!(CnpReceiveFlags & CNP_RECV_FLAG_MULTICAST));
|
||
CnAssert(!(CnpReceiveFlags & CNP_RECV_FLAG_SIGNATURE_VERIFIED));
|
||
CnAssert(header->Type != CcmpMcastHeartbeatMsgType);
|
||
|
||
request = CnpAllocateReceiveRequest(
|
||
CcmpReceiveRequestPool,
|
||
Network,
|
||
BytesAvailable,
|
||
CcmpCompleteReceivePacket
|
||
);
|
||
|
||
if (request != NULL) {
|
||
PCCMP_RECEIVE_CONTEXT context = request->UpperProtocolContext;
|
||
|
||
context->Network = Network;
|
||
context->SourceNodeId = SourceNodeId;
|
||
context->TsduSize = BytesAvailable;
|
||
context->CnpReceiveFlags = CnpReceiveFlags;
|
||
|
||
*Irp = request->Irp;
|
||
|
||
IF_CNDBG(CN_DEBUG_CCMPRECV) {
|
||
CNPRINT(("[CCMP] Fetching packet data, src node %u, "
|
||
"BI %u, BA %u, CNP flags %x.\n",
|
||
SourceNodeId, BytesIndicated,
|
||
BytesAvailable, CnpReceiveFlags));
|
||
|
||
}
|
||
|
||
CnTrace(CCMP_RECV_DETAIL, CcmpTraceCompleteReceive,
|
||
"[CCMP] Fetching packet data, src node %u, "
|
||
"BI %u, BA %u, CNP flags %x.",
|
||
SourceNodeId, // LOGULONG
|
||
BytesIndicated, // LOGULONG
|
||
BytesAvailable, // LOGULONG
|
||
CnpReceiveFlags // LOGXLONG
|
||
);
|
||
|
||
CnVerifyCpuLockMask(
|
||
0, // Required
|
||
0xFFFFFFFF, // Forbidden
|
||
0 // Maximum
|
||
);
|
||
|
||
return(STATUS_MORE_PROCESSING_REQUIRED);
|
||
}
|
||
else {
|
||
IF_CNDBG(CN_DEBUG_CCMPRECV) {
|
||
CNPRINT(("[CCMP] Dropped incoming packet - "
|
||
"out of resources, src node %u.\n",
|
||
SourceNodeId));
|
||
|
||
}
|
||
CnTrace(CCMP_RECV_ERROR, CcmpTraceDropReceiveOOR,
|
||
"[CCMP] Dropped incoming packet - out of resources, "
|
||
"src node %u.",
|
||
SourceNodeId // LOGULONG
|
||
);
|
||
}
|
||
}
|
||
else {
|
||
IF_CNDBG(CN_DEBUG_CCMPRECV) {
|
||
CNPRINT(("[CCMP] Dropped incoming runt packet, "
|
||
"src node %u, BI %u, BA %u, CNP flags %x.\n",
|
||
SourceNodeId, BytesIndicated, BytesAvailable,
|
||
CnpReceiveFlags));
|
||
|
||
}
|
||
CnTrace(CCMP_RECV_ERROR, CcmpTraceDropReceiveRunt,
|
||
"[CCMP] Dropped incoming runt packet, src node %u, "
|
||
"BI %u, BA %u, CNP flags %x.",
|
||
SourceNodeId, // LOGULONG
|
||
BytesIndicated, // LOGULONG
|
||
BytesAvailable, // LOGULONG
|
||
CnpReceiveFlags // LOGXLONG
|
||
);
|
||
}
|
||
|
||
//
|
||
// Something went wrong. Drop the packet.
|
||
//
|
||
*BytesTaken += BytesAvailable;
|
||
|
||
CnVerifyCpuLockMask(
|
||
0, // Required
|
||
0xFFFFFFFF, // Forbidden
|
||
0 // Maximum
|
||
);
|
||
|
||
return(STATUS_SUCCESS);
|
||
|
||
} // CcmpReceivePacketHandler
|
||
|
||
PVOID
|
||
SignatureAllocate(
|
||
IN POOL_TYPE PoolType,
|
||
IN SIZE_T NumberOfBytes,
|
||
IN ULONG Tag
|
||
)
|
||
{
|
||
PSIGNATURE_DATA SignatureData;
|
||
|
||
CnAssert( NumberOfBytes == ( sizeof(SIGNATURE_DATA) + AllocatedSignatureBufferSize ));
|
||
|
||
//
|
||
// allocate the space and then construct an MDL describing it
|
||
//
|
||
|
||
SignatureData = ExAllocatePoolWithTag( PoolType, NumberOfBytes, Tag );
|
||
|
||
if ( SignatureData != NULL ) {
|
||
|
||
SignatureData->SigMDL = IoAllocateMdl(SignatureData->PacketSignature,
|
||
AllocatedSignatureBufferSize,
|
||
FALSE,
|
||
FALSE,
|
||
NULL);
|
||
|
||
if ( SignatureData->SigMDL != NULL ) {
|
||
|
||
MmBuildMdlForNonPagedPool(SignatureData->SigMDL);
|
||
CN_INIT_SIGNATURE( SignatureData, CN_SIGNATURE_TAG );
|
||
} else {
|
||
|
||
ExFreePool( SignatureData );
|
||
SignatureData = NULL;
|
||
}
|
||
}
|
||
|
||
return SignatureData;
|
||
}
|
||
|
||
VOID
|
||
SignatureFree(
|
||
IN PVOID Buffer
|
||
)
|
||
{
|
||
PSIGNATURE_DATA SignatureData = (PSIGNATURE_DATA)Buffer;
|
||
|
||
CN_ASSERT_SIGNATURE( SignatureData, CN_SIGNATURE_TAG );
|
||
IoFreeMdl( SignatureData->SigMDL );
|
||
|
||
ExFreePool( SignatureData );
|
||
}
|
||
|
||
VOID
|
||
CxDeleteSecurityContext(
|
||
IN CL_NODE_ID NodeId
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Delete the security context associated with the specified node
|
||
|
||
Arguments:
|
||
|
||
NodeId - Id of the node blah blah blah
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PCLUSNET_SECURITY_DATA contextData = &SecurityContexts[ NodeId ];
|
||
|
||
if ( VALID_SSPI_HANDLE( contextData->Inbound )) {
|
||
|
||
DeleteSecurityContext( &contextData->Inbound );
|
||
INVALIDATE_SSPI_HANDLE( contextData->Inbound );
|
||
}
|
||
|
||
if ( VALID_SSPI_HANDLE( contextData->Outbound )) {
|
||
|
||
DeleteSecurityContext( &contextData->Outbound );
|
||
INVALIDATE_SSPI_HANDLE( contextData->Outbound );
|
||
}
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CxImportSecurityContext(
|
||
IN CL_NODE_ID NodeId,
|
||
IN PWCHAR PackageName,
|
||
IN ULONG PackageNameSize,
|
||
IN ULONG SignatureSize,
|
||
IN PVOID ServerContext,
|
||
IN PVOID ClientContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
import a security context that was established in user mode into
|
||
the kernel SSP. We are passed pointers to the structures in user
|
||
mode, so they have be probed and used within try/except blocks.
|
||
|
||
Arguments:
|
||
|
||
NodeId - # of node with which a security context was established
|
||
|
||
PackageName - user process pointer to security package name
|
||
|
||
PackageNameSize - length, in bytes, of PackageName
|
||
|
||
SignatureSize - size, in bytes, needed for a Signature Buffer
|
||
|
||
ServerContext - user process pointer to area that contains the
|
||
SecBuffer for an inbound security context
|
||
|
||
ClientContext - same as ServerContext, but for outbound security
|
||
context
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if everything worked ok, otherwise some error in issperr.h
|
||
|
||
--*/
|
||
|
||
{
|
||
PSecBuffer InboundSecBuffer = (PSecBuffer)ServerContext;
|
||
PSecBuffer OutboundSecBuffer = (PSecBuffer)ClientContext;
|
||
|
||
PVOID CapturedInboundSecData;
|
||
ULONG CapturedInboundSecDataSize;
|
||
PVOID CapturedOutboundSecData;
|
||
ULONG CapturedOutboundSecDataSize;
|
||
|
||
CtxtHandle InboundContext;
|
||
CtxtHandle OutboundContext;
|
||
NTSTATUS Status;
|
||
|
||
PWCHAR KPackageName = NULL;
|
||
PSecBuffer KInboundSecBuffer = NULL;
|
||
PSecBuffer KOutboundSecBuffer = NULL;
|
||
PVOID KInboundData = NULL;
|
||
PVOID KOutboundData = NULL;
|
||
CN_IRQL SecContextIrql;
|
||
SECURITY_STRING PackageNameDesc;
|
||
|
||
//
|
||
// even though this routine is not marked pagable, make sure that we're
|
||
// not running at raised IRQL since DeleteSecurityContext will puke.
|
||
//
|
||
PAGED_CODE();
|
||
|
||
IF_CNDBG( CN_DEBUG_INIT )
|
||
CNPRINT(("[CCMP]: Importing security contexts from %ws\n",
|
||
PackageName));
|
||
|
||
if ( AllocatedSignatureBufferSize == 0 ) {
|
||
//
|
||
// first time in this routine, so create a lookaside list pool for
|
||
// signature buffers and their MDLs
|
||
//
|
||
|
||
CnAssert( SignatureLL == NULL );
|
||
SignatureLL = CnAllocatePool( sizeof( NPAGED_LOOKASIDE_LIST ));
|
||
|
||
if ( SignatureLL != NULL ) {
|
||
//
|
||
// with the support of multiple packages, the only way to
|
||
// determine the sig buffer size was after a context had been
|
||
// generated. Knowing the max size of all sig buffers used by the
|
||
// service before this routine is called will prevent having to
|
||
// add a bunch of synchronization code that would allocate new
|
||
// buffers and phase out the old buffer pool. on NT5, NTLM uses 16
|
||
// bytes while kerberos uses 37b. We've asked security for a call
|
||
// that will give us the max sig size for a set of packages but
|
||
// that hasn't materialized, hence we force the sig buffer size to
|
||
// something that will work for both NTLM and kerberos. But this
|
||
// discussion is kinda moot since we don't use kerberos anyway on
|
||
// NT5.
|
||
//
|
||
|
||
// AllocatedSignatureBufferSize = SignatureSize;
|
||
AllocatedSignatureBufferSize = 64;
|
||
|
||
#if 0
|
||
ExInitializeNPagedLookasideList(SignatureLL,
|
||
SignatureAllocate,
|
||
SignatureFree,
|
||
0,
|
||
sizeof( SIGNATURE_DATA ) + SignatureSize,
|
||
CN_POOL_TAG,
|
||
4);
|
||
#endif
|
||
ExInitializeNPagedLookasideList(SignatureLL,
|
||
SignatureAllocate,
|
||
SignatureFree,
|
||
0,
|
||
sizeof( SIGNATURE_DATA ) + AllocatedSignatureBufferSize,
|
||
CN_POOL_TAG,
|
||
4);
|
||
} else {
|
||
IF_CNDBG( CN_DEBUG_INIT )
|
||
CNPRINT(("[CCMP]: no memory for signature LL\n"));
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto error_exit;
|
||
}
|
||
|
||
} else if ( SignatureSize > AllocatedSignatureBufferSize ) {
|
||
|
||
//
|
||
// the signature buffer is growing. the problem is that the lookaside
|
||
// list is already in use by other nodes.
|
||
//
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// validate the pointers passed in as the SecBuffers
|
||
//
|
||
|
||
try {
|
||
ProbeForRead( PackageName,
|
||
PackageNameSize,
|
||
sizeof( UCHAR ) );
|
||
|
||
ProbeForRead( InboundSecBuffer,
|
||
sizeof( SecBuffer ),
|
||
sizeof( UCHAR ) );
|
||
|
||
ProbeForRead( OutboundSecBuffer,
|
||
sizeof( SecBuffer ),
|
||
sizeof( UCHAR ) );
|
||
|
||
//
|
||
// made it this far; now capture the internal pointers and their
|
||
// lengths. Probe the embedded pointers in the SecBuffers using the
|
||
// captured data
|
||
//
|
||
CapturedInboundSecData = InboundSecBuffer->pvBuffer;
|
||
CapturedInboundSecDataSize = InboundSecBuffer->cbBuffer;
|
||
|
||
CapturedOutboundSecData = OutboundSecBuffer->pvBuffer;
|
||
CapturedOutboundSecDataSize = OutboundSecBuffer->cbBuffer;
|
||
|
||
ProbeForRead( CapturedInboundSecData,
|
||
CapturedInboundSecDataSize,
|
||
sizeof( UCHAR ) );
|
||
|
||
ProbeForRead( CapturedOutboundSecData,
|
||
CapturedOutboundSecDataSize,
|
||
sizeof( UCHAR ) );
|
||
|
||
//
|
||
// make local copies of everything since security doesn't
|
||
// handle accvios very well
|
||
//
|
||
|
||
KPackageName = CnAllocatePoolWithQuota( PackageNameSize );
|
||
if ( KPackageName == NULL ) {
|
||
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
RtlCopyMemory( KPackageName, PackageName, PackageNameSize );
|
||
|
||
KInboundSecBuffer = CnAllocatePoolWithQuota( sizeof( SecBuffer ));
|
||
if ( KInboundSecBuffer == NULL ) {
|
||
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
*KInboundSecBuffer = *InboundSecBuffer;
|
||
KInboundSecBuffer->cbBuffer = CapturedInboundSecDataSize;
|
||
|
||
KOutboundSecBuffer = CnAllocatePoolWithQuota( sizeof( SecBuffer ));
|
||
if ( KOutboundSecBuffer == NULL ) {
|
||
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
*KOutboundSecBuffer = *OutboundSecBuffer;
|
||
KOutboundSecBuffer->cbBuffer = CapturedOutboundSecDataSize;
|
||
|
||
KInboundData = CnAllocatePoolWithQuota( KInboundSecBuffer->cbBuffer );
|
||
if ( KInboundData == NULL ) {
|
||
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
RtlCopyMemory( KInboundData, CapturedInboundSecData, CapturedInboundSecDataSize );
|
||
KInboundSecBuffer->pvBuffer = KInboundData;
|
||
|
||
KOutboundData = CnAllocatePoolWithQuota( KOutboundSecBuffer->cbBuffer );
|
||
if ( KOutboundData == NULL ) {
|
||
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
RtlCopyMemory( KOutboundData, CapturedOutboundSecData, CapturedOutboundSecDataSize );
|
||
KOutboundSecBuffer->pvBuffer = KOutboundData;
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
//
|
||
// An exception was incurred while attempting to probe or copy
|
||
// from one of the caller's parameters. Simply return an
|
||
// appropriate error status code.
|
||
//
|
||
|
||
Status = GetExceptionCode();
|
||
IF_CNDBG( CN_DEBUG_INIT )
|
||
CNPRINT(("[CCMP]: Buffer probe failed %08X", Status ));
|
||
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// import the data we were handed
|
||
//
|
||
|
||
RtlInitUnicodeString( &PackageNameDesc, KPackageName );
|
||
|
||
Status = ImportSecurityContext(&PackageNameDesc,
|
||
KInboundSecBuffer,
|
||
NULL,
|
||
&InboundContext);
|
||
|
||
if ( NT_SUCCESS( Status )) {
|
||
|
||
Status = ImportSecurityContext(&PackageNameDesc,
|
||
KOutboundSecBuffer,
|
||
NULL,
|
||
&OutboundContext);
|
||
|
||
if ( NT_SUCCESS( Status )) {
|
||
CtxtHandle oldInbound;
|
||
CtxtHandle oldOutbound;
|
||
PCLUSNET_SECURITY_DATA contextData = &SecurityContexts[ NodeId ];
|
||
|
||
INVALIDATE_SSPI_HANDLE( oldInbound );
|
||
INVALIDATE_SSPI_HANDLE( oldOutbound );
|
||
|
||
//
|
||
// DeleteSecurityContext can't be called at raised IRQL so make
|
||
// copies of the contexts to be deleted under the lock. After
|
||
// releasing the lock, we can delete the old contexts.
|
||
//
|
||
|
||
CnAcquireLock( &SecCtxtLock, &SecContextIrql );
|
||
|
||
if ( VALID_SSPI_HANDLE( contextData->Inbound )) {
|
||
oldInbound = contextData->Inbound;
|
||
}
|
||
|
||
if ( VALID_SSPI_HANDLE( contextData->Outbound )) {
|
||
oldOutbound = contextData->Outbound;
|
||
}
|
||
|
||
contextData->Inbound = InboundContext;
|
||
contextData->Outbound = OutboundContext;
|
||
contextData->SignatureBufferSize = SignatureSize;
|
||
|
||
//
|
||
// Update MaxSignatureSize -- the largest signature imported
|
||
// so far.
|
||
//
|
||
if (SignatureSize > MaxSignatureSize) {
|
||
MaxSignatureSize = SignatureSize;
|
||
}
|
||
|
||
CnReleaseLock( &SecCtxtLock, SecContextIrql );
|
||
|
||
if ( VALID_SSPI_HANDLE( oldInbound )) {
|
||
DeleteSecurityContext( &oldInbound );
|
||
}
|
||
|
||
if ( VALID_SSPI_HANDLE( oldOutbound )) {
|
||
DeleteSecurityContext( &oldOutbound );
|
||
}
|
||
} else {
|
||
IF_CNDBG( CN_DEBUG_INIT )
|
||
CNPRINT(("[CCMP]: import of outbound security context failed %08X\n", Status ));
|
||
|
||
DeleteSecurityContext( &InboundContext );
|
||
|
||
goto error_exit;
|
||
}
|
||
} else {
|
||
IF_CNDBG( CN_DEBUG_INIT )
|
||
CNPRINT(("[CCMP]: import of inbound security context failed %08X\n", Status ));
|
||
goto error_exit;
|
||
}
|
||
|
||
error_exit:
|
||
|
||
//
|
||
// Clean up allocations.
|
||
//
|
||
|
||
if ( KPackageName ) {
|
||
CnFreePool( KPackageName );
|
||
}
|
||
|
||
if ( KInboundSecBuffer ) {
|
||
CnFreePool( KInboundSecBuffer );
|
||
}
|
||
|
||
if ( KOutboundSecBuffer ) {
|
||
CnFreePool( KOutboundSecBuffer );
|
||
}
|
||
|
||
if ( KInboundData ) {
|
||
CnFreePool( KInboundData );
|
||
}
|
||
|
||
if ( KOutboundData ) {
|
||
CnFreePool( KOutboundData );
|
||
}
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// The following is only executed in an error situation.
|
||
//
|
||
|
||
IF_CNDBG( CN_DEBUG_INIT ) {
|
||
CNPRINT(("[CCMP]: CxImportSecurityContext returning %08X%\n", Status));
|
||
}
|
||
|
||
if (CcmpMcastHBSendRequestPool != NULL) {
|
||
CnpDeleteSendRequestPool(CcmpMcastHBSendRequestPool);
|
||
CcmpMcastHBSendRequestPool = NULL;
|
||
}
|
||
if (SignatureLL != NULL) {
|
||
ExDeleteNPagedLookasideList(SignatureLL);
|
||
CnFreePool(SignatureLL);
|
||
SignatureLL = NULL;
|
||
}
|
||
|
||
return Status;
|
||
|
||
} // CxImportSecurityContext
|
||
|