|
|
/*++
Copyright (c) 1997-2001 Microsoft Corporation
Module Name:
driver.c
Abstract:
This module contains the DriverEntry and other initialization code for the IPSEC module of the Tcpip transport.
Author:
Sanjay Anand (SanjayAn) 2-January-1997 ChunYe
Environment:
Kernel mode
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(INIT, IPSecGeneralInit)
#pragma alloc_text(PAGE, IPSecDispatch)
#pragma alloc_text(PAGE, IPSecBindToIP)
#pragma alloc_text(PAGE, IPSecUnbindFromIP)
#pragma alloc_text(PAGE, IPSecFreeConfig)
#pragma alloc_text(PAGE, IPSecInitMdlPool)
#pragma alloc_text(PAGE, AllocateCacheStructures)
#pragma alloc_text(PAGE, FreeExistingCache)
#pragma alloc_text(PAGE, FreePatternDbase)
#pragma alloc_text(PAGE, OpenRegKey)
#pragma alloc_text(PAGE, GetRegDWORDValue)
#pragma alloc_text(PAGE, IPSecCryptoInitialize)
#pragma alloc_text(PAGE, IPSecCryptoDeinitialize)
#endif
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++
Routine Description:
This routine performs initialization of the IPSEC module. It creates the device object for the transport provider and performs other driver initialization.
Arguments:
DriverObject - Pointer to driver object created by the system.
RegistryPath - The name of IPSEC's node in the registry.
Return Value:
The function value is the final status from the initialization operation.
--*/ { PDEVICE_OBJECT deviceObject = NULL; WCHAR deviceNameBuffer[] = DD_IPSEC_DEVICE_NAME; WCHAR symbolicLinkBuffer[] = DD_IPSEC_SYM_NAME; UNICODE_STRING symbolicLinkName; UNICODE_STRING deviceNameUnicodeString; NTSTATUS status; NTSTATUS status1;
//DbgBreakPoint();
IPSEC_DEBUG(LOAD, ("Entering DriverEntry\n"));
//
// Init g_ipsec structure and read reg keys.
//
IPSecZeroMemory(&g_ipsec, sizeof(g_ipsec)); IPSecReadRegistry();
//
// Create the device - do we need a device at all?
//
// Setup the handlers.
//
//
// Initialize the driver object with this driver's entry points.
//
g_ipsec.IPSecDriverObject = DriverObject;
DriverObject->MajorFunction [IRP_MJ_CREATE] = DriverObject->MajorFunction [IRP_MJ_CLOSE] = DriverObject->MajorFunction [IRP_MJ_CLEANUP] = DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] = DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] = IPSecDispatch;
DriverObject->DriverUnload = IPSecUnload;
RtlInitUnicodeString (&deviceNameUnicodeString, deviceNameBuffer);
status = IoCreateDevice( DriverObject, 0, // DeviceExtensionSize
&deviceNameUnicodeString, // DeviceName
FILE_DEVICE_NETWORK, // DeviceType
FILE_DEVICE_SECURE_OPEN, // DeviceCharacteristics
FALSE, // Exclusive
&deviceObject); // *DeviceObject
if (!NT_SUCCESS (status)) { IPSEC_DEBUG(LOAD, ("Failed to create device: %lx\n", status));
LOG_EVENT( DriverObject, EVENT_IPSEC_CREATE_DEVICE_FAILED, 1, 1, &deviceNameUnicodeString.Buffer, 0, NULL);
goto err; }
deviceObject->Flags |= DO_BUFFERED_IO;
IPSecDevice = deviceObject;
RtlInitUnicodeString (&symbolicLinkName, symbolicLinkBuffer);
status = IoCreateSymbolicLink(&symbolicLinkName, &deviceNameUnicodeString);
if (!NT_SUCCESS (status)) { IPSEC_DEBUG(LOAD, ("Failed to create symbolic link: %lx\n", status));
LOG_EVENT( DriverObject, EVENT_IPSEC_CREATE_DEVICE_FAILED, 2, 1, &deviceNameUnicodeString.Buffer, 0, NULL);
IoDeleteDevice(DriverObject->DeviceObject);
goto err; }
//
// General structs init here.
// Allocates the SA Table etc.
//
status = IPSecGeneralInit();
if (!NT_SUCCESS (status)) { IPSEC_DEBUG(LOAD, ("Failed to init general structs: %lx\n", status));
//
// Free the general structs and SA Table etc.
//
status1 = IPSecGeneralFree();
if (!NT_SUCCESS (status1)) { IPSEC_DEBUG(LOAD, ("Failed to free config: %lx\n", status1)); }
LOG_EVENT( DriverObject, EVENT_IPSEC_NO_RESOURCES_FOR_INIT, 1, 0, NULL, 0, NULL);
IoDeleteSymbolicLink(&symbolicLinkName);
IoDeleteDevice(DriverObject->DeviceObject);
goto err; }
//
// Wait for TCP/IP to load and call IOCTL_IPSEC_SET_TCPIP_STATUS where we
// would finish the initialization.
//
status = STATUS_SUCCESS;
IPSEC_DEBUG(LOAD, ("Exiting DriverEntry; SUCCESS\n"));
err: return status; }
VOID IPSecUnload( IN PDRIVER_OBJECT DriverObject ) /*++
Routine Description:
Called when the driver is unloaded.
Arguments:
DriverObject
Return Value:
None
--*/ { UNICODE_STRING IPSecLinkName; KIRQL OldIrq; KIRQL kIrql; NTSTATUS status; INT class;
IPSEC_DEBUG(LOAD, ("Entering IPSecUnload\n"));
//
// Set IPSEC_DRIVER_UNLOADING bit.
//
IPSEC_DRIVER_UNLOADING() = TRUE;
//
// Stop the reaper timer.
//
IPSecStopTimer(&g_ipsec.ReaperTimer);
//
// Stop the EventLog timer.
//
IPSecStopTimer(&g_ipsec.EventLogTimer);
//
// Complete the Acquire Irp with error status
//
if (g_ipsec.AcquireInfo.Irp) { IPSEC_DEBUG(ACQUIRE, ("Unload: Completing Irp..\n")); if (g_ipsec.AcquireInfo.InMe) { IoAcquireCancelSpinLock(&g_ipsec.AcquireInfo.Irp->CancelIrql); IPSecAcquireIrpCancel(NULL, g_ipsec.AcquireInfo.Irp); } }
//
// Stop timers for all SAs (of all states)
//
IPSecStopSATimers();
//
// Wait for all timers to clear before going further
//
while (IPSEC_GET_VALUE(g_ipsec.NumTimers) != 0) { IPSEC_DELAY_EXECUTION(); }
//
// Cleanup any larval SAs
//
AcquireWriteLock(&g_ipsec.SADBLock, &kIrql); ACQUIRE_LOCK(&g_ipsec.AcquireInfo.Lock, &OldIrq); IPSecFlushLarvalSAList(); IPSecFlushSAExpirations(); RELEASE_LOCK(&g_ipsec.AcquireInfo.Lock, OldIrq); ReleaseWriteLock(&g_ipsec.SADBLock, kIrql);
//
// Free the SA Table.
//
status = IPSecFreeConfig();
if (!NT_SUCCESS (status)) { IPSEC_DEBUG(LOAD, ("Failed to free config: %lx\n", status)); }
//
// Free the MDL pools and run down all buffered packets.
//
status = IPSecQuiesce();
if (!NT_SUCCESS (status)) { IPSEC_DEBUG(LOAD, ("Failed to reach quiescent state: %lx\n", status)); }
//
// Destroy timer structures
//
ACQUIRE_LOCK(&g_ipsec.TimerLock, &kIrql);
for (class = 0; class < IPSEC_CLASS_MAX; class++) { ASSERT(g_ipsec.TimerList[class].TimerCount == 0); IPSecFreeMemory(g_ipsec.TimerList[class].pTimers); }
RELEASE_LOCK(&g_ipsec.TimerLock, kIrql);
IPSecCryptoDeinitialize();
#if GPC
IPSecGpcDeinitialize(); #endif
RtlInitUnicodeString(&IPSecLinkName, DD_IPSEC_SYM_NAME);
IoDeleteSymbolicLink(&IPSecLinkName);
IoDeleteDevice(DriverObject->DeviceObject);
IPSEC_DEBUG(LOAD, ("Exiting IPSecUnload\n")); }
NTSTATUS IPSecDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++
Routine Description:
Dispatch Routine for the driver. Gets the current irp stack location, validates the parameters and routes the calls
Arguments:
DeviceObject Irp
Return Value:
Status as returned by the worker functions
--*/ { PIO_STACK_LOCATION irpStack; PVOID pvIoBuffer; LONG inputBufferLength; LONG outputBufferLength; ULONG ioControlCode; NTSTATUS status = STATUS_SUCCESS; LONG dwSize = 0;
PAGED_CODE();
IPSEC_DEBUG(IOCTL, ("Entering IPSecDispath\n"));
Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0;
//
// Get a pointer to the current location in the Irp. This is where
// the function codes and parameters are located.
//
irpStack = IoGetCurrentIrpStackLocation(Irp);
//
// Get the pointer to the input/output buffer and its length.
//
pvIoBuffer = Irp->AssociatedIrp.SystemBuffer; inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength; outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
switch (irpStack->MajorFunction) { case IRP_MJ_CREATE: { IPSEC_DEBUG(IOCTL, ("IRP_MJ_CREATE\n")); break; }
case IRP_MJ_CLOSE: { IPSEC_DEBUG(IOCTL, ("IRP_MJ_CLOSE\n")); break; }
case IRP_MJ_DEVICE_CONTROL: { ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
IPSEC_DEBUG(IOCTL, ("IRP_MJ_DEVICE_CONTROL: %lx\n", ioControlCode));
if (ioControlCode != IOCTL_IPSEC_SET_TCPIP_STATUS) { if (IPSEC_DRIVER_IS_INACTIVE()) { status = STATUS_INVALID_DEVICE_STATE; break; }
if (!IPSecCryptoInitialize()) { status = STATUS_CRYPTO_SYSTEM_INVALID; break; } }
IPSEC_INCREMENT(g_ipsec.NumIoctls);
switch (ioControlCode) { case IOCTL_IPSEC_ADD_FILTER: { PIPSEC_ADD_FILTER pAddFilter = (PIPSEC_ADD_FILTER)pvIoBuffer;
IPSEC_DEBUG(IOCTL, ("IOCTL_IPSEC_ADD_FILTER\n"));
dwSize = sizeof(IPSEC_ADD_FILTER);
if (inputBufferLength < dwSize) { status = STATUS_BUFFER_TOO_SMALL; break; }
//
// Check the size of the entry
//
if (pAddFilter->NumEntries == 0) { status = STATUS_SUCCESS; } else { dwSize = FIELD_OFFSET(IPSEC_ADD_FILTER, pInfo[0]) + pAddFilter->NumEntries * sizeof(IPSEC_FILTER_INFO);
if (dwSize < FIELD_OFFSET(IPSEC_ADD_FILTER, pInfo[0]) || inputBufferLength < dwSize) { status = STATUS_BUFFER_TOO_SMALL; IPSEC_DEBUG(IOCTL, ("returning: %lx\n", status)); break; } status = IPSecAddFilter(pAddFilter); }
break; }
case IOCTL_IPSEC_DELETE_FILTER: { PIPSEC_DELETE_FILTER pDelFilter = (PIPSEC_DELETE_FILTER)pvIoBuffer;
IPSEC_DEBUG(IOCTL, ("IOCTL_IPSEC_DELETE_FILTER\n"));
dwSize = sizeof(IPSEC_DELETE_FILTER);
if (inputBufferLength < dwSize) { status = STATUS_BUFFER_TOO_SMALL; break; }
//
// Check the size of the entry
//
if (pDelFilter->NumEntries == 0) { status = STATUS_SUCCESS; } else { dwSize = FIELD_OFFSET(IPSEC_DELETE_FILTER, pInfo[0]) + pDelFilter->NumEntries * sizeof(IPSEC_FILTER_INFO);
if (dwSize < FIELD_OFFSET(IPSEC_DELETE_FILTER, pInfo[0]) || inputBufferLength < dwSize) { status = STATUS_BUFFER_TOO_SMALL; break; }
status = IPSecDeleteFilter(pDelFilter); }
break; }
case IOCTL_IPSEC_ENUM_SAS: {
IPSEC_DEBUG(IOCTL, ("IOCTL_IPSEC_ENUM_SAS\n")); dwSize = sizeof(IPSEC_ENUM_SAS);
//
// Output/Input in the same buffer at MdlAddress
//
status = IPSecEnumSAs(Irp, &dwSize); break; }
case IOCTL_IPSEC_ENUM_FILTERS: { IPSEC_DEBUG(IOCTL, ("IOCTL_IPSEC_ENUM_FILTERS\n")); dwSize = sizeof(IPSEC_ENUM_FILTERS);
//
// Output/Input in the same buffer at MdlAddress
//
status = IPSecEnumFilters(Irp, &dwSize); break; }
case IOCTL_IPSEC_QUERY_STATS: { //
// The minimum size is without any Keys
//
IPSEC_DEBUG(IOCTL, ("IOCTL_IPSEC_QUERY_STATS\n")); dwSize = sizeof(IPSEC_QUERY_STATS);
if (outputBufferLength < dwSize) { status = STATUS_BUFFER_TOO_SMALL; break; }
*((PIPSEC_QUERY_STATS)pvIoBuffer) = g_ipsec.Statistics;
status = STATUS_SUCCESS; break; }
case IOCTL_IPSEC_ADD_SA: { //
// Adds the SA to the relevant database.
// Typically used to add outbound SAs to the DB.
//
IPSEC_DEBUG(IOCTL, ("IOCTL_IPSEC_ADD_SA\n"));
//
// The minimum size is without any Keys
//
dwSize = IPSEC_ADD_SA_NO_KEY_SIZE;
if (inputBufferLength < dwSize) { status = STATUS_BUFFER_TOO_SMALL; break; }
status = IPSecAddSA((PIPSEC_ADD_SA)pvIoBuffer, inputBufferLength);
ASSERT(status != STATUS_PENDING);
if (outputBufferLength >= sizeof(NTSTATUS)) { (*(NTSTATUS *)pvIoBuffer) = status; dwSize = sizeof(NTSTATUS); } else { dwSize = 0; }
status = STATUS_SUCCESS; break; }
case IOCTL_IPSEC_UPDATE_SA: { //
// This completes the negotiation kicked off via the Acquire.
//
// Adds the SA to the relevant database.
// Typically used to complete inbound SA acquisitions.
//
IPSEC_DEBUG(IOCTL, ("IOCTL_IPSEC_UPDATE_SA\n"));
//
// The minimum size is without any Keys
//
dwSize = IPSEC_UPDATE_SA_NO_KEY_SIZE; if (inputBufferLength < dwSize) { status = STATUS_BUFFER_TOO_SMALL; break; }
status = IPSecUpdateSA((PIPSEC_UPDATE_SA)pvIoBuffer, inputBufferLength);
ASSERT(status != STATUS_PENDING);
if (outputBufferLength >= sizeof(NTSTATUS)) { (*(NTSTATUS *)pvIoBuffer)=status; dwSize = sizeof(NTSTATUS); } else { dwSize = 0; }
status = STATUS_SUCCESS; break; }
case IOCTL_IPSEC_EXPIRE_SA: { //
// Deref the particular SA - delete when ref cnt drops to 0.
//
IPSEC_DEBUG(IOCTL, ("IOCTL_IPSEC_EXPIRE_SA\n"));
dwSize = sizeof(IPSEC_EXPIRE_SA); if (inputBufferLength < dwSize) { status = STATUS_BUFFER_TOO_SMALL; break; }
status = IPSecExpireSA((PIPSEC_EXPIRE_SA)pvIoBuffer); break; }
case IOCTL_IPSEC_GET_SPI: { //
// returns the SPI for an inbound SA
//
IPSEC_DEBUG(IOCTL, ("IOCTL_IPSEC_GET_SPI\n"));
dwSize = sizeof(IPSEC_GET_SPI); if (outputBufferLength < dwSize) { status = STATUS_BUFFER_TOO_SMALL; break; }
status = IPSecGetSPI((PIPSEC_GET_SPI)pvIoBuffer); break; }
case IOCTL_IPSEC_POST_FOR_ACQUIRE_SA: { //
// The SAAPI client posts a request that we complete when
// an SA needs to be initialized or updated (due to
// re-key).
// We keep the Irp around until we need an SA to be
// negotiated.
//
IPSEC_DEBUG(IOCTL, ("IPSEC_POST_FOR_ACQUIRE_SA\n"));
dwSize = sizeof(IPSEC_POST_FOR_ACQUIRE_SA); if (outputBufferLength < dwSize) { IPSEC_DEBUG(IOCTL, ("IPSEC_POST_FOR_ACQUIRE_SA: bad size: dwSize: %lx, input: %lx\n", dwSize, inputBufferLength));
status = STATUS_BUFFER_TOO_SMALL; break; }
Irp->IoStatus.Status = STATUS_PENDING;
status = IPSecHandleAcquireRequest( Irp, (PIPSEC_POST_FOR_ACQUIRE_SA)pvIoBuffer);
if (status == STATUS_PENDING) { IPSEC_DECREMENT(g_ipsec.NumIoctls); return status; }
break; }
case IOCTL_IPSEC_QUERY_EXPORT: { //
// Queries whether the driver is built for export. Used by the IPSEC components
// to decide what key lengths to use for encryption.
//
IPSEC_DEBUG(IOCTL, ("IPSEC_QUERY_EXPORT\n"));
dwSize = sizeof(IPSEC_QUERY_EXPORT); if (outputBufferLength < dwSize) { IPSEC_DEBUG(IOCTL, ("IPSEC_QUERY_EXPORT: bad size: dwSize: %lx, input: %lx\n", dwSize, inputBufferLength)); status = STATUS_BUFFER_TOO_SMALL; break; }
((PIPSEC_QUERY_EXPORT)pvIoBuffer)->Export = FALSE;
status = STATUS_SUCCESS; break; }
case IOCTL_IPSEC_QUERY_SPI: { IPSEC_DEBUG(IOCTL, ("Entered Query SPI\n"));
dwSize = sizeof(IPSEC_QUERY_SPI); if (inputBufferLength < dwSize) { IPSEC_DEBUG(IOCTL, ("IPSEC_QUERY_SPI: bad size: dwSize: %lx, input: %lx\n", dwSize, inputBufferLength)); status = STATUS_BUFFER_TOO_SMALL; break; }
status = IPSecQuerySpi((PIPSEC_QUERY_SPI)pvIoBuffer); break; }
case IOCTL_IPSEC_DELETE_SA: { IPSEC_DEBUG(IOCTL, ("Entered Delete SA\n"));
dwSize = sizeof(IPSEC_DELETE_SA); if (inputBufferLength < dwSize) { IPSEC_DEBUG(IOCTL, ("IPSEC_DELETE_SA: bad size: dwSize: %lx, input: %lx\n", dwSize, inputBufferLength)); status = STATUS_BUFFER_TOO_SMALL; break; }
status = IPSecDeleteSA((PIPSEC_DELETE_SA)pvIoBuffer); break; }
case IOCTL_IPSEC_SET_OPERATION_MODE: { IPSEC_DEBUG(IOCTL, ("Entered Set Operation Mode\n"));
dwSize = sizeof(IPSEC_SET_OPERATION_MODE); if (inputBufferLength < dwSize) { IPSEC_DEBUG(IOCTL, ("IPSEC_SET_OPERATION_MODE: bad size: dwSize: %lx, input: %lx\n", dwSize, inputBufferLength)); status = STATUS_BUFFER_TOO_SMALL; break; }
status = IPSecSetOperationMode((PIPSEC_SET_OPERATION_MODE)pvIoBuffer); break; }
case IOCTL_IPSEC_SET_TCPIP_STATUS: { IPSEC_DEBUG(IOCTL, ("Entered Set Tcpip Status\n"));
if (Irp->RequestorMode != KernelMode) { status = STATUS_ACCESS_DENIED; break; }
dwSize = sizeof(IPSEC_SET_TCPIP_STATUS); if (inputBufferLength < dwSize) { IPSEC_DEBUG(IOCTL, ("IPSEC_SET_TCPIP_STATUS: bad size: dwSize: %lx, input: %lx\n", dwSize, inputBufferLength)); status = STATUS_BUFFER_TOO_SMALL; break; }
status = IPSecSetTcpipStatus((PIPSEC_SET_TCPIP_STATUS)pvIoBuffer);
break; }
case IOCTL_IPSEC_REGISTER_PROTOCOL: {
dwSize = sizeof(IPSEC_REGISTER_PROTOCOL); if (inputBufferLength < dwSize) { status = STATUS_BUFFER_TOO_SMALL; break; }
status = IPSecRegisterProtocols( (PIPSEC_REGISTER_PROTOCOL) pvIoBuffer ); break; }
default: {
status = STATUS_INVALID_PARAMETER; break; } }
IPSEC_DECREMENT(g_ipsec.NumIoctls);
break ; }
default: { IPSEC_DEBUG(IOCTL, ("IPSEC: unknown IRP_MJ_XXX: %lx\n", irpStack->MajorFunction)); status = STATUS_INVALID_PARAMETER; break; } }
ASSERT(status != STATUS_PENDING); ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
Irp->IoStatus.Status = status; Irp->IoStatus.Information = MIN(dwSize, outputBufferLength);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
IPSEC_DEBUG(IOCTL, ("Exiting IPSecDispath\n"));
return status; }
NTSTATUS IPSecBindToIP() /*++
Routine Description:
This bind exchanges a number of entrypoints with IP so that
- packets relevant to IPSEC can be handed over from the IP driver. - buffered packets can be flushed. - SA Table indices can be plumbed. - ....
Arguments:
NONE
Return Value:
The function value is the final status from the bind operation.
--*/ { NTSTATUS status; IPSEC_FUNCTIONS ipsecFns;
PAGED_CODE();
IPSEC_DEBUG(LOAD, ("Entering IPSecBindToIP\n"));
ipsecFns.Version = IP_IPSEC_BIND_VERSION; ipsecFns.IPSecHandler = IPSecHandlePacket; ipsecFns.IPSecQStatus = IPSecQueryStatus; ipsecFns.IPSecSendCmplt = IPSecSendComplete; ipsecFns.IPSecNdisStatus = IPSecNdisStatus; ipsecFns.IPSecRcvFWPacket = IPSecRcvFWPacket;
status = TCPIP_SET_IPSEC(&ipsecFns);
if (status != IP_SUCCESS) { IPSEC_DEBUG(LOAD, ("Failed to bind to IP: %lx\n", status)); } else { IPSEC_DRIVER_BOUND() = TRUE; IPSEC_DRIVER_SEND_BOUND() = TRUE; }
IPSEC_DEBUG(LOAD, ("Exiting IPSecBindToIP\n"));
return status; }
NTSTATUS IPSecUnbindFromIP() /*++
Routine Description:
This unbinds from the Filter Driver
Arguments:
NONE
Return Value:
The function value is the final status from the bind operation.
--*/ { NTSTATUS status; IPSEC_FUNCTIONS ipsecFns={0};
PAGED_CODE();
IPSEC_DEBUG(LOAD, ("Entering IPSecUnbindFromIP\n"));
ipsecFns.Version = IP_IPSEC_BIND_VERSION;
status = TCPIP_UNSET_IPSEC(&ipsecFns);
if (status != IP_SUCCESS) { IPSEC_DEBUG(LOAD, ("Failed to bind to IP: %lx\n", status)); } else { IPSEC_DRIVER_BOUND() = FALSE; }
IPSEC_DEBUG(LOAD, ("Exiting IPSecUnbindFromIP\n"));
return status; }
NTSTATUS IPSecUnbindSendFromIP() /*++
Routine Description:
Unbinds just the send handler from IP
Arguments:
NONE
Return Value:
The function value is the final status from the bind operation.
--*/ { NTSTATUS status; IPSEC_FUNCTIONS ipsecFns={0};
PAGED_CODE();
IPSEC_DEBUG(LOAD, ("Entering IPSecUnbindSendFromIP\n"));
ipsecFns.Version = IP_IPSEC_BIND_VERSION;
status = TCPIP_UNSET_IPSEC_SEND(&ipsecFns);
if (status != IP_SUCCESS) { IPSEC_DEBUG(LOAD, ("Failed to bind to IP: %lx\n", status)); } else { IPSEC_DRIVER_SEND_BOUND() = FALSE; }
IPSEC_DEBUG(LOAD, ("Exiting IPSecUnbindSendFromIP\n"));
return status; }
NTSTATUS OpenRegKey( PHANDLE HandlePtr, PWCHAR KeyName ) /*++
Routine Description:
Opens a Registry key and returns a handle to it.
Arguments:
HandlePtr - The varible into which to write the opened handle. KeyName - The name of the Registry key to open.
Return Value:
STATUS_SUCCESS or an appropriate failure code.
--*/ { NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING UKeyName;
PAGED_CODE();
RtlInitUnicodeString(&UKeyName, KeyName);
memset(&ObjectAttributes, 0, sizeof(OBJECT_ATTRIBUTES)); InitializeObjectAttributes(&ObjectAttributes, &UKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = ZwOpenKey(HandlePtr, KEY_READ, &ObjectAttributes);
return Status; }
NTSTATUS GetRegDWORDValue( HANDLE KeyHandle, PWCHAR ValueName, PULONG ValueData ) /*++
Routine Description:
Reads a REG_DWORD value from the registry into the supplied variable.
Arguments:
KeyHandle - Open handle to the parent key of the value to read. ValueName - The name of the value to read. ValueData - The variable into which to read the data.
Return Value:
STATUS_SUCCESS or an appropriate failure code.
--*/ { NTSTATUS status; ULONG resultLength; PKEY_VALUE_FULL_INFORMATION keyValueFullInformation; UCHAR keybuf[WORK_BUFFER_SIZE]; UNICODE_STRING UValueName;
PAGED_CODE();
RtlInitUnicodeString(&UValueName, ValueName);
keyValueFullInformation = (PKEY_VALUE_FULL_INFORMATION)keybuf; RtlZeroMemory(keyValueFullInformation, sizeof(keyValueFullInformation));
status = ZwQueryValueKey(KeyHandle, &UValueName, KeyValueFullInformation, keyValueFullInformation, WORK_BUFFER_SIZE, &resultLength);
if (NT_SUCCESS(status)) { if (keyValueFullInformation->Type != REG_DWORD) { status = STATUS_INVALID_PARAMETER_MIX; } else { *ValueData = *((ULONG UNALIGNED *)((PCHAR)keyValueFullInformation + keyValueFullInformation->DataOffset)); } }
return status; }
NTSTATUS GetRegStringValue( HANDLE KeyHandle, PWCHAR ValueName, PKEY_VALUE_PARTIAL_INFORMATION *ValueData, PUSHORT ValueSize )
/*++
Routine Description:
Reads a REG_*_SZ string value from the Registry into the supplied key value buffer. If the buffer string buffer is not large enough, it is reallocated.
Arguments:
KeyHandle - Open handle to the parent key of the value to read. ValueName - The name of the value to read. ValueData - Destination for the read data. ValueSize - Size of the ValueData buffer. Updated on output.
Return Value:
STATUS_SUCCESS or an appropriate failure code.
--*/ { NTSTATUS status; ULONG resultLength; UNICODE_STRING UValueName;
PAGED_CODE();
RtlInitUnicodeString(&UValueName, ValueName);
status = ZwQueryValueKey( KeyHandle, &UValueName, KeyValuePartialInformation, *ValueData, (ULONG) *ValueSize, &resultLength );
if ( (status == STATUS_BUFFER_OVERFLOW) || (status == STATUS_BUFFER_TOO_SMALL) ) { PVOID temp;
//
// Free the old buffer and allocate a new one of the
// appropriate size.
//
ASSERT(resultLength > (ULONG) *ValueSize);
if (resultLength <= 0xFFFF) {
temp = IPSecAllocateMemory(resultLength, IPSEC_TAG_IOCTL);
if (temp != NULL) {
if (*ValueData != NULL) { IPSecFreeMemory(*ValueData); }
*ValueData = temp; *ValueSize = (USHORT) resultLength;
status = ZwQueryValueKey(KeyHandle, &UValueName, KeyValuePartialInformation, *ValueData, *ValueSize, &resultLength );
ASSERT( (status != STATUS_BUFFER_OVERFLOW) && (status != STATUS_BUFFER_TOO_SMALL) ); } else { status = STATUS_INSUFFICIENT_RESOURCES; } } else { status = STATUS_BUFFER_TOO_SMALL; } }
return status; }
NTSTATUS GetRegMultiSZValue( HANDLE KeyHandle, PWCHAR ValueName, PUNICODE_STRING ValueData )
/*++
Routine Description:
Reads a REG_MULTI_SZ string value from the Registry into the supplied Unicode string. If the Unicode string buffer is not large enough, it is reallocated.
Arguments:
KeyHandle - Open handle to the parent key of the value to read. ValueName - The name of the value to read. ValueData - Destination Unicode string for the value data.
Return Value:
STATUS_SUCCESS or an appropriate failure code.
--*/ { NTSTATUS status; ULONG resultLength; PKEY_VALUE_PARTIAL_INFORMATION keyValuePartialInformation; UNICODE_STRING UValueName;
PAGED_CODE();
ValueData->Length = 0;
status = GetRegStringValue( KeyHandle, ValueName, (PKEY_VALUE_PARTIAL_INFORMATION *) &(ValueData->Buffer), &(ValueData->MaximumLength) );
if (NT_SUCCESS(status)) {
keyValuePartialInformation = (PKEY_VALUE_PARTIAL_INFORMATION) ValueData->Buffer;
if (keyValuePartialInformation->Type == REG_MULTI_SZ) {
ValueData->Length = (USHORT) keyValuePartialInformation->DataLength;
RtlCopyMemory( ValueData->Buffer, &(keyValuePartialInformation->Data), ValueData->Length ); } else { status = STATUS_INVALID_PARAMETER_MIX; } }
return status;
} // GetRegMultiSZValue
VOID IPSecReadRegistry() /*++
Routine Description:
Reads config info from registry into g_ipsec
Arguments:
Return Value:
status of the read.
--*/ { NTSTATUS status; HANDLE hRegKey; WCHAR IPSecParametersRegistryKey[] = IPSEC_REG_KEY; BOOLEAN isAs = MmIsThisAnNtAsSystem();
g_ipsec.EnableOffload = IPSEC_DEFAULT_ENABLE_OFFLOAD; g_ipsec.DefaultSAIdleTime = IPSEC_DEFAULT_SA_IDLE_TIME; g_ipsec.LogInterval = IPSEC_DEFAULT_LOG_INTERVAL; g_ipsec.EventQueueSize = IPSEC_DEFAULT_EVENT_QUEUE_SIZE; g_ipsec.RekeyTime = IPSEC_DEFAULT_REKEY; g_ipsec.NoDefaultExempt = IPSEC_DEFAULT_NO_DEFAULT_EXEMPT; g_ipsec.NoDefaultExempt = DEFAULT_IPSEC_OPERATION_MODE; g_ipsec.DiagnosticMode = IPSEC_DEFAULT_ENABLE_DIAGNOSTICS;
if (isAs) { g_ipsec.CacheSize = IPSEC_DEFAULT_AS_CACHE_SIZE; g_ipsec.SAHashSize = IPSEC_DEFAULT_AS_SA_HASH_SIZE; } else { g_ipsec.CacheSize = IPSEC_DEFAULT_CACHE_SIZE; g_ipsec.SAHashSize = IPSEC_DEFAULT_SA_HASH_SIZE; }
status = OpenRegKey(&hRegKey, IPSecParametersRegistryKey);
if (NT_SUCCESS(status)) { //
// Expected configuration values. We use reasonable defaults if they
// aren't available for some reason.
//
IPSEC_REG_READ_DWORD( hRegKey, IPSEC_REG_PARAM_ENABLE_OFFLOAD, &g_ipsec.EnableOffload, IPSEC_DEFAULT_ENABLE_OFFLOAD, IPSEC_MAX_ENABLE_OFFLOAD, IPSEC_MIN_ENABLE_OFFLOAD);
IPSEC_REG_READ_DWORD( hRegKey, IPSEC_REG_PARAM_SA_IDLE_TIME, &g_ipsec.DefaultSAIdleTime, IPSEC_DEFAULT_SA_IDLE_TIME, IPSEC_MAX_SA_IDLE_TIME, IPSEC_MIN_SA_IDLE_TIME);
IPSEC_REG_READ_DWORD( hRegKey, IPSEC_REG_PARAM_EVENT_QUEUE_SIZE, &g_ipsec.EventQueueSize, IPSEC_DEFAULT_EVENT_QUEUE_SIZE, IPSEC_MAX_EVENT_QUEUE_SIZE, IPSEC_MIN_EVENT_QUEUE_SIZE);
IPSEC_REG_READ_DWORD( hRegKey, IPSEC_REG_PARAM_LOG_INTERVAL, &g_ipsec.LogInterval, IPSEC_DEFAULT_LOG_INTERVAL, IPSEC_MAX_LOG_INTERVAL, IPSEC_MIN_LOG_INTERVAL);
IPSEC_REG_READ_DWORD( hRegKey, IPSEC_REG_PARAM_REKEY_TIME, &g_ipsec.RekeyTime, IPSEC_DEFAULT_REKEY, IPSEC_MAX_REKEY, IPSEC_MIN_REKEY);
IPSEC_REG_READ_DWORD( hRegKey, IPSEC_REG_PARAM_CACHE_SIZE, &g_ipsec.CacheSize, isAs? IPSEC_DEFAULT_AS_CACHE_SIZE: IPSEC_DEFAULT_CACHE_SIZE, IPSEC_MAX_CACHE_SIZE, IPSEC_MIN_CACHE_SIZE);
IPSEC_REG_READ_DWORD( hRegKey, IPSEC_REG_PARAM_SA_HASH_SIZE, &g_ipsec.SAHashSize, isAs? IPSEC_DEFAULT_AS_SA_HASH_SIZE: IPSEC_DEFAULT_SA_HASH_SIZE, IPSEC_MAX_SA_HASH_SIZE, IPSEC_MIN_SA_HASH_SIZE);
IPSEC_REG_READ_DWORD( hRegKey, IPSEC_REG_PARAM_NO_DEFAULT_EXEMPT, &g_ipsec.NoDefaultExempt, IPSEC_DEFAULT_NO_DEFAULT_EXEMPT, IPSEC_MAX_NO_DEFAULT_EXEMPT, IPSEC_MIN_NO_DEFAULT_EXEMPT);
IPSEC_REG_READ_DWORD( hRegKey, IPSEC_REG_PARAM_ENABLE_DIAGNOSTICS, &g_ipsec.DiagnosticMode, IPSEC_DEFAULT_ENABLE_DIAGNOSTICS, IPSEC_MAX_ENABLE_DIAGNOSTICS, IPSEC_MIN_ENABLE_DIAGNOSTICS);
IPSEC_REG_READ_DWORD( hRegKey, IPSEC_REG_PARAM_OPERATION_MODE, &(ULONG)g_ipsec.OperationMode, DEFAULT_IPSEC_OPERATION_MODE, IPSEC_OPERATION_MODE_MAX-1, 0);
ZwClose(hRegKey); }
g_ipsec.CacheHalfSize = g_ipsec.CacheSize / 2;
//
// Init SAIdleTime for low memory reaper
//
IPSEC_CONVERT_SECS_TO_100NS(g_ipsec.SAIdleTime, g_ipsec.DefaultSAIdleTime); }
NTSTATUS IPSecGeneralInit() /*++
Routine Description:
General structures are initialized here.
Arguments:
None
Return Value:
--*/ { PSA_TABLE_ENTRY pSA; LONG i; NTSTATUS status = STATUS_SUCCESS;;
PAGED_CODE();
IPSEC_DEBUG(LOAD, ("Entering IPSecGeneralInit\n"));
//
// init the acquireinfo struct
//
InitializeListHead(&g_ipsec.AcquireInfo.PendingAcquires); InitializeListHead(&g_ipsec.AcquireInfo.PendingNotifies); InitializeListHead(&g_ipsec.LarvalSAList); INIT_LOCK(&g_ipsec.LarvalListLock); INIT_LOCK(&g_ipsec.AcquireInfo.Lock);
//
// Set up the hashes/tables
//
InitializeMRSWLock(&g_ipsec.SADBLock); InitializeMRSWLock(&g_ipsec.SPIListLock);
g_ipsec.IPProtInfo.pi_xmitdone = IPSecProtocolSendComplete; g_ipsec.IPProtInfo.pi_protocol = PROTOCOL_ESP;
//
// init filter linked lists
//
for (i = MIN_FILTER; i <= MAX_FILTER; i++) { InitializeListHead(&g_ipsec.FilterList[i]); }
//
// SAs in a hash table, hashed by <SPI, Dest addr>
//
g_ipsec.pSADb = IPSecAllocateMemory(g_ipsec.SAHashSize * sizeof(SA_HASH), IPSEC_TAG_INIT);
if (!g_ipsec.pSADb) { IPSEC_DEBUG(LOAD, ("Failed to alloc SADb hash\n")); return STATUS_INSUFFICIENT_RESOURCES; }
IPSecInitFlag |= INIT_SA_DATABASE;
IPSecZeroMemory(g_ipsec.pSADb, g_ipsec.SAHashSize * sizeof(SA_HASH));
for (i = 0; i < g_ipsec.SAHashSize; i++) { PSA_HASH Entry = &g_ipsec.pSADb[i]; InitializeListHead(&Entry->SAList); }
//
// Initialize the MDL pools.
//
status = IPSecInitMdlPool(); if (!NT_SUCCESS (status)) { IPSEC_DEBUG(LOAD, ("Failed to alloc MDL pools\n")); return STATUS_INSUFFICIENT_RESOURCES; }
IPSecInitFlag |= INIT_MDL_POOLS;
//
// Initialize the cache structures.
//
if (!AllocateCacheStructures()) { IPSEC_DEBUG(LOAD, ("Failed to alloc cache structs\n")); return STATUS_INSUFFICIENT_RESOURCES; }
IPSecInitFlag |= INIT_CACHE_STRUCT;
//
// Allocate EventQueue memory.
//
g_ipsec.IPSecLogMemory = IPSecAllocateMemory( g_ipsec.EventQueueSize * sizeof(IPSEC_EVENT_CTX), IPSEC_TAG_EVT_QUEUE);
if (!g_ipsec.IPSecLogMemory) { return STATUS_INSUFFICIENT_RESOURCES; }
IPSecInitFlag |= INIT_DEBUG_MEMORY;
g_ipsec.IPSecLogMemoryLoc = &g_ipsec.IPSecLogMemory[0]; g_ipsec.IPSecLogMemoryEnd = &g_ipsec.IPSecLogMemory[g_ipsec.EventQueueSize * sizeof(IPSEC_EVENT_CTX)];
//
// Init the timer stuff.
//
if (!IPSecInitTimer()) { IPSEC_DEBUG(LOAD, ("Failed to init timer\n")); return STATUS_INSUFFICIENT_RESOURCES; }
IPSecInitFlag |= INIT_TIMERS;
#if GPC
status = IPSecGpcInitialize(); if (status != STATUS_SUCCESS) { IPSEC_DEBUG(LOAD, ("Failed to register GPC clients\n")); } #endif
//
// Arm the reaper timer
//
IPSEC_DEBUG(LOAD, ("Starting ReaperTimer\n")); IPSecStartTimer(&g_ipsec.ReaperTimer, IPSecReaper, IPSEC_REAPER_TIME, (PVOID)NULL);
//
// Start EventLog timer
//
IPSEC_DEBUG(LOAD, ("Starting EventLogTimer\n")); IPSecStartTimer(&g_ipsec.EventLogTimer, IPSecFlushEventLog, g_ipsec.LogInterval, (PVOID)NULL);
IPSEC_DEBUG(LOAD, ("Exiting IPSecGeneralInit\n"));
return STATUS_SUCCESS; }
NTSTATUS IPSecGeneralFree() /*++
Routine Description:
Free general structures if IPSecGeneralInit fails.
Arguments:
None
Return Value:
--*/ { INT index;
//
// Free SA database.
//
if (IPSecInitFlag & INIT_SA_DATABASE) { if (g_ipsec.pSADb) { IPSecFreeMemory(g_ipsec.pSADb); } }
//
// Free MDL pool.
//
if (IPSecInitFlag & INIT_MDL_POOLS) { IPSecDeinitMdlPool(); }
//
// Free cache struct.
//
if (IPSecInitFlag & INIT_CACHE_STRUCT) { FreeExistingCache(); }
//
// Free EventQueue memory.
//
if (IPSecInitFlag & INIT_DEBUG_MEMORY) { if (g_ipsec.IPSecLogMemory) { IPSecFreeMemory(g_ipsec.IPSecLogMemory); } }
//
// Free timers allocated.
//
if (IPSecInitFlag & INIT_TIMERS) { for (index = 0; index < IPSEC_CLASS_MAX; index++) { IPSecFreeMemory(g_ipsec.TimerList[index].pTimers); } }
return STATUS_SUCCESS; }
NTSTATUS IPSecFreeConfig() /*++
Routine Description:
Free the SA table etc.
Arguments:
None
Return Value:
--*/ { IPSEC_DEBUG(LOAD, ("Entering IPSecFreeConfig\n"));
PAGED_CODE();
FreeExistingCache(); FreePatternDbase();
if (g_ipsec.IPSecLogMemory) { IPSecFreeMemory(g_ipsec.IPSecLogMemory); }
IPSEC_DEBUG(LOAD, ("Exiting IPSecFreeConfig\n")); return STATUS_SUCCESS;
}
NTSTATUS IPSecInitMdlPool() /*++
Routine Description:
Create the MDL pool for AH and ESP headers.
Arguments:
None
Return Value:
--*/ { PAGED_CODE(); IPSEC_DEBUG(LOAD, ("Entering IPSecInitMdlPool\n"));
g_ipsec.IPSecSmallBufferSize = IPSEC_SMALL_BUFFER_SIZE; g_ipsec.IPSecLargeBufferSize = IPSEC_LARGE_BUFFER_SIZE; g_ipsec.IPSecSendCompleteCtxSize = sizeof(IPSEC_SEND_COMPLETE_CONTEXT);
g_ipsec.IPSecSmallBufferListDepth = IPSEC_LIST_DEPTH; g_ipsec.IPSecLargeBufferListDepth = IPSEC_LIST_DEPTH; g_ipsec.IPSecSendCompleteCtxDepth = IPSEC_LIST_DEPTH;
g_ipsec.IPSecCacheLineSize = IPSEC_CACHE_LINE_SIZE;
//
// Initialize the lookaside lists.
//
g_ipsec.IPSecLookasideLists = IPSecAllocateMemory( sizeof(*g_ipsec.IPSecLookasideLists), IPSEC_TAG_LOOKASIDE_LISTS);
if (g_ipsec.IPSecLookasideLists == NULL) { return STATUS_INSUFFICIENT_RESOURCES; }
//
// Initialize the IPSEC buffer lookaside lists.
//
ExInitializeNPagedLookasideList(&g_ipsec.IPSecLookasideLists->LargeBufferList, IPSecAllocateBufferPool, NULL, 0, g_ipsec.IPSecLargeBufferSize, IPSEC_TAG_BUFFER_POOL, (USHORT)g_ipsec.IPSecLargeBufferListDepth);
ExInitializeNPagedLookasideList(&g_ipsec.IPSecLookasideLists->SmallBufferList, IPSecAllocateBufferPool, NULL, 0, g_ipsec.IPSecSmallBufferSize, IPSEC_TAG_BUFFER_POOL, (USHORT)g_ipsec.IPSecSmallBufferListDepth);
ExInitializeNPagedLookasideList(&g_ipsec.IPSecLookasideLists->SendCompleteCtxList, NULL, NULL, 0, g_ipsec.IPSecSendCompleteCtxSize, IPSEC_TAG_SEND_COMPLETE, (USHORT)g_ipsec.IPSecSendCompleteCtxDepth);
IPSEC_DEBUG(LOAD, ("Exiting IPSecInitMdlPool\n")); return STATUS_SUCCESS; }
VOID IPSecDeinitMdlPool() /*++
Routine Description:
Free the MDL pool for AH and ESP headers.
Arguments:
None
Return Value:
--*/ { PAGED_CODE(); IPSEC_DEBUG(LOAD, ("Entering IPSecDeinitMdlPool\n"));
//
// Destroy the lookaside lists.
//
if (g_ipsec.IPSecLookasideLists != NULL) { ExDeleteNPagedLookasideList(&g_ipsec.IPSecLookasideLists->LargeBufferList); ExDeleteNPagedLookasideList(&g_ipsec.IPSecLookasideLists->SmallBufferList); ExDeleteNPagedLookasideList(&g_ipsec.IPSecLookasideLists->SendCompleteCtxList);
IPSecFreeMemory(g_ipsec.IPSecLookasideLists); }
IPSEC_DEBUG(LOAD, ("Exiting IPSecDeinitMdlPool\n")); }
NTSTATUS IPSecQuiesce() /*++
Routine Description:
Destroy MDL pools and run down all driver activity
Arguments:
None
Return Value:
--*/ { IPSEC_DEBUG(LOAD, ("Entering IPSecQuiesce\n")); IPSecDeinitMdlPool(); IPSEC_DEBUG(LOAD, ("Exiting IPSecQuiesce\n")); return STATUS_SUCCESS; }
BOOLEAN AllocateCacheStructures() /*++
Routine Description:
Allocates the necessary memory for cache (which is an array of pointers to cache entries) Allocates necessary number of cache entries (but doesnt initialize them) Allocates a small number of entries and puts them on the free list (doesnt initialize these either)
Arguments:
None
Return Value:
True if the function completely succeeds, else FALSE. If FALSE, it is upto the CALLER to do a rollback and clear any allocated memory
--*/ { ULONG i;
PAGED_CODE();
g_ipsec.ppCache = IPSecAllocateMemory(g_ipsec.CacheSize * sizeof(PFILTER_CACHE), IPSEC_TAG_INIT);
if (!g_ipsec.ppCache) { IPSEC_DEBUG(LOAD, ("Couldnt allocate memory for Input Cache\n")); return FALSE; }
IPSecZeroMemory(g_ipsec.ppCache, g_ipsec.CacheSize * sizeof(PFILTER_CACHE));
for (i = 0; i < g_ipsec.CacheSize; i++) { PFILTER_CACHE pTemp1;
pTemp1 = IPSecAllocateMemory(sizeof(FILTER_CACHE), IPSEC_TAG_INIT);
if (!pTemp1) { FreeExistingCache(); return FALSE; }
IPSecZeroMemory(pTemp1, sizeof(FILTER_CACHE));
g_ipsec.ppCache[i] = pTemp1;
}
return TRUE; }
VOID FreeExistingCache() /*++
Routine Description
Frees all the cache entries, free entries and cache pointer array
Arguments
None
Return Value
None
--*/ { ULONG i;
PAGED_CODE();
IPSEC_DEBUG(LOAD, ("Freeing existing cache...\n"));
IPSecResetCacheTable();
if (g_ipsec.ppCache) { for (i = 0; i < g_ipsec.CacheSize; i++) { if (g_ipsec.ppCache[i]) { ExFreePool(g_ipsec.ppCache[i]); } }
ExFreePool(g_ipsec.ppCache); g_ipsec.ppCache = NULL; } }
VOID FreePatternDbase() /*++
Routine Description
Frees all filters and SAs.
Arguments
None
Return Value
None
--*/ { PLIST_ENTRY pEntry; PFILTER pFilter; PSA_TABLE_ENTRY pSA; LONG i, j;
PAGED_CODE();
//
// Free all masked filters and associated (outbound) SAs
//
for (i = MIN_FILTER; i <= MAX_FILTER; i++) {
while (!IsListEmpty(&g_ipsec.FilterList[i])) {
pEntry = RemoveHeadList(&g_ipsec.FilterList[i]);
pFilter = CONTAINING_RECORD(pEntry, FILTER, MaskedLinkage);
IPSEC_DEBUG(LOAD, ("Freeing filter: %lx\n", pFilter));
//
// Free each SA under it.
//
for (j = 0; j < pFilter->SAChainSize; j++) {
while (!IsListEmpty(&pFilter->SAChain[j])) {
pEntry = RemoveHeadList(&pFilter->SAChain[j]);
pSA = CONTAINING_RECORD(pEntry, SA_TABLE_ENTRY, sa_FilterLinkage);
IPSEC_DEBUG(LOAD, ("Freeing SA: %lx\n", pSA));
//
// Remove SA from miniport if plumbed
//
if (pSA->sa_Flags & FLAGS_SA_HW_PLUMBED) { IPSecDelHWSA(pSA); }
//
// Also remove the inbound SAs from their SPI list
// so we dont double free them below.
//
IPSecRemoveSPIEntry(pSA);
//
// Stop the timer if armed and deref SA.
//
IPSecStopTimerDerefSA(pSA); } }
#if GPC
IPSecUninstallGpcFilter(pFilter); #endif
IPSecFreeFilter(pFilter); } }
//
// Free all SAs under the SPI hashes.
//
for (i = 0; i < g_ipsec.SAHashSize; i++) { PSA_HASH pHash = &g_ipsec.pSADb[i];
while (!IsListEmpty(&pHash->SAList)) {
pEntry = RemoveHeadList(&pHash->SAList);
pSA = CONTAINING_RECORD(pEntry, SA_TABLE_ENTRY, sa_SPILinkage);
IPSEC_DEBUG(LOAD, ("Freeing SA: %lx\n", pSA));
if (pSA->sa_Flags & FLAGS_SA_HW_PLUMBED) { IPSecDelHWSA(pSA); }
IPSecStopTimerDerefSA(pSA); } }
IPSecFreeMemory(g_ipsec.pSADb);
IPSEC_DEBUG(LOAD, ("Freed filters/SAs\n")); }
SIZE_T IPSecCalculateBufferSize( IN SIZE_T BufferDataSize ) /*++
Routine Description:
Determines the size of an AFD buffer structure given the amount of data that the buffer contains.
Arguments:
BufferDataSize - data length of the buffer.
AddressSize - length of address structure for the buffer.
Return Value:
Number of bytes needed for an IPSEC_LA_BUFFER structure for data of this size.
--*/ { SIZE_T mdlSize; SIZE_T bufferSize;
ASSERT(BufferDataSize != 0);
ASSERT(g_ipsec.IPSecCacheLineSize < 100);
//
// Determine the sizes of the various components of an IPSEC_LA_BUFFER
// structure. Note that these are all worst-case calculations--
// actual sizes of the MDL and the buffer may be smaller.
//
bufferSize = BufferDataSize + g_ipsec.IPSecCacheLineSize; mdlSize = MmSizeOfMdl( (PVOID)(PAGE_SIZE-1), bufferSize );
return ((sizeof(IPSEC_LA_BUFFER) + mdlSize + bufferSize + 3) & ~3);
}
VOID IPSecInitializeBuffer( IN PIPSEC_LA_BUFFER IPSecBuffer, IN SIZE_T BufferDataSize ) /*++
Routine Description:
Initializes an IPSec buffer. Sets up fields in the actual IPSEC_LA_BUFFER structure and initializes the MDL associated with the buffer. This routine assumes that the caller has properly allocated sufficient space for all this.
Arguments:
IPSecBuffer - points to the IPSEC_LA_BUFFER structure to initialize.
BufferDataSize - the size of the data buffer that goes along with the buffer structure.
Return Value:
None
--*/ { SIZE_T mdlSize;
//
// Set up the MDL pointer but don't build it yet. We have to wait
// until after the data buffer is built to build the MDL.
//
mdlSize = MmSizeOfMdl( (PVOID)(PAGE_SIZE-1), BufferDataSize ); IPSecBuffer->Mdl = (PMDL)&IPSecBuffer->Data[0];
IPSEC_DEBUG(POOL, ("IPSecBuffer: %lx, MDL: %lx\n", IPSecBuffer, IPSecBuffer->Mdl));
//
// Set up the data buffer pointer and length. Note that the buffer
// MUST begin on a cache line boundary so that we can use the fast
// copy routines like RtlCopyMemory on the buffer.
//
IPSecBuffer->Buffer = (PVOID) (((ULONG_PTR)((PCHAR)IPSecBuffer->Mdl + mdlSize) + g_ipsec.IPSecCacheLineSize - 1 ) & ~((ULONG_PTR)(g_ipsec.IPSecCacheLineSize - 1)));
IPSecBuffer->BufferLength = (ULONG)BufferDataSize; // Sundown - FIX
//
// Now build the MDL and set up a pointer to the MDL in the IRP.
//
MmInitializeMdl( IPSecBuffer->Mdl, IPSecBuffer->Buffer, BufferDataSize ); MmBuildMdlForNonPagedPool( IPSecBuffer->Mdl );
}
PVOID IPSecAllocateBufferPool( IN POOL_TYPE PoolType, IN SIZE_T NumberOfBytes, IN ULONG Tag ) /*++
Routine Description:
Used by the lookaside list allocation function to allocate a new IPSec buffer structure. The returned structure will be fully initialized.
Arguments:
PoolType - passed to ExAllocatePoolWithTag.
NumberOfBytes - the number of bytes required for the data buffer portion of the IPSec buffer.
Tag - passed to ExAllocatePoolWithTag.
Return Value:
PVOID - a fully initialized PIPSEC_LA_BUFFER, or NULL if the allocation attempt fails.
--*/ { PIPSEC_LA_BUFFER IPSecBuffer; SIZE_T bytesRequired;
//
// The requested length must be the same as one of the standard
// IPSec buffer sizes.
//
ASSERT( NumberOfBytes == g_ipsec.IPSecSmallBufferSize || NumberOfBytes == g_ipsec.IPSecLargeBufferSize );
//
// Determine how much data we'll actually need for the buffer.
//
bytesRequired = IPSecCalculateBufferSize(NumberOfBytes);
//
// Get nonpaged pool for the buffer.
//
IPSecBuffer = IPSecAllocateMemory( bytesRequired, Tag ); if ( IPSecBuffer == NULL ) { return NULL; }
//
// Initialize the buffer and return a pointer to it.
//
IPSecInitializeBuffer( IPSecBuffer, NumberOfBytes );
return IPSecBuffer;
}
PIPSEC_LA_BUFFER IPSecGetBuffer( IN CLONG BufferDataSize, IN ULONG Tag ) /*++
Routine Description:
Obtains a buffer of the appropriate size for the caller. Uses the preallocated buffers if possible, or else allocates a new buffer structure if required.
Arguments:
BufferDataSize - the size of the data buffer that goes along with the buffer structure.
Return Value:
PIPSEC_LA_BUFFER - a pointer to an IPSEC_LA_BUFFER structure, or NULL if one was not available or could not be allocated.
--*/ { PIPSEC_LA_BUFFER IPSecBuffer; SIZE_T bufferSize; PLIST_ENTRY listEntry; PNPAGED_LOOKASIDE_LIST lookasideList;
//
// If possible, allocate the buffer from one of the lookaside lists.
//
if (BufferDataSize <= g_ipsec.IPSecLargeBufferSize) {
if ( BufferDataSize <= g_ipsec.IPSecSmallBufferSize ) {
lookasideList = &g_ipsec.IPSecLookasideLists->SmallBufferList; BufferDataSize = g_ipsec.IPSecSmallBufferSize;
} else {
lookasideList = &g_ipsec.IPSecLookasideLists->LargeBufferList; BufferDataSize = g_ipsec.IPSecLargeBufferSize;
}
IPSecBuffer = ExAllocateFromNPagedLookasideList( lookasideList );
if (!IPSecBuffer) { return NULL; }
IPSecBuffer->Tag = Tag;
return IPSecBuffer; }
//
// Couldn't find an appropriate buffer that was preallocated.
// Allocate one manually. If the buffer size requested was
// zero bytes, give them four bytes. This is because some of
// the routines like MmSizeOfMdl() cannot handle getting passed
// in a length of zero.
//
// !!! It would be good to ROUND_TO_PAGES for this allocation
// if appropriate, then use entire buffer size.
//
if ( BufferDataSize == 0 ) { BufferDataSize = sizeof(ULONG); }
bufferSize = IPSecCalculateBufferSize(BufferDataSize);
IPSecBuffer = IPSecAllocateMemory(bufferSize, IPSEC_TAG_BUFFER_POOL);
if ( IPSecBuffer == NULL ) { return NULL; }
//
// Initialize the IPSec buffer structure and return it.
//
IPSecInitializeBuffer(IPSecBuffer, BufferDataSize);
IPSecBuffer->Tag = Tag;
return IPSecBuffer; }
VOID IPSecReturnBuffer ( IN PIPSEC_LA_BUFFER IPSecBuffer ) /*++
Routine Description:
Returns an IPSec buffer to the appropriate global list, or frees it if necessary.
Arguments:
IPSecBufferHeader - points to the IPSec_BUFFER_HEADER structure to return or free.
Return Value:
None
--*/ { PNPAGED_LOOKASIDE_LIST lookasideList;
//
// If appropriate, return the buffer to one of the IPSec buffer
// lookaside lists.
//
if (IPSecBuffer->BufferLength <= g_ipsec.IPSecLargeBufferSize) {
if (IPSecBuffer->BufferLength==g_ipsec.IPSecSmallBufferSize) { lookasideList = &g_ipsec.IPSecLookasideLists->SmallBufferList; } else { ASSERT (IPSecBuffer->BufferLength==g_ipsec.IPSecLargeBufferSize); lookasideList = &g_ipsec.IPSecLookasideLists->LargeBufferList; }
ExFreeToNPagedLookasideList( lookasideList, IPSecBuffer );
return;
}
IPSecFreeMemory(IPSecBuffer); }
NTSTATUS IPSecWriteEvent( PDRIVER_OBJECT IPSecDriverObject, IN ULONG EventCode, IN NTSTATUS NtStatusCode, IN ULONG OffloadStatus, IN ULONG ExtraStatus1, IN ULONG ExtraStatus2, IN PVOID RawDataBuffer, IN USHORT RawDataLength, IN USHORT NumberOfInsertionStrings, ... )
#define LAST_NAMED_ARGUMENT NumberOfInsertionStrings
/*++
Routine Description:
This function allocates an I/O error log record, fills it in and writes it to the I/O error log.
Arguments:
Return Value:
None.
--*/ { PIO_ERROR_LOG_PACKET ErrorLogEntry; va_list ParmPtr; // Pointer to stack parms.
PCHAR DumpData; LONG Length; ULONG i, SizeOfRawData, RemainingSpace, TotalErrorLogEntryLength; ULONG SizeOfStringData = 0; PWSTR StringOffset, InsertionString;
if (NumberOfInsertionStrings != 0) { va_start (ParmPtr, LAST_NAMED_ARGUMENT);
for (i = 0; i < NumberOfInsertionStrings; i += 1) { InsertionString = va_arg (ParmPtr, PWSTR); Length = wcslen (InsertionString); while ((Length > 0) && (InsertionString[Length-1] == L' ')) { Length--; }
SizeOfStringData += (Length + 1) * sizeof(WCHAR); } }
//
// Ideally we want the packet to hold the servername and ExtraInformation.
// Usually the ExtraInformation gets truncated.
//
TotalErrorLogEntryLength = min (RawDataLength + sizeof(IO_ERROR_LOG_PACKET) + 1 + SizeOfStringData, ERROR_LOG_MAXIMUM_SIZE);
RemainingSpace = TotalErrorLogEntryLength - FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData); if (RemainingSpace > SizeOfStringData) { SizeOfRawData = RemainingSpace - SizeOfStringData; } else { SizeOfStringData = RemainingSpace; SizeOfRawData = 0; }
ErrorLogEntry = IoAllocateErrorLogEntry (IPSecDriverObject, (UCHAR) TotalErrorLogEntryLength); if (ErrorLogEntry == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); }
//
// Fill in the error log entry
//
ErrorLogEntry->ErrorCode = EventCode; ErrorLogEntry->UniqueErrorValue = OffloadStatus; ErrorLogEntry->FinalStatus = NtStatusCode; ErrorLogEntry->MajorFunctionCode = 0; ErrorLogEntry->RetryCount = 0; ErrorLogEntry->IoControlCode = 0; ErrorLogEntry->DeviceOffset.LowPart = ExtraStatus1; ErrorLogEntry->DeviceOffset.HighPart = ExtraStatus2; ErrorLogEntry->DumpDataSize = 0; ErrorLogEntry->NumberOfStrings = 0; ErrorLogEntry->SequenceNumber = 0; ErrorLogEntry->StringOffset = (USHORT) (ROUND_UP_COUNT (FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + SizeOfRawData, ALIGN_WORD));
//
// Append the dump data. This information is typically an SMB header.
//
if ((RawDataBuffer) && (SizeOfRawData)) { DumpData = (PCHAR) ErrorLogEntry->DumpData; Length = min (RawDataLength, (USHORT)SizeOfRawData); RtlCopyMemory (DumpData, RawDataBuffer, Length); ErrorLogEntry->DumpDataSize = (USHORT)Length; }
//
// Add the debug informatuion strings
//
if (NumberOfInsertionStrings) { StringOffset = (PWSTR) ((PCHAR)ErrorLogEntry + ErrorLogEntry->StringOffset);
//
// Set up ParmPtr to point to first of the caller's parameters.
//
va_start(ParmPtr, LAST_NAMED_ARGUMENT);
for (i = 0 ; i < NumberOfInsertionStrings ; i+= 1) { InsertionString = va_arg(ParmPtr, PWSTR); Length = wcslen(InsertionString); while ( (Length > 0) && (InsertionString[Length-1] == L' ')) { Length--; }
if (((Length + 1) * sizeof(WCHAR)) > SizeOfStringData) { Length = (SizeOfStringData/sizeof(WCHAR)) - 1; }
if (Length > 0) { RtlCopyMemory (StringOffset, InsertionString, Length*sizeof(WCHAR)); StringOffset += Length; *StringOffset++ = L'\0';
SizeOfStringData -= (Length + 1) * sizeof(WCHAR);
ErrorLogEntry->NumberOfStrings += 1; } } }
IoWriteErrorLogEntry(ErrorLogEntry);
return(STATUS_SUCCESS); }
VOID IPSecLogEvents( IN PVOID Context ) /*++
Routine Description:
Dumps events from the circular buffer to the eventlog when the circular buffer overflows.
Arguments:
Context - unused.
Return Value:
None
--*/ { PIPSEC_LOG_EVENT pLogEvent; LONG LogSize; PUCHAR pLog;
pLogEvent = (PIPSEC_LOG_EVENT)Context; LogSize = 0; pLog = (PUCHAR)pLogEvent + FIELD_OFFSET(IPSEC_LOG_EVENT, pLog[0]);
while (LogSize < pLogEvent->LogSize) { PIPSEC_EVENT_CTX ctx = (PIPSEC_EVENT_CTX)pLog;
if (ctx->EventCode == EVENT_IPSEC_DROP_PACKET_INBOUND || ctx->EventCode == EVENT_IPSEC_DROP_PACKET_OUTBOUND) { WCHAR IPAddrBufferS[(sizeof(IPAddr) * 4) + 1]; WCHAR IPAddrBufferD[(sizeof(IPAddr) * 4) + 1]; WCHAR IPProtocolBuffer[(sizeof(IPAddr) * 4) + 1]; WCHAR IPSPortBuffer[(sizeof(IPAddr) * 4) + 1]; WCHAR IPDPortBuffer[(sizeof(IPAddr) * 4) + 1]; PWCHAR stringlist[5]; IPHeader UNALIGNED *pIPH; USHORT SrcPort=0; USHORT DestPort=0; ULONG HeaderLen;
pIPH = (IPHeader UNALIGNED *)ctx->pPacket; HeaderLen=(pIPH->iph_verlen & (UCHAR)~IP_VER_FLAG) << 2; IPSecIPAddrToUnicodeString( pIPH->iph_src, IPAddrBufferS); IPSecIPAddrToUnicodeString( pIPH->iph_dest, IPAddrBufferD); IPSecCountToUnicodeString ( pIPH->iph_protocol, IPProtocolBuffer);
if (pIPH->iph_protocol == PROTOCOL_TCP || pIPH->iph_protocol == PROTOCOL_UDP) { RtlCopyMemory(&SrcPort,&ctx->pPacket[HeaderLen],sizeof(USHORT)); RtlCopyMemory(&DestPort,&ctx->pPacket[HeaderLen+sizeof(USHORT)],sizeof(USHORT)); }
IPSecCountToUnicodeString ( NET_SHORT(SrcPort), IPSPortBuffer);
IPSecCountToUnicodeString ( NET_SHORT(DestPort), IPDPortBuffer);
IPSecWriteEvent( g_ipsec.IPSecDriverObject, ctx->EventCode, ctx->DropStatus.IPSecStatus, ctx->DropStatus.OffloadStatus, ctx->DropStatus.Flags, 0, ctx->pPacket, (USHORT)ctx->PacketSize, 5, IPAddrBufferS, IPAddrBufferD, IPProtocolBuffer, IPSPortBuffer, IPDPortBuffer); } else if (ctx->Addr && ctx->EventCount > 0) { WCHAR IPAddrBuffer[(sizeof(IPAddr) * 4) + 1]; WCHAR CountBuffer[MAX_COUNT_STRING_LEN + 1]; PWCHAR stringList[2];
IPSecIPAddrToUnicodeString( ctx->Addr, IPAddrBuffer); IPSecCountToUnicodeString( ctx->EventCount, CountBuffer); stringList[0] = CountBuffer; stringList[1] = IPAddrBuffer; LOG_EVENT( g_ipsec.IPSecDriverObject, ctx->EventCode, ctx->UniqueEventValue, 2, stringList, 0, NULL); } else if (ctx->Addr) { WCHAR IPAddrBuffer[(sizeof(IPAddr) * 4) + 1]; PWCHAR stringList[1];
IPSecIPAddrToUnicodeString( ctx->Addr, IPAddrBuffer); stringList[0] = IPAddrBuffer; LOG_EVENT( g_ipsec.IPSecDriverObject, ctx->EventCode, ctx->UniqueEventValue, 1, stringList, 0, NULL); } else { LOG_EVENT( g_ipsec.IPSecDriverObject, ctx->EventCode, ctx->UniqueEventValue, 0, NULL, 0, NULL); }
if (ctx->pPacket) { IPSecFreeLogBuffer(ctx->pPacket); ctx->pPacket=NULL; } pLog += sizeof(IPSEC_EVENT_CTX); LogSize += sizeof(IPSEC_EVENT_CTX); }
IPSecFreeMemory(pLogEvent);
IPSEC_DECREMENT(g_ipsec.NumWorkers); }
VOID IPSecBufferEvent( IN IPAddr Addr, IN ULONG EventCode, IN ULONG UniqueEventValue, IN BOOLEAN fBufferEvent ) /*++
Routine Description:
Buffers events in a circular buffer; dumps them to the eventlog when the circular buffer overflows.
Arguments:
Addr - [OPTIONAL] the source IP addr of the offending peer.
EventCode - Identifies the error message.
UniqueEventValue - Identifies this instance of a given error message.
Return Value:
None
--*/ { KIRQL kIrql;
if (!(g_ipsec.DiagnosticMode & IPSEC_DIAGNOSTIC_ENABLE_LOG)) { return; }
ACQUIRE_LOCK(&g_ipsec.EventLogLock, &kIrql);
if (fBufferEvent) { PIPSEC_EVENT_CTX ctx;
g_ipsec.IPSecBufferedEvents++;
ctx = (PIPSEC_EVENT_CTX)g_ipsec.IPSecLogMemoryLoc; ctx--; while (ctx >= (PIPSEC_EVENT_CTX)g_ipsec.IPSecLogMemory) { if (ctx->Addr == Addr && ctx->EventCode == EventCode && ctx->UniqueEventValue == UniqueEventValue) { //
// Found a duplicate; update count and exit.
//
ctx->EventCount++; if (g_ipsec.IPSecBufferedEvents >= g_ipsec.EventQueueSize) { goto logit; }
RELEASE_LOCK(&g_ipsec.EventLogLock, kIrql); return; } ctx--; } }
((PIPSEC_EVENT_CTX)g_ipsec.IPSecLogMemoryLoc)->Addr = Addr; ((PIPSEC_EVENT_CTX)g_ipsec.IPSecLogMemoryLoc)->EventCode = EventCode; ((PIPSEC_EVENT_CTX)g_ipsec.IPSecLogMemoryLoc)->UniqueEventValue = UniqueEventValue;
((PIPSEC_EVENT_CTX)g_ipsec.IPSecLogMemoryLoc)->pPacket=NULL; ((PIPSEC_EVENT_CTX)g_ipsec.IPSecLogMemoryLoc)->PacketSize=0;
if (fBufferEvent) { ((PIPSEC_EVENT_CTX)g_ipsec.IPSecLogMemoryLoc)->EventCount = 1; } else { ((PIPSEC_EVENT_CTX)g_ipsec.IPSecLogMemoryLoc)->EventCount = 0; }
g_ipsec.IPSecLogMemoryLoc += sizeof(IPSEC_EVENT_CTX);
logit: if (!fBufferEvent || g_ipsec.IPSecLogMemoryLoc >= g_ipsec.IPSecLogMemoryEnd || g_ipsec.IPSecBufferedEvents >= g_ipsec.EventQueueSize) { //
// Flush the logs.
//
IPSecQueueLogEvent(); }
RELEASE_LOCK(&g_ipsec.EventLogLock, kIrql); }
NTSTATUS CopyOutboundPacketToBuffer( IN PUCHAR pIPHeader, IN PVOID pData, OUT PUCHAR * pPacket, OUT ULONG * PacketSize ) { PNDIS_BUFFER pTemp; ULONG Length; ULONG dataLength=0; IPHeader UNALIGNED *pIPH; ULONG HeaderLen=0; PUCHAR pBuffer; ULONG CopyPos=0; PUCHAR pPacketData;
pIPH = (IPHeader UNALIGNED *)pIPHeader;
pTemp = (PNDIS_BUFFER)pData;
while (pTemp) { pBuffer = NULL; Length = 0;
NdisQueryBufferSafe(pTemp, &pBuffer, &Length, NormalPagePriority);
if (!pBuffer) { return STATUS_UNSUCCESSFUL; }
dataLength += Length;
pTemp = NDIS_BUFFER_LINKAGE(pTemp); } HeaderLen=(pIPH->iph_verlen & (UCHAR)~IP_VER_FLAG) << 2;
dataLength += HeaderLen;
if (dataLength > IPSEC_LOG_PACKET_SIZE) { dataLength = IPSEC_LOG_PACKET_SIZE; }
if (dataLength < sizeof(IPHeader)) { // doesn't even have a full ip header
return STATUS_UNSUCCESSFUL; } if ((pIPH->iph_protocol == PROTOCOL_TCP) || (pIPH->iph_protocol == PROTOCOL_UDP)) { if (dataLength - HeaderLen < 8) { // not enough room for ports
return STATUS_UNSUCCESSFUL; } }
*pPacket = IPSecAllocateLogBuffer(dataLength); if (! (*pPacket)) { return STATUS_UNSUCCESSFUL; } *PacketSize=dataLength;
pTemp = (PNDIS_BUFFER)pData; CopyPos=0;
while (pTemp && CopyPos < dataLength) { IPSecQueryNdisBuf(pTemp,&pPacketData,&Length); if (CopyPos + Length > dataLength) { Length = (dataLength - CopyPos); } RtlCopyMemory(*pPacket+CopyPos,pPacketData,Length); CopyPos += Length; pTemp = NDIS_BUFFER_LINKAGE(pTemp); }
return STATUS_SUCCESS; }
//
// pData is data after IPHeader, IPRcvBuf.
//
NTSTATUS CopyInboundPacketToBuffer( IN PUCHAR pIPHeader, IN PVOID pData, OUT PUCHAR * pPacket, OUT ULONG * PacketSize ) { IPRcvBuf *pTemp; ULONG Length; ULONG dataLength=0; IPHeader UNALIGNED *pIPH; ULONG HeaderLen=0; PUCHAR pBuffer; ULONG CopyPos=0; PUCHAR pPacketData;
pIPH = (IPHeader UNALIGNED *)pIPHeader;
pTemp = (IPRcvBuf*)pData;
while (pTemp) { pBuffer = NULL; Length = 0;
IPSecQueryRcvBuf(pTemp, &pBuffer, &Length);
if (!pBuffer) { return STATUS_UNSUCCESSFUL; }
dataLength += Length;
pTemp = IPSEC_BUFFER_LINKAGE(pTemp); } HeaderLen=(pIPH->iph_verlen & (UCHAR)~IP_VER_FLAG) << 2;
dataLength += HeaderLen;
if (dataLength > IPSEC_LOG_PACKET_SIZE) { dataLength = IPSEC_LOG_PACKET_SIZE; }
// Sanity check length
if (dataLength < sizeof(IPHeader)) { // doesn't even have a full ip header
return STATUS_UNSUCCESSFUL; } if ((pIPH->iph_protocol == PROTOCOL_TCP) || (pIPH->iph_protocol == PROTOCOL_UDP)) { if (dataLength - HeaderLen < 8) { // not enough room for ports
return STATUS_UNSUCCESSFUL; } }
*pPacket = IPSecAllocateLogBuffer(dataLength); if (! (*pPacket)) { return STATUS_UNSUCCESSFUL; } *PacketSize=dataLength;
pTemp = (IPRcvBuf*)pData;
RtlCopyMemory(*pPacket,pIPH,HeaderLen); CopyPos=HeaderLen;
while (pTemp && CopyPos < dataLength) { IPSecQueryRcvBuf(pTemp,&pPacketData,&Length); if (CopyPos + Length > dataLength) { Length = (dataLength - CopyPos); } RtlCopyMemory(*pPacket+CopyPos,pPacketData,Length); CopyPos += Length; pTemp = IPSEC_BUFFER_LINKAGE(pTemp); }
return STATUS_SUCCESS; }
VOID IPSecBufferPacketDrop( IN PUCHAR pIPHeader, IN PVOID pData, IN OUT PULONG pIpsecFlags, IN PIPSEC_DROP_STATUS pDropStatus ) /*++
Routine Description:
Buffers events in a circular buffer; dumps them to the eventlog when the circular buffer overflows.
Arguments:
EventCode - Identifies the error message.
Return Value:
None
--*/ { KIRQL kIrql; PIPSEC_EVENT_CTX ctx; IPHeader UNALIGNED *pIPH; PNDIS_BUFFER pTemp; PUCHAR pPacket=NULL; ULONG PacketSize=0; ULONG Status; BOOL bLockHeld=FALSE;
pIPH = (IPHeader UNALIGNED *)pIPHeader;
if (*pIpsecFlags & IPSEC_FLAG_INCOMING) { if (!(g_ipsec.DiagnosticMode & IPSEC_DIAGNOSTIC_INBOUND)) { // Don't log
goto out; } Status=CopyInboundPacketToBuffer(pIPHeader, pData, &pPacket, &PacketSize); } else { if (!(g_ipsec.DiagnosticMode & IPSEC_DIAGNOSTIC_OUTBOUND)) { //Don't log
goto out; } Status=CopyOutboundPacketToBuffer(pIPHeader, pData, &pPacket, &PacketSize); } if (Status != STATUS_SUCCESS) { goto out; }
ACQUIRE_LOCK(&g_ipsec.EventLogLock, &kIrql); bLockHeld=TRUE;
g_ipsec.IPSecBufferedEvents++; ctx = (PIPSEC_EVENT_CTX)g_ipsec.IPSecLogMemoryLoc; ctx--;
((PIPSEC_EVENT_CTX)g_ipsec.IPSecLogMemoryLoc)->Addr=pIPH->iph_src;
if (*pIpsecFlags & IPSEC_FLAG_INCOMING) { ((PIPSEC_EVENT_CTX)g_ipsec.IPSecLogMemoryLoc)->EventCode = EVENT_IPSEC_DROP_PACKET_INBOUND; } else { ((PIPSEC_EVENT_CTX)g_ipsec.IPSecLogMemoryLoc)->EventCode = EVENT_IPSEC_DROP_PACKET_OUTBOUND;; } ((PIPSEC_EVENT_CTX)g_ipsec.IPSecLogMemoryLoc)->EventCount = 1;
((PIPSEC_EVENT_CTX)g_ipsec.IPSecLogMemoryLoc)->pPacket = pPacket; ((PIPSEC_EVENT_CTX)g_ipsec.IPSecLogMemoryLoc)->PacketSize = PacketSize; if (pDropStatus) { RtlCopyMemory(&(((PIPSEC_EVENT_CTX)g_ipsec.IPSecLogMemoryLoc)->DropStatus), pDropStatus,sizeof(IPSEC_DROP_STATUS)); } else { RtlZeroMemory(&(((PIPSEC_EVENT_CTX)g_ipsec.IPSecLogMemoryLoc)->DropStatus), sizeof(IPSEC_DROP_STATUS)); }
g_ipsec.IPSecLogMemoryLoc += sizeof(IPSEC_EVENT_CTX);
if (g_ipsec.IPSecLogMemoryLoc >= g_ipsec.IPSecLogMemoryEnd || g_ipsec.IPSecBufferedEvents >= g_ipsec.EventQueueSize) { //
// Flush the logs.
//
IPSecQueueLogEvent(); }
out: if (bLockHeld) { RELEASE_LOCK(&g_ipsec.EventLogLock, kIrql); } }
VOID IPSecQueueLogEvent( VOID ) /*++
Routine Description:
Copies the LogMemory to a temporary buffer and schedule an event to flush logs.
Arguments:
None
Return Value:
None
Notes:
Called with EventLogLock held.
--*/ { PIPSEC_LOG_EVENT pLogEvent; LONG LogSize; PUCHAR pLog;
LogSize = (LONG)(g_ipsec.IPSecLogMemoryLoc - g_ipsec.IPSecLogMemory);
//
// Reset the log memory so we can record again.
//
g_ipsec.IPSecLogMemoryLoc = g_ipsec.IPSecLogMemory; g_ipsec.IPSecBufferedEvents = 0;
if (LogSize <= 0) { ASSERT(FALSE); return; }
pLogEvent = IPSecAllocateMemory(LogSize + FIELD_OFFSET(IPSEC_LOG_EVENT, pLog[0]), IPSEC_TAG_EVT_QUEUE);
if (!pLogEvent) { return; }
pLogEvent->LogSize = LogSize;
pLog = (PUCHAR)pLogEvent + FIELD_OFFSET(IPSEC_LOG_EVENT, pLog[0]); RtlCopyMemory(pLog, g_ipsec.IPSecLogMemory, LogSize);
//
// Queue work item to dump these into the eventlog.
//
ExInitializeWorkItem(&pLogEvent->LogQueueItem, IPSecLogEvents, pLogEvent); ExQueueWorkItem(&pLogEvent->LogQueueItem, DelayedWorkQueue);
IPSEC_INCREMENT(g_ipsec.NumWorkers); }
#if FIPS
BOOLEAN IPSecFipsInitialize( VOID ) /*++
Routine Description:
Initialize the FIPS library table.
Arguments:
Called at PASSIVE level.
Return Value:
TRUE/FALSE.
--*/ { UNICODE_STRING DeviceName; PDEVICE_OBJECT pFipsDeviceObject = NULL; PIRP pIrp; IO_STATUS_BLOCK StatusBlock; KEVENT Event; NTSTATUS status;
PAGED_CODE();
//
// Return success if FIPS already initialized.
//
if (IPSEC_DRIVER_INIT_FIPS()) { return TRUE; }
RtlInitUnicodeString(&DeviceName, FIPS_DEVICE_NAME);
//
// Get the file and device objects for FIPS.
//
status = IoGetDeviceObjectPointer( &DeviceName, FILE_ALL_ACCESS, &g_ipsec.FipsFileObject, &pFipsDeviceObject);
if (!NT_SUCCESS(status)) { g_ipsec.FipsFileObject = NULL; return FALSE; }
//
// Build the request to send to FIPS to get library table.
//
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
pIrp = IoBuildDeviceIoControlRequest( IOCTL_FIPS_GET_FUNCTION_TABLE, pFipsDeviceObject, NULL, 0, &g_ipsec.FipsFunctionTable, sizeof(FIPS_FUNCTION_TABLE), FALSE, &Event, &StatusBlock); if (pIrp == NULL) { IPSEC_DEBUG(LOAD, ("IoBuildDeviceIoControlRequest IOCTL_FIPS_GET_FUNCTION_TABLE failed.\n"));
ObDereferenceObject(g_ipsec.FipsFileObject); g_ipsec.FipsFileObject = NULL;
return FALSE; } status = IoCallDriver(pFipsDeviceObject, pIrp); if (status == STATUS_PENDING) { status = KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL); if (status == STATUS_SUCCESS) { status = StatusBlock.Status; } }
if (status != STATUS_SUCCESS) { IPSEC_DEBUG(LOAD, ("IoCallDriver: IOCTL_FIPS_GET_FUNCTION_TABLE failed %#x\n", status));
ObDereferenceObject(g_ipsec.FipsFileObject); g_ipsec.FipsFileObject = NULL;
return FALSE; } IPSEC_DRIVER_INIT_FIPS() = TRUE;
return TRUE; } #endif
BOOLEAN IPSecCryptoInitialize( VOID ) /*++
Routine Description:
Initialize RNG and FIPS library table.
Arguments:
None
Return Value:
TRUE/FALSE
--*/ { PAGED_CODE();
if (IPSEC_DRIVER_INIT_CRYPTO()) { return TRUE; }
#if FIPS
//
// Init the FIPS crypto library.
//
if (!IPSecFipsInitialize()) { return FALSE; } #endif
//
// Init the RC4 key for RNG.
//
if (!IPSEC_DRIVER_INIT_RNG()) { InitializeRNG(NULL);
if (!IPSecInitRandom()) { ShutdownRNG(NULL); return FALSE; }
IPSEC_DRIVER_INIT_RNG() = TRUE; }
IPSEC_DRIVER_INIT_CRYPTO() = TRUE;
return TRUE; }
BOOLEAN IPSecCryptoDeinitialize( VOID ) /*++
Routine Description:
Deinitialize RNG and dereference FipsFileObject.
Arguments:
None
Return Value:
TRUE/FALSE
--*/ { PAGED_CODE();
//
// Don't forget to shutdown RNG or we will leak memory.
//
if (IPSEC_DRIVER_INIT_RNG()) { ShutdownRNG(NULL); }
#if FIPS
//
// Dereference FipsFileObject.
//
if (g_ipsec.FipsFileObject) { ObDereferenceObject(g_ipsec.FipsFileObject); } #endif
return TRUE; }
NTSTATUS IPSecRegisterProtocols( PIPSEC_REGISTER_PROTOCOL pIpsecRegisterProtocol ) { if (pIpsecRegisterProtocol->RegisterProtocol == IPSEC_REGISTER_PROTOCOLS) { if (!IPSEC_GET_VALUE(gdwInitEsp)) { if (TCPIP_REGISTER_PROTOCOL( PROTOCOL_ESP, NULL, NULL, IPSecESPStatus, NULL, NULL, NULL )) { IPSEC_SET_VALUE(gdwInitEsp, 1); } else { ASSERT(FALSE); return (STATUS_INSUFFICIENT_RESOURCES); } } if (!IPSEC_GET_VALUE(gdwInitAh)) { if (TCPIP_REGISTER_PROTOCOL( PROTOCOL_AH, NULL, NULL, IPSecAHStatus, NULL, NULL, NULL )) { IPSEC_SET_VALUE(gdwInitAh, 1); } else { ASSERT(FALSE); TCPIP_DEREGISTER_PROTOCOL(PROTOCOL_ESP); IPSEC_SET_VALUE(gdwInitEsp, 0); return (STATUS_INSUFFICIENT_RESOURCES); } } } else if (pIpsecRegisterProtocol->RegisterProtocol == IPSEC_DEREGISTER_PROTOCOLS) { if (IPSEC_GET_VALUE(gdwInitEsp)) { TCPIP_DEREGISTER_PROTOCOL(PROTOCOL_ESP); IPSEC_SET_VALUE(gdwInitEsp, 0); } if (IPSEC_GET_VALUE(gdwInitAh)) { TCPIP_DEREGISTER_PROTOCOL(PROTOCOL_AH); IPSEC_SET_VALUE(gdwInitAh, 0); } } else { return (STATUS_INVALID_PARAMETER); }
return (STATUS_SUCCESS); }
|