1495 lines
38 KiB
C
1495 lines
38 KiB
C
|
/*++
|
|||
|
|
|||
|
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
|
|||
|
|