Driver: Extract Callouts & Device functions from fortdrv.c

This commit is contained in:
Nodir Temirkhodjaev 2021-03-16 21:36:12 +03:00
parent 034875ac62
commit 65866dedf3
12 changed files with 1195 additions and 1107 deletions

View File

@ -9,7 +9,7 @@ SOURCES += \
HEADERS += \ HEADERS += \
$$PWD/common/common.h \ $$PWD/common/common.h \
$$PWD/common/fortconf.h \ $$PWD/common/fortconf.h \
$$PWD/common/fortdev.h \ $$PWD/common/fortdef.h \
$$PWD/common/fortlog.h \ $$PWD/common/fortlog.h \
$$PWD/common/fortprov.h \ $$PWD/common/fortprov.h \
$$PWD/common/wildmatch.h $$PWD/common/wildmatch.h

View File

@ -6,6 +6,8 @@ SOURCES += \
dummy.c \ dummy.c \
fortbuf.c \ fortbuf.c \
fortcnf.c \ fortcnf.c \
fortcout.c \
fortdev.c \
fortdrv.c \ fortdrv.c \
fortpkt.c \ fortpkt.c \
fortstat.c \ fortstat.c \
@ -21,6 +23,8 @@ SOURCES += \
HEADERS += \ HEADERS += \
fortbuf.h \ fortbuf.h \
fortcnf.h \ fortcnf.h \
fortcout.h \
fortdev.h \
fortdrv.h \ fortdrv.h \
fortpkt.h \ fortpkt.h \
fortstat.h \ fortstat.h \

View File

