Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3210 lines
82 KiB

/*++
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);
}