windows-nt/Source/XPSP1/NT/base/cluster/clusnet/xport/cnpmisc.c
2020-09-26 16:20:57 +08:00

1495 lines
38 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
cnpmisc.c
Abstract:
Miscellaneous routines for the Cluster Network Protocol.
Author:
Mike Massa (mikemas) January 24, 1997
Revision History:
Who When What
-------- -------- ----------------------------------------------
mikemas 01-24-97 created
Notes:
--*/
#include "precomp.h"
#pragma hdrstop
#include "cnpmisc.tmh"
#include <tdiinfo.h>
#include <tcpinfo.h>
#include <fipsapi.h>
#include <sspi.h>
//
// Local function prototypes
//
NTSTATUS
CnpRestartDeviceControl (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
#ifdef ALLOC_PRAGMA
//
// All of this code is pageable.
//
#pragma alloc_text(PAGE, CnpTdiSetEventHandler)
#pragma alloc_text(PAGE, CnpIssueDeviceControl)
#endif // ALLOC_PRAGMA
NTSTATUS
CnpRestartDeviceControl (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PBOOLEAN reuseIrp = (PBOOLEAN) Context;
//
// If there was an MDL in the IRP, free it and reset the pointer to
// NULL. The IO system can't handle a nonpaged pool MDL being freed
// in an IRP, which is why we do it here.
//
if ( Irp->MdlAddress != NULL ) {
IoFreeMdl( Irp->MdlAddress );
Irp->MdlAddress = NULL;
}
//
// Mark the IRP pending, if necessary.
//
if (Irp->PendingReturned) {
IoMarkIrpPending(Irp);
}
//
// If we are reusing a client IRP, tell the I/O manager not to
// halt I/O completion processing immediately.
//
if (*reuseIrp) {
if (Irp->UserIosb != NULL) {
*(Irp->UserIosb) = Irp->IoStatus;
}
if (Irp->UserEvent != NULL) {
KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);
}
return STATUS_MORE_PROCESSING_REQUIRED;
} else {
return STATUS_SUCCESS;
}
} // CnpRestartDeviceControl
NTSTATUS
CnpIssueDeviceControl (
IN PFILE_OBJECT FileObject,
IN PDEVICE_OBJECT DeviceObject,
IN PVOID IrpParameters,
IN ULONG IrpParametersLength,
IN PVOID MdlBuffer,
IN ULONG MdlBufferLength,
IN UCHAR MinorFunction,
IN PIRP ClientIrp OPTIONAL
)
/*++
Routine Description:
Issues a device control request to a TDI provider and waits for the
request to complete.
Arguments:
FileObject - a pointer to the file object corresponding to a TDI
handle
DeviceObject - a pointer to the device object corresponding to the
FileObject.
IrpParameters - information to write to the parameters section of the
stack location of the IRP.
IrpParametersLength - length of the parameter information. Cannot be
greater than 16.
MdlBuffer - if non-NULL, a buffer of nonpaged pool to be mapped
into an MDL and placed in the MdlAddress field of the IRP.
MdlBufferLength - the size of the buffer pointed to by MdlBuffer.
MinorFunction - the minor function code for the request.
ClientIrp - client IRP that may be reusable for this ioctl
Return Value:
NTSTATUS -- Indicates the status of the request.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PIRP irp;
PIO_STACK_LOCATION irpSp;
KEVENT event;
IO_STATUS_BLOCK ioStatusBlock;
PDEVICE_OBJECT deviceObject;
PMDL mdl;
KPROCESSOR_MODE clientRequestorMode;
PKEVENT clientUserEvent;
PIO_STATUS_BLOCK clientIosb;
PMDL clientMdl;
BOOLEAN reuseIrp = FALSE;
PAGED_CODE( );
//
// Initialize the kernel event that will signal I/O completion.
//
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
//
// If there is a ClientIrp available, check if it has sufficient
// stack locations.
//
if (ClientIrp != NULL
&& CnpIsIrpStackSufficient(ClientIrp, DeviceObject)) {
//
// Reuse the client IRP rather than allocating a new one.
//
reuseIrp = TRUE;
irp = ClientIrp;
//
// Save state from client IRP
//
clientRequestorMode = irp->RequestorMode;
clientUserEvent = irp->UserEvent;
clientIosb = irp->UserIosb;
clientMdl = irp->MdlAddress;
} else {
//
// Reference the passed in file object. This is necessary because
// the IO completion routine dereferences it.
//
ObReferenceObject( FileObject );
//
// Set the file object event to a non-signaled state.
//
(VOID) KeResetEvent( &FileObject->Event );
//
// Attempt to allocate and initialize the I/O Request Packet (IRP)
// for this operation.
//
irp = IoAllocateIrp( (DeviceObject)->StackSize, TRUE );
if ( irp == NULL ) {
ObDereferenceObject( FileObject );
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Fill in the service independent parameters in the IRP.
//
irp->Flags = (LONG)IRP_SYNCHRONOUS_API;
irp->PendingReturned = FALSE;
irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
irp->AssociatedIrp.SystemBuffer = NULL;
irp->UserBuffer = NULL;
irp->Tail.Overlay.Thread = PsGetCurrentThread();
irp->Tail.Overlay.OriginalFileObject = FileObject;
irp->Tail.Overlay.AuxiliaryBuffer = NULL;
//
// Queue the IRP to the thread.
//
IoEnqueueIrp( irp );
}
//
// If an MDL buffer was specified, get an MDL, map the buffer,
// and place the MDL pointer in the IRP.
//
if ( MdlBuffer != NULL ) {
mdl = IoAllocateMdl(
MdlBuffer,
MdlBufferLength,
FALSE,
FALSE,
irp
);
if ( mdl == NULL ) {
if (!reuseIrp) {
IoFreeIrp( irp );
ObDereferenceObject( FileObject );
} else {
irp->MdlAddress = clientMdl;
}
return STATUS_INSUFFICIENT_RESOURCES;
}
MmBuildMdlForNonPagedPool( mdl );
} else {
irp->MdlAddress = NULL;
}
irp->RequestorMode = KernelMode;
irp->UserIosb = &ioStatusBlock;
irp->UserEvent = &event;
//
// Put the file object pointer in the stack location.
//
irpSp = IoGetNextIrpStackLocation( irp );
irpSp->FileObject = FileObject;
irpSp->DeviceObject = DeviceObject;
//
// Fill in the service-dependent parameters for the request.
//
CnAssert( IrpParametersLength <= sizeof(irpSp->Parameters) );
RtlCopyMemory( &irpSp->Parameters, IrpParameters, IrpParametersLength );
irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
irpSp->MinorFunction = MinorFunction;
//
// Set up a completion routine which we'll use to free the MDL
// allocated previously.
//
IoSetCompletionRoutine(
irp,
CnpRestartDeviceControl,
(PVOID) &reuseIrp,
TRUE,
TRUE,
TRUE
);
status = IoCallDriver( DeviceObject, irp );
//
// If necessary, wait for the I/O to complete.
//
if ( status == STATUS_PENDING ) {
KeWaitForSingleObject(
(PVOID)&event,
UserRequest,
KernelMode,
FALSE,
NULL
);
}
//
// If the request was successfully queued, get the final I/O status.
//
if ( NT_SUCCESS(status) ) {
status = ioStatusBlock.Status;
}
//
// Before returning, restore the client IRP
//
if (reuseIrp) {
irp->RequestorMode = clientRequestorMode;
irp->UserIosb = clientIosb;
irp->UserEvent = clientUserEvent;
irp->MdlAddress = clientMdl;
}
return status;
} // CnpIssueDeviceControl
NTSTATUS
CnpTdiSetEventHandler(
IN PFILE_OBJECT FileObject,
IN PDEVICE_OBJECT DeviceObject,
IN ULONG EventType,
IN PVOID EventHandler,
IN PVOID EventContext,
IN PIRP ClientIrp OPTIONAL
)
/*++
Routine Description:
Sets up a TDI indication handler on a connection or address object
(depending on the file handle). This is done synchronously, which
shouldn't usually be an issue since TDI providers can usually complete
indication handler setups immediately.
Arguments:
FileObject - a pointer to the file object for an open connection or
address object.
DeviceObject - a pointer to the device object associated with the
file object.
EventType - the event for which the indication handler should be
called.
EventHandler - the routine to call when tghe specified event occurs.
EventContext - context which is passed to the indication routine.
ClientIrp - client IRP that may be passed to CnpIssueDeviceControl
for reuse
Return Value:
NTSTATUS -- Indicates the status of the request.
--*/
{
TDI_REQUEST_KERNEL_SET_EVENT parameters;
NTSTATUS status;
PAGED_CODE( );
parameters.EventType = EventType;
parameters.EventHandler = EventHandler;
parameters.EventContext = EventContext;
status = CnpIssueDeviceControl(
FileObject,
DeviceObject,
&parameters,
sizeof(parameters),
NULL,
0,
TDI_SET_EVENT_HANDLER,
ClientIrp
);
return(status);
} // CnpTdiSetEventHandler
NTSTATUS
CnpTdiErrorHandler(
IN PVOID TdiEventContext,
IN NTSTATUS Status
)
{
return(STATUS_SUCCESS);
} // CnpTdiErrorHandler
VOID
CnpAttachSystemProcess(
VOID
)
/*++
Routine Description:
Attach to the system process, as determined during DriverEntry
and stored in CnSystemProcess.
Arguments:
None.
Return value:
None.
Notes:
Must be followed by a call to CnpDetachSystemProcess.
Implemented in this module due to header conflicts with
ntddk.h.
--*/
{
KeAttachProcess(CnSystemProcess);
return;
} // CnpAttachSystemProcess
VOID
CnpDetachSystemProcess(
VOID
)
/*++
Routine Description:
Detach from the system process.
Arguments:
None.
Return value:
None.
Notes:
Must be preceded by a call to CnpDetachSystemProcess.
Implemented in this module due to header conflicts with
ntddk.h.
--*/
{
KeDetachProcess();
return;
} // CnpDetachSystemProcess
NTSTATUS
CnpOpenDevice(
IN LPWSTR DeviceName,
OUT HANDLE *Handle
)
/*++
Routine Description:
Opens a handle to DeviceName. Since no EaBuffer is specified,
CnpOpenDevice opens a control channel for TDI transports.
Arguments:
DeviceName - device to open
Handle - resulting handle, NULL on failure
Return Value:
Status of ZwCreateFile
Notes:
Specifies OBJ_KERNEL_HANDLE, meaning that the resulting
handle is only valid in kernel-mode. This routine
cannot be called to obtain a handle that will be
exported to user-mode.
--*/
{
UNICODE_STRING nameString;
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK iosb;
NTSTATUS status;
*Handle = (HANDLE) NULL;
RtlInitUnicodeString(&nameString, DeviceName);
InitializeObjectAttributes(
&objectAttributes,
&nameString,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR) NULL
);
status = ZwCreateFile(
Handle,
SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
&objectAttributes,
&iosb,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN_IF,
0,
NULL,
0
);
if (!NT_SUCCESS(status)) {
IF_CNDBG(CN_DEBUG_OPEN) {
CNPRINT(("[Clusnet] Failed to open device %S, status %lx\n",
DeviceName, status));
}
*Handle = NULL;
}
return(status);
} // CnpOpenDevice
NTSTATUS
CnpZwDeviceControl(
IN HANDLE Handle,
IN ULONG IoControlCode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
IN PVOID OutputBuffer,
IN ULONG OutputBufferLength
)
{
NTSTATUS status = STATUS_SUCCESS;
IO_STATUS_BLOCK iosb;
HANDLE event;
PAGED_CODE();
status = ZwCreateEvent( &event,
EVENT_ALL_ACCESS,
NULL,
SynchronizationEvent,
FALSE );
if (NT_SUCCESS(status)) {
status = ZwDeviceIoControlFile(
Handle,
event,
NULL,
NULL,
&iosb,
IoControlCode,
InputBuffer,
InputBufferLength,
OutputBuffer,
OutputBufferLength
);
if (status == STATUS_PENDING) {
status = ZwWaitForSingleObject( event, FALSE, NULL );
CnAssert( status == STATUS_SUCCESS );
status = iosb.Status;
}
ZwClose( event );
}
return(status);
} // CnpZwDeviceControl
#define TCP_SET_INFO_EX_BUFFER_PREALLOCSIZE 16
#define TCP_SET_INFO_EX_PREALLOCSIZE \
(FIELD_OFFSET(TCP_REQUEST_SET_INFORMATION_EX, Buffer) \
+ TCP_SET_INFO_EX_BUFFER_PREALLOCSIZE \
)
NTSTATUS
CnpSetTcpInfoEx(
IN HANDLE Handle,
IN ULONG Entity,
IN ULONG Class,
IN ULONG Type,
IN ULONG Id,
IN PVOID Value,
IN ULONG ValueLength
)
{
NTSTATUS status;
PTCP_REQUEST_SET_INFORMATION_EX setInfoEx;
UCHAR infoBuf[TCP_SET_INFO_EX_PREALLOCSIZE]={0};
//
// Check if we need to dynamically allocate.
//
if (ValueLength > TCP_SET_INFO_EX_BUFFER_PREALLOCSIZE) {
setInfoEx = CnAllocatePool(
FIELD_OFFSET(TCP_REQUEST_SET_INFORMATION_EX, Buffer)
+ ValueLength
);
if (setInfoEx == NULL) {
return(STATUS_INSUFFICIENT_RESOURCES);
}
RtlZeroMemory(
setInfoEx,
FIELD_OFFSET(TCP_REQUEST_SET_INFORMATION_EX, Buffer) + ValueLength
);
} else {
setInfoEx = (PTCP_REQUEST_SET_INFORMATION_EX)&infoBuf[0];
}
setInfoEx->ID.toi_entity.tei_entity = Entity;
setInfoEx->ID.toi_entity.tei_instance = 0;
setInfoEx->ID.toi_class = Class;
setInfoEx->ID.toi_type = Type;
setInfoEx->ID.toi_id = Id;
setInfoEx->BufferSize = ValueLength;
RtlCopyMemory(setInfoEx->Buffer, Value, ValueLength);
status = CnpZwDeviceControl(
Handle,
IOCTL_TCP_SET_INFORMATION_EX,
setInfoEx,
FIELD_OFFSET(TCP_REQUEST_SET_INFORMATION_EX, Buffer)
+ ValueLength,
NULL,
0
);
//
// Free the buffer, if dynamically allocated
//
if (setInfoEx != (PTCP_REQUEST_SET_INFORMATION_EX)&infoBuf[0]) {
CnFreePool(setInfoEx);
}
return(status);
} // CnpSetTcpInfoEx
NTSTATUS
CnpMakeSignature(
IN PSecBufferDesc Data,
IN DESTable * DesTable,
IN PVOID SigBuffer, OPTIONAL
IN ULONG SigBufferLength, OPTIONAL
OUT PSecBuffer * SigSecBuffer, OPTIONAL
OUT ULONG * SigLen OPTIONAL
)
/*++
Routine Description:
Builds a signature for Data.
Arguments:
Data - data to be signed, packaged in a SecBufferDesc. All
SecBuffers in Data should be of type SECBUFFER_DATA
except exactly one which has type SECBUFFER_TOKEN.
Other buffers will be ignored.
DESTable - DES table containing encryption/decryption keys
SigBuffer - Buffer in which to place completed signature. If NULL,
signature is written into signature secbuffer (has
type SECBUFFER_TOKEN in Data).
SigBufferLength - length of buffer at SigBuffer, if provided
SigSecBuffer - If non-NULL, returns pointer to signature secbuffer
from Data.
SigLen - on success, contains length of signature written
on SEC_E_BUFFER_TOO_SMALL, contains required signature length
undefined otherwise
Return value:
SEC_E_OK if successful.
SEC_E_SECPKG_NOT_FOUND if the security buffer version is wrong.
SEC_E_BUFFER_TOO_SMALL if SigBufferLength is too small.
SEC_E_INVALID_TOKEN if Data is a misformed SecBuffer.
--*/
{
A_SHA_CTX shaCtxt;
UCHAR hashBuffer[CX_SIGNATURE_LENGTH] = { 0 };
ULONG hashSize;
ULONG bufIndex;
PSecBuffer sigSecBuffer = NULL;
PSecBuffer curBuffer;
PUCHAR curBlock;
PUCHAR encryptedHashBuffer;
ULONG status;
//
// Verify the version.
//
if (Data->ulVersion != SECBUFFER_VERSION) {
status = SEC_E_SECPKG_NOT_FOUND;
goto error_exit;
}
//
// Verify that the provided sig buffer is big enough.
//
if (SigBuffer != NULL && SigBufferLength < CX_SIGNATURE_LENGTH) {
status = SEC_E_BUFFER_TOO_SMALL;
goto error_exit;
}
//
// Initialize the SHA context.
//
CxFipsFunctionTable.FipsSHAInit(&shaCtxt);
//
// Hash the data.
//
for (bufIndex = 0, curBuffer = &(Data->pBuffers[bufIndex]);
bufIndex < Data->cBuffers;
bufIndex++, curBuffer++) {
//
// Process this buffer according to its type.
//
if (curBuffer->BufferType == SECBUFFER_DATA) {
//
// Hash this buffer.
//
CxFipsFunctionTable.FipsSHAUpdate(
&shaCtxt,
(PUCHAR) curBuffer->pvBuffer,
curBuffer->cbBuffer
);
} else if (curBuffer->BufferType == SECBUFFER_TOKEN) {
if (sigSecBuffer != NULL) {
status = SEC_E_INVALID_TOKEN;
goto error_exit;
} else {
sigSecBuffer = curBuffer;
//
// Verify that the signature buffer is big enough.
//
if (sigSecBuffer->cbBuffer < A_SHA_DIGEST_LEN) {
*SigLen = CX_SIGNATURE_LENGTH;
status = SEC_E_BUFFER_TOO_SMALL;
goto error_exit;
}
//
// Set the output buffer.
//
if (SigBuffer == NULL) {
encryptedHashBuffer = sigSecBuffer->pvBuffer;
} else {
encryptedHashBuffer = SigBuffer;
}
}
}
}
//
// Verify that we found a buffer for the signature.
//
if (sigSecBuffer == NULL) {
status = SEC_E_INVALID_TOKEN;
goto error_exit;
}
//
// Complete the hash.
//
CxFipsFunctionTable.FipsSHAFinal(&shaCtxt, hashBuffer);
//
// Encrypt the hash one DES block at a time.
//
for (bufIndex = 0;
bufIndex < CX_SIGNATURE_LENGTH;
bufIndex += DES_BLOCKLEN) {
CxFipsFunctionTable.FipsDes(
&(encryptedHashBuffer[bufIndex]),
&(hashBuffer[bufIndex]),
DesTable,
ENCRYPT
);
}
if (SigSecBuffer != NULL) {
*SigSecBuffer = sigSecBuffer;
}
if (SigLen != NULL) {
*SigLen = CX_SIGNATURE_LENGTH;
}
status = SEC_E_OK;
error_exit:
return(status);
} // CnpMakeSignature
NTSTATUS
CnpVerifySignature(
IN PSecBufferDesc Data,
IN DESTable * DesTable
)
/*++
Routine Description:
Verifies a signature for data.
Arguments:
Data - data to be verified, packaged in a SecBufferDesc. All
SecBuffers in Data should be of type SECBUFFER_DATA
except exactly one which has type SECBUFFER_TOKEN.
Other buffers will be ignored.
DESTable - DES table containing encryption/decryption keys
Return value:
SEC_E_OK if the signature is correct.
SEC_E_SECPKG_NOT_FOUND if the security buffer version is wrong.
SEC_E_INVALID_TOKEN if Data is a misformed SecBuffer.
SEC_E_MESSAGE_ALTERED if signature is incorrect (including if it
is the wrong length).
--*/
{
UCHAR encryptedHashBuffer[CX_SIGNATURE_LENGTH];
PSecBuffer sigBuffer = NULL;
ULONG status;
status = CnpMakeSignature(
Data,
DesTable,
encryptedHashBuffer,
sizeof(encryptedHashBuffer),
&sigBuffer,
NULL
);
if (status == STATUS_SUCCESS) {
//
// Compare the generated signature to the provided signature.
//
if (RtlCompareMemory(
encryptedHashBuffer,
sigBuffer->pvBuffer,
sigBuffer->cbBuffer
) != sigBuffer->cbBuffer) {
status = SEC_E_MESSAGE_ALTERED;
} else {
status = SEC_E_OK;
}
}
return(status);
} // CnpVerifySignature
NTSTATUS
CnpSignMulticastMessage(
IN PCNP_SEND_REQUEST SendRequest,
IN PMDL DataMdl,
IN OUT CL_NETWORK_ID * NetworkId,
OUT ULONG * SigDataLen OPTIONAL
)
/*++
Routine Description:
Sign a message.
If NetworkId is not ClusterAnyNetworkId, the mcast group
field must be set (and already referenced) in the SendRequest.
This is the group that will be used to send the packet.
Arguments:
SendRequest - send request, used to locate the upper protocol
header to sign, as well as the signature buffer.
DataMdl - data to sign
NetworkId - IN: network on which to send the message, or
ClusterAnyNetworkId if it should be chosen
OUT: network id chosen to send packet
SigDataLen - OUT (OPTIONAL): number of bytes occupied in
message by signature data and signature
--*/
{
NTSTATUS status;
PCNP_NETWORK network;
PCNP_MULTICAST_GROUP mcastGroup;
BOOLEAN mcastGroupReferenced = FALSE;
CNP_HEADER UNALIGNED * cnpHeader;
CNP_SIGNATURE UNALIGNED * cnpSig;
SecBufferDesc sigDescriptor;
SecBuffer sigSecBufferPrealloc[4];
PSecBuffer sigSecBuffer = NULL;
ULONG secBufferCount;
ULONG sigLen;
PMDL mdl;
PSecBuffer curBuffer;
CnAssert(SendRequest != NULL);
CnAssert(((CNP_HEADER UNALIGNED *)SendRequest->CnpHeader)->Version ==
CNP_VERSION_MULTICAST);
//
// Determine which network to use.
//
if (*NetworkId != ClusterAnyNetworkId) {
mcastGroup = SendRequest->McastGroup;
CnAssert(mcastGroup != NULL);
} else {
network = CnpGetBestMulticastNetwork();
if (network == NULL) {
CnTrace(CNP_SEND_ERROR, CnpMcastGetBestNetwork,
"[CNP] Failed to find best multicast network."
);
status = STATUS_NETWORK_UNREACHABLE;
goto error_exit;
}
//
// Get the network id and mcast group before releasing
// the network lock.
//
*NetworkId = network->Id;
mcastGroup = network->CurrentMcastGroup;
if (mcastGroup == NULL) {
CnTrace(CNP_SEND_ERROR, CnpMcastGroupNull,
"[CNP] Best multicast network %u has null "
"multicast group.",
network->Id
);
CnReleaseLock(&(network->Lock), network->Irql);
status = STATUS_NETWORK_UNREACHABLE;
goto error_exit;
}
CnpReferenceMulticastGroup(mcastGroup);
mcastGroupReferenced = TRUE;
CnReleaseLock(&(network->Lock), network->Irql);
}
CnAssert(mcastGroup->SignatureLength <= CX_SIGNATURE_LENGTH);
//
// Determine how many sig sec buffers we will need.
// The common case is four: one for a header,
// one for the data, one for the salt, and one for
// the signature. We prealloc sig buffers on the
// stack for the common case, but we dynamically
// allocate if needed (e.g. if the data is a chain
// of MDLs).
//
secBufferCount = 3;
for (mdl = DataMdl; mdl != NULL; mdl = mdl->Next) {
secBufferCount++;
}
//
// Allocate the sig sec buffers.
//
if (secBufferCount <= 4) {
sigSecBuffer = &sigSecBufferPrealloc[0];
} else {
sigSecBuffer = CnAllocatePool(
secBufferCount * sizeof(SecBuffer)
);
if (sigSecBuffer == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto error_exit;
}
}
//
// Prepare the descriptor for the message and signature.
//
sigDescriptor.cBuffers = secBufferCount;
sigDescriptor.pBuffers = sigSecBuffer;
sigDescriptor.ulVersion = SECBUFFER_VERSION;
curBuffer = sigSecBuffer;
//
// Header.
//
if (SendRequest->UpperProtocolHeader != NULL) {
CnAssert(SendRequest->UpperProtocolHeaderLength > 0);
curBuffer->BufferType = SECBUFFER_DATA;
curBuffer->cbBuffer = SendRequest->UpperProtocolHeaderLength;
curBuffer->pvBuffer = SendRequest->UpperProtocolHeader;
curBuffer++;
}
//
// The payload provided by our client.
//
for (mdl = DataMdl; mdl != NULL; mdl = mdl->Next) {
curBuffer->BufferType = SECBUFFER_DATA;
curBuffer->cbBuffer = MmGetMdlByteCount(mdl);
curBuffer->pvBuffer = MmGetMdlVirtualAddress(mdl);
curBuffer++;
}
//
// The salt.
//
curBuffer->BufferType = SECBUFFER_DATA;
curBuffer->cbBuffer = mcastGroup->SaltLength;
curBuffer->pvBuffer = mcastGroup->Salt;
curBuffer++;
//
// The Signature.
//
cnpHeader = (CNP_HEADER UNALIGNED *)(SendRequest->CnpHeader);
cnpSig = (CNP_SIGNATURE UNALIGNED *)(cnpHeader + 1);
curBuffer->BufferType = SECBUFFER_TOKEN;
curBuffer->pvBuffer = cnpSig->SigBuffer;
curBuffer->cbBuffer = CX_SIGNATURE_LENGTH;
status = CnpMakeSignature(
&sigDescriptor,
&(mcastGroup->DesTable),
NULL,
0,
NULL,
&sigLen
);
if (status == STATUS_SUCCESS && sigLen <= CX_SIGNATURE_LENGTH) {
//
// Fill in the CNP signature data.
//
cnpSig->PayloadOffset = (USHORT) CNP_SIG_LENGTH(CX_SIGNATURE_LENGTH);
cnpSig->Version = CNP_SIG_VERSION_1;
cnpSig->NetworkId = *NetworkId;
cnpSig->ClusterNetworkBrand = mcastGroup->McastNetworkBrand;
cnpSig->SigBufferLen = (USHORT) sigLen;
} else {
IF_CNDBG(CN_DEBUG_CNPSEND) {
CNPRINT(("[CNP] MakeSignature failed or returned "
"an unexpected length, status %x, "
"expected length %d, returned length %d.\n",
status, CX_SIGNATURE_LENGTH, sigLen));
}
CnTrace(CNP_SEND_ERROR, CnpMcastMakeSigFailed,
"[CNP] MakeSignature failed or returned "
"an unexpected length, status %!status!, "
"expected length %d, returned length %d.",
status, CX_SIGNATURE_LENGTH, sigLen
);
status = STATUS_CLUSTER_NO_SECURITY_CONTEXT;
}
if (SigDataLen != NULL) {
*SigDataLen = cnpSig->PayloadOffset;
}
SendRequest->McastGroup = mcastGroup;
error_exit:
if (sigSecBuffer != NULL &&
sigSecBuffer != &sigSecBufferPrealloc[0]) {
CnFreePool(sigSecBuffer);
sigSecBuffer = NULL;
}
if (status != STATUS_SUCCESS && mcastGroupReferenced) {
CnAssert(mcastGroup != NULL);
CnpDereferenceMulticastGroup(mcastGroup);
mcastGroupReferenced = FALSE;
}
return(status);
} // CnpSignMulticastMessage
NTSTATUS
CnpVerifyMulticastMessage(
IN PCNP_NETWORK Network,
IN PVOID Tsdu,
IN ULONG TsduLength,
IN ULONG ExpectedPayload,
OUT ULONG * BytesTaken,
OUT BOOLEAN * CurrentGroup
)
/*++
Routine Description:
Verify a message.
Arguments:
Network - network on which message arrived
Tsdu - points to protocol header
TsduLength - length of TSDU, including signature data
ExpectedPayload - expected payload after signature data
BytesTaken - OUT: quantity of data consumed by signature
CurrentGroup - OUT: whether signature matched current
multicast group.
Return value:
SEC_E_OK or error status.
--*/
{
NTSTATUS status;
CNP_SIGNATURE UNALIGNED * cnpSig = Tsdu;
ULONG sigBufBytes = 0;
ULONG sigPayOffBytes = 0;
PVOID payload;
ULONG payloadLength;
PCNP_MULTICAST_GROUP currMcastGroup = NULL;
PCNP_MULTICAST_GROUP prevMcastGroup = NULL;
SecBufferDesc sigDescriptor;
SecBuffer sigSecBufferPrealloc[3];
PSecBuffer sigSecBuffer = NULL;
PSecBuffer curBuffer;
//
// Verify that the signature is present. Do not
// dereference any signature data until we know
// it's there.
//
if (
// Verify that signature header data is present.
(TsduLength < (ULONG)CNP_SIGHDR_LENGTH) ||
// Verify that signature buffer is present
(TsduLength < (sigBufBytes = CNP_SIG_LENGTH(cnpSig->SigBufferLen))) ||
// Verify that the payload offset is reasonable
(TsduLength <= (sigPayOffBytes = cnpSig->PayloadOffset)) ||
// Verify that the expected payload is present
(TsduLength - sigPayOffBytes != ExpectedPayload)
) {
IF_CNDBG(CN_DEBUG_CNPRECV) {
CNPRINT(("[CNP] Cannot verify mcast packet with "
"mis-sized payload: TsduLength %u, required "
"sig hdr %u, sig buffer %u, "
"payload offset %u, expected payload %u.\n",
TsduLength,
CNP_SIGHDR_LENGTH,
sigBufBytes,
sigPayOffBytes,
ExpectedPayload
));
}
CnTrace(CNP_RECV_ERROR, CnpTraceReceiveTooSmall,
"[CNP] Cannot verify mcast packet with "
"undersized payload: TsduLength %u, required "
"sig hdr %u, sig buffer %u, "
"payload offset %u, expected payload %u.\n",
TsduLength,
CNP_SIGHDR_LENGTH,
sigBufBytes,
sigPayOffBytes,
ExpectedPayload
);
//
// Drop it.
//
status = SEC_E_INCOMPLETE_MESSAGE;
goto error_exit;
}
//
// Verify that the signature protocol is understood.
//
if (cnpSig->Version != CNP_SIG_VERSION_1) {
IF_CNDBG(CN_DEBUG_CNPRECV) {
CNPRINT(("[CNP] Cannot verify mcast packet with "
"unknown signature version: %u.\n",
cnpSig->Version
));
}
CnTrace(
CNP_RECV_ERROR, CnpTraceRecvUnknownSigVersion,
"[CNP] Cannot verify mcast packet with "
"unknown signature version: %u.",
cnpSig->Version
);
//
// Drop it.
//
status = SEC_E_BAD_PKGID;
goto error_exit;
}
//
// Locate the payload following the signature data.
//
payload = (PVOID)((PUCHAR)(cnpSig) + sigPayOffBytes);
payloadLength = TsduLength - sigPayOffBytes;
//
// Lock the network object and reference the
// multicast groups.
//
CnAcquireLock(&(Network->Lock), &(Network->Irql));
currMcastGroup = Network->CurrentMcastGroup;
if (currMcastGroup != NULL) {
CnpReferenceMulticastGroup(currMcastGroup);
}
prevMcastGroup = Network->CurrentMcastGroup;
if (prevMcastGroup != NULL) {
CnpReferenceMulticastGroup(prevMcastGroup);
}
CnReleaseLock(&(Network->Lock), Network->Irql);
//
// Verify that the packet network id matches the
// local network object.
//
if (cnpSig->NetworkId != Network->Id) {
IF_CNDBG(CN_DEBUG_CNPRECV) {
CNPRINT(("[CNP] Mcast packet has bad network "
"id: found %d, expected %d.\n",
cnpSig->NetworkId,
Network->Id
));
}
CnTrace(
CNP_RECV_ERROR, CnpTraceReceiveBadNetworkId,
"[CNP] Mcast packet has bad network id: "
"found %d, expected %d.",
cnpSig->NetworkId,
Network->Id
);
//
// Drop it.
//
status = SEC_E_TARGET_UNKNOWN;
goto error_exit;
}
//
// Verify that the brand matches either the current or
// previous multicast group.
//
if (currMcastGroup != NULL &&
cnpSig->ClusterNetworkBrand != currMcastGroup->McastNetworkBrand) {
// can't use currMcastGroup
CnpDereferenceMulticastGroup(currMcastGroup);
currMcastGroup = NULL;
}
if (prevMcastGroup != NULL &&
cnpSig->ClusterNetworkBrand != prevMcastGroup->McastNetworkBrand) {
// can't use prevMcastGroup
CnpDereferenceMulticastGroup(prevMcastGroup);
prevMcastGroup = NULL;
}
if (currMcastGroup == NULL && prevMcastGroup == NULL) {
IF_CNDBG(CN_DEBUG_CNPRECV) {
CNPRINT(("[CNP] Recv'd mcast packet with brand %x, "
"but no matching multicast groups.\n",
cnpSig->ClusterNetworkBrand
));
}
CnTrace(
CNP_RECV_ERROR, CnpTraceReceiveBadBrand,
"[CNP] Recv'd mcast packet with brand %x, "
"but no matching multicast groups.",
cnpSig->ClusterNetworkBrand
);
//
// Drop it.
//
status = SEC_E_TARGET_UNKNOWN;
goto error_exit;
}
//
// Build the signature descriptor for verification. The bytes
// that were signed (and hence need to be verified) include
// the payload data, starting after the signature, and the salt.
//
sigSecBuffer = &sigSecBufferPrealloc[0];
curBuffer = sigSecBuffer;
sigDescriptor.cBuffers = 2;
sigDescriptor.pBuffers = sigSecBuffer;
sigDescriptor.ulVersion = SECBUFFER_VERSION;
//
// Data.
//
if (payloadLength > 0) {
sigDescriptor.cBuffers = 3;
curBuffer->BufferType = SECBUFFER_DATA;
curBuffer->cbBuffer = payloadLength;
curBuffer->pvBuffer = payload;
curBuffer++;
}
//
// Signature.
//
curBuffer->BufferType = SECBUFFER_TOKEN;
curBuffer->cbBuffer = cnpSig->SigBufferLen;
curBuffer->pvBuffer = (PVOID)&(cnpSig->SigBuffer[0]);
curBuffer++;
/*CNPRINT(("[CNP] Verifying message of length %d with "
"sig of length %d.\n",
HeaderLength + payloadLength,
cnpSig->SigBufferLen));*/
//
// Try the current multicast group, and if necessary,
// the previous multicast group.
//
status = SEC_E_INVALID_TOKEN;
if (currMcastGroup != NULL) {
//
// Salt.
//
curBuffer->BufferType = SECBUFFER_DATA;
curBuffer->cbBuffer = currMcastGroup->SaltLength;
curBuffer->pvBuffer = currMcastGroup->Salt;
status = CnpVerifySignature(
&sigDescriptor,
&(currMcastGroup->DesTable)
);
if (status == SEC_E_OK && CurrentGroup != NULL) {
*CurrentGroup = TRUE;
}
}
if (status != SEC_E_OK && prevMcastGroup != NULL) {
curBuffer->cbBuffer = prevMcastGroup->SaltLength;
curBuffer->pvBuffer = prevMcastGroup->Salt;
status = CnpVerifySignature(
&sigDescriptor,
&(prevMcastGroup->DesTable)
);
if (status == SEC_E_OK && CurrentGroup != NULL) {
*CurrentGroup = FALSE;
}
}
if (status == SEC_E_OK) {
*BytesTaken = sigPayOffBytes;
}
error_exit:
if (currMcastGroup != NULL) {
CnpDereferenceMulticastGroup(currMcastGroup);
}
if (prevMcastGroup != NULL) {
CnpDereferenceMulticastGroup(prevMcastGroup);
}
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(status);
} // CnpVerifyMulticastMessage