@ -1,5 +1,5 @@
#ifndef FORTDEV_H #ifndef FORTDEF_H
#define FORTDEV_H #define FORTDEF_H
#include "common.h" #include "common.h"
@ -73,6 +73,8 @@ DEFINE_GUID(FORT_GUID_FILTER_REAUTH_OUT, 0x749709ce, 0x9686, 0x4056, 0xb8, 0x9a,
0xdf, 0xc8, 0x98); 0xdf, 0xc8, 0x98);
#define FORT_DEVICE_NAME "\\\\.\\fortfw" #define FORT_DEVICE_NAME "\\\\.\\fortfw"
#define FORT_NT_DEVICE_NAME L"\\Device\\fortfw"
#define FORT_DOS_DEVICE_NAME L"\\DosDevices\\fortfw"
#define FORT_DEVICE_TYPE 0xD000 #define FORT_DEVICE_TYPE 0xD000
#define FORT_IOCTL_BASE 0xD00 #define FORT_IOCTL_BASE 0xD00
@ -88,4 +90,4 @@ DEFINE_GUID(FORT_GUID_FILTER_REAUTH_OUT, 0x749709ce, 0x9686, 0x4056, 0xb8, 0x9a,
#define FORT_IOCTL_SETZONES FORT_CTL_CODE(6, FILE_WRITE_DATA) #define FORT_IOCTL_SETZONES FORT_CTL_CODE(6, FILE_WRITE_DATA)
#define FORT_IOCTL_SETZONEFLAG FORT_CTL_CODE(7, FILE_WRITE_DATA) #define FORT_IOCTL_SETZONEFLAG FORT_CTL_CODE(7, FILE_WRITE_DATA)
#endif // FORTDEV_H #endif // FORTDEF_H

View File

@ -1,6 +1,6 @@
/* Fort Firewall Driver Provider (Un)Registration */ /* Fort Firewall Driver Provider (Un)Registration */
#include "fortdev.h" #include "fortdef.h"
#include "fortprov.h" #include "fortprov.h"
FORT_API void fort_prov_unregister(HANDLE transEngine) FORT_API void fort_prov_unregister(HANDLE transEngine)

770
src/driver/fortcout.c Normal file
View File

@ -0,0 +1,770 @@
/* Fort Firewall Callouts */
#include "fortcout.h"
#include "common/fortdef.h"
#include "common/fortprov.h"
#include "fortdev.h"
static void fort_callout_classify_block(FWPS_CLASSIFY_OUT0 *classifyOut)
{
classifyOut->actionType = FWP_ACTION_BLOCK;
classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
}
static void fort_callout_classify_drop(FWPS_CLASSIFY_OUT0 *classifyOut)
{
classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
fort_callout_classify_block(classifyOut);
}
static void fort_callout_classify_permit(
const FWPS_FILTER0 *filter, FWPS_CLASSIFY_OUT0 *classifyOut)
{
classifyOut->actionType = FWP_ACTION_PERMIT;
if ((filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)) {
classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
}
}
static void fort_callout_classify_continue(FWPS_CLASSIFY_OUT0 *classifyOut)
{
classifyOut->actionType = FWP_ACTION_CONTINUE;
}
static void fort_callout_classify_v4(const FWPS_INCOMING_VALUES0 *inFixedValues,
const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues, const FWPS_FILTER0 *filter,
FWPS_CLASSIFY_OUT0 *classifyOut, int flagsField, int localIpField, int remoteIpField,
int localPortField, int remotePortField, int ipProtoField, BOOL inbound)
{
PIRP irp = NULL;
ULONG_PTR info;
const UINT32 flags = inFixedValues->incomingValue[flagsField].value.uint32;
const UINT32 remote_ip = inFixedValues->incomingValue[remoteIpField].value.uint32;
if (!fort_device()->conf.conf_flags.filter_locals
&& ((flags & FWP_CONDITION_FLAG_IS_LOOPBACK)
|| remote_ip == 0xFFFFFFFF)) { /* Local broadcast */
fort_callout_classify_permit(filter, classifyOut);
return;
}
PFORT_CONF_REF conf_ref = fort_conf_ref_take(&fort_device()->conf);
if (conf_ref == NULL) {
if (fort_device_flag(&fort_device()->conf, FORT_DEVICE_PROV_BOOT)) {
fort_callout_classify_block(classifyOut);
} else {
fort_callout_classify_continue(classifyOut);
}
return;
}
const FORT_CONF_FLAGS conf_flags = conf_ref->conf.flags;
const UINT32 process_id = (UINT32) inMetaValues->processId;
const UINT32 path_len =
inMetaValues->processPath->size - sizeof(WCHAR); /* chop terminating zero */
const PVOID path = inMetaValues->processPath->data;
UCHAR block_reason = FORT_BLOCK_REASON_UNKNOWN;
BOOL blocked = TRUE;
if (conf_flags.filter_enabled) {
if (conf_flags.stop_traffic)
goto end;
if (!fort_conf_ip_is_inet(&conf_ref->conf,
(fort_conf_zones_ip_included_func *) fort_conf_zones_ip_included,
&fort_device()->conf, remote_ip)) {
blocked = FALSE; /* permit (LAN) */
goto end;
}
if (conf_flags.stop_inet_traffic)
goto end;
if (!fort_conf_ip_inet_included(&conf_ref->conf,
(fort_conf_zones_ip_included_func *) fort_conf_zones_ip_included,
&fort_device()->conf, remote_ip)) {
block_reason = FORT_BLOCK_REASON_IP_INET;
goto end;
}
} else {
blocked = FALSE; /* permit (Filter Disabled) */
if (!(conf_flags.log_stat && conf_flags.log_stat_no_filter))
goto end;
}
FORT_APP_FLAGS app_flags =
fort_conf_app_find(&conf_ref->conf, path, path_len, fort_conf_exe_find);
if (!blocked /* collect traffic, when Filter Disabled */
|| (app_flags.v == 0 && conf_flags.allow_all_new) /* collect new Blocked Programs */
|| !fort_conf_app_blocked(&conf_ref->conf, app_flags, &block_reason)) {
if (conf_flags.log_stat) {
const UINT64 flow_id = inMetaValues->flowHandle;
const IPPROTO ip_proto =
(IPPROTO) inFixedValues->incomingValue[ipProtoField].value.uint8;
const BOOL is_tcp = (ip_proto == IPPROTO_TCP);
const UCHAR group_index = app_flags.group_index;
const BOOL is_reauth = (flags & FWP_CONDITION_FLAG_IS_REAUTHORIZE);
BOOL is_new_proc = FALSE;
NTSTATUS status;
status = fort_flow_associate(&fort_device()->stat, flow_id, process_id, group_index,
is_tcp, is_reauth, &is_new_proc);
if (!NT_SUCCESS(status)) {
if (status == FORT_STATUS_FLOW_BLOCK) {
block_reason = FORT_BLOCK_REASON_REAUTH;
blocked = TRUE;
goto end;
}
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Classify v4: Flow assoc. error: %x\n", status);
} else if (is_new_proc) {
fort_buffer_proc_new_write(
&fort_device()->buffer, process_id, path_len, path, &irp, &info);
}
}
blocked = FALSE; /* permit */
}
if (app_flags.v == 0 && (conf_flags.allow_all_new || conf_flags.log_blocked)
&& conf_flags.filter_enabled) {
app_flags.blocked = (UCHAR) blocked;
app_flags.alerted = 1;
app_flags.is_new = 1;
if (NT_SUCCESS(fort_conf_ref_exe_add_path(conf_ref, path, path_len, app_flags))) {
fort_buffer_blocked_write(
&fort_device()->buffer, blocked, process_id, path_len, path, &irp, &info);
}
}
end:
if (blocked) {
/* Log the blocked connection */
if (block_reason != FORT_BLOCK_REASON_UNKNOWN && conf_flags.log_blocked_ip) {
const UINT32 local_ip = inFixedValues->incomingValue[localIpField].value.uint32;
const UINT16 local_port = inFixedValues->incomingValue[localPortField].value.uint16;
const UINT16 remote_port = inFixedValues->incomingValue[remotePortField].value.uint16;
const IPPROTO ip_proto =
(IPPROTO) inFixedValues->incomingValue[ipProtoField].value.uint8;
fort_buffer_blocked_ip_write(&fort_device()->buffer, inbound, block_reason, ip_proto,
local_port, remote_port, local_ip, remote_ip, process_id, path_len, path, &irp,
&info);
}
/* Block the connection */
fort_callout_classify_block(classifyOut);
} else {
/* Allow the connection */
fort_callout_classify_permit(filter, classifyOut);
}
fort_conf_ref_put(&fort_device()->conf, conf_ref);
if (irp != NULL) {
fort_request_complete_info(irp, STATUS_SUCCESS, info);
}
}
static void NTAPI fort_callout_connect_v4(const FWPS_INCOMING_VALUES0 *inFixedValues,
const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues, void *layerData,
const FWPS_FILTER0 *filter, const UINT64 flowContext, FWPS_CLASSIFY_OUT0 *classifyOut)
{
UNUSED(layerData);
UNUSED(flowContext);
fort_callout_classify_v4(inFixedValues, inMetaValues, filter, classifyOut,
FWPS_FIELD_ALE_AUTH_CONNECT_V4_FLAGS, FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS,
FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS,
FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT,
FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT,
FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL, FALSE);
}
static void NTAPI fort_callout_accept_v4(const FWPS_INCOMING_VALUES0 *inFixedValues,
const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues, void *layerData,
const FWPS_FILTER0 *filter, const UINT64 flowContext, FWPS_CLASSIFY_OUT0 *classifyOut)
{
UNUSED(layerData);
UNUSED(flowContext);
fort_callout_classify_v4(inFixedValues, inMetaValues, filter, classifyOut,
FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_FLAGS,
FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_ADDRESS,
FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_REMOTE_ADDRESS,
FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_PORT,
FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_REMOTE_PORT,
FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_PROTOCOL, TRUE);
}
static NTSTATUS NTAPI fort_callout_notify(
FWPS_CALLOUT_NOTIFY_TYPE notifyType, const GUID *filterKey, const FWPS_FILTER0 *filter)
{
UNUSED(notifyType);
UNUSED(filterKey);
UNUSED(filter);
return STATUS_SUCCESS;
}
static void NTAPI fort_packet_inject_complete(
PFORT_PACKET pkt, PNET_BUFFER_LIST clonedNetBufList, BOOLEAN dispatchLevel)
{
fort_defer_packet_free(&fort_device()->defer, pkt, clonedNetBufList, dispatchLevel);
}
static void fort_callout_defer_packet_flush(UINT32 list_bits, BOOL dispatchLevel)
{
fort_defer_packet_flush(
&fort_device()->defer, fort_packet_inject_complete, list_bits, dispatchLevel);
}
static void fort_callout_defer_stream_flush(UINT64 flow_id, BOOL dispatchLevel)
{
UNUSED(dispatchLevel);
fort_defer_stream_flush(&fort_device()->defer, fort_packet_inject_complete, flow_id, FALSE);
}
FORT_API void fort_callout_defer_flush(void)
{
fort_callout_defer_packet_flush(FORT_DEFER_FLUSH_ALL, FALSE);
fort_callout_defer_stream_flush(FORT_DEFER_STREAM_ALL, FALSE);
}
static void fort_callout_flow_classify_v4(const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues,
UINT64 flowContext, FWPS_CLASSIFY_OUT0 *classifyOut, UINT32 dataSize, BOOL is_tcp,
BOOL inbound)
{
const UINT32 headerSize = inbound ? inMetaValues->transportHeaderSize : 0;
UNUSED(classifyOut);
fort_flow_classify(&fort_device()->stat, flowContext, headerSize + dataSize, is_tcp, inbound);
}
static void NTAPI fort_callout_stream_classify_v4(const FWPS_INCOMING_VALUES0 *inFixedValues,
const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues, FWPS_STREAM_CALLOUT_IO_PACKET0 *packet,
const FWPS_FILTER0 *filter, UINT64 flowContext, FWPS_CLASSIFY_OUT0 *classifyOut)
{
const FWPS_STREAM_DATA0 *streamData = packet->streamData;
const UINT32 streamFlags = streamData->flags;
const UINT32 dataSize = (UINT32) streamData->dataLength;
const BOOL inbound = (streamFlags & FWPS_STREAM_FLAG_RECEIVE) != 0;
UNUSED(inFixedValues);
fort_callout_flow_classify_v4(inMetaValues, flowContext, classifyOut, dataSize, TRUE, inbound);
/* Flush flow's deferred TCP packets on FIN */
#if 0
if (streamFlags & (FWPS_STREAM_FLAG_RECEIVE_DISCONNECT
| FWPS_STREAM_FLAG_SEND_DISCONNECT)) {
PFORT_FLOW flow = (PFORT_FLOW) flowContext;
const UCHAR flow_flags = fort_flow_flags(flow);
if (flow_flags & FORT_FLOW_SPEED_LIMIT) {
fort_callout_defer_packet_flush(flow->flow_id, FORT_DEFER_FLUSH_ALL, FALSE);
}
if (flow_flags & FORT_FLOW_FRAGMENT) {
fort_callout_defer_stream_flush(flow->flow_id, FALSE);
}
goto permit;
}
#endif
/* Fragment first TCP packet */
if ((streamFlags
& (FWPS_STREAM_FLAG_SEND | FWPS_STREAM_FLAG_SEND_EXPEDITED
| FWPS_STREAM_FLAG_SEND_DISCONNECT))
== FWPS_STREAM_FLAG_SEND) {
PFORT_FLOW flow = (PFORT_FLOW) flowContext;
const UCHAR flow_flags = fort_flow_flags(flow);
const UCHAR fragment_flags = (flow_flags
& (FORT_FLOW_FRAGMENT | FORT_FLOW_FRAGMENT_DEFER | FORT_FLOW_FRAGMENTED));
if (fragment_flags != 0 && !(fragment_flags & FORT_FLOW_FRAGMENTED)) {
const UCHAR fragment_size = 3;
if (fragment_flags & FORT_FLOW_FRAGMENT_DEFER) {
const NTSTATUS status = fort_defer_stream_add(&fort_device()->defer, inFixedValues,
inMetaValues, streamData, filter, inbound);
if (NT_SUCCESS(status))
goto drop;
fort_flow_flags_set(flow, FORT_FLOW_FRAGMENTED, TRUE);
} else if (dataSize > fragment_size) {
packet->countBytesEnforced = fragment_size;
fort_flow_flags_set(flow, FORT_FLOW_FRAGMENT_DEFER, TRUE);
}
}
}
/* permit: */
fort_callout_classify_permit(filter, classifyOut);
return;
drop:
fort_callout_classify_drop(classifyOut);
return;
}
static void NTAPI fort_callout_datagram_classify_v4(const FWPS_INCOMING_VALUES0 *inFixedValues,
const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues, const PNET_BUFFER_LIST netBufList,
const FWPS_FILTER0 *filter, UINT64 flowContext, FWPS_CLASSIFY_OUT0 *classifyOut)
{
const PNET_BUFFER netBuf = NET_BUFFER_LIST_FIRST_NB(netBufList);
const UINT32 dataSize = NET_BUFFER_DATA_LENGTH(netBuf);
const FWP_DIRECTION direction =
(FWP_DIRECTION) inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_DIRECTION]
.value.uint8;
const BOOL inbound = (direction == FWP_DIRECTION_INBOUND);
fort_callout_flow_classify_v4(inMetaValues, flowContext, classifyOut, dataSize, FALSE, inbound);
fort_callout_classify_permit(filter, classifyOut);
}
static void NTAPI fort_callout_flow_delete_v4(UINT16 layerId, UINT32 calloutId, UINT64 flowContext)
{
UNUSED(layerId);
UNUSED(calloutId);
fort_flow_delete(&fort_device()->stat, flowContext);
}
static void NTAPI fort_callout_transport_classify_v4(const FWPS_INCOMING_VALUES0 *inFixedValues,
const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues, PNET_BUFFER_LIST netBufList,
const FWPS_FILTER0 *filter, UINT64 flowContext, FWPS_CLASSIFY_OUT0 *classifyOut,
BOOL inbound)
{
PNET_BUFFER netBuf;
if (!FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED)
&& netBufList != NULL && (netBuf = NET_BUFFER_LIST_FIRST_NB(netBufList)) != NULL) {
PFORT_FLOW flow = (PFORT_FLOW) flowContext;
const UCHAR flow_flags = fort_flow_flags(flow);
const UCHAR defer_flag = inbound ? FORT_FLOW_DEFER_IN : FORT_FLOW_DEFER_OUT;
const UCHAR speed_limit = inbound ? FORT_FLOW_SPEED_LIMIT_OUT : FORT_FLOW_SPEED_LIMIT_IN;
const UCHAR speed_defer_flags = speed_limit | defer_flag;
const BOOL defer_flow = (flow_flags & speed_defer_flags) == speed_defer_flags
&& !fort_device_flag(&fort_device()->conf, FORT_DEVICE_POWER_OFF);
const BOOL fragment_packet = !inbound
&& (flow_flags & (FORT_FLOW_FRAGMENT_DEFER | FORT_FLOW_FRAGMENTED))
== FORT_FLOW_FRAGMENT_DEFER;
/* Position in the packet data:
* FWPS_LAYER_INBOUND_TRANSPORT_V4: The beginning of the data.
* FWPS_LAYER_OUTBOUND_TRANSPORT_V4: The beginning of the transport header.
*/
const UINT32 headerOffset = inbound ? 0 : sizeof(TCP_HEADER);
/* Ignore TCP RST-packets */
#if 0
const BOOL ignore_tcp_rst = inbound && fort_device()->conf_flags.ignore_tcp_rst;
if (ignore_tcp_rst) {
TCP_HEADER buf;
PTCP_HEADER tcpHeader;
UINT32 tcpFlags;
if (headerOffset != 0) {
NdisRetreatNetBufferDataStart(netBuf, headerOffset, 0, NULL);
}
tcpHeader = NdisGetDataBuffer(netBuf, sizeof(TCP_HEADER), &buf, 1, 0);
tcpFlags = tcpHeader ? tcpHeader->flags : 0;
if (headerOffset != 0) {
NdisAdvanceNetBufferDataStart(netBuf, headerOffset, FALSE, NULL);
}
if (tcpHeader == NULL)
goto permit;
if (tcpFlags & TCP_FLAG_RST)
goto block;
}
#endif
/* Defer TCP Pure (zero length) ACK-packets */
if (defer_flow && NET_BUFFER_DATA_LENGTH(netBuf) == headerOffset) {
const NTSTATUS status = fort_defer_packet_add(&fort_device()->defer, inFixedValues,
inMetaValues, netBufList, inbound, flow->opt.group_index);
if (NT_SUCCESS(status))
goto drop;
if (status == STATUS_CANT_TERMINATE_SELF) {
/* Clear ACK deferring */
fort_flow_flags_set(flow, defer_flag, FALSE);
}
goto permit;
}
/* Fragment first TCP packet */
if (fragment_packet) {
fort_defer_stream_flush(
&fort_device()->defer, fort_packet_inject_complete, flow->flow_id, FALSE);
fort_flow_flags_set(flow, FORT_FLOW_FRAGMENTED, TRUE);
}
}
permit:
fort_callout_classify_permit(filter, classifyOut);
return;
drop:
fort_callout_classify_drop(classifyOut);
return;
}
static void NTAPI fort_callout_in_transport_classify_v4(const FWPS_INCOMING_VALUES0 *inFixedValues,
const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues, const PNET_BUFFER_LIST netBufList,
const FWPS_FILTER0 *filter, UINT64 flowContext, FWPS_CLASSIFY_OUT0 *classifyOut)
{
fort_callout_transport_classify_v4(
inFixedValues, inMetaValues, netBufList, filter, flowContext, classifyOut, TRUE);
}
static void NTAPI fort_callout_out_transport_classify_v4(const FWPS_INCOMING_VALUES0 *inFixedValues,
const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues, const PNET_BUFFER_LIST netBufList,
const FWPS_FILTER0 *filter, UINT64 flowContext, FWPS_CLASSIFY_OUT0 *classifyOut)
{
fort_callout_transport_classify_v4(
inFixedValues, inMetaValues, netBufList, filter, flowContext, classifyOut, FALSE);
}
static void NTAPI fort_callout_delete_v4(UINT16 layerId, UINT32 calloutId, UINT64 flowContext)
{
UNUSED(layerId);
UNUSED(calloutId);
UNUSED(flowContext);
}
FORT_API NTSTATUS fort_callout_install(PDEVICE_OBJECT device)
{
FWPS_CALLOUT0 c;
NTSTATUS status;
RtlZeroMemory(&c, sizeof(FWPS_CALLOUT0));
c.notifyFn = (FWPS_CALLOUT_NOTIFY_FN0) fort_callout_notify;
/* IPv4 connect callout */
c.calloutKey = FORT_GUID_CALLOUT_CONNECT_V4;
c.classifyFn = (FWPS_CALLOUT_CLASSIFY_FN0) fort_callout_connect_v4;
status = FwpsCalloutRegister0(device, &c, &fort_device()->connect4_id);
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Register Connect V4: Error: %x\n", status);
return status;
}
/* IPv4 accept callout */
c.calloutKey = FORT_GUID_CALLOUT_ACCEPT_V4;
c.classifyFn = (FWPS_CALLOUT_CLASSIFY_FN0) fort_callout_accept_v4;
status = FwpsCalloutRegister0(device, &c, &fort_device()->accept4_id);
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Register Accept V4: Error: %x\n", status);
return status;
}
/* IPv4 stream callout */
c.calloutKey = FORT_GUID_CALLOUT_STREAM_V4;
c.classifyFn = (FWPS_CALLOUT_CLASSIFY_FN0) fort_callout_stream_classify_v4;
c.flowDeleteFn = fort_callout_flow_delete_v4;
c.flags = FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW;
status = FwpsCalloutRegister0(device, &c, &fort_device()->stat.stream4_id);
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Register Stream V4: Error: %x\n", status);
return status;
}
/* IPv4 datagram callout */
c.calloutKey = FORT_GUID_CALLOUT_DATAGRAM_V4;
c.classifyFn = (FWPS_CALLOUT_CLASSIFY_FN0) fort_callout_datagram_classify_v4;
/* reuse c.flowDeleteFn & c.flags */
status = FwpsCalloutRegister0(device, &c, &fort_device()->stat.datagram4_id);
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Register Datagram V4: Error: %x\n", status);
return status;
}
/* IPv4 inbound transport callout */
c.calloutKey = FORT_GUID_CALLOUT_IN_TRANSPORT_V4;
c.classifyFn = (FWPS_CALLOUT_CLASSIFY_FN0) fort_callout_in_transport_classify_v4;
c.flowDeleteFn = fort_callout_delete_v4;
/* reuse c.flags */
status = FwpsCalloutRegister0(device, &c, &fort_device()->stat.in_transport4_id);
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Register Inbound Transport V4: Error: %x\n", status);
return status;
}
/* IPv4 outbound transport callout */
c.calloutKey = FORT_GUID_CALLOUT_OUT_TRANSPORT_V4;
c.classifyFn = (FWPS_CALLOUT_CLASSIFY_FN0) fort_callout_out_transport_classify_v4;
/* reuse c.flowDeleteFn & c.flags */
status = FwpsCalloutRegister0(device, &c, &fort_device()->stat.out_transport4_id);
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Register Outbound Transport V4: Error: %x\n", status);
return status;
}
return STATUS_SUCCESS;
}
FORT_API void fort_callout_remove(void)
{
if (fort_device()->connect4_id) {
FwpsCalloutUnregisterById0(fort_device()->connect4_id);
fort_device()->connect4_id = 0;
}
if (fort_device()->accept4_id) {
FwpsCalloutUnregisterById0(fort_device()->accept4_id);
fort_device()->accept4_id = 0;
}
if (fort_device()->stat.stream4_id) {
FwpsCalloutUnregisterById0(fort_device()->stat.stream4_id);
fort_device()->stat.stream4_id = 0;
}
if (fort_device()->stat.datagram4_id) {
FwpsCalloutUnregisterById0(fort_device()->stat.datagram4_id);
fort_device()->stat.datagram4_id = 0;
}
if (fort_device()->stat.in_transport4_id) {
FwpsCalloutUnregisterById0(fort_device()->stat.in_transport4_id);
fort_device()->stat.in_transport4_id = 0;
}
if (fort_device()->stat.out_transport4_id) {
FwpsCalloutUnregisterById0(fort_device()->stat.out_transport4_id);
fort_device()->stat.out_transport4_id = 0;
}
}
FORT_API NTSTATUS fort_callout_force_reauth(
const FORT_CONF_FLAGS old_conf_flags, UINT32 defer_flush_bits)
{
PFORT_STAT stat = &fort_device()->stat;
NTSTATUS status;
fort_timer_update(&fort_device()->log_timer, FALSE);
/* Check app group periods & update group_bits */
{
int periods_n = 0;
fort_conf_ref_period_update(&fort_device()->conf, TRUE, &periods_n);
fort_timer_update(&fort_device()->app_timer, (periods_n != 0));
}
const FORT_CONF_FLAGS conf_flags = fort_device()->conf.conf_flags;
/* Handle log_stat */
if (old_conf_flags.log_stat != conf_flags.log_stat) {
fort_stat_update(stat, conf_flags.log_stat);
if (!conf_flags.log_stat) {
defer_flush_bits = FORT_DEFER_FLUSH_ALL;
}
}
if (defer_flush_bits != 0) {
fort_callout_defer_packet_flush(defer_flush_bits, FALSE);
}
/* Open provider */
HANDLE engine;
if ((status = fort_prov_open(&engine)))
goto end;
fort_prov_trans_begin(engine);
/* Check provider filters */
BOOL prov_recreated = FALSE;
if (old_conf_flags.prov_boot != conf_flags.prov_boot) {
fort_prov_unregister(engine);
if ((status = fort_prov_register(engine, conf_flags.prov_boot)))
goto cleanup;
prov_recreated = TRUE;
}
/* Check flow filter */
{
const PFORT_CONF_GROUP conf_group = &stat->conf_group;
const UINT16 filter_bits = (conf_group->fragment_bits | conf_group->limit_bits);
const BOOL old_filter_transport =
fort_device_flag(&fort_device()->conf, FORT_DEVICE_FILTER_TRANSPORT) != 0;
const BOOL filter_transport = (conf_flags.group_bits & filter_bits) != 0;
if (prov_recreated || old_conf_flags.log_stat != conf_flags.log_stat
|| old_filter_transport != filter_transport) {
fort_device_flag_set(
&fort_device()->conf, FORT_DEVICE_FILTER_TRANSPORT, filter_transport);
fort_prov_flow_unregister(engine);
if (conf_flags.log_stat) {
if ((status = fort_prov_flow_register(engine, filter_transport)))
goto cleanup;
}
}
}
/* Force reauth filter */
if ((status = fort_prov_reauth(engine)))
goto cleanup;
fort_timer_update(&fort_device()->log_timer,
(conf_flags.allow_all_new || conf_flags.log_blocked || conf_flags.log_stat
|| conf_flags.log_blocked_ip));
cleanup:
if (NT_SUCCESS(status)) {
status = fort_prov_trans_commit(engine);
} else {
fort_prov_trans_abort(engine);
}
fort_prov_close(engine);
end:
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL, "FORT: Callout Reauth: Error: %x\n",
status);
}
return status;
}
FORT_API void fort_callout_timer(void)
{
PFORT_BUFFER buf = &fort_device()->buffer;
PFORT_STAT stat = &fort_device()->stat;
KLOCK_QUEUE_HANDLE buf_lock_queue;
KLOCK_QUEUE_HANDLE stat_lock_queue;
PIRP irp = NULL;
ULONG_PTR info;
/* Lock buffer */
fort_buffer_dpc_begin(buf, &buf_lock_queue);
/* Lock stat */
fort_stat_dpc_begin(stat, &stat_lock_queue);
/* Get current Unix time */
{
LARGE_INTEGER system_time;
PCHAR out;
KeQuerySystemTime(&system_time);
if (stat->system_time.QuadPart != system_time.QuadPart
&& NT_SUCCESS(fort_buffer_prepare(buf, FORT_LOG_TIME_SIZE, &out, &irp, &info))) {
const INT64 unix_time = fort_system_to_unix_time(system_time.QuadPart);
stat->system_time = system_time;
fort_log_time_write(out, unix_time);
}
}
/* Flush traffic statistics */
while (stat->proc_active_count != 0) {
const UINT16 proc_count = (stat->proc_active_count < FORT_LOG_STAT_BUFFER_PROC_COUNT)
? stat->proc_active_count
: FORT_LOG_STAT_BUFFER_PROC_COUNT;
const UINT32 len = FORT_LOG_STAT_SIZE(proc_count);
PCHAR out;
NTSTATUS status;
status = fort_buffer_prepare(buf, len, &out, &irp, &info);
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL, "FORT: Callout Timer: Error: %x\n",
status);
break;
}
fort_log_stat_traf_header_write(out, proc_count);
out += FORT_LOG_STAT_HEADER_SIZE;
fort_stat_dpc_traf_flush(stat, proc_count, out);
}
/* Flush process group statistics */
const UINT32 defer_flush_bits = fort_stat_dpc_group_flush(stat);
/* Unlock stat */
fort_stat_dpc_end(&stat_lock_queue);
/* Flush pending buffer */
if (irp == NULL) {
fort_buffer_dpc_flush_pending(buf, &irp, &info);
}
/* Unlock buffer */
fort_buffer_dpc_end(&buf_lock_queue);
if (irp != NULL) {
fort_request_complete_info(irp, STATUS_SUCCESS, info);
}
/* Flush deferred packets */
fort_callout_defer_packet_flush(defer_flush_bits, TRUE);
}

