|
|
/*++
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, ¶meters, 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
|