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
|
||
|