27
src/driver/fortcout.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef FORTCOUT_H
#define FORTCOUT_H
#include "fortdrv.h"
#include "common/fortconf.h"
#if defined(__cplusplus)
extern "C" {
#endif
FORT_API void fort_callout_defer_flush(void);
FORT_API NTSTATUS fort_callout_install(PDEVICE_OBJECT device);
FORT_API void fort_callout_remove(void);
FORT_API NTSTATUS fort_callout_force_reauth(
const FORT_CONF_FLAGS old_conf_flags, UINT32 defer_flush_bits);
FORT_API void fort_callout_timer(void);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // FORTCOUT_H

276
src/driver/fortdev.c Normal file
View File

@ -0,0 +1,276 @@
/* Fort Firewall Driver Device */
#include "../version/fort_version.h"
#include "fortdev.h"
#include "common/fortdef.h"
#include "fortcout.h"
static PFORT_DEVICE g_device = NULL;
FORT_API PFORT_DEVICE fort_device()
{
return g_device;
}
FORT_API void fort_device_set(PFORT_DEVICE device)
{
g_device = device;
}
static void fort_worker_reauth(void)
{
const FORT_CONF_FLAGS conf_flags = g_device->conf.conf_flags;
NTSTATUS status;
status = fort_callout_force_reauth(conf_flags, 0);
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL, "FORT: Worker Reauth: Error: %x\n",
status);
}
}
FORT_API void fort_app_period_timer(void)
{
if (fort_conf_ref_period_update(&g_device->conf, FALSE, NULL)) {
fort_worker_queue(&g_device->worker, FORT_WORKER_REAUTH, &fort_worker_reauth);
}
}
FORT_API NTSTATUS fort_device_create(PDEVICE_OBJECT device, PIRP irp)
{
NTSTATUS status = STATUS_SUCCESS;
UNUSED(device);
/* Device opened */
if (fort_device_flag_set(&g_device->conf, FORT_DEVICE_IS_OPENED, TRUE)
& FORT_DEVICE_IS_OPENED) {
status = STATUS_SHARING_VIOLATION; /* Only one client may connect */
}
if (NT_SUCCESS(status)) {
/* Clear buffer */
fort_buffer_clear(&g_device->buffer);
}
fort_request_complete(irp, status);
return status;
}
FORT_API NTSTATUS fort_device_close(PDEVICE_OBJECT device, PIRP irp)
{
UNUSED(device);
fort_request_complete(irp, STATUS_SUCCESS);
return STATUS_SUCCESS;
}
FORT_API NTSTATUS fort_device_cleanup(PDEVICE_OBJECT device, PIRP irp)
{
UNUSED(device);
/* Device closed */
fort_device_flag_set(
&g_device->conf, (FORT_DEVICE_IS_OPENED | FORT_DEVICE_IS_VALIDATED), FALSE);
/* Clear conf */
{
const FORT_CONF_FLAGS old_conf_flags = fort_conf_ref_set(&g_device->conf, NULL);
fort_conf_zones_set(&g_device->conf, NULL);
fort_callout_force_reauth(old_conf_flags, FORT_DEFER_FLUSH_ALL);
}
/* Clear buffer */
fort_buffer_clear(&g_device->buffer);
fort_request_complete(irp, STATUS_SUCCESS);
return STATUS_SUCCESS;
}
static void fort_device_cancel_pending(PDEVICE_OBJECT device, PIRP irp)
{
ULONG_PTR info;
NTSTATUS status;
UNUSED(device);
status = fort_buffer_cancel_pending(&g_device->buffer, irp, &info);
IoReleaseCancelSpinLock(irp->CancelIrql); /* before IoCompleteRequest()! */
fort_request_complete_info(irp, status, info);
}
FORT_API NTSTATUS fort_device_control(PDEVICE_OBJECT device, PIRP irp)
{
ULONG_PTR info = 0;
NTSTATUS status = STATUS_UNSUCCESSFUL;
UNUSED(device);
const PIO_STACK_LOCATION irp_stack = IoGetCurrentIrpStackLocation(irp);
const ULONG control_code = irp_stack->Parameters.DeviceIoControl.IoControlCode;
if (control_code != (ULONG) FORT_IOCTL_VALIDATE
&& !fort_device_flag(&g_device->conf, FORT_DEVICE_IS_VALIDATED))
goto end;
switch (control_code) {
case FORT_IOCTL_VALIDATE: {
const PFORT_CONF_VERSION conf_ver = irp->AssociatedIrp.SystemBuffer;
const ULONG len = irp_stack->Parameters.DeviceIoControl.InputBufferLength;
if (len == sizeof(FORT_CONF_VERSION)) {
if (conf_ver->driver_version == DRIVER_VERSION) {
fort_device_flag_set(&g_device->conf, FORT_DEVICE_IS_VALIDATED, TRUE);
status = STATUS_SUCCESS;
}
}
break;
}
case FORT_IOCTL_SETCONF: {
const PFORT_CONF_IO conf_io = irp->AssociatedIrp.SystemBuffer;
const ULONG len = irp_stack->Parameters.DeviceIoControl.InputBufferLength;
if (len > sizeof(FORT_CONF_IO)) {
const PFORT_CONF conf = &conf_io->conf;
PFORT_CONF_REF conf_ref = fort_conf_ref_new(conf, len - FORT_CONF_IO_CONF_OFF);
if (conf_ref == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
} else {
PFORT_STAT stat = &g_device->stat;
const FORT_CONF_FLAGS old_conf_flags = fort_conf_ref_set(&g_device->conf, conf_ref);
const UINT32 defer_flush_bits =
(stat->conf_group.limit_2bits ^ conf_io->conf_group.limit_2bits);
fort_stat_conf_update(stat, conf_io);
status = fort_callout_force_reauth(old_conf_flags, defer_flush_bits);
}
}
break;
}
case FORT_IOCTL_SETFLAGS: {
const PFORT_CONF_FLAGS conf_flags = irp->AssociatedIrp.SystemBuffer;
const ULONG len = irp_stack->Parameters.DeviceIoControl.InputBufferLength;
if (len == sizeof(FORT_CONF_FLAGS)) {
const FORT_CONF_FLAGS old_conf_flags =
fort_conf_ref_flags_set(&g_device->conf, conf_flags);
const UINT32 defer_flush_bits =
(old_conf_flags.group_bits != conf_flags->group_bits ? FORT_DEFER_FLUSH_ALL
: 0);
status = fort_callout_force_reauth(old_conf_flags, defer_flush_bits);
}
break;
}
case FORT_IOCTL_GETLOG: {
PVOID out = irp->AssociatedIrp.SystemBuffer;
const ULONG out_len = irp_stack->Parameters.DeviceIoControl.OutputBufferLength;
if (out_len < FORT_BUFFER_SIZE) {
status = STATUS_BUFFER_TOO_SMALL;
} else {
status = fort_buffer_xmove(&g_device->buffer, irp, out, out_len, &info);
if (status == STATUS_PENDING) {
KIRQL cirq;
IoMarkIrpPending(irp);
IoAcquireCancelSpinLock(&cirq);
IoSetCancelRoutine(irp, fort_device_cancel_pending);
IoReleaseCancelSpinLock(cirq);
return STATUS_PENDING;
}
}
break;
}
case FORT_IOCTL_ADDAPP:
case FORT_IOCTL_DELAPP: {
const PFORT_APP_ENTRY app_entry = irp->AssociatedIrp.SystemBuffer;
const ULONG len = irp_stack->Parameters.DeviceIoControl.InputBufferLength;
if (len > sizeof(FORT_APP_ENTRY) && len >= (sizeof(FORT_APP_ENTRY) + app_entry->path_len)) {
PFORT_CONF_REF conf_ref = fort_conf_ref_take(&g_device->conf);
if (conf_ref == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
} else {
if (control_code == (ULONG) FORT_IOCTL_ADDAPP) {
status = fort_conf_ref_exe_add_entry(conf_ref, app_entry, FALSE);
} else {
fort_conf_ref_exe_del_entry(conf_ref, app_entry);
status = STATUS_SUCCESS;
}
fort_conf_ref_put(&g_device->conf, conf_ref);
if (NT_SUCCESS(status)) {
fort_worker_reauth();
}
}
}
break;
}
case FORT_IOCTL_SETZONES: {
const PFORT_CONF_ZONES zones = irp->AssociatedIrp.SystemBuffer;
const ULONG len = irp_stack->Parameters.DeviceIoControl.InputBufferLength;
if (len >= FORT_CONF_ZONES_DATA_OFF) {
PFORT_CONF_ZONES conf_zones = fort_conf_zones_new(zones, len);
if (conf_zones == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
} else {
fort_conf_zones_set(&g_device->conf, conf_zones);
fort_worker_reauth();
status = STATUS_SUCCESS;
}
}
break;
}
case FORT_IOCTL_SETZONEFLAG: {
const PFORT_CONF_ZONE_FLAG zone_flag = irp->AssociatedIrp.SystemBuffer;
const ULONG len = irp_stack->Parameters.DeviceIoControl.InputBufferLength;
if (len == sizeof(FORT_CONF_ZONE_FLAG)) {
fort_conf_zone_flag_set(&g_device->conf, zone_flag);
fort_worker_reauth();
status = STATUS_SUCCESS;
}
break;
}
default:
break;
}
end:
if (!NT_SUCCESS(status) && status != FORT_STATUS_USER_ERROR) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL, "FORT: Device Control: Error: %x\n",
status);
}
fort_request_complete_info(irp, status, info);
return status;
}

55
src/driver/fortdev.h Normal file
View File

@ -0,0 +1,55 @@
#ifndef FORTDEV_H
#define FORTDEV_H
#include "fortdrv.h"
#include "fortbuf.h"
#include "fortcnf.h"
#include "fortpkt.h"
#include "fortstat.h"
#include "forttmr.h"
#include "fortwrk.h"
typedef struct fort_device
{
UINT32 connect4_id;
UINT32 accept4_id;
PCALLBACK_OBJECT power_cb_obj;
PVOID power_cb_reg;
PCALLBACK_OBJECT systime_cb_obj;
PVOID systime_cb_reg;
FORT_DEVICE_CONF conf;
FORT_BUFFER buffer;
FORT_STAT stat;
FORT_DEFER defer;
FORT_TIMER log_timer;
FORT_TIMER app_timer;
FORT_WORKER worker;
} FORT_DEVICE, *PFORT_DEVICE;
#if defined(__cplusplus)
extern "C" {
#endif
FORT_API PFORT_DEVICE fort_device();
FORT_API void fort_device_set(PFORT_DEVICE device);
FORT_API void fort_app_period_timer(void);
FORT_API NTSTATUS fort_device_create(PDEVICE_OBJECT device, PIRP irp);
FORT_API NTSTATUS fort_device_close(PDEVICE_OBJECT device, PIRP irp);
FORT_API NTSTATUS fort_device_cleanup(PDEVICE_OBJECT device, PIRP irp);
FORT_API NTSTATUS fort_device_control(PDEVICE_OBJECT device, PIRP irp);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // FORTDEV_H

File diff suppressed because it is too large Load Diff

View File

@ -14,4 +14,13 @@
#define fort_mem_alloc(size, tag) ExAllocatePoolWithTag(NonPagedPool, (size), (tag)) #define fort_mem_alloc(size, tag) ExAllocatePoolWithTag(NonPagedPool, (size), (tag))
#define fort_mem_free(p, tag) ExFreePoolWithTag((p), (tag)) #define fort_mem_free(p, tag) ExFreePoolWithTag((p), (tag))
#define fort_request_complete_info(irp, status, info) \
do { \
(irp)->IoStatus.Status = (status); \
(irp)->IoStatus.Information = (info); \
IoCompleteRequest((irp), IO_NO_INCREMENT); \
} while (0)
#define fort_request_complete(irp, status) fort_request_complete_info((irp), (status), 0)
#endif // FORTDRV_H #endif // FORTDRV_H

View File

@ -16,4 +16,6 @@
#include "fortstat.c" #include "fortstat.c"
#include "forttmr.c" #include "forttmr.c"
#include "fortwrk.c" #include "fortwrk.c"
#include "fortcout.c"
#include "fortdev.c"
#include "fortdrv.c" #include "fortdrv.c"

View File

@ -1,7 +1,7 @@
#include "fortcommon.h" #include "fortcommon.h"
#include <common/fortconf.h> #include <common/fortconf.h>
#include <common/fortdev.h> #include <common/fortdef.h>
#include <common/fortlog.h> #include <common/fortlog.h>
#include <common/fortprov.h> #include <common/fortprov.h>