📄 filter.c
字号:
/* Copyright (c) 2002-2005 Vladislav Goncharov.
*
* Redistribution and use in source forms, with and without modification,
* are permitted provided that this entire comment appears intact.
*
* Redistribution in binary form may occur without any restrictions.
*
* This software is provided ``AS IS'' without any warranties of any kind.
*/
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
//
// $Id: filter.c,v 1.17 2005/03/14 18:28:26 vlad Exp $
/*
* Filtering related routines
*/
#include <ntddk.h>
#include <tdikrnl.h>
#include "sock.h"
#include "filter.h"
#include "memtrack.h"
#include "packet.h"
#include "pid_pname.h"
#include "sids.h"
#include "tdi_fw.h"
// size of cyclic queue for logging
#define REQUEST_QUEUE_SIZE 1024
/* rules chains (main (first entry) and process-related) */
static struct
{
struct {
struct flt_rule *head;
struct flt_rule *tail;
char *pname; // name of process
BOOLEAN active; // filter chain is active
} chain[MAX_CHAINS_COUNT];
KSPIN_LOCK guard;
} g_rules;
/* "ALLOW * * FROM ANY TO ANY" rule */
static struct flt_rule g_allow_all =
{
{0},
FILTER_ALLOW,
IPPROTO_ANY,
DIRECTION_ANY,
0, // from
0,
0,
0,
0, // to
0,
0,
0,
RULE_LOG_LOG,
"", // setup mask before using it!
"startup" // rule for startup only
};
/* logging request queue */
static struct
{
struct flt_request *data;
KSPIN_LOCK guard;
ULONG head; /* write to head */
ULONG tail; /* read from tail */
HANDLE event_handle;
PKEVENT event;
} g_queue;
// init
NTSTATUS filter_init(void)
{
NTSTATUS status;
int i;
pid_pname_init();
sids_init();
/* rules chain */
KeInitializeSpinLock(&g_rules.guard);
for (i = 0; i < MAX_CHAINS_COUNT; i++)
{
g_rules.chain[i].head = g_rules.chain[i].tail = NULL;
g_rules.chain[i].pname = NULL;
g_rules.chain[i].active = FALSE;
}
// setup the first rule "ALLOW * * FROM ANY TO ANY"
for (i = 0; i < sizeof(g_allow_all.sid_mask); i++)
g_allow_all.sid_mask[i] = (UCHAR)-1;
g_rules.chain[0].head = malloc_np(sizeof(g_allow_all));
if (g_rules.chain[0].head == NULL)
{
KdPrint(("[tdi_fw] filter_init: malloc_np!\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
memcpy(g_rules.chain[0].head, &g_allow_all, sizeof(g_allow_all));
g_rules.chain[0].tail = g_rules.chain[0].head;
g_rules.chain[0].active = TRUE;
/* request queue */
KeInitializeSpinLock(&g_queue.guard);
g_queue.data = (struct flt_request *)malloc_np(sizeof(struct flt_request) * REQUEST_QUEUE_SIZE);
if (g_queue.data == NULL)
{
KdPrint(("[tdi_fw] filter_init: malloc_np!\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
memset(g_queue.data, 0, sizeof(struct flt_request) * REQUEST_QUEUE_SIZE);
g_queue.head = g_queue.tail = 0;
return STATUS_SUCCESS;
}
// init for user part starting
NTSTATUS filter_init_2(void)
{
NTSTATUS status;
if (g_queue.event_handle == NULL)
{
UNICODE_STRING str;
OBJECT_ATTRIBUTES oa;
RtlInitUnicodeString(&str, L"\\BaseNamedObjects\\tdifw_request");
InitializeObjectAttributes(&oa, &str, 0, NULL, NULL);
status = ZwCreateEvent(&g_queue.event_handle, EVENT_ALL_ACCESS, &oa, SynchronizationEvent, FALSE);
if (status != STATUS_SUCCESS)
{
KdPrint(("[tdi_fw] filter_init_2: ZwCreateEvent: 0x%x\n", status));
return status;
}
}
if (g_queue.event == NULL)
{
status = ObReferenceObjectByHandle(g_queue.event_handle, EVENT_ALL_ACCESS, NULL, KernelMode,
&g_queue.event, NULL);
if (status != STATUS_SUCCESS)
{
KdPrint(("[tdi_fw] filter_init_2: ObReferenceObjectByHandle: 0x%x\n", status));
return status;
}
}
// try to communicate with packet driver
init_packet();
return STATUS_SUCCESS;
}
// cleanup for user part
void filter_free_2(void)
{
free_packet();
if (g_queue.event != NULL)
{
ObDereferenceObject(g_queue.event);
g_queue.event = NULL;
}
if (g_queue.event_handle != NULL)
{
ZwClose(g_queue.event_handle);
g_queue.event_handle = NULL;
}
}
// free
void filter_free(void)
{
KIRQL irql;
struct plist_entry *ple;
int i;
// clear all chains
for (i = 0; i < MAX_CHAINS_COUNT; i++)
clear_flt_chain(i);
/* clear request queue */
KeAcquireSpinLock(&g_queue.guard, &irql);
for (i = 0; i < REQUEST_QUEUE_SIZE; i++)
{
if (g_queue.data[i].pname != NULL)
free(g_queue.data[i].pname);
if (g_queue.data[i].sid_a != NULL)
free(g_queue.data[i].sid_a);
}
free(g_queue.data);
KeReleaseSpinLock(&g_queue.guard, irql);
set_sid_list(NULL, 0);
pid_pname_free();
}
#define CHECK_BIT(char_mask, num) \
((char_mask)[(num) / 8] & (1 << ((num) % 8)))
// quick filter (I mean "synchronous" (can work at DISPATCH_LEVEL))
int quick_filter(struct flt_request *request, struct flt_rule *rule)
{
const struct sockaddr_in *from, *to;
struct flt_rule *r;
struct plist_entry *ple;
KIRQL irql;
int chain, result, sid_id;
//return FILTER_ALLOW;
// not IP
if (request->addr.len != sizeof(struct sockaddr_in) ||
request->addr.from.sa_family != AF_INET ||
request->addr.to.sa_family != AF_INET)
{
KdPrint(("[tdi_fw] quick_filter: not ip addr!\n"));
return FILTER_DENY;
}
from = (const struct sockaddr_in *)&request->addr.from;
to = (const struct sockaddr_in *)&request->addr.to;
// default behavior: DENY and LOG
result = FILTER_DENY;
if (rule != NULL)
{
memset(rule, 0, sizeof(*rule));
rule->result = result;
rule->log = TRUE;
strcpy(rule->rule_id, "default");
}
chain = pid_pname_get_context(request->pid);
if (!g_rules.chain[chain].active)
{
// chain is not active; don't check request
return result;
}
if (request->sid_a != NULL)
sid_id = get_sid_id(request->sid_a, request->sid_a_size);
else
sid_id = 0; // default sid_id
// quick filter
KeAcquireSpinLock(&g_rules.guard, &irql);
#define CHECK_ADDR_PORT(r_addr_from, r_mask_from, r_port_from, r_port2_from, \
r_addr_to, r_mask_to, r_port_to, r_port2_to) \
((r_addr_from & r_mask_from) == (from->sin_addr.s_addr & r_mask_from) && \
(r_addr_to & r_mask_to) == (to->sin_addr.s_addr & r_mask_to) && \
(r_port_from == 0 || \
((r_port2_from == 0) ? (r_port_from == from->sin_port) : \
(ntohs(from->sin_port) >= ntohs(r_port_from) && ntohs(from->sin_port) <= ntohs(r_port2_from)))) && \
(r_port_to == 0 || \
((r_port2_to == 0) ? (r_port_to == to->sin_port) : \
(ntohs(to->sin_port) >= ntohs(r_port_to) && ntohs(to->sin_port) <= ntohs(r_port2_to))))) \
// go through rules
for (r = g_rules.chain[chain].head; r != NULL; r = r->next)
// Can anybody understand it?
if ((r->proto == IPPROTO_ANY || r->proto == request->proto)
&& ((r->direction != DIRECTION_ANY && r->direction == request->direction
&& CHECK_ADDR_PORT(r->addr_from, r->mask_from, r->port_from, r->port2_from,
r->addr_to, r->mask_to, r->port_to, r->port2_to))
|| (r->direction == DIRECTION_ANY
&& ((request->direction == DIRECTION_OUT
&& CHECK_ADDR_PORT(r->addr_from, r->mask_from, r->port_from, r->port2_from,
r->addr_to, r->mask_to, r->port_to, r->port2_to))
|| (request->direction == DIRECTION_IN
&& CHECK_ADDR_PORT(r->addr_to, r->mask_to, r->port_to,
r->port2_to, r->addr_from, r->mask_from, r->port_from, r->port2_from)))))
&& CHECK_BIT(r->sid_mask, sid_id))
{
result = r->result;
KdPrint(("[tdi_fw] quick_filter: found rule with result: %d\n", result));
if (rule != NULL)
{
memcpy(rule, r, sizeof(*rule));
rule->next = NULL; // useless field
}
break;
}
KeReleaseSpinLock(&g_rules.guard, irql);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -