431 lines
10 KiB
C
431 lines
10 KiB
C
/*++
|
||
|
||
Copyright (c) 2000, Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
ipfw.c
|
||
|
||
Abstract:
|
||
|
||
This module implements a driver which demonstrates the use of
|
||
the TCP/IP driver's support for firewall hooks. It interacts with
|
||
a user-mode control-program to support registration of multiple
|
||
firewall routines.
|
||
|
||
Author:
|
||
|
||
Abolade Gbadegesin (aboladeg) 7-March-2000
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include <ndis.h>
|
||
#include <ipfirewall.h>
|
||
#include "ipfw.h"
|
||
|
||
//
|
||
// Structure: IPFW_ROUTINE
|
||
//
|
||
// Used to manage the table of routines registered with TCP/IP.
|
||
//
|
||
|
||
typedef struct _IPFW_ROUTINE {
|
||
IPPacketFirewallPtr Routine;
|
||
UINT Priority;
|
||
ULONG Flags;
|
||
ULONG PacketCount;
|
||
} IPFW_ROUTINE, *PIPFW_ROUTINE;
|
||
|
||
#define IPFW_ROUTINE_FLAG_REGISTERED 0x00000001
|
||
|
||
extern IPFW_ROUTINE IpfwRoutineTable[];
|
||
|
||
#define DEFINE_IPFW_ROUTINE(_Index) \
|
||
FORWARD_ACTION IpfwRoutine##_Index( \
|
||
VOID** Data, \
|
||
UINT ReceiveIndex, \
|
||
UINT* SendIndex, \
|
||
PUCHAR DestinationType, \
|
||
PVOID Context, \
|
||
UINT ContextLength, \
|
||
IPRcvBuf** OutputData \
|
||
) { \
|
||
InterlockedIncrement(&IpfwRoutineTable[_Index].PacketCount); \
|
||
return FORWARD; \
|
||
}
|
||
#define INCLUDE_IPFW_ROUTINE(_Index) \
|
||
{ IpfwRoutine##_Index, 0, 0 },
|
||
|
||
DEFINE_IPFW_ROUTINE(0)
|
||
DEFINE_IPFW_ROUTINE(1)
|
||
DEFINE_IPFW_ROUTINE(2)
|
||
DEFINE_IPFW_ROUTINE(3)
|
||
DEFINE_IPFW_ROUTINE(4)
|
||
DEFINE_IPFW_ROUTINE(5)
|
||
DEFINE_IPFW_ROUTINE(6)
|
||
DEFINE_IPFW_ROUTINE(7)
|
||
DEFINE_IPFW_ROUTINE(8)
|
||
DEFINE_IPFW_ROUTINE(9)
|
||
|
||
IPFW_ROUTINE IpfwRoutineTable[IPFW_ROUTINE_COUNT] = {
|
||
INCLUDE_IPFW_ROUTINE(0)
|
||
INCLUDE_IPFW_ROUTINE(1)
|
||
INCLUDE_IPFW_ROUTINE(2)
|
||
INCLUDE_IPFW_ROUTINE(3)
|
||
INCLUDE_IPFW_ROUTINE(4)
|
||
INCLUDE_IPFW_ROUTINE(5)
|
||
INCLUDE_IPFW_ROUTINE(6)
|
||
INCLUDE_IPFW_ROUTINE(7)
|
||
INCLUDE_IPFW_ROUTINE(8)
|
||
INCLUDE_IPFW_ROUTINE(9)
|
||
};
|
||
KSPIN_LOCK IpfwRoutineLock;
|
||
PDEVICE_OBJECT IpfwDeviceObject = NULL;
|
||
|
||
//
|
||
// FORWARD DECLARATIONS
|
||
//
|
||
|
||
NTSTATUS
|
||
IpfwClose(
|
||
PDEVICE_OBJECT DeviceObject,
|
||
PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
IpfwCreate(
|
||
PDEVICE_OBJECT DeviceObject,
|
||
PIRP Irp
|
||
);
|
||
|
||
VOID
|
||
IpfwUnload(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
);
|
||
|
||
NTSTATUS
|
||
DriverEntry(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine implements the standard driver-entry for an NT driver.
|
||
It is responsible for registering with the TCP/IP driver.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - object to be initialized with NT driver entrypoints
|
||
|
||
RegistryPath - contains path to this driver's registry key
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - indicates success/failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
UNICODE_STRING DeviceName;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
HANDLE ServiceKey;
|
||
NTSTATUS status;
|
||
|
||
KdPrint(("DriverEntry\n"));
|
||
|
||
KeInitializeSpinLock(&IpfwRoutineLock);
|
||
|
||
//
|
||
// Create a device-object on which to communicate with the control program.
|
||
//
|
||
|
||
RtlInitUnicodeString(&DeviceName, DD_IPFW_DEVICE_NAME);
|
||
status =
|
||
IoCreateDevice(
|
||
DriverObject,
|
||
0,
|
||
&DeviceName,
|
||
FILE_DEVICE_NETWORK,
|
||
FILE_DEVICE_SECURE_OPEN,
|
||
FALSE,
|
||
&IpfwDeviceObject
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
KdPrint(("DriverEntry: IoCreateDevice=%08x\n", status));
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Create dispatch points for create/open, cleanup, unload.
|
||
//
|
||
|
||
DriverObject->MajorFunction[IRP_MJ_CREATE] = IpfwCreate;
|
||
DriverObject->MajorFunction[IRP_MJ_CLOSE] = IpfwClose;
|
||
DriverObject->DriverUnload = IpfwUnload;
|
||
|
||
return STATUS_SUCCESS;
|
||
} // DriverEntry
|
||
|
||
|
||
NTSTATUS
|
||
IpfwClose(
|
||
PDEVICE_OBJECT DeviceObject,
|
||
PIRP Irp
|
||
)
|
||
{
|
||
UNICODE_STRING DeviceString;
|
||
KEVENT Event;
|
||
PFILE_OBJECT FileObject;
|
||
ULONG i;
|
||
IO_STATUS_BLOCK IoStatus;
|
||
PDEVICE_OBJECT IpDeviceObject;
|
||
KIRQL Irql;
|
||
PIRP RegisterIrp;
|
||
IP_SET_FIREWALL_HOOK_INFO SetHookInfo;
|
||
NTSTATUS status;
|
||
|
||
KdPrint(("IpfwClose\n"));
|
||
|
||
i = PtrToUlong(IoGetCurrentIrpStackLocation(Irp)->FileObject->FsContext);
|
||
|
||
#if DBG
|
||
KeAcquireSpinLock(&IpfwRoutineLock, &Irql);
|
||
ASSERT(IpfwRoutineTable[i].Flags & IPFW_ROUTINE_FLAG_REGISTERED);
|
||
KeReleaseSpinLock(&IpfwRoutineLock, Irql);
|
||
#endif
|
||
|
||
//
|
||
// Revoke the registration made on behalf of the client whose file-object
|
||
// is being closed.
|
||
// Obtain a pointer to the IP device-object,
|
||
// construct a registration IRP, and attempt to register the routine
|
||
// selected above.
|
||
//
|
||
|
||
RtlInitUnicodeString(&DeviceString, DD_IP_DEVICE_NAME);
|
||
status =
|
||
IoGetDeviceObjectPointer(
|
||
&DeviceString,
|
||
SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE,
|
||
&FileObject,
|
||
&IpDeviceObject
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
ObReferenceObject(IpDeviceObject);
|
||
SetHookInfo.FirewallPtr = IpfwRoutineTable[i].Routine;
|
||
SetHookInfo.Priority = 0; // Unused
|
||
SetHookInfo.Add = FALSE;
|
||
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
|
||
RegisterIrp =
|
||
IoBuildDeviceIoControlRequest(
|
||
IOCTL_IP_SET_FIREWALL_HOOK,
|
||
IpDeviceObject,
|
||
(PVOID)&SetHookInfo,
|
||
sizeof(SetHookInfo),
|
||
NULL,
|
||
0,
|
||
FALSE,
|
||
&Event,
|
||
&IoStatus
|
||
);
|
||
if (!RegisterIrp) {
|
||
status = STATUS_UNSUCCESSFUL;
|
||
} else {
|
||
status = IoCallDriver(IpDeviceObject, RegisterIrp);
|
||
if (status == STATUS_PENDING) {
|
||
KeWaitForSingleObject(
|
||
&Event, Executive, KernelMode, FALSE, NULL
|
||
);
|
||
status = IoStatus.Status;
|
||
}
|
||
|
||
ASSERT(NT_SUCCESS(status));
|
||
}
|
||
|
||
ObDereferenceObject((PVOID)FileObject);
|
||
ObDereferenceObject(IpDeviceObject);
|
||
}
|
||
|
||
//
|
||
// Release the entry in the table of routines.
|
||
//
|
||
|
||
KeAcquireSpinLock(&IpfwRoutineLock, &Irql);
|
||
IpfwRoutineTable[i].Flags &= ~IPFW_ROUTINE_FLAG_REGISTERED;
|
||
KeReleaseSpinLock(&IpfwRoutineLock, Irql);
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
Irp->IoStatus.Information = 0;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return STATUS_SUCCESS;
|
||
} // IpfwClose
|
||
|
||
|
||
NTSTATUS
|
||
IpfwCreate(
|
||
PDEVICE_OBJECT DeviceObject,
|
||
PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked by the I/O manager to inform us that a handle has
|
||
been opened on our device-object.
|
||
--*/
|
||
|
||
{
|
||
PIPFW_CREATE_PACKET CreatePacket;
|
||
UNICODE_STRING DeviceString;
|
||
PFILE_FULL_EA_INFORMATION EaBuffer;
|
||
KEVENT Event;
|
||
PFILE_OBJECT FileObject;
|
||
ULONG i;
|
||
IO_STATUS_BLOCK IoStatus;
|
||
PDEVICE_OBJECT IpDeviceObject;
|
||
KIRQL Irql;
|
||
UINT Priority;
|
||
PIRP RegisterIrp;
|
||
IP_SET_FIREWALL_HOOK_INFO SetHookInfo;
|
||
NTSTATUS status;
|
||
|
||
KdPrint(("IpfwCreate\n"));
|
||
|
||
//
|
||
// Extract the parameters supplied by the caller.
|
||
//
|
||
|
||
if ((EaBuffer = Irp->AssociatedIrp.SystemBuffer) &&
|
||
EaBuffer->EaValueLength >= sizeof(IPFW_CREATE_PACKET)) {
|
||
CreatePacket =
|
||
(PIPFW_CREATE_PACKET)
|
||
(EaBuffer->EaName + EaBuffer->EaNameLength + 1);
|
||
Priority = CreatePacket->Priority;
|
||
} else {
|
||
Priority = 0;
|
||
}
|
||
|
||
//
|
||
// Look for a free entry in the function-table
|
||
//
|
||
|
||
KeAcquireSpinLock(&IpfwRoutineLock, &Irql);
|
||
for (i = 0; i < IPFW_ROUTINE_COUNT; i++) {
|
||
if (!(IpfwRoutineTable[i].Flags & IPFW_ROUTINE_FLAG_REGISTERED)) {
|
||
IpfwRoutineTable[i].Flags |= IPFW_ROUTINE_FLAG_REGISTERED;
|
||
break;
|
||
}
|
||
}
|
||
KeReleaseSpinLock(&IpfwRoutineLock, Irql);
|
||
|
||
if (i >= IPFW_ROUTINE_COUNT) {
|
||
status = STATUS_UNSUCCESSFUL;
|
||
} else {
|
||
|
||
//
|
||
// Obtain a pointer to the IP device-object,
|
||
// construct a registration IRP, and attempt to register the routine
|
||
// selected above.
|
||
//
|
||
|
||
RtlInitUnicodeString(&DeviceString, DD_IP_DEVICE_NAME);
|
||
status =
|
||
IoGetDeviceObjectPointer(
|
||
&DeviceString,
|
||
SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE,
|
||
&FileObject,
|
||
&IpDeviceObject
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
ObReferenceObject(IpDeviceObject);
|
||
SetHookInfo.FirewallPtr = IpfwRoutineTable[i].Routine;
|
||
SetHookInfo.Priority = Priority ? Priority : i + 1;
|
||
SetHookInfo.Add = TRUE;
|
||
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
|
||
RegisterIrp =
|
||
IoBuildDeviceIoControlRequest(
|
||
IOCTL_IP_SET_FIREWALL_HOOK,
|
||
IpDeviceObject,
|
||
(PVOID)&SetHookInfo,
|
||
sizeof(SetHookInfo),
|
||
NULL,
|
||
0,
|
||
FALSE,
|
||
&Event,
|
||
&IoStatus
|
||
);
|
||
if (!RegisterIrp) {
|
||
status = STATUS_UNSUCCESSFUL;
|
||
} else {
|
||
status = IoCallDriver(IpDeviceObject, RegisterIrp);
|
||
if (status == STATUS_PENDING) {
|
||
KeWaitForSingleObject(
|
||
&Event, Executive, KernelMode, FALSE, NULL
|
||
);
|
||
status = IoStatus.Status;
|
||
}
|
||
}
|
||
|
||
ObDereferenceObject((PVOID)FileObject);
|
||
ObDereferenceObject(IpDeviceObject);
|
||
}
|
||
|
||
//
|
||
// If the routine was successfully registered, remember its index
|
||
// in the client's file-object. Otherwise, if the routine could not be
|
||
// registered for any reason, release it.
|
||
//
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
IoGetCurrentIrpStackLocation(Irp)->FileObject->FsContext = UlongToPtr(i);
|
||
} else {
|
||
KeAcquireSpinLock(&IpfwRoutineLock, &Irql);
|
||
IpfwRoutineTable[i].Flags &= ~IPFW_ROUTINE_FLAG_REGISTERED;
|
||
KeReleaseSpinLock(&IpfwRoutineLock, Irql);
|
||
}
|
||
}
|
||
|
||
IoStatus.Status = status;
|
||
IoStatus.Information = 0;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
|
||
return status;
|
||
|
||
} // IpfwCreate
|
||
|
||
|
||
VOID
|
||
IpfwUnload(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked by the I/O manager to unload this driver.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - the object for this driver
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
KdPrint(("IpfwUnload\n"));
|
||
if (IpfwDeviceObject) {
|
||
IoDeleteDevice(IpfwDeviceObject);
|
||
}
|
||
}
|
||
|