From 9a79f08bcd0f45c594b0ce6bb2a17ba3c0028331 Mon Sep 17 00:00:00 2001 From: 0x9dec1980 <0x9dec1980> Date: Fri, 30 Sep 2016 10:56:20 +0200 Subject: [PATCH] tdifw-1.4.4 --- tdifw-1.4.4/dirs | 4 + tdifw-1.4.4/drv/MAKEFILE | 7 + tdifw-1.4.4/drv/SOURCES | 39 + tdifw-1.4.4/drv/conn_state.c | 767 +++++++++++++ tdifw-1.4.4/drv/conn_state.h | 41 + tdifw-1.4.4/drv/disp_conn.c | 483 ++++++++ tdifw-1.4.4/drv/disp_dg.c | 281 +++++ tdifw-1.4.4/drv/disp_ev.c | 160 +++ tdifw-1.4.4/drv/disp_obj.c | 460 ++++++++ tdifw-1.4.4/drv/disp_sr.c | 122 ++ tdifw-1.4.4/drv/dispatch.c | 48 + tdifw-1.4.4/drv/dispatch.h | 49 + tdifw-1.4.4/drv/ev_conn.c | 404 +++++++ tdifw-1.4.4/drv/ev_dg.c | 147 +++ tdifw-1.4.4/drv/ev_recv.c | 249 +++++ tdifw-1.4.4/drv/events.c | 37 + tdifw-1.4.4/drv/events.h | 70 ++ tdifw-1.4.4/drv/filter.c | 593 ++++++++++ tdifw-1.4.4/drv/filter.h | 39 + tdifw-1.4.4/drv/ipc.c | 207 ++++ tdifw-1.4.4/drv/memtrack.c | 204 ++++ tdifw-1.4.4/drv/memtrack.h | 48 + tdifw-1.4.4/drv/ndis_hk_ioctl.h | 130 +++ tdifw-1.4.4/drv/obj_tbl.c | 566 ++++++++++ tdifw-1.4.4/drv/obj_tbl.h | 108 ++ tdifw-1.4.4/drv/packet.c | 780 +++++++++++++ tdifw-1.4.4/drv/packet.h | 11 + tdifw-1.4.4/drv/pid_pname.c | 269 +++++ tdifw-1.4.4/drv/pid_pname.h | 18 + tdifw-1.4.4/drv/sids.c | 211 ++++ tdifw-1.4.4/drv/sids.h | 30 + tdifw-1.4.4/drv/sock.c | 41 + tdifw-1.4.4/drv/sock.h | 86 ++ tdifw-1.4.4/drv/tdi_fw.c | 985 +++++++++++++++++ tdifw-1.4.4/drv/tdi_fw.h | 148 +++ tdifw-1.4.4/install/MAKEFILE | 7 + tdifw-1.4.4/install/SOURCES | 16 + tdifw-1.4.4/install/tdi_install.c | 299 +++++ tdifw-1.4.4/ipc.h | 215 ++++ tdifw-1.4.4/net.h | 77 ++ tdifw-1.4.4/svc/MAKEFILE | 7 + tdifw-1.4.4/svc/SOURCES | 19 + tdifw-1.4.4/svc/flt_rule.c | 373 +++++++ tdifw-1.4.4/svc/flt_rule.h | 10 + tdifw-1.4.4/svc/iphlpapi.h | 49 + tdifw-1.4.4/svc/iphlpapi.lib | Bin 0 -> 2640 bytes tdifw-1.4.4/svc/main.c | 474 ++++++++ tdifw-1.4.4/svc/msg.h | 45 + tdifw-1.4.4/svc/msg.mc | 6 + tdifw-1.4.4/svc/msg.rc | 2 + tdifw-1.4.4/svc/tdi_fw_svc.rc | 1 + tdifw-1.4.4/svc/tdifw_svc.c | 1716 +++++++++++++++++++++++++++++ tdifw-1.4.4/svc/tdifw_svc.h | 42 + tdifw-1.4.4/svc/thread.h | 19 + 54 files changed, 11219 insertions(+) create mode 100644 tdifw-1.4.4/dirs create mode 100644 tdifw-1.4.4/drv/MAKEFILE create mode 100644 tdifw-1.4.4/drv/SOURCES create mode 100644 tdifw-1.4.4/drv/conn_state.c create mode 100644 tdifw-1.4.4/drv/conn_state.h create mode 100644 tdifw-1.4.4/drv/disp_conn.c create mode 100644 tdifw-1.4.4/drv/disp_dg.c create mode 100644 tdifw-1.4.4/drv/disp_ev.c create mode 100644 tdifw-1.4.4/drv/disp_obj.c create mode 100644 tdifw-1.4.4/drv/disp_sr.c create mode 100644 tdifw-1.4.4/drv/dispatch.c create mode 100644 tdifw-1.4.4/drv/dispatch.h create mode 100644 tdifw-1.4.4/drv/ev_conn.c create mode 100644 tdifw-1.4.4/drv/ev_dg.c create mode 100644 tdifw-1.4.4/drv/ev_recv.c create mode 100644 tdifw-1.4.4/drv/events.c create mode 100644 tdifw-1.4.4/drv/events.h create mode 100644 tdifw-1.4.4/drv/filter.c create mode 100644 tdifw-1.4.4/drv/filter.h create mode 100644 tdifw-1.4.4/drv/ipc.c create mode 100644 tdifw-1.4.4/drv/memtrack.c create mode 100644 tdifw-1.4.4/drv/memtrack.h create mode 100644 tdifw-1.4.4/drv/ndis_hk_ioctl.h create mode 100644 tdifw-1.4.4/drv/obj_tbl.c create mode 100644 tdifw-1.4.4/drv/obj_tbl.h create mode 100644 tdifw-1.4.4/drv/packet.c create mode 100644 tdifw-1.4.4/drv/packet.h create mode 100644 tdifw-1.4.4/drv/pid_pname.c create mode 100644 tdifw-1.4.4/drv/pid_pname.h create mode 100644 tdifw-1.4.4/drv/sids.c create mode 100644 tdifw-1.4.4/drv/sids.h create mode 100644 tdifw-1.4.4/drv/sock.c create mode 100644 tdifw-1.4.4/drv/sock.h create mode 100644 tdifw-1.4.4/drv/tdi_fw.c create mode 100644 tdifw-1.4.4/drv/tdi_fw.h create mode 100644 tdifw-1.4.4/install/MAKEFILE create mode 100644 tdifw-1.4.4/install/SOURCES create mode 100644 tdifw-1.4.4/install/tdi_install.c create mode 100644 tdifw-1.4.4/ipc.h create mode 100644 tdifw-1.4.4/net.h create mode 100644 tdifw-1.4.4/svc/MAKEFILE create mode 100644 tdifw-1.4.4/svc/SOURCES create mode 100644 tdifw-1.4.4/svc/flt_rule.c create mode 100644 tdifw-1.4.4/svc/flt_rule.h create mode 100644 tdifw-1.4.4/svc/iphlpapi.h create mode 100644 tdifw-1.4.4/svc/iphlpapi.lib create mode 100644 tdifw-1.4.4/svc/main.c create mode 100644 tdifw-1.4.4/svc/msg.h create mode 100644 tdifw-1.4.4/svc/msg.mc create mode 100644 tdifw-1.4.4/svc/msg.rc create mode 100644 tdifw-1.4.4/svc/tdi_fw_svc.rc create mode 100644 tdifw-1.4.4/svc/tdifw_svc.c create mode 100644 tdifw-1.4.4/svc/tdifw_svc.h create mode 100644 tdifw-1.4.4/svc/thread.h diff --git a/tdifw-1.4.4/dirs b/tdifw-1.4.4/dirs new file mode 100644 index 00000000..a50b24c6 --- /dev/null +++ b/tdifw-1.4.4/dirs @@ -0,0 +1,4 @@ +DIRS= \ + drv \ + svc \ + install diff --git a/tdifw-1.4.4/drv/MAKEFILE b/tdifw-1.4.4/drv/MAKEFILE new file mode 100644 index 00000000..58189757 --- /dev/null +++ b/tdifw-1.4.4/drv/MAKEFILE @@ -0,0 +1,7 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the driver components of the Windows NT DDK +# + +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/tdifw-1.4.4/drv/SOURCES b/tdifw-1.4.4/drv/SOURCES new file mode 100644 index 00000000..39d98855 --- /dev/null +++ b/tdifw-1.4.4/drv/SOURCES @@ -0,0 +1,39 @@ +TARGETNAME=tdifw_drv +TARGETPATH=obj +TARGETTYPE=DRIVER + +TARGETLIBS=$(DDK_LIB_PATH)\tdi.lib + +C_DEFINES=-DUSE_PACKET_ENGINE +# You can specify this define too: -DUSE_TDI_HOOKING + +USER_INCLUDES=..;$(NTMAKEENV)\..\src\network\inc + +SOURCES= tdi_fw.c \ + dispatch.c \ + disp_conn.c \ + disp_dg.c \ + disp_ev.c \ + disp_obj.c \ + events.c \ + ev_conn.c \ + ev_dg.c \ + filter.c \ + memtrack.c \ + obj_tbl.c \ + sock.c \ + ipc.c \ + pid_pname.c \ + packet.c \ + conn_state.c \ + sids.c \ + disp_sr.c \ + ev_recv.c + +!if "$(NTDEBUG)"=="ntsdnodbg" +NTDEBUG= +!endif + +!ifndef NTDEBUG +NTDEBUG=retail +!endif diff --git a/tdifw-1.4.4/drv/conn_state.c b/tdifw-1.4.4/drv/conn_state.c new file mode 100644 index 00000000..24279247 --- /dev/null +++ b/tdifw-1.4.4/drv/conn_state.c @@ -0,0 +1,767 @@ +/* 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: conn_state.c,v 1.5 2005/03/14 18:28:26 vlad Exp $ + +#include +#include +#include "sock.h" + +#include "conn_state.h" +#include "ipc.h" +#include "memtrack.h" +#include "obj_tbl.h" +#include "tdi_fw.h" + +// how much entry in connection will live in "CLOSED" state? (sec) +#define MAX_CLOSED_TIME 20 + +struct listen_entry { + struct listen_entry *next; + struct listen_entry *prev; /* using double-linked list */ + int ipproto; + ULONG addr; // IPv4 only (yet) + USHORT port; + PFILE_OBJECT addrobj; +}; + +static struct listen_entry **g_listen = NULL; + +static KSPIN_LOCK g_listen_guard; +// !!! to avoid deadlocks with g_ot_hash_guard this spinlock MUST be acquired _after_ g_conn_guard !!! + +#define LISTEN_HASH_SIZE 0x1000 +#define CALC_LISTEN_HASH(ipproto, port) ((ULONG)((ipproto) + (port)) % LISTEN_HASH_SIZE) + +struct conn_entry { + struct conn_entry *next; + struct conn_entry *prev; /* using double-linked list */ + int state; + ULONG laddr; // IPv4 only (yet) + USHORT lport; + ULONG raddr; + USHORT rport; + PFILE_OBJECT connobj; + + struct conn_entry *next_to_del; + LARGE_INTEGER ticks; +}; + +static struct conn_entry **g_conn = NULL; +static struct conn_entry *g_conn_to_del = NULL; +static KSPIN_LOCK g_conn_guard; +static KEVENT g_conn_new_to_del; +static HANDLE g_conn_thread; + +#define CONN_HASH_SIZE 0x1000 +#define CALC_CONN_HASH(laddr, lport, raddr, rport) ((ULONG)((laddr) + (lport) + (raddr) + (rport)) % CONN_HASH_SIZE) + +static void conn_thread(PVOID param); + +//---------------------------------------------------------------------------- + +NTSTATUS +conn_state_init(void) +{ + NTSTATUS status; + + KeInitializeSpinLock(&g_listen_guard); + + g_listen = (struct listen_entry **)malloc_np(sizeof(struct listen_entry *) * LISTEN_HASH_SIZE); + if (g_listen == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto done; + } + + memset(g_listen, 0, sizeof(struct listen_entry *) * LISTEN_HASH_SIZE); + + KeInitializeSpinLock(&g_conn_guard); + + g_conn = (struct conn_entry **)malloc_np(sizeof(struct conn_entry *) * CONN_HASH_SIZE); + if (g_conn == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto done; + } + + memset(g_conn, 0, sizeof(struct conn_entry *) * CONN_HASH_SIZE); + + KeInitializeEvent(&g_conn_new_to_del, SynchronizationEvent, FALSE); + + // create thread to delete g_conn_to_del entries on timeout + status = PsCreateSystemThread(&g_conn_thread, THREAD_ALL_ACCESS, NULL, NULL, NULL, conn_thread, NULL); + +done: + if (status != STATUS_SUCCESS) { + if (g_listen != NULL) + free(g_listen); + if (g_conn != NULL) + free(g_conn); + } + return status; +} + +// this function MUST be executed after ot_free() +void +conn_state_free(void) +{ + HANDLE thread = g_conn_thread; + + g_conn_thread = NULL; // signal for thread to cleanup & exit + KeSetEvent(&g_conn_new_to_del, 0, FALSE); + ZwWaitForSingleObject(thread, FALSE, NULL); + ZwClose(thread); + + if (g_listen != NULL) { + free(g_listen); + g_listen = NULL; + } + + if (g_conn != NULL) { + free(g_conn); + g_conn = NULL; + } +} + +//---------------------------------------------------------------------------- + +NTSTATUS +add_listen(struct ot_entry *ote_addr) +{ + TA_ADDRESS *address = (TA_ADDRESS *)(ote_addr->local_addr); + struct listen_entry *le; + KIRQL irql; + ULONG hash; + + if (address->AddressType != TDI_ADDRESS_TYPE_IP) + return STATUS_INVALID_PARAMETER; + + le = (struct listen_entry *)malloc_np(sizeof(*le)); + if (le == NULL) + return STATUS_INSUFFICIENT_RESOURCES; + + memset(le, 0, sizeof(*le)); + + le->addrobj = ote_addr->fileobj; + le->addr = ((TDI_ADDRESS_IP *)(address->Address))->in_addr; + le->port = ((TDI_ADDRESS_IP *)(address->Address))->sin_port; + le->ipproto = ote_addr->ipproto; + + KdPrint(("[tdi_fw] add_list: got LISTEN %x:%u (ipproto=%d)\n", le->addr, ntohs(le->port), le->ipproto)); + + // save le in ote + + if (ote_addr->listen_entry != NULL) { + KdPrint(("[tdi_fw] add_listen: duplicate listen for addrobj!\n")); + + free(le); + return STATUS_OBJECT_NAME_EXISTS; + } + + ote_addr->listen_entry = le; + + // add to our hash + + hash = CALC_LISTEN_HASH(le->ipproto, le->port); + + KeAcquireSpinLock(&g_listen_guard, &irql); + + le->next = g_listen[hash]; + if (g_listen[hash] != NULL) + g_listen[hash]->prev = le; + g_listen[hash] = le; + + KeReleaseSpinLock(&g_listen_guard, irql); + + return STATUS_SUCCESS; +} + +void +del_listen_obj(struct listen_entry *le, BOOLEAN no_guard) +{ + KIRQL irql; + + KdPrint(("[tdi_fw] del_listen_obj: NOT_LISTEN %x:%u (ipproto=%d)\n", le->addr, ntohs(le->port), le->ipproto)); + + if (!no_guard) + KeAcquireSpinLock(&g_listen_guard, &irql); // lock our hash + + // delete le from our hash + + if (le->prev != NULL) + le->prev->next = le->next; + else { + ULONG hash = CALC_LISTEN_HASH(le->ipproto, le->port); + g_listen[hash] = le->next; + } + + if (le->next != NULL) + le->next->prev = le->prev; + + free(le); + + if (!no_guard) + KeReleaseSpinLock(&g_listen_guard, irql); // unlock our hash +} + +BOOLEAN +is_listen(ULONG addr, USHORT port, int ipproto) +{ + ULONG hash = CALC_LISTEN_HASH(ipproto, port); + struct listen_entry *le; + KIRQL irql; + BOOLEAN result = FALSE; + + KeAcquireSpinLock(&g_listen_guard, &irql); + + for (le = g_listen[hash]; le != NULL; le = le->next) { + if (le->ipproto == ipproto && le->addr == addr && le->port == port) { + result = TRUE; + break; + } + } + + KeReleaseSpinLock(&g_listen_guard, irql); + return result; +} + +// another UGLY solution for broadcasts :-( +BOOLEAN +is_bcast_listen(ULONG addr, USHORT port, int ipproto) +{ + ULONG hash = CALC_LISTEN_HASH(ipproto, port); + struct listen_entry *le; + KIRQL irql; + BOOLEAN result = FALSE; + + KeAcquireSpinLock(&g_listen_guard, &irql); + + for (le = g_listen[hash]; le != NULL; le = le->next) { + if (le->ipproto == ipproto && le->port == port) { + ULONG addr_l = ntohl(le->addr), addr_p = ntohl(addr); + int i; + + result = TRUE; + + for (i = 31; i >= 0; i--) + if ((addr_l & (1 << i)) != (addr_p & (1 << i)) && (addr_p & (1 << i)) == 0) { + result = FALSE; + break; + } + + if (result) + break; + } + } + + KeReleaseSpinLock(&g_listen_guard, irql); + return result; +} + +NTSTATUS +enum_listen(struct listen_nfo *buf, ULONG *buf_len, ULONG buf_size) +{ + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql; + ULONG hash; + struct listen_entry *le; + + *buf_len = 0; + + if (buf_size < sizeof(struct listen_nfo)) + return STATUS_INVALID_PARAMETER; + + KeAcquireSpinLock(&g_ot_hash_guard, &irql); // lock obj_tbl (avoid deadlocks!) + KeAcquireSpinLockAtDpcLevel(&g_listen_guard); // lock our hash + + for (hash = 0; hash < LISTEN_HASH_SIZE; hash++) { + for (le = g_listen[hash]; le != NULL; le = le->next) { + struct ot_entry *ote; + + buf->addr = le->addr; + buf->port = le->port; + buf->ipproto = le->ipproto; + + // try to get pid + ote = ot_find_fileobj(le->addrobj, NULL); // g_ot_hash_guard already acquired + if (ote != NULL) + buf->pid = ote->pid; + else + buf->pid = 0; + + *buf_len += sizeof(struct listen_nfo); + buf++; + + if (*buf_len + sizeof(struct listen_nfo) > buf_size) { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + } + + if (status != STATUS_SUCCESS) + break; + } + + KeReleaseSpinLockFromDpcLevel(&g_listen_guard); // unlock our hash + KeReleaseSpinLock(&g_ot_hash_guard, irql); // unlock obj_tbl + + return status; +} + +//---------------------------------------------------------------------------- + +NTSTATUS +add_tcp_conn(struct ot_entry *ote_conn, int tcp_state) +{ + TA_ADDRESS *local_addr = (TA_ADDRESS *)(ote_conn->local_addr), + *remote_addr = (TA_ADDRESS *)(ote_conn->remote_addr); + struct conn_entry *ce; + KIRQL irql; + ULONG hash; + + if (local_addr->AddressType != TDI_ADDRESS_TYPE_IP || remote_addr->AddressType != TDI_ADDRESS_TYPE_IP) + return STATUS_INVALID_PARAMETER; + + ce = (struct conn_entry *)malloc_np(sizeof(*ce)); + if (ce == NULL) + return STATUS_INSUFFICIENT_RESOURCES; + + memset(ce, 0, sizeof(*ce)); + + ce->connobj = ote_conn->fileobj; + ce->laddr = ((TDI_ADDRESS_IP *)(local_addr->Address))->in_addr; + ce->lport = ((TDI_ADDRESS_IP *)(local_addr->Address))->sin_port; + ce->raddr = ((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr; + ce->rport = ((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port; + ce->state = tcp_state; + + KdPrint(("[tdi_fw] add_tcp_conn: got CONNECT %x:%u <-> %x:%u (state=%d)\n", + ce->laddr, ntohs(ce->lport), ce->raddr, ntohs(ce->rport), tcp_state)); + + // save ce in ote + + if (ote_conn->conn_entry != NULL) { + KdPrint(("[tdi_fw] add_conn: duplicate conn (0x%x:%u -> 0x%x:%u)!\n", + ote_conn->conn_entry->laddr, ote_conn->conn_entry->lport, + ote_conn->conn_entry->raddr, ote_conn->conn_entry->rport)); + + free(ce); + return STATUS_OBJECT_NAME_EXISTS; + } + + ote_conn->conn_entry = ce; + + // add to our hash + + hash = CALC_CONN_HASH(ce->laddr, ce->lport, ce->raddr, ce->rport); + + KeAcquireSpinLock(&g_conn_guard, &irql); + + ce->next = g_conn[hash]; + if (g_conn[hash] != NULL) + g_conn[hash]->prev = ce; + g_conn[hash] = ce; + + KeReleaseSpinLock(&g_conn_guard, irql); + + return STATUS_SUCCESS; +} + +void +del_tcp_conn(PFILE_OBJECT connobj, BOOLEAN is_disconnect) +{ + KIRQL irql; + NTSTATUS status; + struct ot_entry *ote_conn; + struct conn_entry *ce; + + KeAcquireSpinLock(&g_ot_hash_guard, &irql); // lock obj_tbl first (avoid deadlocks!) + KeAcquireSpinLockAtDpcLevel(&g_conn_guard); // lock our hash + + ote_conn = ot_find_fileobj(connobj, NULL); // obj_tbl already locked + if (ote_conn == NULL) + goto done; + + ce = ote_conn->conn_entry; + ote_conn->conn_entry = NULL; + + if (is_disconnect && ote_conn->log_disconnect) + log_disconnect(ote_conn); + + if (ce != NULL) + del_tcp_conn_obj(ce, TRUE); + +done: + KeReleaseSpinLockFromDpcLevel(&g_conn_guard); // unlock our hash + KeReleaseSpinLock(&g_ot_hash_guard, irql); // unlock obj_tbl +} + +void +del_tcp_conn_obj(struct conn_entry *ce, BOOLEAN no_guard) +{ + KIRQL irql; + + KdPrint(("[tdi_fw] del_tcp_conn_obj: CLOSED %x:%u <-> %x:%u (state=%d)\n", + ce->laddr, ntohs(ce->lport), ce->raddr, ntohs(ce->rport), ce->state)); + + if (!no_guard) + KeAcquireSpinLock(&g_conn_guard, &irql); // lock our hash + + // set state to TCP_STATE_CLOSED and add to special list to queue it for deleting + ce->state = TCP_STATE_CLOSED; + ce->next_to_del = g_conn_to_del; + g_conn_to_del = ce; + + KeQueryTickCount(&ce->ticks); + + KeSetEvent(&g_conn_new_to_del, IO_NO_INCREMENT, FALSE); + + ce->connobj = NULL; // no connection object related for now!!! + + KdPrint(("[tdi_fw] del_tcp_conn_obj: state table entry scheduled for deletion!\n")); + + if (!no_guard) + KeReleaseSpinLock(&g_conn_guard, irql); // unlock our hash +} + +NTSTATUS +set_tcp_conn_state(PFILE_OBJECT connobj, int state) +{ + KIRQL irql; + struct ot_entry *ote_conn; + NTSTATUS status; + + KeAcquireSpinLock(&g_ot_hash_guard, &irql); // lock obj_tbl first (avoid deadlocks!) + KeAcquireSpinLockAtDpcLevel(&g_conn_guard); // lock our hash + + ote_conn = ot_find_fileobj(connobj, NULL); // obj_tbl already locked + if (ote_conn == NULL) { + KdPrint(("[tdi_fw] set_tcp_conn_state: ot_find_fileobj(0x%x)!\n", connobj)); + status = STATUS_OBJECT_NAME_NOT_FOUND; + goto done; + } + + if (ote_conn->conn_entry != NULL) { + ote_conn->conn_entry->state = state; + + KdPrint(("[tdi_fw] set_tcp_conn_state: got CHANGE CONNECT STATE %x:%u <-> %x:%u (state=%d)\n", + ote_conn->conn_entry->laddr, ntohs(ote_conn->conn_entry->lport), + ote_conn->conn_entry->raddr, ntohs(ote_conn->conn_entry->rport), + ote_conn->conn_entry->state)); + + status = STATUS_SUCCESS; + } else { + KdPrint(("[tdi_fw] set_tcp_conn_state: no conn_entry!\n")); + status = STATUS_INVALID_PARAMETER; + } + +done: + KeReleaseSpinLockFromDpcLevel(&g_conn_guard); // unlock our hash + KeReleaseSpinLock(&g_ot_hash_guard, irql); // unlock obj_tbl + + return status; +} + +NTSTATUS +set_tcp_conn_local(PFILE_OBJECT connobj, TA_ADDRESS *local) +{ + KIRQL irql; + struct ot_entry *ote_conn; + NTSTATUS status; + + KeAcquireSpinLock(&g_ot_hash_guard, &irql); // lock obj_tbl first (avoid deadlocks!) + KeAcquireSpinLockAtDpcLevel(&g_conn_guard); // lock our hash + + ote_conn = ot_find_fileobj(connobj, NULL); // obj_tbl already locked + if (ote_conn == NULL) { + KdPrint(("[tdi_fw] set_tcp_conn_local: ot_find_fileobj(0x%x)!\n", connobj)); + status = STATUS_OBJECT_NAME_NOT_FOUND; + goto done; + } + + if (ote_conn->conn_entry != NULL) { + struct conn_entry *ce = ote_conn->conn_entry; + ULONG hash; + + // remove our conn_entry from list and recalculate hash + + if (ce->prev != NULL) + ce->prev->next = ce->next; + else { + hash = CALC_CONN_HASH(ce->laddr, ce->lport, ce->raddr, ce->rport); + g_conn[hash] = ce->next; + } + + if (ce->next != NULL) + ce->next->prev = ce->prev; + + ce->laddr = ((TDI_ADDRESS_IP *)(local->Address))->in_addr; + ce->lport = ((TDI_ADDRESS_IP *)(local->Address))->sin_port; + + hash = CALC_CONN_HASH(ce->laddr, ce->lport, ce->raddr, ce->rport); + + KdPrint(("[tdi_fw] set_tcp_conn_local: got CHANGE CONNECT LOCAL %x:%u <-> %x:%u (state=%d)\n", + ce->laddr, ntohs(ce->lport), ce->raddr, ntohs(ce->rport), ce->state)); + + // and now add our conn_entry under new hash value + + ce->next = g_conn[hash]; + if (g_conn[hash] != NULL) + g_conn[hash]->prev = ce; + g_conn[hash] = ce; + + ce->prev = NULL; + + status = STATUS_SUCCESS; + + } else { + KdPrint(("[tdi_fw] set_tcp_conn_local: no conn_entry!\n")); + status = STATUS_INVALID_PARAMETER; + } + +done: + KeReleaseSpinLockFromDpcLevel(&g_conn_guard); // unlock our hash + KeReleaseSpinLock(&g_ot_hash_guard, irql); // unlock obj_tbl + + return status; +} + +int +get_tcp_conn_state(ULONG laddr, USHORT lport, ULONG raddr, USHORT rport) +{ + ULONG hash = CALC_CONN_HASH(laddr, lport, raddr, rport); + KIRQL irql; + struct conn_entry *ce; + int result; + + KeAcquireSpinLock(&g_conn_guard, &irql); + + result = TCP_STATE_NONE; + + for (ce = g_conn[hash]; ce != NULL; ce = ce->next) + if (ce->laddr == laddr && ce->lport == lport && + ce->raddr == raddr && ce->rport == rport) { + + result = ce->state; + break; + } + + KeReleaseSpinLock(&g_conn_guard, irql); + + return result; +} + +int +get_tcp_conn_state_by_obj(PFILE_OBJECT connobj) +{ + KIRQL irql; + struct ot_entry *ote_conn; + NTSTATUS status; + int result = TCP_STATE_NONE; + + KeAcquireSpinLock(&g_ot_hash_guard, &irql); // lock obj_tbl first (avoid deadlocks!) + KeAcquireSpinLockAtDpcLevel(&g_conn_guard); // lock our hash + + ote_conn = ot_find_fileobj(connobj, NULL); // obj_tbl already locked + if (ote_conn == NULL) { + KdPrint(("[tdi_fw] set_tcp_conn_state: ot_find_fileobj(0x%x)!\n", connobj)); + goto done; + } + + if (ote_conn->conn_entry != NULL) + result = ote_conn->conn_entry->state; + +done: + KeReleaseSpinLockFromDpcLevel(&g_conn_guard); // unlock our hash + KeReleaseSpinLock(&g_ot_hash_guard, irql); // unlock obj_tbl + + return result; +} + +NTSTATUS +enum_tcp_conn(struct tcp_conn_nfo *buf, ULONG *buf_len, ULONG buf_size) +{ + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql; + ULONG hash; + struct conn_entry *ce; + + *buf_len = 0; + + if (buf_size < sizeof(struct tcp_conn_nfo)) + return STATUS_INVALID_PARAMETER; + + KeAcquireSpinLock(&g_ot_hash_guard, &irql); // lock obj_tbl first (avoid deadlocks!) + KeAcquireSpinLockAtDpcLevel(&g_conn_guard); // lock our hash + + for (hash = 0; hash < CONN_HASH_SIZE; hash++) { + for (ce = g_conn[hash]; ce != NULL; ce = ce->next) { + struct ot_entry *ote; + + if (ce->state == TCP_STATE_CLOSED) + continue; // don't log "closed" + + buf->laddr = ce->laddr; + buf->lport = ce->lport; + buf->raddr = ce->raddr; + buf->rport = ce->rport; + buf->state = ce->state; + + // try to get pid + ote = ot_find_fileobj(ce->connobj, NULL); // obj_tbl already locked + if (ote != NULL) { + buf->pid = ote->pid; + buf->bytes_in = ote->bytes_in; + buf->bytes_out = ote->bytes_out; + } else { + buf->pid = 0; + buf->bytes_in = 0; + buf->bytes_out = 0; + } + + *buf_len += sizeof(struct tcp_conn_nfo); + buf++; + + if (*buf_len + sizeof(struct tcp_conn_nfo) > buf_size) { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + } + + if (status != STATUS_SUCCESS) + break; + } + + KeReleaseSpinLockFromDpcLevel(&g_conn_guard); // unlock our_hash + KeReleaseSpinLock(&g_ot_hash_guard, irql); // unlock obj_tbl + + return status; +} + +void +conn_thread(PVOID param) +{ + LARGE_INTEGER timeout; + + timeout.QuadPart = 0; + + do { + LARGE_INTEGER ticks; + struct conn_entry *ce, *prev_ce, *ce2; + KIRQL irql; + + KeWaitForSingleObject(&g_conn_new_to_del, Executive, KernelMode, FALSE, + (timeout.QuadPart != 0) ? &timeout : NULL); + + KdPrint(("[tdi_fw] conn_thread: wake up!\n")); + + KeQueryTickCount(&ticks); + + // delete all entries older than MAX_CLOSED_TIME sec + // or all entries if (g_conn_thread == NULL) + + // and also find min timeout to next entry + + KeAcquireSpinLock(&g_conn_guard, &irql); + + prev_ce = NULL; + timeout.QuadPart = 0; + for (ce = g_conn_to_del; ce != NULL;) { + __int64 delta = (ticks.QuadPart - ce->ticks.QuadPart) * KeQueryTimeIncrement(); + + KdPrint(("[tdi_fw] conn_thread: delta %d msec\n", delta / 10000)); + + if (delta >= MAX_CLOSED_TIME * 1000 * 10000 || + g_conn_thread == NULL) { + + KdPrint(("[tdi_fw] conn_thread: remove it!\n")); + + // remove this entry!!! + + if (prev_ce == NULL) + g_conn_to_del = ce->next_to_del; + else + prev_ce->next_to_del = ce->next_to_del; + + // delete ce from our hash + + if (ce->prev != NULL) + ce->prev->next = ce->next; + else { + ULONG hash = CALC_CONN_HASH(ce->laddr, ce->lport, ce->raddr, ce->rport); + g_conn[hash] = ce->next; + } + + if (ce->next != NULL) + ce->next->prev = ce->prev; + + ce2 = ce->next_to_del; + free(ce); + + ce = ce2; + + } else { + delta = (MAX_CLOSED_TIME * 1000 * 10000 - delta); + + if (delta > -timeout.QuadPart || timeout.QuadPart == 0) { + timeout.QuadPart = -delta; // how much to wait? + + KdPrint(("[tdi_fw] conn_thread: we must wait %d ms!\n", timeout.QuadPart / 10000)); + } + + prev_ce = ce; + ce = ce->next_to_del; + } + } + + KeReleaseSpinLock(&g_conn_guard, irql); + + } while(g_conn_thread != NULL); +} + +void +log_disconnect(struct ot_entry *ote_conn) +{ + TA_ADDRESS *local_addr, *remote_addr; + struct flt_request request; + + local_addr = (TA_ADDRESS *)(ote_conn->local_addr); + remote_addr = (TA_ADDRESS *)(ote_conn->remote_addr); + + KdPrint(("[tdi_flt] del_tcp_conn: %x:%u -> %x:%u\n", + ntohl(((TDI_ADDRESS_IP *)(local_addr->Address))->in_addr), + ntohs(((TDI_ADDRESS_IP *)(local_addr->Address))->sin_port), + ntohl(((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr), + ntohs(((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port))); + + memset(&request, 0, sizeof(request)); + + request.struct_size = sizeof(request); + + request.result = FILTER_DISCONNECT; + request.proto = ote_conn->ipproto; + request.direction = DIRECTION_ANY; + + request.pid = (ULONG)-1; // don't use pid because on close there's no info in database + + // get user SID & attributes! + if ((request.sid_a = copy_sid_a(ote_conn->sid_a, ote_conn->sid_a_size)) != NULL) + request.sid_a_size = ote_conn->sid_a_size; + + memcpy(&request.addr.from, &local_addr->AddressType, sizeof(struct sockaddr)); + memcpy(&request.addr.to, &remote_addr->AddressType, sizeof(struct sockaddr)); + request.addr.len = sizeof(struct sockaddr_in); + + request.log_bytes_in = ote_conn->bytes_in; + request.log_bytes_out = ote_conn->bytes_out; + + log_request(&request); +} diff --git a/tdifw-1.4.4/drv/conn_state.h b/tdifw-1.4.4/drv/conn_state.h new file mode 100644 index 00000000..ca989495 --- /dev/null +++ b/tdifw-1.4.4/drv/conn_state.h @@ -0,0 +1,41 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs) +// +// $Id: conn_state.h,v 1.2 2003/09/01 08:42:17 dev Exp $ + +#ifndef _conn_state_h_ +#define _conn_state_h_ + +#include "ipc.h" + +NTSTATUS conn_state_init(void); +void conn_state_free(void); + +/* work with listening ports for all protocols */ + +NTSTATUS add_listen(struct ot_entry *ote_addr); + +void del_listen_obj(struct listen_entry *le, BOOLEAN no_guard); + +BOOLEAN is_listen(ULONG addr, USHORT port, int ipproto); + +BOOLEAN is_bcast_listen(ULONG addr, USHORT port, int ipproto); + +NTSTATUS enum_listen(struct listen_nfo *buf, ULONG *buf_len, ULONG buf_size); + +/* work with TCP only connections */ + +NTSTATUS add_tcp_conn(struct ot_entry *ote_conn, int tcp_state); +void del_tcp_conn(PFILE_OBJECT connobj, BOOLEAN is_disconnect); + +void del_tcp_conn_obj(struct conn_entry *ce, BOOLEAN no_guard); +void log_disconnect(struct ot_entry *ote_conn); + +NTSTATUS set_tcp_conn_state(PFILE_OBJECT connobj, int state); +NTSTATUS set_tcp_conn_local(PFILE_OBJECT connobj, TA_ADDRESS *local); + +int get_tcp_conn_state(ULONG laddr, USHORT lport, ULONG raddr, USHORT rport); +int get_tcp_conn_state_by_obj(PFILE_OBJECT connobj); + +NTSTATUS enum_tcp_conn(struct tcp_conn_nfo *buf, ULONG *buf_len, ULONG buf_size); + +#endif diff --git a/tdifw-1.4.4/drv/disp_conn.c b/tdifw-1.4.4/drv/disp_conn.c new file mode 100644 index 00000000..aa89f4e1 --- /dev/null +++ b/tdifw-1.4.4/drv/disp_conn.c @@ -0,0 +1,483 @@ +/* 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: disp_conn.c,v 1.11 2005/03/14 18:28:26 vlad Exp $ + +/* + * This file contains TDI_CONNECT & TDI_DISCONNECT handlers + */ + +#include +#include +#include "sock.h" + +#include "conn_state.h" +#include "dispatch.h" +#include "events.h" +#include "memtrack.h" +#include "obj_tbl.h" +#include "sids.h" +#include "tdi_fw.h" + +struct delayed_ucn_param { + WORK_QUEUE_ITEM item; + PDEVICE_OBJECT devobj; + PFILE_OBJECT fileobj; +}; + +struct uci_param { + PFILE_OBJECT connobj; + char address[]; +}; + +static void delayed_ucn(PVOID p); +static NTSTATUS update_conn_info_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context); + + +static NTSTATUS tdi_connect_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context); +static NTSTATUS tdi_disconnect_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context); + +//---------------------------------------------------------------------------- + +/* + * TDI_CONNECT handler + */ + +int +tdi_connect(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) +{ + PTDI_REQUEST_KERNEL_CONNECT param = (PTDI_REQUEST_KERNEL_CONNECT)(&irps->Parameters); + TA_ADDRESS *remote_addr = ((TRANSPORT_ADDRESS *)(param->RequestConnectionInformation->RemoteAddress))->Address; + PFILE_OBJECT addrobj; + NTSTATUS status; + TA_ADDRESS *local_addr; + int result = FILTER_DENY, ipproto; + struct ot_entry *ote_conn = NULL, *ote_addr; + KIRQL irql; + struct flt_request request; + struct flt_rule rule; + + memset(&request, 0, sizeof(request)); + + KdPrint(("[tdi_fw] tdi_connect: connobj 0x%x, to address %x:%u\n", + irps->FileObject, + ntohl(((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr), + ntohs(((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port))); + + // check device object: TCP or UDP + if (irps->DeviceObject != g_tcpfltobj && irps->DeviceObject != g_udpfltobj) { + KdPrint(("[tdi_fw] tdi_connect: unknown DeviceObject 0x%x!\n", irps->DeviceObject)); + goto done; + } + + ote_conn = ot_find_fileobj(irps->FileObject, &irql); + if (ote_conn == NULL) { + KdPrint(("[tdi_fw] tdi_connect: ot_find_fileobj(0x%x)!\n", irps->FileObject)); + goto done; + } + + if (get_original_devobj(irps->DeviceObject, &ipproto) == NULL || + (ipproto != IPPROTO_TCP && ipproto != IPPROTO_UDP)) { + // invalid device object! + KdPrint(("[tdi_fw] tdi_connect: invalid device object 0x%x!\n", irps->DeviceObject)); + goto done; + } + + if (ipproto == IPPROTO_TCP) { + /* + * For TCP: get addrobj by connobj and get local address by it + */ + + addrobj = ote_conn->associated_fileobj; + if (addrobj == NULL) { + KdPrint(("[tdi_fw] tdi_connect: empty addrobj!\n")); + goto done; + } + + ote_addr = ot_find_fileobj(addrobj, NULL); // we're already in spinlock + if (ote_addr == NULL) { + KdPrint(("[tdi_fw] tdi_connect: ot_find_fileobj(0x%x)!\n", addrobj)); + goto done; + } + + } else { + /* + * For UDP: connobj and addrobj are the same + */ + KdPrint(("[tdi_fw] tdi_connect: connected UDP socket detected\n")); + + // for connected UDP sockets connobj and addrobj are the same + addrobj= irps->FileObject; + ote_addr = ote_conn; + } + + local_addr = (TA_ADDRESS *)(ote_addr->local_addr); + + // sanity check + if (local_addr->AddressLength != remote_addr->AddressLength) { + KdPrint(("[tdi_fw] tdi_connect: different addr lengths! (%u != %u)\n", + local_addr->AddressLength, remote_addr->AddressLength)); + goto done; + } + + // set remote address with connobj + + if (remote_addr->AddressLength > sizeof(ote_conn->remote_addr)) { + KdPrint(("[tdi_fw] tdi_connect: address too long! (%u)\n", remote_addr->AddressLength)); + goto done; + } + memcpy(ote_conn->remote_addr, remote_addr, remote_addr->AddressLength); + + // set local address with connobj + + if (local_addr->AddressLength > sizeof(ote_conn->local_addr)) { + KdPrint(("[tdi_fw] tdi_connect: address to long! (%u)\n", local_addr->AddressLength)); + goto done; + } + memcpy(ote_conn->local_addr, local_addr, local_addr->AddressLength); + + KdPrint(("[tdi_fw] tdi_connect(pid:%u/%u): %x:%u -> %x:%u (ipproto = %d)\n", + ote_conn->pid, PsGetCurrentProcessId(), + ntohl(((TDI_ADDRESS_IP *)(local_addr->Address))->in_addr), + ntohs(((TDI_ADDRESS_IP *)(local_addr->Address))->sin_port), + ntohl(((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr), + ntohs(((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port), ipproto)); + + /* + * Call quick_filter + */ + + request.struct_size = sizeof(request); + + request.type = TYPE_CONNECT; + request.direction = DIRECTION_OUT; + request.proto = ipproto; + + // don't use ote_conn->pid because one process can create connection object + // but another one can connect + request.pid = (ULONG)PsGetCurrentProcessId(); + if (request.pid == 0) { + // avoid idle process pid (XXX do we need this?) + request.pid = ote_addr->pid; + } + + // get user SID & attributes (can't call get_current_sid_a at DISPATCH_LEVEL) + if ((request.sid_a = copy_sid_a(ote_addr->sid_a, ote_addr->sid_a_size)) != NULL) + request.sid_a_size = ote_addr->sid_a_size; + + memcpy(&request.addr.from, &local_addr->AddressType, sizeof(struct sockaddr)); + memcpy(&request.addr.to, &remote_addr->AddressType, sizeof(struct sockaddr)); + request.addr.len = sizeof(struct sockaddr_in); + + memset(&rule, 0, sizeof(rule)); + + result = quick_filter(&request, &rule); + + memcpy(request.log_rule_id, rule.rule_id, RULE_ID_SIZE); + + if (result == FILTER_ALLOW && ipproto == IPPROTO_TCP) { + struct flt_request *context_req = NULL; + + // add connection with state "SYN_SENT" + status = add_tcp_conn(ote_conn, TCP_STATE_SYN_SENT); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] tdi_connect: add_conn: 0x%x!\n", status)); + + result = FILTER_DENY; + goto done; // don't log this failure + } + + if (rule.log >= RULE_LOG_LOG) { + // set ote_conn->log_disconnect + ote_conn->log_disconnect = (rule.log >= RULE_LOG_COUNT); + + // copy request for completion (LOG success or not) + context_req = (struct flt_request *)malloc_np(sizeof(*context_req)); + if (context_req != NULL) { + memcpy(context_req, &request, sizeof(*context_req)); + + // don't free SID + request.sid_a = NULL; + + // don't log request in this time + rule.log = RULE_LOG_NOLOG; + } + } + + // set completion to add connection info to connection table + completion->routine = tdi_connect_complete; + completion->context = context_req; + } + + // if logging is needed log request + if (rule.log >= RULE_LOG_LOG) + log_request(&request); + +done: + // cleanup + if (ote_conn != NULL) + KeReleaseSpinLock(&g_ot_hash_guard, irql); + if (request.sid_a != NULL) + free(request.sid_a); + + if (result != FILTER_ALLOW) { + irp->IoStatus.Status = STATUS_REMOTE_NOT_LISTENING; // set fake status + } + + return result; +} + +NTSTATUS +tdi_connect_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) +{ + NTSTATUS status; + struct flt_request *request = (struct flt_request *)Context; + PIO_STACK_LOCATION irps = IoGetCurrentIrpStackLocation(Irp); + + KdPrint(("[tdi_fw] tdi_connect_complete: status 0x%x\n", Irp->IoStatus.Status)); + + if (Irp->IoStatus.Status == STATUS_SUCCESS) { + + if (request != NULL) + log_request(request); // log successful connection + + // very good! set connection state to "ESTABLISHED" + status = set_tcp_conn_state(irps->FileObject, TCP_STATE_ESTABLISHED_OUT); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] tdi_connect_complete: set_tcp_conn_state: 0x%x!\n", status)); + + // set fake status + Irp->IoStatus.Status = STATUS_REMOTE_NOT_LISTENING; + // TDI client will close connection object and connection will not "hang" (maybe) + goto done; + } + + // and update local address for this connection in state table + update_conn_info(DeviceObject, irps->FileObject); + + } else { + + if (request != NULL) { + + switch (Irp->IoStatus.Status) { // are status codes correct? + case STATUS_CONNECTION_REFUSED: + case STATUS_CONNECTION_RESET: + request->type = TYPE_CONNECT_RESET; + break; + case STATUS_CONNECTION_ABORTED: + case STATUS_CANCELLED: + request->type = TYPE_CONNECT_CANCELED; + break; + case STATUS_IO_TIMEOUT: + request->type = TYPE_CONNECT_TIMEOUT; + break; + case STATUS_NETWORK_UNREACHABLE: + case STATUS_HOST_UNREACHABLE: + case STATUS_PROTOCOL_UNREACHABLE: + case STATUS_PORT_UNREACHABLE: + request->type = TYPE_CONNECT_UNREACH; + break; + default: + request->type = TYPE_CONNECT_ERROR; + } + + // anyway save status + request->status = Irp->IoStatus.Status; + + log_request(request); + } + + del_tcp_conn(irps->FileObject, FALSE); + } + +done: + if (request != NULL) { + if (request->sid_a != NULL) + free(request->sid_a); + free(request); + } + return tdi_generic_complete(DeviceObject, Irp, Context); +} + +void +delayed_ucn(PVOID p) +{ + struct delayed_ucn_param *ucn_param = (struct delayed_ucn_param *)p; + + update_conn_info(ucn_param->devobj, ucn_param->fileobj); + + free(ucn_param); +} + +/* query local address and port for connection */ +void +update_conn_info(PDEVICE_OBJECT devobj, PFILE_OBJECT connobj) +{ + PIRP query_irp; + PMDL mdl = NULL; + struct uci_param *uci_param = NULL; + + // MUST be executed at PASSIVE_LEVEL + + if (KeGetCurrentIrql() != PASSIVE_LEVEL) { + // do it a bit later :-) + struct delayed_ucn_param *ucn_param = (struct delayed_ucn_param *)malloc_np(sizeof(*ucn_param)); + if (ucn_param != NULL) { + + memset(ucn_param, 0, sizeof(*ucn_param)); + + ucn_param->devobj = devobj; + ucn_param->fileobj = connobj; + + ExInitializeWorkItem(&ucn_param->item, delayed_ucn, ucn_param); + ExQueueWorkItem(&ucn_param->item, DelayedWorkQueue); // DelayedWorkQueue a good value? + + } else { + KdPrint(("[ndis_hk] tdi_connect_complete: malloc_np!\n")); + // so we'll live without known local address :-( + } + return; + } + + // we're at PASSIVE_LEVEL + + query_irp = TdiBuildInternalDeviceControlIrp(TDI_QUERY_INFORMATION, devobj, connobj, NULL, NULL); + if (query_irp == NULL) { + KdPrint(("[tdi_fw] update_conn_info: TdiBuildInternalDeviceControlIrp!\n")); + goto done; + } + + uci_param = (struct uci_param *)malloc_np(sizeof(*uci_param) + TDI_ADDRESS_INFO_MAX); + if (uci_param == NULL) { + KdPrint(("[tdi_fw] update_conn_info: malloc_np!\n")); + goto done; + } + + memset(uci_param, 0, sizeof(*uci_param) + TDI_ADDRESS_INFO_MAX); + uci_param->connobj = connobj; + + mdl = IoAllocateMdl(uci_param->address, TDI_ADDRESS_INFO_MAX, FALSE, FALSE, NULL); + if (mdl == NULL) { + KdPrint(("[tdi_fw] update_conn_info: IoAllocateMdl!\n")); + goto done; + } + MmBuildMdlForNonPagedPool(mdl); + + TdiBuildQueryInformation(query_irp, devobj, connobj, + update_conn_info_complete, uci_param, + TDI_QUERY_ADDRESS_INFO, mdl); + + IoCallDriver(devobj, query_irp); + + query_irp = NULL; + mdl = NULL; + uci_param = NULL; + +done: + // cleanup + if (mdl != NULL) + IoFreeMdl(mdl); + if (uci_param != NULL) + ExFreePool(uci_param); + if (query_irp != NULL) + IoCompleteRequest(query_irp, IO_NO_INCREMENT); +} + +NTSTATUS +update_conn_info_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) +{ + struct uci_param *param = (struct uci_param *)Context; + TA_ADDRESS *addr = ((TDI_ADDRESS_INFO *)(param->address))->Address.Address; + NTSTATUS status; + + status = set_tcp_conn_local(param->connobj, addr); + if (status != STATUS_SUCCESS) + KdPrint(("[tdi_fw] update_conn_info_complete: set_tcp_conn_local: 0x%x!\n", status)); + + // cleanup MDL to avoid unlocking pages from NonPaged pool + if (Irp->MdlAddress != NULL) { + IoFreeMdl(Irp->MdlAddress); + Irp->MdlAddress = NULL; + } + + free(param); + return STATUS_SUCCESS; +} + +//---------------------------------------------------------------------------- + +/* + * TDI_DISCONNECT handler + */ + +int +tdi_disconnect(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) +{ + TDI_REQUEST_KERNEL_DISCONNECT *param = (TDI_REQUEST_KERNEL_DISCONNECT *)(&irps->Parameters); + + KdPrint(("[tdi_fw] tdi_disconnect: connobj 0x%x (flags: 0x%x)\n", + irps->FileObject, param->RequestFlags)); + + if (param->RequestFlags & TDI_DISCONNECT_RELEASE) { + int state = get_tcp_conn_state_by_obj(irps->FileObject), new_state; + + if (state == TCP_STATE_ESTABLISHED_IN || state == TCP_STATE_ESTABLISHED_OUT) + new_state = TCP_STATE_FIN_WAIT1; + else if (state == TCP_STATE_CLOSE_WAIT) + new_state = TCP_STATE_LAST_ACK; + else + KdPrint(("[tdi_fw] tdi_disconnect: weird conn state: %d\n", state)); + + set_tcp_conn_state(irps->FileObject, new_state); + + completion->routine = tdi_disconnect_complete; + completion->context = (PVOID)new_state; + + } else { + + // set TCP_STATE_CLOSED and delete object in completion + + set_tcp_conn_state(irps->FileObject, TCP_STATE_CLOSED); + + completion->routine = tdi_disconnect_complete; + completion->context = (PVOID)TCP_STATE_CLOSED; + + } + + return FILTER_ALLOW; +} + +NTSTATUS +tdi_disconnect_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) +{ + PIO_STACK_LOCATION irps = IoGetCurrentIrpStackLocation(Irp); + int state = (int)Context; + + KdPrint(("[tdi_fw] tdi_disconnect_complete: connobj 0x%x; status: 0x%x\n", + irps->FileObject, Irp->IoStatus.Status)); + + if (Irp->IoStatus.Status == STATUS_SUCCESS) { + + // update TCP state table + + if (state == TCP_STATE_FIN_WAIT1) + set_tcp_conn_state(irps->FileObject, TCP_STATE_FIN_WAIT2); + else if (state == TCP_STATE_LAST_ACK) + del_tcp_conn(irps->FileObject, TRUE); + else if (state == TCP_STATE_CLOSED) + del_tcp_conn(irps->FileObject, TRUE); + else + KdPrint(("[tdi_fw] tdi_disconnect_complete: weird conn state: %d\n", state)); + } + + return tdi_generic_complete(DeviceObject, Irp, Context); +} diff --git a/tdifw-1.4.4/drv/disp_dg.c b/tdifw-1.4.4/drv/disp_dg.c new file mode 100644 index 00000000..44267417 --- /dev/null +++ b/tdifw-1.4.4/drv/disp_dg.c @@ -0,0 +1,281 @@ +/* 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: disp_dg.c,v 1.12 2003/09/04 15:20:09 dev Exp $ + +/* + * This file contains TDI_SEND_DATAGRAM and TDI_RECEIVE_DATAGRAM handlers + */ + +#include +#include +#include "sock.h" + +#include "dispatch.h" +#include "filter.h" +#include "memtrack.h" +#include "obj_tbl.h" +#include "sids.h" +#include "tdi_fw.h" + +static NTSTATUS tdi_receive_datagram_complete( + IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context); + + +//---------------------------------------------------------------------------- + +/* + * TDI_SEND_DATAGRAM handler + */ + +int +tdi_send_datagram(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) +{ + TDI_REQUEST_KERNEL_SENDDG *param = (TDI_REQUEST_KERNEL_SENDDG *)(&irps->Parameters); + TA_ADDRESS *local_addr, *remote_addr; + NTSTATUS status; + struct ot_entry *ote_addr = NULL; + KIRQL irql; + int result = FILTER_DENY, ipproto; + struct flt_request request; + struct flt_rule rule; + + memset(&request, 0, sizeof(request)); + + // check device object: UDP or RawIP + if (get_original_devobj(irps->DeviceObject, &ipproto) == NULL || + (ipproto != IPPROTO_UDP && ipproto != IPPROTO_IP)) { + // unknown device object! + KdPrint(("[tdi_fw] tdi_send_datagram: unknown DeviceObject 0x%x!\n", + irps->DeviceObject)); + goto done; + } + + // get local address of address object + + ote_addr = ot_find_fileobj(irps->FileObject, &irql); + if (ote_addr == NULL) { + KdPrint(("[tdi_fw] tdi_send_datagram: ot_find_fileobj(0x%x)!\n", irps->FileObject)); +#if DBG + // address object was created before driver was started + result = FILTER_ALLOW; +#endif + goto done; + } + + KdPrint(("[tdi_fw] tdi_send_datagram: addrobj 0x%x (size: %u)\n", irps->FileObject, + param->SendLength)); + + local_addr = (TA_ADDRESS *)(ote_addr->local_addr); + remote_addr = ((TRANSPORT_ADDRESS *)(param->SendDatagramInformation->RemoteAddress))->Address; + + KdPrint(("[tdi_fw] tdi_send_datagram(pid:%u/%u): %x:%u -> %x:%u\n", + ote_addr->pid, PsGetCurrentProcessId(), + ntohl(((TDI_ADDRESS_IP *)(local_addr->Address))->in_addr), + ntohs(((TDI_ADDRESS_IP *)(local_addr->Address))->sin_port), + ntohl(((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr), + ntohs(((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port))); + + request.struct_size = sizeof(request); + + request.type = TYPE_DATAGRAM; + request.direction = DIRECTION_OUT; + request.proto = ipproto; + + // don't use ote_addr->pid because one process can create address object + // but another one can send datagram on it + request.pid = (ULONG)PsGetCurrentProcessId(); + if (request.pid == 0) { + // some NetBT datagrams are sent in context of idle process: avoid it + request.pid = ote_addr->pid; + } + + // get user SID & attributes (can't call get_current_sid_a at DISPATCH_LEVEL) + if ((request.sid_a = copy_sid_a(ote_addr->sid_a, ote_addr->sid_a_size)) != NULL) + request.sid_a_size = ote_addr->sid_a_size; + + memcpy(&request.addr.from, &local_addr->AddressType, sizeof(struct sockaddr)); + memcpy(&request.addr.to, &remote_addr->AddressType, sizeof(struct sockaddr)); + request.addr.len = sizeof(struct sockaddr_in); + + memset(&rule, 0, sizeof(rule)); + + result = quick_filter(&request, &rule); + + memcpy(request.log_rule_id, rule.rule_id, RULE_ID_SIZE); + + if (rule.log >= RULE_LOG_LOG) { + ULONG bytes = param->SendLength; + + // traffic stats + KeAcquireSpinLockAtDpcLevel(&g_traffic_guard); + + g_traffic[TRAFFIC_TOTAL_OUT] += bytes; + + if (rule.log >= RULE_LOG_COUNT) { + request.log_bytes_out = bytes; + + g_traffic[TRAFFIC_COUNTED_OUT] += bytes; + + } else + request.log_bytes_out = (ULONG)-1; + + KeReleaseSpinLockFromDpcLevel(&g_traffic_guard); + + log_request(&request); + } + +done: + + // cleanup + if (ote_addr != NULL) + KeReleaseSpinLock(&g_ot_hash_guard, irql); + if (request.sid_a != NULL) + free(request.sid_a); + + if (result == FILTER_DENY) + irp->IoStatus.Status = STATUS_INVALID_ADDRESS; // set fake status + + return result; +} + +//---------------------------------------------------------------------------- + +/* + * TDI_RECEIVE_DATAGRAM handler + */ + +int +tdi_receive_datagram(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) +{ + KdPrint(("[tdi_fw] tdi_receive_datagram: addrobj 0x%x\n", irps->FileObject)); + + completion->routine = tdi_receive_datagram_complete; + + return FILTER_ALLOW; +} + +NTSTATUS +tdi_receive_datagram_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) +{ + PIO_STACK_LOCATION irps = IoGetCurrentIrpStackLocation(Irp); + TDI_REQUEST_KERNEL_RECEIVEDG *param = (TDI_REQUEST_KERNEL_RECEIVEDG *)(&irps->Parameters); + PFILE_OBJECT addrobj = irps->FileObject; + struct ot_entry *ote_addr = NULL; + KIRQL irql; + int result = FILTER_DENY, ipproto; + NTSTATUS status = STATUS_SUCCESS; + struct flt_request request; + struct flt_rule rule; + TA_ADDRESS *local_addr, *remote_addr; + + memset(&request, 0, sizeof(request)); + + // check device object: UDP or RawIP + if (get_original_devobj(DeviceObject, &ipproto) == NULL || + (ipproto != IPPROTO_UDP && ipproto != IPPROTO_IP)) { + // unknown device object! + KdPrint(("[tdi_fw] tdi_receive_datagram_complete: unknown DeviceObject 0x%x!\n", + DeviceObject)); + status = STATUS_UNSUCCESSFUL; + goto done; + } + + KdPrint(("[tdi_fw] tdi_receive_datagram_complete: addrobj 0x%x; status 0x%x; information %u\n", + addrobj, Irp->IoStatus.Status, Irp->IoStatus.Information)); + + if (Irp->IoStatus.Status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] tdi_receive_datagram_complete: status 0x%x\n", + Irp->IoStatus.Status)); + status = Irp->IoStatus.Status; + goto done; + } + + ote_addr = ot_find_fileobj(addrobj, &irql); + if (ote_addr == NULL) { + KdPrint(("[tdi_fw] tdi_receive_datagram_complete: ot_find_fileobj(0x%x)!\n", + addrobj)); + status = STATUS_UNSUCCESSFUL; + goto done; + } + + request.struct_size = sizeof(request); + + request.type = TYPE_DATAGRAM; + request.direction = DIRECTION_IN; + request.proto = ipproto; + request.pid = ote_addr->pid; + + // get user SID & attributes! + if ((request.sid_a = copy_sid_a(ote_addr->sid_a, ote_addr->sid_a_size)) != NULL) + request.sid_a_size = ote_addr->sid_a_size; + + local_addr = (TA_ADDRESS *)(ote_addr->local_addr); + remote_addr = ((TRANSPORT_ADDRESS *)(param->ReceiveDatagramInformation->RemoteAddress))->Address; + + KdPrint(("[tdi_fw] tdi_receive_datagram_complete(pid:%u): %x:%u -> %x:%u\n", + ote_addr->pid, + ntohl(((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr), + ntohs(((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port), + ntohl(((TDI_ADDRESS_IP *)(local_addr->Address))->in_addr), + ntohs(((TDI_ADDRESS_IP *)(local_addr->Address))->sin_port))); + + memcpy(&request.addr.from, &remote_addr->AddressType, sizeof(struct sockaddr)); + memcpy(&request.addr.to, &local_addr->AddressType, sizeof(struct sockaddr)); + request.addr.len = sizeof(struct sockaddr_in); + + memset(&rule, 0, sizeof(rule)); + + result = quick_filter(&request, &rule); + + memcpy(request.log_rule_id, rule.rule_id, RULE_ID_SIZE); + + if (rule.log >= RULE_LOG_LOG) { + ULONG bytes = Irp->IoStatus.Information; + + // traffic stats + KeAcquireSpinLockAtDpcLevel(&g_traffic_guard); + + g_traffic[TRAFFIC_TOTAL_IN] += bytes; + + if (rule.log >= RULE_LOG_COUNT) { + request.log_bytes_in = bytes; + + g_traffic[TRAFFIC_COUNTED_IN] += bytes; + + } else + request.log_bytes_in = (ULONG)-1; + + KeReleaseSpinLockFromDpcLevel(&g_traffic_guard); + + log_request(&request); + } + +done: + // convert result to NTSTATUS + if (result == FILTER_ALLOW) + status = STATUS_SUCCESS; + else { /* FILTER_DENY */ + + if (status == STATUS_SUCCESS) + status = Irp->IoStatus.Status = STATUS_ACCESS_DENIED; // good status? + + } + + // cleanup + if (ote_addr != NULL) + KeReleaseSpinLock(&g_ot_hash_guard, irql); + if (request.sid_a != NULL) + free(request.sid_a); + + return tdi_generic_complete(DeviceObject, Irp, Context); +} diff --git a/tdifw-1.4.4/drv/disp_ev.c b/tdifw-1.4.4/drv/disp_ev.c new file mode 100644 index 00000000..85b97ad7 --- /dev/null +++ b/tdifw-1.4.4/drv/disp_ev.c @@ -0,0 +1,160 @@ +/* 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: disp_ev.c,v 1.5 2003/07/14 12:12:36 dev Exp $ + +/* + * This file contains TDI_SET_EVENT_HANDLER handler + */ + +#include +#include +#include "sock.h" + +#include "conn_state.h" +#include "dispatch.h" +#include "events.h" +#include "memtrack.h" +#include "obj_tbl.h" +#include "sids.h" +#include "tdi_fw.h" + +int +tdi_set_event_handler(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) +{ + PTDI_REQUEST_KERNEL_SET_EVENT r = (PTDI_REQUEST_KERNEL_SET_EVENT)&irps->Parameters; + NTSTATUS status; + struct ot_entry *ote_addr = NULL; + KIRQL irql; + int result = FILTER_DENY; + TDI_EVENT_CONTEXT *ctx; + + KdPrint(("[tdi_fw] tdi_set_event_handler: [%s] devobj 0x%x; addrobj 0x%x; EventType: %d\n", + r->EventHandler ? "(+)ADD" : "(-)REMOVE", + irps->DeviceObject, + irps->FileObject, + r->EventType)); + + ote_addr = ot_find_fileobj(irps->FileObject, &irql); + if (ote_addr == NULL) { + KdPrint(("[tdi_fw] tdi_set_event_handler: ot_find_fileobj(0x%x)\n", irps->FileObject)); + if (r->EventHandler == NULL) { + // for fileobjects loaded earlier than our driver allow removing + result = FILTER_ALLOW; + } + goto done; + } + + if (r->EventType < 0 || r->EventType >= MAX_EVENT) { + KdPrint(("[tdi_fw] tdi_set_event_handler: unknown EventType %d!\n", r->EventType)); + result = FILTER_ALLOW; + goto done; + } + + ctx = &ote_addr->ctx[r->EventType]; + + if (r->EventHandler != NULL) { + /* add EventHandler */ + int i; + + for (i = 0; g_tdi_event_handlers[i].event != (ULONG)-1; i++) + if (g_tdi_event_handlers[i].event == r->EventType) + break; + + if (g_tdi_event_handlers[i].event == (ULONG)-1) { + KdPrint(("[tdi_fw] tdi_set_event_handler: unknown EventType %d!\n", r->EventType)); + result = FILTER_ALLOW; + goto done; + } + + ctx->old_handler = r->EventHandler; + ctx->old_context = r->EventContext; + + if (g_tdi_event_handlers[i].handler != NULL) { + r->EventHandler = g_tdi_event_handlers[i].handler; + r->EventContext = ctx; + } else { + r->EventHandler = NULL; + r->EventContext = NULL; + } + + KdPrint(("[tdi_fw] tdi_set_event_handler: old_handler 0x%x; old_context 0x%x\n", + r->EventHandler, r->EventContext)); + + } else { + /* remove EventHandler */ + ctx->old_handler = NULL; + ctx->old_context = NULL; + } + + // change LISTEN state + if (r->EventType == TDI_EVENT_CONNECT) { + TA_ADDRESS *local_addr; + + if (r->EventHandler != NULL) { + // add "LISTEN" info + status = add_listen(ote_addr); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] tdi_set_event_handler: add_listen: 0x%x!\n", status)); + goto done; + } + } else if (ote_addr->listen_entry != NULL) { + // remove "LISTEN" info + del_listen_obj(ote_addr->listen_entry, FALSE); + + ote_addr->listen_entry = NULL; + } + + // log it if address is not 127.0.0.1 + local_addr = (TA_ADDRESS *)(ote_addr->local_addr); + if (ntohl(((TDI_ADDRESS_IP *)(local_addr->Address))->in_addr) != 0x7f000001) { + struct flt_request request; + + memset(&request, 0, sizeof(request)); + + request.struct_size = sizeof(request); + + request.type = (r->EventHandler != NULL) ? TYPE_LISTEN : TYPE_NOT_LISTEN; + request.proto = IPPROTO_TCP; // correct? + + if (r->EventHandler != NULL) { + // for removing event handler ProcessNotifyProc can be already called + request.pid = (ULONG)PsGetCurrentProcessId(); + if (request.pid == 0) { + // avoid idle process pid (XXX do we need this?) + request.pid = ote_addr->pid; + } + } else + request.pid = (ULONG)-1; + + // get user SID & attributes (can't call get_current_sid_a at DISPATCH_LEVEL) + if ((request.sid_a = copy_sid_a(ote_addr->sid_a, ote_addr->sid_a_size)) != NULL) + request.sid_a_size = ote_addr->sid_a_size; + + memcpy(&request.addr.from, &local_addr->AddressType, sizeof(struct sockaddr)); + request.addr.len = sizeof(struct sockaddr_in); + + log_request(&request); + + if (request.sid_a != NULL) + free(request.sid_a); + } + } + + result = FILTER_ALLOW; +done: + // cleanup + if (ote_addr != NULL) + KeReleaseSpinLock(&g_ot_hash_guard, irql); + + return result; +} diff --git a/tdifw-1.4.4/drv/disp_obj.c b/tdifw-1.4.4/drv/disp_obj.c new file mode 100644 index 00000000..915fbe4a --- /dev/null +++ b/tdifw-1.4.4/drv/disp_obj.c @@ -0,0 +1,460 @@ +/* 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: disp_obj.c,v 1.10 2003/09/04 15:20:09 dev Exp $ + +/* + * This file contains TDI_CREATE, TDI_CLEANUP, TDI_ASSOCIATE_ADDRESS and + * TDI_DISASSOCIATE_ADDRESS handlers + */ + +#include +#include +#include "sock.h" + +#include "conn_state.h" +#include "dispatch.h" +#include "events.h" +#include "memtrack.h" +#include "obj_tbl.h" +#include "pid_pname.h" +#include "sids.h" +#include "tdi_fw.h" + +/* IRP completion routines and their contexts */ + +static NTSTATUS tdi_create_addrobj_complete( + IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context); + +// context for tdi_create_addrobj_complete2 +typedef struct { + TDI_ADDRESS_INFO *tai; /* address info -- result of TDI_QUERY_ADDRESS_INFO */ + PFILE_OBJECT fileobj; /* FileObject from IO_STACK_LOCATION */ +} TDI_CREATE_ADDROBJ2_CTX; + +static NTSTATUS tdi_create_addrobj_complete2( + IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context); + +//---------------------------------------------------------------------------- + +/* + * TDI_CREATE handler + */ + +int +tdi_create(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) +{ + NTSTATUS status; + FILE_FULL_EA_INFORMATION *ea = (FILE_FULL_EA_INFORMATION *)irp->AssociatedIrp.SystemBuffer; + + /* pid resolving stuff: a good place for it (PASSIVE level, begin of working with TDI-objects) */ + ULONG pid = (ULONG)PsGetCurrentProcessId(); + + // if process name is unknown try to resolve it + if (!pid_pname_resolve(pid, NULL, 0)) { + KEVENT event; + struct flt_request request; + + KeInitializeEvent(&event, NotificationEvent, FALSE); + pid_pname_set_event(pid, &event); + + memset(&request, 0, sizeof(request)); + request.struct_size = sizeof(request); + + request.type = TYPE_RESOLVE_PID; + request.pid = pid; + + // get user SID & attributes! + request.sid_a = get_current_sid_a(&request.sid_a_size); + + if (log_request(&request)) { + // wait a little for reply from user-mode application + LARGE_INTEGER li; + li.QuadPart = 5000 * -10000; // 5 sec + + status = KeWaitForSingleObject(&event, UserRequest, KernelMode, FALSE, &li); + + } else { + // check all rulesets: we've got the only _default_ ruleset active + status = default_chain_only() ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; + } + + if (request.sid_a != NULL) + free(request.sid_a); + + // reset wait event + pid_pname_set_event(pid, NULL); + + if (status != STATUS_SUCCESS) + return FILTER_DENY; // deny it! + } + + /* TDI_CREATE related stuff */ + + if (ea != NULL) { + /* + * We have FILE_FULL_EA_INFORMATION + */ + + PDEVICE_OBJECT devobj; + int ipproto; + + devobj = get_original_devobj(irps->DeviceObject, &ipproto); + if (devobj == NULL) { + KdPrint(("[tdi_fw] tdi_create: unknown device object 0x%x!\n", irps->DeviceObject)); + return FILTER_DENY; + } + // NOTE: for RawIp you can extract protocol number from irps->FileObject->FileName + + if (ea->EaNameLength == TDI_TRANSPORT_ADDRESS_LENGTH && + memcmp(ea->EaName, TdiTransportAddress, TDI_TRANSPORT_ADDRESS_LENGTH) == 0) { + + PIRP query_irp; + + /* + * This is creation of address object + */ + + KdPrint(("[tdi_fw] tdi_create: devobj 0x%x; addrobj 0x%x\n", + irps->DeviceObject, + irps->FileObject)); + + status = ot_add_fileobj(irps->DeviceObject, irps->FileObject, FILEOBJ_ADDROBJ, ipproto, NULL); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] tdi_create: ot_add_fileobj: 0x%x\n", status)); + return FILTER_DENY; + } + + // while we're on PASSIVE_LEVEL build control IRP for completion + query_irp = TdiBuildInternalDeviceControlIrp(TDI_QUERY_INFORMATION, + devobj, irps->FileObject, NULL, NULL); + if (query_irp == NULL) { + KdPrint(("[tdi_fw] tdi_create: TdiBuildInternalDeviceControlIrp\n")); + return FILTER_DENY; + } + + /* set IRP completion & context for completion */ + + completion->routine = tdi_create_addrobj_complete; + completion->context = query_irp; + + } else if (ea->EaNameLength == TDI_CONNECTION_CONTEXT_LENGTH && + memcmp(ea->EaName, TdiConnectionContext, TDI_CONNECTION_CONTEXT_LENGTH) == 0) { + + /* + * This is creation of connection object + */ + + CONNECTION_CONTEXT conn_ctx = *(CONNECTION_CONTEXT *) + (ea->EaName + ea->EaNameLength + 1); + + KdPrint(("[tdi_fw] tdi_create: devobj 0x%x; connobj 0x%x; conn_ctx 0x%x\n", + irps->DeviceObject, + irps->FileObject, + conn_ctx)); + + status = ot_add_fileobj(irps->DeviceObject, irps->FileObject, + FILEOBJ_CONNOBJ, ipproto, conn_ctx); + + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] tdi_create: ot_add_fileobj: 0x%x\n", status)); + return FILTER_DENY; + } + } + + } else { + /* + * This is creation of control object + */ + + KdPrint(("[tdi_fw] tdi_create(pid:%u): devobj 0x%x; Control Object: 0x%x\n", + pid, irps->DeviceObject, irps->FileObject)); + } + + return FILTER_ALLOW; +} + +/* this completion routine queries address and port from address object */ +NTSTATUS +tdi_create_addrobj_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) +{ + NTSTATUS status; + PIO_STACK_LOCATION irps = IoGetCurrentIrpStackLocation(Irp); + PIRP query_irp = (PIRP)Context; + PDEVICE_OBJECT devobj; + TDI_CREATE_ADDROBJ2_CTX *ctx = NULL; + PMDL mdl = NULL; + + KdPrint(("[tdi_fw] tdi_create_addrobj_complete: devobj 0x%x; addrobj 0x%x\n", + DeviceObject, irps->FileObject)); + + if (Irp->IoStatus.Status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] tdi_create_addrobj_complete: status 0x%x\n", Irp->IoStatus.Status)); + + status = Irp->IoStatus.Status; + goto done; + } + + // query addrobj address:port + + ctx = (TDI_CREATE_ADDROBJ2_CTX *)malloc_np(sizeof(TDI_CREATE_ADDROBJ2_CTX)); + if (ctx == NULL) { + KdPrint(("[tdi_fw] tdi_create_addrobj_complete: malloc_np\n")); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto done; + } + ctx->fileobj = irps->FileObject; + + ctx->tai = (TDI_ADDRESS_INFO *)malloc_np(TDI_ADDRESS_INFO_MAX); + if (ctx->tai == NULL) { + KdPrint(("[tdi_fw] tdi_create_addrobj_complete: malloc_np!\n")); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto done; + } + + mdl = IoAllocateMdl(ctx->tai, TDI_ADDRESS_INFO_MAX, FALSE, FALSE, NULL); + if (mdl == NULL) { + KdPrint(("[tdi_fw] tdi_create_addrobj_complete: IoAllocateMdl!\n")); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto done; + } + MmBuildMdlForNonPagedPool(mdl); + + devobj = get_original_devobj(DeviceObject, NULL); // use original devobj! + if (devobj == NULL) { + KdPrint(("[tdi_fw] tdi_create_addrobj_complete: get_original_devobj!\n")); + + status = STATUS_INVALID_PARAMETER; + goto done; + } + + TdiBuildQueryInformation(query_irp, devobj, irps->FileObject, + tdi_create_addrobj_complete2, ctx, + TDI_QUERY_ADDRESS_INFO, mdl); + + status = IoCallDriver(devobj, query_irp); + query_irp = NULL; + mdl = NULL; + ctx = NULL; + + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] tdi_create_addrobj_complete: IoCallDriver: 0x%x\n", status)); + goto done; + } + + status = STATUS_SUCCESS; + +done: + // cleanup + if (mdl != NULL) + IoFreeMdl(mdl); + + if (ctx != NULL) { + if (ctx->tai != NULL) + free(ctx->tai); + free(ctx); + } + + if (query_irp != NULL) + IoCompleteRequest(query_irp, IO_NO_INCREMENT); + + Irp->IoStatus.Status = status; + + if (status != STATUS_SUCCESS) { + // tdi_create failed - remove fileobj from hash + ot_del_fileobj(irps->FileObject, NULL); + } + + return tdi_generic_complete(DeviceObject, Irp, Context); +} + +/* this completion routine gets address and port from reply to TDI_QUERY_ADDRESS_INFO */ +NTSTATUS +tdi_create_addrobj_complete2(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) +{ + NTSTATUS status; + TDI_CREATE_ADDROBJ2_CTX *ctx = (TDI_CREATE_ADDROBJ2_CTX *)Context; + TA_ADDRESS *addr = ctx->tai->Address.Address; + struct ot_entry *ote_addr; + KIRQL irql; + int ipproto; + + KdPrint(("[tdi_fw] tdi_create_addrobj_complete2: address: %x:%u\n", + ntohl(((TDI_ADDRESS_IP *)(addr->Address))->in_addr), + ntohs(((TDI_ADDRESS_IP *)(addr->Address))->sin_port))); + + // save address + + ote_addr = ot_find_fileobj(ctx->fileobj, &irql); + if (ote_addr == NULL) { + KdPrint(("[tdi_fw] tdi_create_addrobj_complete2: ot_find_fileobj(0x%x)\n", + ctx->fileobj)); + status = STATUS_OBJECT_NAME_NOT_FOUND; + goto done; + } + + if (addr->AddressLength > sizeof(ote_addr->local_addr)) { + KdPrint(("[tdi_fw] tdi_create_addrobj_complete2: address too long! (%u)\n", + addr->AddressLength)); + status = STATUS_BUFFER_OVERFLOW; + goto done; + } + memcpy(ote_addr->local_addr, addr, addr->AddressLength); + + if (ote_addr->ipproto != IPPROTO_TCP) { + // set "LISTEN" state for this addrobj + status = add_listen(ote_addr); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] tdi_create_addrobj_complete2: add_listen: 0x%x!\n", status)); + goto done; + } + } + + status = STATUS_SUCCESS; +done: + if (ote_addr != NULL) + KeReleaseSpinLock(&g_ot_hash_guard, irql); + + // cleanup MDL to avoid unlocking pages from NonPaged pool + if (Irp->MdlAddress != NULL) { + IoFreeMdl(Irp->MdlAddress); + Irp->MdlAddress = NULL; + } + + free(ctx->tai); + free(ctx); + + // success anyway + return STATUS_SUCCESS; +} + +//---------------------------------------------------------------------------- + +/* + * TDI_CLEANUP handler + */ + +int +tdi_cleanup(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) +{ + NTSTATUS status; + int type; + + // delete fileobj + + status = ot_del_fileobj(irps->FileObject, &type); + if (status != STATUS_SUCCESS) + KdPrint(("[tdi_fw] tdi_cleanup: del_fileobj: 0x%x!\n", status)); + else + KdPrint(("[tdi_fw] tdi_cleanup: fileobj 0x%x, type %d\n", irps->FileObject, type)); + + // success anyway + return FILTER_ALLOW; +} + +//---------------------------------------------------------------------------- + +/* + * TDI_ASSOCIATE_ADDRESS handler + * + * With help of this routine we can get address object by connection object + * and get connection object by connection context and address object + */ +int +tdi_associate_address(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) +{ + HANDLE addr_handle = ((TDI_REQUEST_KERNEL_ASSOCIATE *)(&irps->Parameters))->AddressHandle; + PFILE_OBJECT addrobj = NULL; + NTSTATUS status; + struct ot_entry *ote_conn = NULL; + KIRQL irql; + int result = FILTER_DENY; + + KdPrint(("[tdi_fw] tdi_associate_address: devobj 0x%x; connobj 0x%x\n", + irps->DeviceObject, irps->FileObject)); + + status = ObReferenceObjectByHandle(addr_handle, GENERIC_READ, NULL, KernelMode, &addrobj, NULL); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] tdi_associate_address: ObReferenceObjectByHandle: 0x%x\n", status)); + goto done; + } + + KdPrint(("[tdi_fw] tdi_associate_address: connobj = 0x%x ---> addrobj = 0x%x\n", + irps->FileObject, addrobj)); + + // associate addrobj with connobj + + ote_conn = ot_find_fileobj(irps->FileObject, &irql); + if (ote_conn == NULL) { + KdPrint(("[tdi_fw] tdi_associate_address: ot_find_fileobj(0x%x)\n", irps->FileObject)); + goto done; + } + ote_conn->associated_fileobj = addrobj; + + // add (conn_ctx, addrobj)->connobj + + status = ot_add_conn_ctx(addrobj, ote_conn->conn_ctx, irps->FileObject); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] tdi_associate_address: ot_add_conn_ctx: 0x%x\n", status)); + goto done; + } + + result = FILTER_ALLOW; +done: + if (addrobj != NULL) + ObDereferenceObject(addrobj); + + // cleanup + if (ote_conn != NULL) + KeReleaseSpinLock(&g_ot_hash_guard, irql); + + return result; +} + +//---------------------------------------------------------------------------- + +/* + * TDI_DISASSOCIATE_ADDRESS handler + */ +int +tdi_disassociate_address(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) +{ + struct ot_entry *ote_conn = NULL; + KIRQL irql; + NTSTATUS status; + + KdPrint(("[tdi_fw] tdi_disassociate_address: connobj 0x%x\n", irps->FileObject)); + + // delete connnection object + ote_conn = ot_find_fileobj(irps->FileObject, &irql); + if (ote_conn == NULL) { + KdPrint(("[tdi_fw] tdi_disassociate_address: ot_find_fileobj(0x%x)\n", irps->FileObject)); + goto done; + } + + // delete link of (addrobj, conn_ctx)->connobj + status = ot_del_conn_ctx(ote_conn->associated_fileobj, ote_conn->conn_ctx); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] tdi_disassociate_address: ot_del_conn_ctx: 0x%x\n", status)); + goto done; + } + +done: + if (ote_conn != NULL) + KeReleaseSpinLock(&g_ot_hash_guard, irql); + + // success anyway + return FILTER_ALLOW; +} diff --git a/tdifw-1.4.4/drv/disp_sr.c b/tdifw-1.4.4/drv/disp_sr.c new file mode 100644 index 00000000..6aaae640 --- /dev/null +++ b/tdifw-1.4.4/drv/disp_sr.c @@ -0,0 +1,122 @@ +/* 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: disp_sr.c,v 1.2 2003/09/04 15:20:09 dev Exp $ + +/* + * This file contains TDI_SEND and TDI_RECEIVE handlers + */ + +#include +#include +#include "sock.h" + +#include "dispatch.h" +#include "memtrack.h" +#include "obj_tbl.h" +#include "tdi_fw.h" + +static NTSTATUS tdi_receive_complete( + IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context); + +//---------------------------------------------------------------------------- + +/* + * TDI_SEND handler + */ + +int +tdi_send(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) +{ + TDI_REQUEST_KERNEL_SEND *param = (TDI_REQUEST_KERNEL_SEND *)(&irps->Parameters); + struct ot_entry *ote_conn; + KIRQL irql; + + KdPrint(("[tdi_fw] tdi_send: connobj: 0x%x; SendLength: %u; SendFlags: 0x%x\n", + irps->FileObject, param->SendLength, param->SendFlags)); + + ote_conn = ot_find_fileobj(irps->FileObject, &irql); + if (ote_conn != NULL) { + ULONG bytes = param->SendLength; + + ote_conn->bytes_out += bytes; + + // traffic stats + KeAcquireSpinLockAtDpcLevel(&g_traffic_guard); + + g_traffic[TRAFFIC_TOTAL_OUT] += bytes; + + if (ote_conn->log_disconnect) + g_traffic[TRAFFIC_COUNTED_OUT] += bytes; + + KeReleaseSpinLockFromDpcLevel(&g_traffic_guard); + + KeReleaseSpinLock(&g_ot_hash_guard, irql); + } + + // TODO: process TDI_SEND_AND_DISCONNECT flag (used by IIS for example) + + return FILTER_ALLOW; +} + +//---------------------------------------------------------------------------- + +/* + * TDI_RECEIVE handler + */ + +int +tdi_receive(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) +{ + TDI_REQUEST_KERNEL_RECEIVE *param = (TDI_REQUEST_KERNEL_RECEIVE *)(&irps->Parameters); + + KdPrint(("[tdi_fw] tdi_receive: connobj: 0x%x; ReceiveLength: %u; ReceiveFlags: 0x%x\n", + irps->FileObject, param->ReceiveLength, param->ReceiveFlags)); + + if (!(param->ReceiveFlags & TDI_RECEIVE_PEEK)) { + completion->routine = tdi_receive_complete; + } + + return FILTER_ALLOW; +} + +NTSTATUS +tdi_receive_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) +{ + PIO_STACK_LOCATION irps = IoGetCurrentIrpStackLocation(Irp); + struct ot_entry *ote_conn; + KIRQL irql; + + KdPrint(("[tdi_fw] tdi_receive_complete: connobj: 0x%x; status: 0x%x; received: %u\n", + irps->FileObject, Irp->IoStatus.Status, Irp->IoStatus.Information)); + + ote_conn = ot_find_fileobj(irps->FileObject, &irql); + if (ote_conn != NULL) { + ULONG bytes = Irp->IoStatus.Information; + + ote_conn->bytes_in += bytes; + + // traffic stats + KeAcquireSpinLockAtDpcLevel(&g_traffic_guard); + + g_traffic[TRAFFIC_TOTAL_IN] += bytes; + + if (ote_conn->log_disconnect) + g_traffic[TRAFFIC_COUNTED_IN] += bytes; + + KeReleaseSpinLockFromDpcLevel(&g_traffic_guard); + + KeReleaseSpinLock(&g_ot_hash_guard, irql); + } + + return tdi_generic_complete(DeviceObject, Irp, Context); +} diff --git a/tdifw-1.4.4/drv/dispatch.c b/tdifw-1.4.4/drv/dispatch.c new file mode 100644 index 00000000..cf552a3f --- /dev/null +++ b/tdifw-1.4.4/drv/dispatch.c @@ -0,0 +1,48 @@ +/* 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: dispatch.c,v 1.7 2003/09/04 15:20:09 dev Exp $ + +/* + * Dispatch routines for TDI ioctls + */ + +#include +#include +#include "sock.h" + +#include "dispatch.h" + +#if DBG +# define ENTRY(code, fn) {code, fn, #code} +# define LAST_ENTRY {0, NULL, NULL} +#else +# define ENTRY(code, fn) {code, fn} +# define LAST_ENTRY {0, NULL} +#endif + +struct tdi_ioctl g_tdi_ioctls[] = { + ENTRY(TDI_ASSOCIATE_ADDRESS, tdi_associate_address), + ENTRY(TDI_CONNECT, tdi_connect), + ENTRY(TDI_DISASSOCIATE_ADDRESS, tdi_disassociate_address), + ENTRY(TDI_SET_EVENT_HANDLER, tdi_set_event_handler), + ENTRY(TDI_SEND_DATAGRAM, tdi_send_datagram), + ENTRY(TDI_RECEIVE_DATAGRAM, tdi_receive_datagram), + ENTRY(TDI_DISCONNECT, tdi_disconnect), + ENTRY(TDI_SEND, tdi_send), + ENTRY(TDI_RECEIVE, tdi_receive), +#if 1 // for now only deny stubs for security reasons + ENTRY(TDI_ACCEPT, tdi_deny_stub), + ENTRY(TDI_LISTEN, tdi_deny_stub), +#endif + LAST_ENTRY +}; diff --git a/tdifw-1.4.4/drv/dispatch.h b/tdifw-1.4.4/drv/dispatch.h new file mode 100644 index 00000000..0d8e3759 --- /dev/null +++ b/tdifw-1.4.4/drv/dispatch.h @@ -0,0 +1,49 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs) +// +// $Id: dispatch.h,v 1.4 2003/09/04 15:20:09 dev Exp $ + +#ifndef _dispatch_h_ +#define _dispatch_h_ + +// information about completion routine +struct completion { + PIO_COMPLETION_ROUTINE routine; + PVOID context; +}; + +/* + * TDI ioctl dispatcher function + * returns FILTER_xxx + */ +typedef int tdi_ioctl_fn_t(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion); + +// IRP_MJ_CREATE, IRP_MJ_CLEANUP dispatch routines +extern tdi_ioctl_fn_t tdi_create, tdi_cleanup; + +// IRP_MJ_INTERNAL_DEVICE_CONTROL ioctl dispatch routines +extern tdi_ioctl_fn_t + tdi_associate_address, + tdi_connect, + tdi_disassociate_address, + tdi_set_event_handler, + tdi_send_datagram, + tdi_receive_datagram, + tdi_disconnect, + tdi_send, + tdi_receive, + tdi_deny_stub; + +// helper struct for calling of TDI ioctls +struct tdi_ioctl { + UCHAR MinorFunction; + tdi_ioctl_fn_t *fn; + +#if DBG + // for debugging + const char *desc; +#endif +}; + +extern struct tdi_ioctl g_tdi_ioctls[]; + +#endif diff --git a/tdifw-1.4.4/drv/ev_conn.c b/tdifw-1.4.4/drv/ev_conn.c new file mode 100644 index 00000000..6749e000 --- /dev/null +++ b/tdifw-1.4.4/drv/ev_conn.c @@ -0,0 +1,404 @@ +/* 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: ev_conn.c,v 1.9 2005/09/01 11:19:11 vlad Exp $ + +/* + * This file contain TDI_EVENT_CONNECT & TDI_EVENT_DISCONNECT handlers + */ + +#include +#include +#include "sock.h" + +#include "conn_state.h" +#include "dispatch.h" +#include "events.h" +#include "memtrack.h" +#include "obj_tbl.h" +#include "sids.h" +#include "tdi_fw.h" + +struct accept_param { + PIO_COMPLETION_ROUTINE old_cr; + PVOID old_context; + PFILE_OBJECT fileobj; + UCHAR old_control; +}; + +static NTSTATUS tdi_evconn_accept_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context); + +//---------------------------------------------------------------------------- + +/* + * TDI_EVENT_CONNECT handler + */ + +NTSTATUS +tdi_event_connect( + IN PVOID TdiEventContext, + IN LONG RemoteAddressLength, + IN PVOID RemoteAddress, + IN LONG UserDataLength, + IN PVOID UserData, + IN LONG OptionsLength, + IN PVOID Options, + OUT CONNECTION_CONTEXT *ConnectionContext, + OUT PIRP *AcceptIrp) +{ + TDI_EVENT_CONTEXT *ctx = (TDI_EVENT_CONTEXT *)TdiEventContext; + TA_ADDRESS *remote_addr = ((TRANSPORT_ADDRESS *)RemoteAddress)->Address, *local_addr; + struct ot_entry *ote_addr = NULL, *ote_conn = NULL; + KIRQL irql; + struct flt_request request; + struct flt_rule rule; + int result = FILTER_DENY; + NTSTATUS status; + PIO_STACK_LOCATION irps = NULL; + struct accept_param *param = NULL; + + memset(&request, 0, sizeof(request)); + + KdPrint(("[tdi_fw] tdi_event_connect: addrobj 0x%x\n", ctx->fileobj)); + + ote_addr = ot_find_fileobj(ctx->fileobj, &irql); + if (ote_addr == NULL) { + KdPrint(("[tdi_fw] tdi_event_connect: ot_find_fileobj(0x%x)\n", ctx->fileobj)); + goto done; + } + + local_addr = (TA_ADDRESS *)(ote_addr->local_addr); + + KdPrint(("[tdi_fw] tdi_event_connect(pid:%u): %x:%u -> %x:%u\n", + ote_addr->pid, + ntohl(((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr), + ntohs(((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port), + ntohl(((TDI_ADDRESS_IP *)(local_addr->Address))->in_addr), + ntohs(((TDI_ADDRESS_IP *)(local_addr->Address))->sin_port))); + + /* + * request quick filter + */ + + request.struct_size = sizeof(request); + + request.type = TYPE_CONNECT; + request.direction = DIRECTION_IN; + request.proto = IPPROTO_TCP; + request.pid = ote_addr->pid; + + // get user SID & attributes! + if ((request.sid_a = copy_sid_a(ote_addr->sid_a, ote_addr->sid_a_size)) != NULL) + request.sid_a_size = ote_addr->sid_a_size; + + memcpy(&request.addr.from, &remote_addr->AddressType, sizeof(struct sockaddr)); + memcpy(&request.addr.to, &local_addr->AddressType, sizeof(struct sockaddr)); + request.addr.len = sizeof(struct sockaddr_in); + + result = quick_filter(&request, &rule); + + memcpy(request.log_rule_id, rule.rule_id, RULE_ID_SIZE); + + // log request later + + if (result == FILTER_DENY) + goto done; + + result = FILTER_DENY; + + // leave spinlock before calling original handler + KeReleaseSpinLock(&g_ot_hash_guard, irql); + ote_addr = NULL; + + /* + * run original handler + */ + + status = ((PTDI_IND_CONNECT)(ctx->old_handler)) + (ctx->old_context, RemoteAddressLength, RemoteAddress, + UserDataLength, UserData, OptionsLength, Options, ConnectionContext, + AcceptIrp); + + if (status != STATUS_MORE_PROCESSING_REQUIRED || *AcceptIrp == NULL) { + KdPrint(("[tdi_fw] tdi_event_connect: status from original handler: 0x%x\n", status)); + goto done; + } + + /* + * reinitialize connobj + */ + + irps = IoGetCurrentIrpStackLocation(*AcceptIrp); + KdPrint(("[tdi_fw] tdi_event_connect: connobj 0x%x\n", irps->FileObject)); + + // patch *AcceptIrp to change completion routine + + param = (struct accept_param *)malloc_np(sizeof(*param)); + if (param == NULL) { + KdPrint(("[tdi_fw] tdi_event_connect: malloc_np!\n")); + status = STATUS_INSUFFICIENT_RESOURCES; + goto done; + } + + param->old_cr = irps->CompletionRoutine; + param->old_context = irps->Context; + param->fileobj = irps->FileObject; + + param->old_control = irps->Control; + + // can't use IoSetCompletionRoutine because it uses next not current stack location + irps->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; + irps->CompletionRoutine = tdi_evconn_accept_complete; + irps->Context = param; + + param = NULL; + + // find connobj for changing + + ote_conn = ot_find_fileobj(irps->FileObject, &irql); + if (ote_conn == NULL) { + KdPrint(("[tdi_fw] tdi_event_connect: ot_find_fileobj(0x%x)\n", irps->FileObject)); + status = STATUS_OBJECT_NAME_NOT_FOUND; + goto done; + } + + ASSERT(ote_conn->type == FILEOBJ_CONNOBJ); + + // connobj must be associated with addrobj! + if (ote_conn->associated_fileobj != ctx->fileobj) { + KdPrint(("[tdi_fw] tdi_event_connect: 0x%x != 0x%x\n", ote_conn->associated_fileobj, ctx->fileobj)); + status = STATUS_INVALID_PARAMETER; + goto done; + } + + // change conn_ctx (if needed) + if (ote_conn->conn_ctx != *ConnectionContext) { + // update (conn_ctx, addrobj)->connobj + + status = ot_del_conn_ctx(ote_conn->associated_fileobj, ote_conn->conn_ctx); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] tdi_event_connect: ot_del_conn_ctx: 0x%x\n", status)); + goto done; + } + + ote_conn->conn_ctx = *ConnectionContext; + + status = ot_add_conn_ctx(ote_conn->associated_fileobj, ote_conn->conn_ctx, irps->FileObject); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] tdi_event_connect: ot_add_conn_ctx: 0x%x\n", status)); + goto done; + } + + } + + // clear listen & conn entries in connobj (fileobject can be reused) + + ASSERT(ote_conn->listen_entry == NULL); + if (ote_conn->listen_entry != NULL) + del_listen_obj(ote_conn->listen_entry, FALSE); // free build case + + if (ote_conn->conn_entry != NULL) { + + if (ote_conn->ipproto == IPPROTO_TCP && ote_conn->log_disconnect) + log_disconnect(ote_conn); + + del_tcp_conn_obj(ote_conn->conn_entry, FALSE); + } + + // clear bytes count + ote_conn->bytes_in = ote_conn->bytes_out = 0; + + // setup log_disconnect flag from rule + ote_conn->log_disconnect = (rule.log >= RULE_LOG_COUNT); + + // sanity check + if (local_addr->AddressLength != remote_addr->AddressLength) { + KdPrint(("[tdi_fw] tdi_event_connect: different addr lengths! (%u != %u)\n", + local_addr->AddressLength, + remote_addr->AddressLength)); + status = STATUS_INFO_LENGTH_MISMATCH; + goto done; + } + + // associate remote address with connobj + + if (remote_addr->AddressLength > sizeof(ote_conn->remote_addr)) { + KdPrint(("[tdi_fw] tdi_event_connect: address too long! (%u)\n", + remote_addr->AddressLength)); + status = STATUS_BUFFER_TOO_SMALL; + goto done; + } + memcpy(ote_conn->remote_addr, remote_addr, remote_addr->AddressLength); + + // associate local address with connobj + + if (local_addr->AddressLength > sizeof(ote_conn->local_addr)) { + KdPrint(("[tdi_fw] tdi_event_connect: address too long! (%u)\n", + local_addr->AddressLength)); + status = STATUS_BUFFER_TOO_SMALL; + goto done; + } + memcpy(ote_conn->local_addr, local_addr, local_addr->AddressLength); + + // create connection with "SYN_RCVD" state + status = add_tcp_conn(ote_conn, TCP_STATE_SYN_RCVD); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] tdi_event_connect: add_tcp_conn: 0x%x\n", status)); + goto done; + } + + result = FILTER_ALLOW; + +done: + // if logging is needed log request + if (rule.log >= RULE_LOG_LOG) { + if (result != FILTER_ALLOW && rule.result == FILTER_ALLOW) { + request.type = TYPE_CONNECT_ERROR; // error has been occured + request.status = status; + } + + log_request(&request); + } + + if (result != FILTER_ALLOW) { + // deny incoming connection + + KdPrint(("[tdi_fw] tdi_event_connect: deny on reason 0x%x\n", status)); + + if (irps != NULL) { + // delete connection + if (ote_conn != NULL && ote_conn->conn_entry != NULL) { + del_tcp_conn_obj(ote_conn->conn_entry, FALSE); + ote_conn->conn_entry = NULL; + } + + // release spinlock before IoCompleteRequest to avoid completion call inside spinlock + if (ote_addr != NULL || ote_conn != NULL) { + KeReleaseSpinLock(&g_ot_hash_guard, irql); + + ote_addr = NULL; + ote_conn = NULL; + } + + // destroy accepted IRP + (*AcceptIrp)->IoStatus.Status = STATUS_UNSUCCESSFUL; + IoCompleteRequest(*AcceptIrp, IO_NO_INCREMENT); + } + + *AcceptIrp = NULL; + status = STATUS_CONNECTION_REFUSED; + } else + status = STATUS_MORE_PROCESSING_REQUIRED; + + // cleanup + if (ote_addr != NULL || ote_conn != NULL) + KeReleaseSpinLock(&g_ot_hash_guard, irql); + if (param != NULL) + free(param); + if (request.sid_a != NULL) + free(request.sid_a); + + return status; +} + +NTSTATUS +tdi_evconn_accept_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) +{ + PIO_STACK_LOCATION irps = IoGetNextIrpStackLocation(Irp); + struct accept_param *param = (struct accept_param *)Context; + NTSTATUS status = STATUS_SUCCESS; + + KdPrint(("[tdi_fw] tdi_evconn_accept_complete: status 0x%x\n", Irp->IoStatus.Status)); + + if (Irp->IoStatus.Status == STATUS_SUCCESS) { + set_tcp_conn_state(param->fileobj, TCP_STATE_ESTABLISHED_IN); + + // query & update connection local_addr + update_conn_info(irps->DeviceObject, param->fileobj); + + } else + del_tcp_conn(param->fileobj, TRUE); // TRUE because we logged connection in event handler + + // restore routine and context (and even control!) + irps->CompletionRoutine = param->old_cr; + irps->Context = param->old_context; + irps->Control = param->old_control; + + // call original completion (I like this code :-) + + if (param->old_cr != NULL) { + // call old completion (see the old control) + BOOLEAN b_call = FALSE; + + if (Irp->Cancel) { + // cancel + if (param->old_control & SL_INVOKE_ON_CANCEL) + b_call = TRUE; + } else { + if (Irp->IoStatus.Status >= STATUS_SUCCESS) { + // success + if (param->old_control & SL_INVOKE_ON_SUCCESS) + b_call = TRUE; + } else { + // error + if (param->old_control & SL_INVOKE_ON_ERROR) + b_call = TRUE; + } + } + + if (b_call) + status = param->old_cr(DeviceObject, Irp, param->old_context); + } + + free(param); + return status; +} + +//---------------------------------------------------------------------------- + +/* + * TDI_EVENT_DISCONNECT handler + */ + +NTSTATUS +tdi_event_disconnect( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN LONG DisconnectDataLength, + IN PVOID DisconnectData, + IN LONG DisconnectInformationLength, + IN PVOID DisconnectInformation, + IN ULONG DisconnectFlags) +{ + TDI_EVENT_CONTEXT *ctx = (TDI_EVENT_CONTEXT *)TdiEventContext; + PFILE_OBJECT connobj = ot_find_conn_ctx(ctx->fileobj, ConnectionContext); + + KdPrint(("[tdi_fw] tdi_event_disconnect: connobj: 0x%x (flags: 0x%x)\n", + connobj, DisconnectFlags)); + + if (DisconnectFlags & TDI_DISCONNECT_RELEASE) { + int state = get_tcp_conn_state_by_obj(connobj); + + if (state == TCP_STATE_ESTABLISHED_IN || state == TCP_STATE_ESTABLISHED_OUT) + set_tcp_conn_state(connobj, TCP_STATE_CLOSE_WAIT); + else if (state == TCP_STATE_FIN_WAIT2) + set_tcp_conn_state(connobj, TCP_STATE_TIME_WAIT); + else + KdPrint(("[tdi_fw] tdi_event_disconnect: weird conn state: %d\n", state)); + + } else + del_tcp_conn(connobj, TRUE); + + return ((PTDI_IND_DISCONNECT)(ctx->old_handler))(ctx->old_context, ConnectionContext, + DisconnectDataLength, DisconnectData, DisconnectInformationLength, + DisconnectInformation, DisconnectFlags); +} diff --git a/tdifw-1.4.4/drv/ev_dg.c b/tdifw-1.4.4/drv/ev_dg.c new file mode 100644 index 00000000..61fcfe6d --- /dev/null +++ b/tdifw-1.4.4/drv/ev_dg.c @@ -0,0 +1,147 @@ +/* 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: ev_dg.c,v 1.6 2003/09/04 15:20:09 dev Exp $ + +/* + * This file contains TDI_EVENT_RECEIVE_DATAGRAM handler + */ + +#include +#include +#include "sock.h" + +#include "events.h" +#include "filter.h" +#include "memtrack.h" +#include "obj_tbl.h" +#include "sids.h" +#include "tdi_fw.h" + +//---------------------------------------------------------------------------- + +/* + * TDI_EVENT_RECEIVE_DATAGRAM handler + */ + +NTSTATUS tdi_event_receive_datagram( + IN PVOID TdiEventContext, + IN LONG SourceAddressLength, + IN PVOID SourceAddress, + IN LONG OptionsLength, + IN PVOID Options, + IN ULONG ReceiveDatagramFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *BytesTaken, + IN PVOID Tsdu, + OUT PIRP *IoRequestPacket) +{ + TDI_EVENT_CONTEXT *ctx = (TDI_EVENT_CONTEXT *)TdiEventContext; + struct ot_entry *ote_addr = NULL; + KIRQL irql; + TA_ADDRESS *remote_addr, *local_addr; + NTSTATUS status; + int ipproto, result = FILTER_DENY; + struct flt_request request; + struct flt_rule rule; + + // get local address of address object + + memset(&request, 0, sizeof(request)); + ote_addr = ot_find_fileobj(ctx->fileobj, &irql); + if (ote_addr == NULL) { + KdPrint(("[tdi_fw] tdi_receive_datagram: ot_find_fileobj(0x%x)!\n", ctx->fileobj)); + goto done; + } + + KdPrint(("[tdi_fw] tdi_event_receive_datagram: addrobj 0x%x\n", ctx->fileobj)); + + // check device object: UDP or RawIP + if (get_original_devobj(ote_addr->devobj, &ipproto) == NULL || + (ipproto != IPPROTO_UDP && ipproto != IPPROTO_IP)) { + // unknown device object! + KdPrint(("[tdi_fw] tdi_event_receive_datagram: unknown DeviceObject 0x%x!\n", + ote_addr)); + goto done; + } + + local_addr = (TA_ADDRESS *)(ote_addr->local_addr); + remote_addr = ((TRANSPORT_ADDRESS *)SourceAddress)->Address; + + KdPrint(("[tdi_fw] tdi_event_receive_datagram(pid:%u): %x:%u -> %x:%u\n", + ote_addr->pid, + ntohl(((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr), + ntohs(((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port), + ntohl(((TDI_ADDRESS_IP *)(local_addr->Address))->in_addr), + ntohs(((TDI_ADDRESS_IP *)(local_addr->Address))->sin_port))); + + // call quick filter for datagram + request.struct_size = sizeof(request); + + request.type = TYPE_DATAGRAM; + request.direction = DIRECTION_IN; + request.proto = ipproto; + request.pid = ote_addr->pid; + + // get user SID & attributes (can't call get_current_sid_a at DISPATCH_LEVEL) + if ((request.sid_a = copy_sid_a(ote_addr->sid_a, ote_addr->sid_a_size)) != NULL) + request.sid_a_size = ote_addr->sid_a_size; + + memcpy(&request.addr.from, &remote_addr->AddressType, sizeof(struct sockaddr)); + memcpy(&request.addr.to, &local_addr->AddressType, sizeof(struct sockaddr)); + request.addr.len = sizeof(struct sockaddr_in); + + memset(&rule, 0, sizeof(rule)); + + result = quick_filter(&request, &rule); + + memcpy(request.log_rule_id, rule.rule_id, RULE_ID_SIZE); + + if (rule.log >= RULE_LOG_LOG) { + ULONG bytes = BytesAvailable; + + // traffic stats + KeAcquireSpinLockAtDpcLevel(&g_traffic_guard); + + g_traffic[TRAFFIC_TOTAL_IN] += bytes; + + if (rule.log >= RULE_LOG_COUNT) { + request.log_bytes_in = bytes; + + g_traffic[TRAFFIC_COUNTED_IN] += bytes; + + } else + request.log_bytes_in = (ULONG)-1; + + KeReleaseSpinLockFromDpcLevel(&g_traffic_guard); + + log_request(&request); + } + +done: + // cleanup + if (ote_addr != NULL) + KeReleaseSpinLock(&g_ot_hash_guard, irql); + if (request.sid_a != NULL) + free(request.sid_a); + + if (result == FILTER_ALLOW) { + + return ((PTDI_IND_RECEIVE_DATAGRAM)(ctx->old_handler)) + (ctx->old_context, SourceAddressLength, SourceAddress, OptionsLength, + Options, ReceiveDatagramFlags, BytesIndicated, BytesAvailable, BytesTaken, + Tsdu, IoRequestPacket); + + } else + return STATUS_DATA_NOT_ACCEPTED; +} diff --git a/tdifw-1.4.4/drv/ev_recv.c b/tdifw-1.4.4/drv/ev_recv.c new file mode 100644 index 00000000..f96fdcc5 --- /dev/null +++ b/tdifw-1.4.4/drv/ev_recv.c @@ -0,0 +1,249 @@ +/* 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: ev_recv.c,v 1.4 2005/09/06 12:25:42 vlad Exp $ + +/* + * This file contains TDI_EVENT_RECEIVE and TDI_EVENT_CHAINED_RECEIVE handlers + */ + +#include +#include +#include "sock.h" + +#include "events.h" +#include "memtrack.h" +#include "obj_tbl.h" +#include "tdi_fw.h" + +struct tdi_client_irp_ctx { + PIO_COMPLETION_ROUTINE completion; + PVOID context; + UCHAR old_control; + PFILE_OBJECT connobj; +}; + +static NTSTATUS tdi_client_irp_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context); + +//---------------------------------------------------------------------------- + +/* + * TDI_EVENT_RECEIVE handler + */ + +NTSTATUS +tdi_event_receive( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *BytesTaken, + IN PVOID Tsdu, + OUT PIRP *IoRequestPacket) +{ + TDI_EVENT_CONTEXT *ctx = (TDI_EVENT_CONTEXT *)TdiEventContext; + PFILE_OBJECT connobj = ot_find_conn_ctx(ctx->fileobj, ConnectionContext); + NTSTATUS status; + + KdPrint(("[tdi_fw] tdi_event_receive: addrobj 0x%x; connobj: 0x%x; %u/%u; flags: 0x%x\n", + ctx->fileobj, connobj, BytesIndicated, BytesAvailable, ReceiveFlags)); + + status = ((PTDI_IND_RECEIVE)(ctx->old_handler)) + (ctx->old_context, ConnectionContext, ReceiveFlags, BytesIndicated, + BytesAvailable, BytesTaken, Tsdu, IoRequestPacket); + + KdPrint(("[tdi_fw] tdi_event_receive: status 0x%x; BytesTaken: %u; Irp: 0x%x\n", + status, *BytesTaken, *IoRequestPacket)); + + if (*BytesTaken != 0) { + struct ot_entry *ote_conn; + KIRQL irql; + + ote_conn = ot_find_fileobj(connobj, &irql); + if (ote_conn != NULL) { + ULONG bytes = *BytesTaken; + + ote_conn->bytes_in += bytes; + + // traffic stats + KeAcquireSpinLockAtDpcLevel(&g_traffic_guard); + + g_traffic[TRAFFIC_TOTAL_IN] += bytes; + + if (ote_conn->log_disconnect) + g_traffic[TRAFFIC_COUNTED_IN] += bytes; + + KeReleaseSpinLockFromDpcLevel(&g_traffic_guard); + + KeReleaseSpinLock(&g_ot_hash_guard, irql); + } + } + + if (*IoRequestPacket != NULL) { + // got IRP. replace completion. + struct tdi_client_irp_ctx *new_ctx; + PIO_STACK_LOCATION irps = IoGetCurrentIrpStackLocation(*IoRequestPacket); + + new_ctx = (struct tdi_client_irp_ctx *)malloc_np(sizeof(*new_ctx)); + if (new_ctx != NULL) { + + new_ctx->connobj = connobj; + + if (irps->CompletionRoutine != NULL) { + new_ctx->completion = irps->CompletionRoutine; + new_ctx->context = irps->Context; + new_ctx->old_control = irps->Control; + + } else { + + // we don't use IoSetCompletionRoutine because it uses next not current location + + new_ctx->completion = NULL; + new_ctx->context = NULL; + + } + + irps->CompletionRoutine = tdi_client_irp_complete; + irps->Context = new_ctx; + irps->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; + } + } + + return status; +} + +NTSTATUS +tdi_client_irp_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) +{ + struct tdi_client_irp_ctx *ctx = (struct tdi_client_irp_ctx *)Context; + NTSTATUS status; + + KdPrint(("[tdi_fw] tdi_client_irp_complete: status: 0x%x; len: %u\n", + Irp->IoStatus.Status, Irp->IoStatus.Information)); + + if (Irp->IoStatus.Status == STATUS_SUCCESS) { + + struct ot_entry *ote_conn; + KIRQL irql; + + ote_conn = ot_find_fileobj(ctx->connobj, &irql); + if (ote_conn != NULL) { + ULONG bytes = Irp->IoStatus.Information; + + ote_conn->bytes_in += bytes; + + // traffic stats + KeAcquireSpinLockAtDpcLevel(&g_traffic_guard); + + g_traffic[TRAFFIC_TOTAL_IN] += bytes; + + if (ote_conn->log_disconnect) + g_traffic[TRAFFIC_COUNTED_IN] += bytes; + + KeReleaseSpinLockFromDpcLevel(&g_traffic_guard); + + KeReleaseSpinLock(&g_ot_hash_guard, irql); + } + } + + // call original completion + if (ctx->completion != NULL) { + // call old completion (see the old control) + BOOLEAN b_call = FALSE; + + if (Irp->Cancel) { + // cancel + if (ctx->old_control & SL_INVOKE_ON_CANCEL) + b_call = TRUE; + } else { + if (Irp->IoStatus.Status >= STATUS_SUCCESS) { + // success + if (ctx->old_control & SL_INVOKE_ON_SUCCESS) + b_call = TRUE; + } else { + // error + if (ctx->old_control & SL_INVOKE_ON_ERROR) + b_call = TRUE; + } + } + + if (b_call) { + status = (ctx->completion)(DeviceObject, Irp, ctx->context); + + KdPrint(("[tdi_flt] tdi_client_irp_complete: original handler: 0x%x; status: 0x%x\n", + ctx->completion, status)); + + } else + status = STATUS_SUCCESS; + + } + + free(ctx); + return status; +} + +//---------------------------------------------------------------------------- + +/* + * TDI_EVENT_CHAINED_RECEIVE handler + */ + +NTSTATUS +tdi_event_chained_receive( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, + IN ULONG ReceiveLength, + IN ULONG StartingOffset, + IN PMDL Tsdu, + IN PVOID TsduDescriptor) +{ + TDI_EVENT_CONTEXT *ctx = (TDI_EVENT_CONTEXT *)TdiEventContext; + PFILE_OBJECT connobj = ot_find_conn_ctx(ctx->fileobj, ConnectionContext); + NTSTATUS status; + + KdPrint(("[tdi_fw] tdi_event_chained_receive: addrobj 0x%x; connobj: 0x%x; %u; flags: 0x%x\n", + ctx->fileobj, connobj, ReceiveLength, ReceiveFlags)); + + status = ((PTDI_IND_CHAINED_RECEIVE)(ctx->old_handler)) + (ctx->old_context, ConnectionContext, ReceiveFlags,ReceiveLength , + StartingOffset, Tsdu, TsduDescriptor); + + KdPrint(("[tdi_fw] tdi_event_chained_receive: status 0x%x\n", status)); + + if (status == STATUS_SUCCESS || status == STATUS_PENDING) { + struct ot_entry *ote_conn; + KIRQL irql; + + ote_conn = ot_find_fileobj(connobj, &irql); + if (ote_conn != NULL) { + ULONG bytes = ReceiveLength; + + ote_conn->bytes_in += bytes; + + // traffic stats + KeAcquireSpinLockAtDpcLevel(&g_traffic_guard); + + g_traffic[TRAFFIC_TOTAL_IN] += bytes; + + if (ote_conn->log_disconnect) + g_traffic[TRAFFIC_COUNTED_IN] += bytes; + + KeReleaseSpinLockFromDpcLevel(&g_traffic_guard); + + KeReleaseSpinLock(&g_ot_hash_guard, irql); + } + } + + return status; +} diff --git a/tdifw-1.4.4/drv/events.c b/tdifw-1.4.4/drv/events.c new file mode 100644 index 00000000..d302d854 --- /dev/null +++ b/tdifw-1.4.4/drv/events.c @@ -0,0 +1,37 @@ +/* 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: events.c,v 1.3 2003/09/01 08:42:18 dev Exp $ + +/* + * Replaced event handlers for TDI events + */ + +#include +#include +#include "sock.h" + +#include "events.h" +#include "memtrack.h" + +// to simplify processing don't use chained handlers for datagrams +struct tdi_event_handler g_tdi_event_handlers[]= { + {TDI_EVENT_CONNECT, tdi_event_connect}, + {TDI_EVENT_DISCONNECT, tdi_event_disconnect}, + {TDI_EVENT_RECEIVE_DATAGRAM, tdi_event_receive_datagram}, + {TDI_EVENT_CHAINED_RECEIVE_DATAGRAM, NULL}, + {TDI_EVENT_RECEIVE, tdi_event_receive}, + {TDI_EVENT_RECEIVE_EXPEDITED, tdi_event_receive}, + {TDI_EVENT_CHAINED_RECEIVE, tdi_event_chained_receive}, + {TDI_EVENT_CHAINED_RECEIVE_EXPEDITED, tdi_event_chained_receive}, + {(ULONG)-1, NULL} +}; diff --git a/tdifw-1.4.4/drv/events.h b/tdifw-1.4.4/drv/events.h new file mode 100644 index 00000000..7e7c876f --- /dev/null +++ b/tdifw-1.4.4/drv/events.h @@ -0,0 +1,70 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs) +// +// $Id: events.h,v 1.2 2003/09/01 08:42:18 dev Exp $ + +#ifndef _events_h_ +#define _events_h_ + +#include "obj_tbl.h" + +NTSTATUS tdi_event_connect( + IN PVOID TdiEventContext, + IN LONG RemoteAddressLength, + IN PVOID RemoteAddress, + IN LONG UserDataLength, + IN PVOID UserData, + IN LONG OptionsLength, + IN PVOID Options, + OUT CONNECTION_CONTEXT *ConnectionContext, + OUT PIRP *AcceptIrp); + +NTSTATUS tdi_event_disconnect( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN LONG DisconnectDataLength, + IN PVOID DisconnectData, + IN LONG DisconnectInformationLength, + IN PVOID DisconnectInformation, + IN ULONG DisconnectFlags); + +NTSTATUS tdi_event_receive_datagram( + IN PVOID TdiEventContext, + IN LONG SourceAddressLength, + IN PVOID SourceAddress, + IN LONG OptionsLength, + IN PVOID Options, + IN ULONG ReceiveDatagramFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *BytesTaken, + IN PVOID Tsdu, + OUT PIRP *IoRequestPacket); + +NTSTATUS tdi_event_receive( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *BytesTaken, + IN PVOID Tsdu, + OUT PIRP *IoRequestPacket); + +NTSTATUS tdi_event_chained_receive( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, + IN ULONG ReceiveLength, + IN ULONG StartingOffset, + IN PMDL Tsdu, + IN PVOID TsduDescriptor); + +// helper struct for calling one of the handlers +struct tdi_event_handler { + LONG event; + PVOID handler; +}; + +extern struct tdi_event_handler g_tdi_event_handlers[]; + +#endif diff --git a/tdifw-1.4.4/drv/filter.c b/tdifw-1.4.4/drv/filter.c new file mode 100644 index 00000000..d8601a74 --- /dev/null +++ b/tdifw-1.4.4/drv/filter.c @@ -0,0 +1,593 @@ +/* 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 +#include +#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; + + // 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); + + request->result = result; + return result; +} + +// write request to request queue +BOOLEAN +log_request(struct flt_request *request) +{ + KIRQL irql, irql2; + ULONG next_head; + char pname_buf[256], *pname; + struct plist_entry *ple; + + if (!g_got_log && request->type == TYPE_RESOLVE_PID) // don't log - no log app + return FALSE; + + KeAcquireSpinLock(&g_queue.guard, &irql); + + next_head = (g_queue.head + 1) % REQUEST_QUEUE_SIZE; + + if (next_head == g_queue.tail) { + // queue overflow: reject one entry from tail + KdPrint(("[tdi_fw] log_request: queue overflow!\n")); + + request->log_skipped = g_queue.data[g_queue.tail].log_skipped + 1; + + // free process name & sid! + if (g_queue.data[g_queue.tail].pname != NULL) + free(g_queue.data[g_queue.tail].pname); + if (g_queue.data[g_queue.tail].sid_a != NULL) + free(g_queue.data[g_queue.tail].sid_a); + + g_queue.tail = (g_queue.tail + 1) % REQUEST_QUEUE_SIZE; + + } else + request->log_skipped = 0; + + memcpy(&g_queue.data[g_queue.head], request, sizeof(struct flt_request)); + + // try to get process name + pname = NULL; + if (request->pid != (ULONG)-1 && + pid_pname_resolve(request->pid, pname_buf, sizeof(pname_buf))) { + + KdPrint(("[tdi_fw] log_request: pid:%u; pname:%s\n", + request->pid, pname_buf)); + + // ala strdup() + pname = (char *)malloc_np(strlen(pname_buf) + 1); + if (pname != NULL) + strcpy(pname, pname_buf); + else + KdPrint(("[tdi_fw] log_request: malloc_np!\n")); + } + + g_queue.data[g_queue.head].pname = pname; + g_queue.head = next_head; + + // don't free sid & attributes + if (request->sid_a != NULL) + request->sid_a = NULL; + + KeReleaseSpinLock(&g_queue.guard, irql); + + // signal to user app + if (g_queue.event != NULL) + KeSetEvent(g_queue.event, IO_NO_INCREMENT, FALSE); + + return TRUE; +} + +// read requests from log queue to buffer +ULONG +get_request(char *buf, ULONG buf_size) +{ + ULONG result = 0; + KIRQL irql; + + // sanity check + if (buf_size < sizeof(struct flt_request)) + return 0; + + KeAcquireSpinLock(&g_queue.guard, &irql); + + while (g_queue.head != g_queue.tail) { + ULONG pname_size, sid_a_size; + + if (g_queue.data[g_queue.tail].pname != NULL) + pname_size = strlen(g_queue.data[g_queue.tail].pname) + 1; + else + pname_size = 0; + + if (g_queue.data[g_queue.tail].sid_a != NULL) + sid_a_size = g_queue.data[g_queue.tail].sid_a_size; + else + sid_a_size = 0; + + if (buf_size < sizeof(struct flt_request) + pname_size + sid_a_size) + break; + + memcpy(buf, &g_queue.data[g_queue.tail], sizeof(struct flt_request)); + + if (g_queue.data[g_queue.tail].pname != NULL) { + ((struct flt_request *)buf)->struct_size += pname_size; + + strcpy(buf + sizeof(struct flt_request), g_queue.data[g_queue.tail].pname); + + free(g_queue.data[g_queue.tail].pname); + g_queue.data[g_queue.tail].pname = NULL; + } + + if (g_queue.data[g_queue.tail].sid_a != NULL) { + + // make sid pointer relative only + SID_AND_ATTRIBUTES *sid_a = g_queue.data[g_queue.tail].sid_a; + + sid_a->Sid = (PSID)((char *)(sid_a->Sid) - (char *)sid_a); + + // copy sid_a + memcpy(buf + sizeof(struct flt_request) + pname_size, sid_a, sid_a_size); + + // free sid_a memory + free(sid_a); + g_queue.data[g_queue.tail].sid_a = NULL; + + // increase pname_size and struct size + ((struct flt_request *)buf)->struct_size += sid_a_size; + } + + result += sizeof(struct flt_request) + pname_size + sid_a_size; + buf += sizeof(struct flt_request) + pname_size + sid_a_size; + buf_size -= sizeof(struct flt_request) + pname_size + sid_a_size; + + g_queue.tail = (g_queue.tail + 1) % REQUEST_QUEUE_SIZE; + } + + KdPrint(("[tdi_fw] get_request: copied %u bytes\n", result)); + + KeReleaseSpinLock(&g_queue.guard, irql); + return result; +} + +// add rule to rules chain +NTSTATUS +add_flt_rule(int chain, const struct flt_rule *rule) +{ + NTSTATUS status; + struct flt_rule *new_rule; + KIRQL irql; + + // sanity check + if (chain < 0 || chain >= MAX_CHAINS_COUNT) + return STATUS_INVALID_PARAMETER_1; + + KeAcquireSpinLock(&g_rules.guard, &irql); + + new_rule = (struct flt_rule *)malloc_np(sizeof(struct flt_rule)); + if (new_rule == NULL) { + KdPrint(("[tdi_fw] add_flt_rule: malloc_np\n")); + status = STATUS_INSUFFICIENT_RESOURCES; + goto done; + } + + memcpy(new_rule, rule, sizeof(*new_rule)); + + // append + new_rule->next = NULL; + + if (g_rules.chain[chain].tail == NULL) { + g_rules.chain[chain].head = new_rule; + g_rules.chain[chain].tail = new_rule; + } else { + g_rules.chain[chain].tail->next = new_rule; + g_rules.chain[chain].tail = new_rule; + } + + status = STATUS_SUCCESS; + +done: + KeReleaseSpinLock(&g_rules.guard, irql); + return status; +} + +// clear rules chain +NTSTATUS +clear_flt_chain(int chain) +{ + struct flt_rule *rule; + KIRQL irql; + + // sanity check + if (chain < 0 || chain >= MAX_CHAINS_COUNT) + return STATUS_INVALID_PARAMETER_1; + + /* rules chain */ + KeAcquireSpinLock(&g_rules.guard, &irql); + + for (rule = g_rules.chain[chain].head; rule != NULL;) { + struct flt_rule *rule2 = rule->next; + free(rule); + rule = rule2; + } + + g_rules.chain[chain].head = NULL; + g_rules.chain[chain].tail = NULL; + + if (g_rules.chain[chain].pname != NULL) { + free(g_rules.chain[chain].pname); + g_rules.chain[chain].pname = NULL; + } + + // deactivate chain + g_rules.chain[chain].active = FALSE; + + KeReleaseSpinLock(&g_rules.guard, irql); + return STATUS_SUCCESS; +} + +// set process name for chain +NTSTATUS +set_chain_pname(int chain, char *pname) +{ + KIRQL irql; + NTSTATUS status; + + // sanity check + if (chain < 0 || chain >= MAX_CHAINS_COUNT) + return STATUS_INVALID_PARAMETER_1; + + KdPrint(("[tdi_fw] set_chain_pname: setting name %s for chain %d\n", pname, chain)); + + KeAcquireSpinLock(&g_rules.guard, &irql); + + if (g_rules.chain[chain].pname != NULL) + free(g_rules.chain[chain].pname); + + g_rules.chain[chain].pname = (char *)malloc_np(strlen(pname) + 1); + if (g_rules.chain[chain].pname != NULL) { + // copy pname + strcpy(g_rules.chain[chain].pname, pname); + status = STATUS_SUCCESS; + } else + status = STATUS_INSUFFICIENT_RESOURCES; + + KeReleaseSpinLock(&g_rules.guard, irql); + return status; +} + +// set result of process name by pid resolving +NTSTATUS +set_pid_pname(ULONG pid, char *pname) +{ + KIRQL irql; + int i, chain = 0; + + KdPrint(("[tdi_fw] set_pid_pname: setting pname %s for pid %u\n", pname, pid)); + + KeAcquireSpinLock(&g_rules.guard, &irql); + for (i = 0; i < MAX_CHAINS_COUNT; i++) + if (g_rules.chain[i].pname != NULL && + _stricmp(pname, g_rules.chain[i].pname) == 0) { + + KdPrint(("[tdi_fw] set_pid_pname: found chain %d\n", i)); + chain = i; + + break; + } + KeReleaseSpinLock(&g_rules.guard, irql); + + return pid_pname_set(pid, pname, chain); +} + +// activate rules chain +NTSTATUS +activate_flt_chain(int chain) +{ + // sanity check + if (chain < 0 || chain >= MAX_CHAINS_COUNT) + return STATUS_INVALID_PARAMETER_1; + + g_rules.chain[chain].active = TRUE; + + return STATUS_SUCCESS; +} + +BOOLEAN +default_chain_only(void) +{ + int i; + + if (!g_rules.chain[0].active) + return FALSE; + + for (i = 1; i < MAX_CHAINS_COUNT; i++) + if (g_rules.chain[i].active) + return FALSE; + + return TRUE; + +} diff --git a/tdifw-1.4.4/drv/filter.h b/tdifw-1.4.4/drv/filter.h new file mode 100644 index 00000000..ce6d73ea --- /dev/null +++ b/tdifw-1.4.4/drv/filter.h @@ -0,0 +1,39 @@ +/* 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.h,v 1.5 2003/09/04 15:20:09 dev Exp $ + +#ifndef _filter_h_ +#define _filter_h_ + +#include "ipc.h" + +NTSTATUS filter_init(void); +NTSTATUS filter_init_2(void); + +void filter_free(void); +void filter_free_2(void); + +NTSTATUS add_flt_rule(int chain, const struct flt_rule *rule); +NTSTATUS clear_flt_chain(int chain); +NTSTATUS activate_flt_chain(int chain); +NTSTATUS set_chain_pname(int chain, char *pname); +NTSTATUS set_pid_pname(ULONG pid, char *pname); + +BOOLEAN default_chain_only(void); + +int quick_filter(struct flt_request *request, struct flt_rule *rule); + +BOOLEAN log_request(struct flt_request *request); +ULONG get_request(char *buf, ULONG buf_size); + +#endif diff --git a/tdifw-1.4.4/drv/ipc.c b/tdifw-1.4.4/drv/ipc.c new file mode 100644 index 00000000..b06b3f57 --- /dev/null +++ b/tdifw-1.4.4/drv/ipc.c @@ -0,0 +1,207 @@ +/* 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: ipc.c,v 1.6 2003/09/04 15:20:09 dev Exp $ + +/* + * This file contain replaced TDI_EVENT_CONNECT handler + */ + +#include +#include +#include "sock.h" + +#include "conn_state.h" +#include "filter.h" +#include "ipc.h" +#include "sids.h" +#include "tdi_fw.h" + +static void get_traffic_counters(unsigned __int64 *counters); + +NTSTATUS +process_nfo_request(ULONG code, char *buf, ULONG *buf_len, ULONG buf_size) +{ + NTSTATUS status; + ULONG len = *buf_len; + *buf_len = 0; + + switch (code) { + + case IOCTL_CMD_ENUM_LISTEN: + // enum listening endpoints + + if (buf_size < sizeof(struct listen_nfo) || buf == NULL) { + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + status = enum_listen((struct listen_nfo *)buf, buf_len, buf_size); + break; + + case IOCTL_CMD_ENUM_TCP_CONN: + // enum TCP connections + + if (buf_size < sizeof(struct tcp_conn_nfo) || buf == NULL) { + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + status = enum_tcp_conn((struct tcp_conn_nfo *)buf, buf_len, buf_size); + break; + + case IOCTL_CMD_GET_COUNTERS: + // get traffic counters + + if (buf_size < sizeof(g_traffic)) { + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + get_traffic_counters((unsigned __int64 *)buf); + + *buf_len = sizeof(g_traffic); + status = STATUS_SUCCESS; + break; + + default: + status = STATUS_NOT_SUPPORTED; + } + + return status; +} + +NTSTATUS +process_request(ULONG code, char *buf, ULONG *buf_len, ULONG buf_size) +{ + NTSTATUS status; + ULONG len = *buf_len; + *buf_len = 0; + + switch (code) { + + case IOCTL_CMD_GETREQUEST: + // get data for logging + + if (buf_size < sizeof(struct flt_request) || buf == NULL) { + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + *buf_len = get_request(buf, buf_size); + status = STATUS_SUCCESS; + break; + + case IOCTL_CMD_CLEARCHAIN: + // clear rules chain #i + + if (len != sizeof(int) || buf == NULL) { + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + status = clear_flt_chain(*(int *)buf); + break; + + case IOCTL_CMD_APPENDRULE: + // append rule to chain #i + + if (len != sizeof(struct flt_rule) || buf == NULL) { + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + status = add_flt_rule(((struct flt_rule *)buf)->chain, (struct flt_rule *)buf); + break; + + case IOCTL_CMD_SETCHAINPNAME: + // set chain #i process name + + if (len < sizeof(int) + sizeof(char) || buf == NULL) { + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + if (buf[len - 1] != '\0') { + status = STATUS_INVALID_PARAMETER; // string must be zero-terminated + break; + } + + status = set_chain_pname(*(int *)buf, buf + sizeof(int)); + break; + + case IOCTL_CMD_SETPNAME: + // set process name for pid + + if (len < sizeof(ULONG) + sizeof(char) || buf == NULL) { + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + if (buf[len - 1] != '\0') { + status = STATUS_INVALID_PARAMETER; // string must be zero-terminated + break; + } + + status = set_pid_pname(*(ULONG *)buf, buf + sizeof(ULONG)); + break; + + case IOCTL_CMD_ACTIVATECHAIN: + // active rules chain #i + + if (len != sizeof(int) || buf == NULL) { + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + status = activate_flt_chain(*(int *)buf); + break; + + case IOCTL_CMD_SET_SIDS: + // set SIDs array + + if (len < sizeof(ULONG)) { + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + status = set_sid_list(buf, len); + break; + + case IOCTL_CMD_GET_COUNTERS: + // get traffic counters + + if (buf_size < sizeof(g_traffic)) { + status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + get_traffic_counters((unsigned __int64 *)buf); + + *buf_len = sizeof(g_traffic); + status = STATUS_SUCCESS; + break; + + default: + status = STATUS_NOT_SUPPORTED; + } + + return status; +} + +void +get_traffic_counters(unsigned __int64 *counters) +{ + KIRQL irql; + + KeAcquireSpinLock(&g_traffic_guard, &irql); + memcpy(counters, g_traffic, sizeof(g_traffic)); + KeReleaseSpinLock(&g_traffic_guard, irql); +} diff --git a/tdifw-1.4.4/drv/memtrack.c b/tdifw-1.4.4/drv/memtrack.c new file mode 100644 index 00000000..aec743d3 --- /dev/null +++ b/tdifw-1.4.4/drv/memtrack.c @@ -0,0 +1,204 @@ +/* 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: memtrack.c,v 1.2 2002/12/03 12:14:27 dev Exp $ + +#if DBG + +/* + * Debug NonPaged pool routines (helpers to find memory leaks and pool corruption) + */ + +#include + +#include "memtrack.h" + +#define MAGIC 'TMEM' +#define INT_3 __asm int 3 + +struct prefix { + ULONG magic; + struct prefix *next; + struct prefix *prev; + ULONG size; + const char *file; + ULONG line; + char data[]; +}; + +struct postfix { + ULONG size; + ULONG magic; +}; + +static KSPIN_LOCK guard; +static struct prefix *first, *last; +static ULONG count; + +static struct postfix *check(struct prefix *p); + +void +memtrack_init() +{ + KeInitializeSpinLock(&guard); +} + +void +memtrack_free() +{ + KIRQL irql; + ULONG total = 0; + + KeAcquireSpinLock(&guard, &irql); + + if (first != NULL) { + struct prefix *p; + for (p = first; p; p = p->next) { + check(p); + + KdPrint(("memtrack: memory leak detected! %s:%u (%u bytes)\n", + p->file, p->line, p->size)); + + total += p->size; + } + } + + KeReleaseSpinLock(&guard, irql); + KdPrint(("memtrack: Total memory leakage: %u bytes (%u blocks)\n", total, count)); + + if (total) INT_3; +} + +void * +mt_malloc(ULONG size, const char *file, ULONG line) +{ + KIRQL irql; + struct prefix *data; + struct postfix *pd; + +#if 1 + // check pool integrity + KeAcquireSpinLock(&guard, &irql); + + for (data = first; data; data = data->next) + check(data); + + for (data = last; data; data = data->prev) + check(data); + + KeReleaseSpinLock(&guard, irql); +#endif + + if (size == 0) { + KdPrint(("memtrack: mt_malloc: size == 0!\n")); + INT_3; + return NULL; + } + + data = (struct prefix *)ExAllocatePool(NonPagedPool, + sizeof(struct prefix) + size + sizeof(struct postfix)); + if (data == NULL) + return NULL; + + data->magic = MAGIC; + data->next = NULL; + data->prev = NULL; + data->size = size; + data->file = file; + data->line = line; + + memset(data->data, 0xcc, size); // fill by 0xcc: new + + pd = (struct postfix *)(data->data + data->size); + + pd->size = size; + pd->magic = MAGIC; + + KeAcquireSpinLock(&guard, &irql); + + if (last) { + last->next = data; + data->prev = last; + last = data; + } + else { + data->prev = NULL; + first = last = data; + } + count++; + + KeReleaseSpinLock(&guard, irql); + return data->data; +} + +void +free(void *ptr) +{ + KIRQL irql; + struct prefix *data = (struct prefix *)((char *)ptr - sizeof(struct prefix)); + struct postfix *pd = check(data); + + if (pd == NULL) + return; + + KeAcquireSpinLock(&guard, &irql); + + if (data->next != NULL) + data->next->prev = data->prev; + else + last = data->prev; + if (data->prev != NULL) + data->prev->next = data->next; + else + first = data->next; + + memset(data->data, 0xc9, data->size); // fill by 0xc9: free + + data->size = (ULONG)-1; + pd->size = (ULONG)-1; + + count--; + KeReleaseSpinLock(&guard, irql); + + ExFreePool(data); +} + +struct postfix * +check(struct prefix *p) +{ + struct postfix *pd; + + if (p->magic != MAGIC) { + KdPrint(("memtrack: check: invalid pre-magic! 0x%x\n", p)); + INT_3; + return NULL; + } + + pd = (struct postfix *)(p->data + p->size); + + if (pd->magic != MAGIC) { + KdPrint(("memtrack: memtrack_free: invalid post-magic! 0x%x\n", pd)); + INT_3; + return NULL; + } + + if (p->size != pd->size) { + KdPrint(("memtrack: memtracl_free: invalid post-size! 0x%x 0x%x\n", p, pd)); + INT_3; + return NULL; + } + + return pd; +} + + +#endif /* DBG */ diff --git a/tdifw-1.4.4/drv/memtrack.h b/tdifw-1.4.4/drv/memtrack.h new file mode 100644 index 00000000..c667203f --- /dev/null +++ b/tdifw-1.4.4/drv/memtrack.h @@ -0,0 +1,48 @@ +/* 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: memtrack.h,v 1.1.1.1 2002/09/24 11:12:16 dev Exp $ + +#ifndef _memtrack_h_ +#define _memtrack_h_ + +#define MEM_TAG '1VRD' + +#if DBG + +void memtrack_init(void); +void memtrack_free(void); + +void *mt_malloc(ULONG size, const char *file, ULONG line); + +// allocate memory from nonpaged pool +#define malloc_np(size) mt_malloc((size), __FILE__, __LINE__) + +void free(void *ptr); + +// debug macro +#define _TEST_ME_ __asm int 3 + +#else /* DBG */ + +#define memtrack_init() +#define memtrack_free() + +#define malloc_np(size) ExAllocatePoolWithTag(NonPagedPool, (size), MEM_TAG) +#define free(ptr) ExFreePool(ptr) + + +#define _TEST_ME_ + +#endif /* DBG */ + +#endif diff --git a/tdifw-1.4.4/drv/ndis_hk_ioctl.h b/tdifw-1.4.4/drv/ndis_hk_ioctl.h new file mode 100644 index 00000000..77f13345 --- /dev/null +++ b/tdifw-1.4.4/drv/ndis_hk_ioctl.h @@ -0,0 +1,130 @@ +/* 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: ndis_hk_ioctl.h,v 1.3 2003/07/14 12:26:27 dev Exp $ + +/** + * @file ndis_hk_ioctl.h + * I/O controls and related definitions for ndis_hk control device + */ + +#ifndef _ndis_hk_ioctl_h_ +#define _ndis_hk_ioctl_h_ + +/* device ioctls (for kernel mode use only; internal) */ + +/** MAGIC number for ndis_hk device (useless) */ +#define FILE_DEVICE_NDIS_HK 0x0000c501 + +/** + * Get kernel-mode interface of hooking driver. + * input buffer: (optional) ULONG if_version + * output buffer: struct ndis_hk + */ +#define IOCTL_CMD_GET_KM_IFACE CTL_CODE(FILE_DEVICE_NDIS_HK, 0x801, METHOD_BUFFERED, FILE_READ_DATA) + +/* ndis_hk kernel-mode interface */ + +#if 0 +enum { + DIRECTION_IN = 0, /**< input direction of packets (from network to protocol driver) */ + DIRECTION_OUT = 1 /**< output direction of packets (from protocol driver to network) */ +}; +#endif + +/** + * Main filter function to process input (from upper filter) or output (from lower filter) packet. + * + * executed at DISPATCH_LEVEL + * + * return value: FALSE don't pass unchanged packet to receiver + * if (packet_unchanged == FALSE) function MUST return FALSE + * + * if function want to pass the packet to next filter in stack: + * for DIRECTION_IN: self->lower->process_packet(direction, iface, packet, self->lower, packet_unchanged); + * for DIRECTION_OUT: self->upper->process_packet(direction, iface, packet, self->upper, packet_unchanged); + */ +typedef BOOLEAN process_packet_t( + int direction, int iface, PNDIS_PACKET packet, struct filter_nfo *self, + BOOLEAN packet_unchanged); + +/** + * Function to process interface PnP events. + * + * executed at PASSIVE_LEVEL + * + * For details of NetPnpEvent see DDK documentation. + * If return status is not NDIS_STATUS_SUCCESS don't call next handler in chain. + */ +typedef NDIS_STATUS pnp_event_t( + int iface, PNET_PNP_EVENT NetPnPEvent); + +/** Filter information for attach_filter */ +struct filter_nfo { + int size; /*<< size of structure */ + + process_packet_t *process_packet; /**< function to process packet */ + + struct filter_nfo *upper; /**< upper filter (process input) */ + struct filter_nfo *lower; /**< lower filter (process output) */ + + void *param; /**< place for caller's static param */ + + pnp_event_t *pnp_event; /**< PnP event callback */ +}; + +#ifndef NDIS_HK_INTERFACE_VER +/** the latest NDIS_HK_INTERFACE_VER */ +# define NDIS_HK_INTERFACE_VER 2 +#endif + +/** ndis_hk kernel-mode interface */ +struct ndis_hk_interface { + ULONG version; /**< should be NDIS_HK_INTERFACE_VER */ + + /** + * Get list of adapters + * @param buf output buffer for adapter names + * @param buf_size size in wchar_t of buf (can be 0) + * @return number of wchar_t has to be in buffer + * if greater than buf_size only partial information has been copied + * @see get_adapter_list + */ + int (*get_adapter_list)(wchar_t *buf, int buf_size); + + /** + * Attach or remove filter + * @param flt information about packet filter + * @param add TRUE - attach filter; FALSE - remove filter + * @param to_top TRUE - for attaching to top of stack (useless with add == FALSE) + * @see attach_filter + */ + void (*attach_filter)(struct filter_nfo *flt, BOOLEAN add, BOOLEAN to_top); + +#if NDIS_HK_INTERFACE_VER > 1 + + /** + * NDIS request on hooked adapter + * @param iface interface index + * @param req request (see DDK documentation) + * @return status + */ + NDIS_STATUS (*ndis_request)(int iface, NDIS_REQUEST *req); + +#endif + +#if NDIS_HK_INTERFACE_VER > 2 + // add future definitions here... +#endif +}; + +#endif diff --git a/tdifw-1.4.4/drv/obj_tbl.c b/tdifw-1.4.4/drv/obj_tbl.c new file mode 100644 index 00000000..e405c142 --- /dev/null +++ b/tdifw-1.4.4/drv/obj_tbl.c @@ -0,0 +1,566 @@ +/* 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: obj_tbl.c,v 1.12 2005/03/14 18:28:26 vlad Exp $ + +/* + * Working with connection objects, address objects and links between them + */ + +#include +#include +#include "sock.h" + +#include "conn_state.h" +#include "memtrack.h" +#include "obj_tbl.h" +#include "sids.h" +#include "tdi_fw.h" + +// for searching objects information by file object + +#define HASH_SIZE 0x1000 +#define CALC_HASH(fileobj) (((ULONG)(fileobj) >> 5) % HASH_SIZE) + +static struct ot_entry **g_ot_hash; +KSPIN_LOCK g_ot_hash_guard; + +// for searching connection objects by address object & connection context + +struct ctx_entry { + struct ctx_entry *next; + PFILE_OBJECT addrobj; + CONNECTION_CONTEXT conn_ctx; + PFILE_OBJECT connobj; +}; + +static struct ctx_entry **g_cte_hash; +KSPIN_LOCK g_cte_hash_guard; + +//---------------------------------------------------------------------------- + +NTSTATUS +ot_init(void) +{ + g_ot_hash = (struct ot_entry **)malloc_np(sizeof(*g_ot_hash) * HASH_SIZE); + if (g_ot_hash == NULL) { + KdPrint(("[tdi_fw] ot_init: malloc_np\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + memset(g_ot_hash, 0, sizeof(*g_ot_hash) * HASH_SIZE); + + KeInitializeSpinLock(&g_ot_hash_guard); + + g_cte_hash = (struct ctx_entry **)malloc_np(sizeof(*g_cte_hash) * HASH_SIZE); + if (g_cte_hash == NULL) { + KdPrint(("[tdi_fw] ot_init: malloc_np\n")); + free(g_ot_hash); + return STATUS_INSUFFICIENT_RESOURCES; + } + memset(g_cte_hash, 0, sizeof(*g_cte_hash) * HASH_SIZE); + + KeInitializeSpinLock(&g_cte_hash_guard); + + return STATUS_SUCCESS; +} + +void +ot_free(void) +{ + KIRQL irql; + int i; + struct ot_entry *ote; + + if (g_ot_hash == NULL) + return; // have nothing to do + + // do cleanup for address & connection objects + for (i = 0; i < HASH_SIZE; i++) { + + for (;;) { // do it again and again + PFILE_OBJECT connobj = NULL, event_addrobj; + PVOID event_handler = NULL, event_context; + int event_type; + PDEVICE_OBJECT devobj; + + KeAcquireSpinLock(&g_ot_hash_guard, &irql); + + for (ote = g_ot_hash[i]; ote != NULL; ote = ote->next) { + + if (ote->fileobj == NULL) + continue; // skip processed items + +#ifdef _USE_TDI_HOOKING + devobj = ote->devobj; +#else + devobj = get_original_devobj(ote->devobj, NULL); +#endif + + if (ote->type == FILEOBJ_ADDROBJ) { + // find at least one event handler & remove it @ PASSIVE level + int j; + + event_addrobj = ote->fileobj; + + for (j = 0; j < MAX_EVENT; j++) + if (ote->ctx[j].old_handler != NULL) { + + event_type = j; + event_handler = ote->ctx[j].old_handler; + event_context = ote->ctx[j].old_context; + + KdPrint(("[tdi_fw] ot_free: got event handler to restore (addrobj: 0x%x; type: %d; handler: 0x%x; context: 0x%x\n", + event_addrobj, + event_type, + event_handler, + event_context)); + + ote->ctx[j].old_handler = NULL; + + break; + } + + if (event_handler != NULL) + break; + + KdPrint(("[tdi_fw] ot_free: no event handlers for addrobj: 0x%x\n", + ote->fileobj)); + + // remove this addrobj from "LISTEN" state + if (ote->listen_entry != NULL) { + del_listen_obj(ote->listen_entry, FALSE); + ote->listen_entry = NULL; + } + + // no event handlers + ote->fileobj = NULL; + + } else if (ote->type == FILEOBJ_CONNOBJ) { + // check connobj is connected (remote addr is not 0) + TA_ADDRESS *remote_addr = (TA_ADDRESS *)(ote->remote_addr); + + if (((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr == 0) { + KdPrint(("[tdi_fw] ot_free: connobj 0x%x is not connected\n", connobj)); + + } else { + // disconnect this connection using TDI_DISCONNECT @ PASSIVE level + connobj = ote->fileobj; + + KdPrint(("[tdi_fw] ot_free: got connobj 0x%x (%x:%x) to disconnect\n", + connobj, + ((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr, + ((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port)); + } + + // remove this connobj from "CONNECTED" state + if (ote->conn_entry != NULL) { + del_tcp_conn_obj(ote->conn_entry, FALSE); + ote->conn_entry = NULL; + + // TODO: check if state TCP_STATE_TIME_WAIT don't delete it + } + + // skip this object next time + ote->fileobj = NULL; + } + } + + KeReleaseSpinLock(&g_ot_hash_guard, irql); + + // we're at PASSIVE level + + if (event_handler != NULL) { + // set old event handler + PIRP query_irp; + NTSTATUS status; + + KdPrint(("[tdi_fw] ot_free: got event handler to restore (addrobj: 0x%x; type: %d; handler: 0x%x; context: 0x%x\n", + event_addrobj, + event_type, + event_handler, + event_context)); + + query_irp = TdiBuildInternalDeviceControlIrp(TDI_SET_EVENT_HANDLER, + devobj, event_addrobj, NULL, NULL); + if (query_irp == NULL) { + KdPrint(("[tdi_fw] ot_free: TdiBuildInternalDeviceControlIrp\n")); + continue; + } + + TdiBuildSetEventHandler(query_irp, devobj, event_addrobj, NULL, NULL, + event_type, event_handler, event_context); + + status = IoCallDriver(devobj, query_irp); + if (status != STATUS_SUCCESS) + KdPrint(("[tdi_fw] ot_free: IoCallDriver(set_event_handler): 0x%x\n", status)); + + } else if (connobj != NULL) { + // disconnect connection + PIRP query_irp; + NTSTATUS status; + + KdPrint(("[tdi_fw] ot_free: disconnecting connobj 0x%x\n", connobj)); + + query_irp = TdiBuildInternalDeviceControlIrp(TDI_DISCONNECT, + devobj, connobj, NULL, NULL); + if (query_irp == NULL) { + KdPrint(("[tdi_fw] ot_free: TdiBuildInternalDeviceControlIrp\n")); + continue; + } + + // using TDI_DISCONNECT_RELEASE to make ClientEventDisconnect to be called + TdiBuildDisconnect(query_irp, devobj, connobj, NULL, NULL, NULL, + TDI_DISCONNECT_RELEASE, NULL, NULL); + + status = IoCallDriver(devobj, query_irp); + if (status != STATUS_SUCCESS) + KdPrint(("[tdi_fw] ot_free: IoCallDriver(disconnect): 0x%x\n", status)); + + } else { + + // no objects to process. break! + + break; + } + } + } + + // clear it! + KeAcquireSpinLock(&g_ot_hash_guard, &irql); + + for (i = 0; i < HASH_SIZE; i++) { + struct ot_entry *ote = g_ot_hash[i]; + while (ote != NULL) { + struct ot_entry *ote2 = ote->next; + + KdPrint(("[tdi_fw] ot_free: Warning! fileobj 0x%x type %d exists!\n", + ote->fileobj, ote->type)); + + if (ote->sid_a != NULL) + free(ote->sid_a); + free(ote); + + ote = ote2; + } + } + free(g_ot_hash); + g_ot_hash = NULL; + + KeReleaseSpinLock(&g_ot_hash_guard, irql); + + // and cleanup cte_hash + if (g_cte_hash != NULL) { + KeAcquireSpinLock(&g_cte_hash_guard, &irql); + + for (i = 0; i < HASH_SIZE; i++) { + struct ctx_entry *cte = g_cte_hash[i]; + while (cte) { + struct ctx_entry *cte2 = cte->next; + free(cte); + cte = cte2; + } + } + free(g_cte_hash); + g_cte_hash = NULL; + + KeReleaseSpinLock(&g_cte_hash_guard, irql); + } +} + +//---------------------------------------------------------------------------- + +NTSTATUS +ot_add_fileobj(PDEVICE_OBJECT devobj, PFILE_OBJECT fileobj, int fileobj_type, int ipproto, + CONNECTION_CONTEXT conn_ctx) // must be called at PASSIVE_LEVEL! +{ + ULONG hash = CALC_HASH(fileobj); + KIRQL irql; + struct ot_entry *ote; + NTSTATUS status; + int i; + SID_AND_ATTRIBUTES *sid_a; + ULONG sid_a_size; + + if (fileobj == NULL) + return STATUS_INVALID_PARAMETER_2; + + // while we're at PASSIVE_LEVEL get SID & attributes + sid_a = get_current_sid_a(&sid_a_size); + + KeAcquireSpinLock(&g_ot_hash_guard, &irql); + + for (ote = g_ot_hash[hash]; ote != NULL; ote = ote->next) + if (ote->fileobj == fileobj) + break; + + if (ote == NULL) { + ote = (struct ot_entry *)malloc_np(sizeof(*ote)); + if (ote == NULL) { + KdPrint(("[tdi_fw] ot_add_fileobj: malloc_np\n")); + status = STATUS_INSUFFICIENT_RESOURCES; + goto done; + } + memset(ote, 0, sizeof(*ote)); + + ote->next = g_ot_hash[hash]; + g_ot_hash[hash] = ote; + + ote->fileobj = fileobj; + for (i = 0; i < MAX_EVENT; i++) + ote->ctx[i].fileobj = fileobj; + + } else { + KdPrint(("[tdi_fw] ot_add_fileobj: reuse fileobj 0x%x\n", fileobj)); + + ot_cleanup_ote(ote); + } + + ote->signature = 'OTE '; + ote->pid = (ULONG)PsGetCurrentProcessId(); + + // save SID & attributes + ote->sid_a = sid_a; + ote->sid_a_size = sid_a_size; + sid_a = NULL; + + ote->devobj = devobj; + + ote->type = fileobj_type; + ote->ipproto = ipproto; + ote->conn_ctx = conn_ctx; + + status = STATUS_SUCCESS; + +done: + // cleanup + KeReleaseSpinLock(&g_ot_hash_guard, irql); + if (sid_a != NULL) + free(sid_a); + + return status; +} + +NTSTATUS +ot_del_fileobj(PFILE_OBJECT fileobj, int *fileobj_type) +{ + ULONG hash = CALC_HASH(fileobj); + KIRQL irql; + struct ot_entry *ote, *prev_ote; + NTSTATUS status; + + if (fileobj == NULL) + return STATUS_INVALID_PARAMETER_1; + + KeAcquireSpinLock(&g_ot_hash_guard, &irql); + + prev_ote = NULL; + for (ote = g_ot_hash[hash]; ote; ote = ote->next) { + if (ote->fileobj == fileobj) + break; + prev_ote = ote; + } + + if (ote == NULL) { + KdPrint(("[tdi_fw] ot_del_fileobj: fileobj 0x%x not found!\n", fileobj)); + status = STATUS_OBJECT_NAME_NOT_FOUND; + goto done; + } + + if (ote->type == FILEOBJ_ADDROBJ && ote->listen_entry != NULL) + del_listen_obj(ote->listen_entry, FALSE); + else if (ote->type == FILEOBJ_CONNOBJ && ote->conn_entry != NULL) { + if (ote->ipproto == IPPROTO_TCP && ote->log_disconnect) + log_disconnect(ote); + + del_tcp_conn_obj(ote->conn_entry, FALSE); + } + + if (fileobj_type != NULL) + *fileobj_type = ote->type; + + if (prev_ote != NULL) + prev_ote->next = ote->next; + else + g_ot_hash[hash] = ote->next; + + if (ote->sid_a != NULL) + free(ote->sid_a); + + free(ote); + + status = STATUS_SUCCESS; + +done: + KeReleaseSpinLock(&g_ot_hash_guard, irql); + + return status; +} + +struct ot_entry * +ot_find_fileobj(PFILE_OBJECT fileobj, KIRQL *irql) +{ + ULONG hash = CALC_HASH(fileobj); + struct ot_entry *ote; + + if (fileobj == NULL) + return NULL; + + if (irql != NULL) + KeAcquireSpinLock(&g_ot_hash_guard, irql); + + for (ote = g_ot_hash[hash]; ote != NULL; ote = ote->next) + if (ote->fileobj == fileobj) + break; + + if (ote == NULL) { + KdPrint(("[tdi_fw] ot_find_fileobj: fileobj 0x%x not found!\n", fileobj)); + if (irql != NULL) + KeReleaseSpinLock(&g_ot_hash_guard, *irql); + } + + return ote; +} + +void +ot_cleanup_ote(struct ot_entry *ote) +{ + struct ot_entry *saved_next; + PFILE_OBJECT saved_fileobj; + unsigned int i; + + // set all fields to zero except "next" and "fileobj" (cleanup listen/conn_entry if any) + + saved_next = ote->next; + saved_fileobj = ote->fileobj; + + if (ote->type == FILEOBJ_ADDROBJ && ote->listen_entry != NULL) { + + del_listen_obj(ote->listen_entry, FALSE); + + } else if (ote->type == FILEOBJ_CONNOBJ && ote->conn_entry != NULL) { + if (ote->ipproto == IPPROTO_TCP && ote->log_disconnect) + log_disconnect(ote); + + del_tcp_conn_obj(ote->conn_entry, FALSE); + } + + memset(ote, 0, sizeof(*ote)); + + ote->next = saved_next; + ote->fileobj = saved_fileobj; + + // restore fileobjs + for (i = 0; i < MAX_EVENT; i++) + ote->ctx[i].fileobj = saved_fileobj; + +} + +//---------------------------------------------------------------------------- + +#define CALC_HASH_2(addrobj, conn_ctx) CALC_HASH((ULONG)(addrobj) ^ (ULONG)(conn_ctx)) + +NTSTATUS +ot_add_conn_ctx(PFILE_OBJECT addrobj, CONNECTION_CONTEXT conn_ctx, PFILE_OBJECT connobj) +{ + ULONG hash = CALC_HASH_2(addrobj, conn_ctx); + KIRQL irql; + struct ctx_entry *cte; + NTSTATUS status; + + KeAcquireSpinLock(&g_cte_hash_guard, &irql); + + for (cte = g_cte_hash[hash]; cte != NULL; cte = cte->next) + if (cte->addrobj == addrobj && cte->conn_ctx == conn_ctx) + break; + + if (cte == NULL) { + KdPrint(("[tdi_fw] ot_add_fileobj: reuse addrobj 0x%x, conn_ctx 0x%x\n", + addrobj, conn_ctx)); + + cte = (struct ctx_entry *)malloc_np(sizeof(*cte)); + if (cte == NULL) { + KdPrint(("[tdi_fw] ot_add_conn_ctx: malloc_np\n")); + status = STATUS_INSUFFICIENT_RESOURCES; + goto done; + } + cte->next = g_cte_hash[hash]; + g_cte_hash[hash] = cte; + + cte->addrobj = addrobj; + cte->conn_ctx = conn_ctx; + } + + cte->connobj = connobj; + + status = STATUS_SUCCESS; +done: + + KeReleaseSpinLock(&g_cte_hash_guard, irql); + return status; +} + +NTSTATUS +ot_del_conn_ctx(PFILE_OBJECT addrobj, CONNECTION_CONTEXT conn_ctx) +{ + ULONG hash = CALC_HASH_2(addrobj, conn_ctx); + KIRQL irql; + struct ctx_entry *cte, *prev_cte; + NTSTATUS status; + + KeAcquireSpinLock(&g_cte_hash_guard, &irql); + + prev_cte = NULL; + for (cte = g_cte_hash[hash]; cte != NULL; cte = cte->next) { + if (cte->addrobj == addrobj && cte->conn_ctx == conn_ctx) + break; + prev_cte = cte; + } + + if (cte == NULL) { + KdPrint(("[tdi_fw] ot_del_conn_ctx: addrobj 0x%x not found!\n", addrobj)); + status = STATUS_OBJECT_NAME_NOT_FOUND; + goto done; + } + + if (prev_cte != NULL) + prev_cte->next = cte->next; + else + g_cte_hash[hash] = cte->next; + + free(cte); + + status = STATUS_SUCCESS; +done: + + KeReleaseSpinLock(&g_cte_hash_guard, irql); + return status; +} + +PFILE_OBJECT +ot_find_conn_ctx(PFILE_OBJECT addrobj, CONNECTION_CONTEXT conn_ctx) +{ + ULONG hash = CALC_HASH_2(addrobj, conn_ctx); + KIRQL irql; + struct ctx_entry *cte; + PFILE_OBJECT result = NULL; + + KeAcquireSpinLock(&g_cte_hash_guard, &irql); + + for (cte = g_cte_hash[hash]; cte != NULL; cte = cte->next) + if (cte->addrobj == addrobj && cte->conn_ctx == conn_ctx) { + result = cte->connobj; + break; + } + + KeReleaseSpinLock(&g_cte_hash_guard, irql); + return result; +} diff --git a/tdifw-1.4.4/drv/obj_tbl.h b/tdifw-1.4.4/drv/obj_tbl.h new file mode 100644 index 00000000..139e060d --- /dev/null +++ b/tdifw-1.4.4/drv/obj_tbl.h @@ -0,0 +1,108 @@ +/* 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: obj_tbl.h,v 1.6 2005/03/14 18:28:27 vlad Exp $ + +#ifndef _obj_tbl_h_ +#define _obj_tbl_h_ + +#include "filter.h" + +NTSTATUS ot_init(void); +void ot_free(void); + +#define FILEOBJ_CONTROLOBJ 0 +#define FILEOBJ_ADDROBJ 1 +#define FILEOBJ_CONNOBJ 2 + +NTSTATUS ot_add_fileobj( + PDEVICE_OBJECT devobj, PFILE_OBJECT fileobj, int fileobj_type, int ipproto, + CONNECTION_CONTEXT conn_ctx); + +NTSTATUS ot_del_fileobj( + PFILE_OBJECT fileobj, int *fileobj_type); + +// maximum length of TDI_ADDRESS_TYPE_* +#define TDI_ADDRESS_MAX_LENGTH TDI_ADDRESS_LENGTH_OSI_TSAP +#define TA_ADDRESS_MAX (sizeof(TA_ADDRESS) - 1 + TDI_ADDRESS_MAX_LENGTH) +#define TDI_ADDRESS_INFO_MAX (sizeof(TDI_ADDRESS_INFO) - 1 + TDI_ADDRESS_MAX_LENGTH) + +// max event index +#ifdef TDI_EVENT_ERROR_EX +// 2k +# define MAX_EVENT (TDI_EVENT_ERROR_EX + 1) +#else +// NT4 +# define MAX_EVENT (TDI_EVENT_CHAINED_RECEIVE_EXPEDITED + 1) +#endif + +/* replaced context */ +typedef struct { + PFILE_OBJECT fileobj; /* address object */ + PVOID old_handler; /* old event handler */ + PVOID old_context; /* old event handler context */ +} TDI_EVENT_CONTEXT; + +struct ot_entry { + ULONG signature; + struct ot_entry *next; + + ULONG pid; + + struct _SID_AND_ATTRIBUTES *sid_a; + ULONG sid_a_size; + + PDEVICE_OBJECT devobj; + PFILE_OBJECT fileobj; + PFILE_OBJECT associated_fileobj; + + int type; + int ipproto; + + TDI_EVENT_CONTEXT ctx[MAX_EVENT]; + UCHAR local_addr[TA_ADDRESS_MAX]; + UCHAR remote_addr[TA_ADDRESS_MAX]; + + CONNECTION_CONTEXT conn_ctx; + + struct listen_entry *listen_entry; // for address object + struct conn_entry *conn_entry; // for connection object + + // traffic count for connection object + ULONG bytes_out; + ULONG bytes_in; + + BOOLEAN log_disconnect; +}; + +struct ot_entry *ot_find_fileobj(PFILE_OBJECT fileobj, KIRQL *irql); +// Note: don't forget KeReleaseSpinLock(&g_ot_hash_guard, irql); + +extern KSPIN_LOCK g_ot_hash_guard; + +void ot_cleanup_ote(struct ot_entry *ote); + + +NTSTATUS ot_add_conn_ctx( + PFILE_OBJECT addrobj, CONNECTION_CONTEXT conn_ctx, PFILE_OBJECT connobj); + +NTSTATUS ot_del_conn_ctx( + PFILE_OBJECT addrobj, CONNECTION_CONTEXT conn_ctx); + +PFILE_OBJECT ot_find_conn_ctx( + PFILE_OBJECT addrobj, CONNECTION_CONTEXT conn_ctx); + + +struct _SID_AND_ATTRIBUTES *get_current_sid_a(ULONG *sid_a_size); +struct _SID_AND_ATTRIBUTES *copy_sid_a(struct _SID_AND_ATTRIBUTES *sid_a, ULONG sid_a_size); + +#endif diff --git a/tdifw-1.4.4/drv/packet.c b/tdifw-1.4.4/drv/packet.c new file mode 100644 index 00000000..658ca56a --- /dev/null +++ b/tdifw-1.4.4/drv/packet.c @@ -0,0 +1,780 @@ +/* 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: packet.c,v 1.9 2003/09/12 07:58:36 dev Exp $ + +/* + * Stateful packet checking engine + */ + +#ifdef USE_PACKET_ENGINE + +#include +#include +#include +#include "sock.h" + +#if _WIN32_WINNT >= 0x0500 +/* include pfhook.h */ +typedef ULONG IPAddr, IPMask; +#include +#endif + +#include "conn_state.h" +#include "filter.h" +#include "ipc.h" +#include "ndis_hk_ioctl.h" +#include "net.h" +#include "obj_tbl.h" +#include "packet.h" + +/* prototypes */ + +static NTSTATUS get_iface(void); + +static BOOLEAN filter_packet(int direction, int iface, PNDIS_PACKET packet, struct filter_nfo *self, + BOOLEAN packet_unchanged); + +static void process_ip(int direction, struct ip_hdr *ip_hdr, struct flt_request *log_nfo); + +static BOOLEAN process_transp(int direction, UCHAR proto, + struct ip_hdr *ip_hdr, char *pointer, UINT buffer_len, + struct flt_request *log_nfo); + +static void process_tcp(int direction, struct tcp_hdr *tcp_hdr, struct flt_request *log_nfo); +static void process_udp(int direction, struct udp_hdr *udp_hdr, struct flt_request *log_nfo); +static void process_icmp(int direction, struct icmp_hdr *icmp_hdr, struct flt_request *log_nfo); + +static void init_tcp_states(void); + +static void check_packet(struct flt_request *log_nfo); + +#if _WIN32_WINNT >= 0x0500 + +/* for "ipfilterdriver" */ + +static PF_FORWARD_ACTION hook_proc( + unsigned char *PacketHeader, unsigned char *Packet, unsigned int PacketLength, + unsigned int RecvInterfaceIndex, unsigned int SendInterfaceIndex, + IPAddr RecvLinkNextHop, IPAddr SendLinkNextHop); + +static NTSTATUS set_hook(PacketFilterExtensionPtr hook_fn); + +static BOOLEAN g_set_hook = FALSE; + +#endif + +/* globals */ + +static PDEVICE_OBJECT g_ndis_hk_devobj; +static PFILE_OBJECT g_ndis_hk_fileobj; + +// interface of ndis_hk +struct ndis_hk_interface *g_ndis_hk; + +static struct filter_nfo g_tdi_fw = { + sizeof(g_tdi_fw), + filter_packet, + NULL, + NULL, + NULL, + NULL +}; + +// here's a table with all possible tcp flags (except RST) and connection states + +#define MAX_FLAGS 3 + +static struct { + UCHAR tcp_flags_set; + UCHAR tcp_flags_not_set; +} g_tcp_states[TCP_STATE_MAX][2][MAX_FLAGS]; // init this array in init_tcp_states + + +NTSTATUS +init_packet(void) +{ + NTSTATUS status; + UNICODE_STRING devname; + + // connect with ndis_hk + RtlInitUnicodeString(&devname, L"\\Device\\ndis_hk"); + + status = IoGetDeviceObjectPointer( + &devname, + STANDARD_RIGHTS_ALL, + &g_ndis_hk_fileobj, + &g_ndis_hk_devobj); + if (status == STATUS_SUCCESS) { + + /* using ndis_hk driver */ + + status = get_iface(); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] init_packet get_iface: 0x%x!\n", status)); + goto done; + } + + // attach our filter! + g_ndis_hk->attach_filter(&g_tdi_fw, TRUE, FALSE); // to bottom of filter stack + + } else { +#if _WIN32_WINNT >= 0x0500 + /* try to use "ipfilterdriver" */ + + status = set_hook(hook_proc); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] init_packet set_hook: 0x%x!\n", status)); + goto done; + } + + g_set_hook = TRUE; +#endif + } + + init_tcp_states(); + +done: + if (status != STATUS_SUCCESS) { + // cleanup + free_packet(); + } + + return status; +} + +void +free_packet(void) +{ + if (g_ndis_hk_fileobj != NULL) { + // detach our filter! + if (g_ndis_hk != NULL) { + g_ndis_hk->attach_filter(&g_tdi_fw, FALSE, FALSE); + g_ndis_hk = NULL; + } + + ObDereferenceObject(g_ndis_hk_fileobj); + g_ndis_hk_fileobj = NULL; + } + +#if _WIN32_WINNT >= 0x0500 + if (g_set_hook) { + set_hook(NULL); + g_set_hook = FALSE; + } +#endif +} + +NTSTATUS +get_iface(void) +{ + PIRP irp; + IO_STATUS_BLOCK isb; + + irp = IoBuildDeviceIoControlRequest(IOCTL_CMD_GET_KM_IFACE, + g_ndis_hk_devobj, + NULL, 0, + &g_ndis_hk, sizeof(g_ndis_hk), + TRUE, NULL, &isb); + if (irp == NULL) { + KdPrint(("[tdi_fw] get_iface: IoBuildDeviceIoControlRequest!\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + return IoCallDriver(g_ndis_hk_devobj, irp); +} + +BOOLEAN +filter_packet(int direction, int iface, PNDIS_PACKET packet, struct filter_nfo *self, + BOOLEAN packet_unchanged) +{ + BOOLEAN result = FALSE, log = TRUE; + struct filter_nfo *child; + struct flt_request log_nfo; + PNDIS_BUFFER buffer; + UINT packet_len, buffer_len, buffer_offset, hdr_len; + void *pointer; + struct ether_hdr *ether_hdr; + struct ip_hdr *ip_hdr; + + KdPrint(("[tdi_fw] filter_packet: direction = %s; iface = %d; packet = 0x%x\n", + direction == DIRECTION_IN ? "in" : "out", iface, packet)); + + // get child in filter stack + if (direction == DIRECTION_IN) + child = self->lower; + else + child = self->upper; + + memset(&log_nfo, 0, sizeof(log_nfo)); + log_nfo.struct_size = sizeof(log_nfo); + log_nfo.direction = direction; + + // parse packet + + NdisQueryPacket(packet, NULL, NULL, &buffer, &packet_len); + + if (packet_len < sizeof(struct ether_hdr)) { + KdPrint(("[tdi_fw] filter_packet: too small packet for ether_hdr! (%u)\n", packet_len)); + goto done; + } + + /* process ether_hdr */ + + NdisQueryBuffer(buffer, ðer_hdr, &buffer_len); + + if (buffer_len < sizeof(struct ether_hdr)) { + KdPrint(("[tdi_fw] filter_packet: too small buffer for ether_hdr! (%u)\n", buffer_len)); + goto done; + } + buffer_offset = 0; + +#define PRINT_ETH_ADDR(addr) \ + (addr)[0], (addr)[1], (addr)[2], (addr)[3], (addr)[4], (addr)[5] + + KdPrint(("[tdi_fw] filter_packet: eth %02x-%02x-%02x-%02x-%02x-%02x -> %02x-%02x-%02x-%02x-%02x-%02x (0x%x)\n", + PRINT_ETH_ADDR(ether_hdr->ether_shost), + PRINT_ETH_ADDR(ether_hdr->ether_dhost), + ether_hdr->ether_type)); + + // UGLY way to determine IP broadcasts + if (memcmp(ether_hdr->ether_dhost, "\xff\xff\xff\xff\xff\xff", 6) == 0) + log_nfo.packet.is_broadcast = 1; + + // go to the next header + if (buffer_len > sizeof(struct ether_hdr)) { + + pointer = (char *)ether_hdr + sizeof(struct ether_hdr); + buffer_offset += sizeof(struct ether_hdr); + + buffer_len -= sizeof(struct ether_hdr); + + } else { + // use next buffer in chain + + do { + NdisGetNextBuffer(buffer, &buffer); + NdisQueryBuffer(buffer, &pointer, &buffer_len); + } while (buffer_len == 0); // sometimes there're buffers with zero size in chain + + buffer_offset = 0; + } + + if (ntohs(ether_hdr->ether_type) == ETHERTYPE_IP) { + + /* process ip_hdr */ + + if (buffer_len < sizeof(struct ip_hdr)) { + KdPrint(("[tdi_fw] filter_packet: too small buffer for ip_hdr! (%u)\n", + buffer_len)); + goto done; + } + + ip_hdr = (struct ip_hdr *)pointer; + hdr_len = ip_hdr->ip_hl * 4; + + if (buffer_len < hdr_len) { + KdPrint(("[tdi_fw] filter_packet: too small buffer for ip_hdr! (%u vs. %u)\n", + buffer_len, hdr_len)); + goto done; + } + + // check we've got the first fragment (don't work with another!) + if ((ntohs(ip_hdr->ip_off) & IP_OFFMASK) != 0 && (ip_hdr->ip_off & IP_DF) == 0) { + + KdPrint(("[tdi_fw] filter_packet: got not first fragment\n")); + + result = TRUE; + log = FALSE; + goto done; + } + + process_ip(direction, ip_hdr, &log_nfo); + + // go to the next header + if (buffer_len > hdr_len) { + + pointer = (char *)ip_hdr + hdr_len; + buffer_offset += hdr_len; + + buffer_len -= hdr_len; + + } else { + // use next buffer in chain + + do { + NdisGetNextBuffer(buffer, &buffer); + NdisQueryBuffer(buffer, &pointer, &buffer_len); + } while (buffer_len == 0); // sometimes there're buffers with zero size in chain + + buffer_offset = 0; + } + + result = process_transp(direction, ip_hdr->ip_p, ip_hdr, pointer, buffer_len, &log_nfo); + if (!result) + goto done; + + // using log_nfo->pid as signal to log it + log = (log_nfo.pid == 0); + } else + log = FALSE; // don't log not IP packets + + result = TRUE; // packet headers looks like valid + +done: + if (log) { + log_nfo.result = result ? FILTER_PACKET_LOG : FILTER_PACKET_BAD; + log_nfo.pid = (ULONG)-1; + log_request(&log_nfo); + } + + if (result) + return child->process_packet(direction, iface, packet, child, packet_unchanged); + else + return FALSE; +} + +/* IP header */ +void +process_ip(int direction, struct ip_hdr *ip_hdr, struct flt_request *log_nfo) +{ +#define PRINT_IP_ADDR(addr) \ + ((UCHAR *)&(addr))[0], ((UCHAR *)&(addr))[1], ((UCHAR *)&(addr))[2], ((UCHAR *)&(addr))[3] + + KdPrint(("[tdi_fw] process_ip: ip %d.%d.%d.%d -> %d.%d.%d.%d (proto = %d, ipid = 0x%x)\n", + PRINT_IP_ADDR(ip_hdr->ip_src), + PRINT_IP_ADDR(ip_hdr->ip_dst), + ip_hdr->ip_p, + ip_hdr->ip_id)); + + log_nfo->proto = ip_hdr->ip_p; + + if (direction == DIRECTION_IN) + log_nfo->log_bytes_in = ntohs(ip_hdr->ip_len); + else + log_nfo->log_bytes_out = ntohs(ip_hdr->ip_len); + + log_nfo->addr.len = sizeof(struct sockaddr_in); + + ((struct sockaddr_in *)(&log_nfo->addr.from))->sin_family = AF_INET; + ((struct sockaddr_in *)(&log_nfo->addr.from))->sin_addr.s_addr = ip_hdr->ip_src; + + ((struct sockaddr_in *)(&log_nfo->addr.to))->sin_family = AF_INET; + ((struct sockaddr_in *)(&log_nfo->addr.to))->sin_addr.s_addr = ip_hdr->ip_dst; +} + +/* process TCP, UDP or ICMP header */ +BOOLEAN +process_transp(int direction, UCHAR proto, + struct ip_hdr *ip_hdr, char *pointer, UINT buffer_len, + struct flt_request *log_nfo) +{ + struct tcp_hdr *tcp_hdr; + struct udp_hdr *udp_hdr; + struct icmp_hdr *icmp_hdr; + + switch (proto) { + case IPPROTO_TCP: + + /* process tcp_hdr */ + + if (buffer_len < sizeof(struct tcp_hdr)) { + KdPrint(("[tdi_fw] process_transp: too small buffer for tcp_hdr! (%u)\n", + buffer_len)); + return FALSE; + } + + tcp_hdr = (struct tcp_hdr *)pointer; + + process_tcp(direction, tcp_hdr, log_nfo); + + break; + + case IPPROTO_UDP: + + /* process udp_hdr */ + + if (buffer_len < sizeof(struct udp_hdr)) { + KdPrint(("[tdi_fw] process_transp: too small buffer for udp_hdr! (%u)\n", + buffer_len)); + return FALSE; + } + + udp_hdr = (struct udp_hdr *)pointer; + + process_udp(direction, udp_hdr, log_nfo); + + break; + + case IPPROTO_ICMP: + + /* process icmp_hdr */ + + if (buffer_len < sizeof(struct icmp_hdr)) { + KdPrint(("[tdi_fw] process_transp: too small buffer for icmp_hdr! (%u)\n", + buffer_len)); + return FALSE; + } + + icmp_hdr = (struct icmp_hdr *)pointer; + + process_icmp(direction, icmp_hdr, log_nfo); + + break; + + default: + KdPrint(("[tdi_fw] process_transp: ipproto = %d\n", proto)); + } + + return TRUE; +} + +/* process TCP header and check it against our information about connection objects */ +void +process_tcp(int direction, struct tcp_hdr *tcp_hdr, struct flt_request *log_nfo) +{ +#if DBG + char flags[7]; + + flags[0] = (tcp_hdr->th_flags & TH_FIN) ? 'F' : ' '; + flags[1] = (tcp_hdr->th_flags & TH_SYN) ? 'S' : ' '; + flags[2] = (tcp_hdr->th_flags & TH_RST) ? 'R' : ' '; + flags[3] = (tcp_hdr->th_flags & TH_PUSH) ? 'P' : ' '; + flags[4] = (tcp_hdr->th_flags & TH_ACK) ? 'A' : ' '; + flags[5] = (tcp_hdr->th_flags & TH_URG) ? 'U' : ' '; + flags[6] = '\0'; + + KdPrint(("[tdi_fw] tcp %d -> %d (%s)\n", + ntohs(tcp_hdr->th_sport), + ntohs(tcp_hdr->th_dport), + flags)); +#endif + + log_nfo->packet.tcp_flags = tcp_hdr->th_flags; + ((struct sockaddr_in *)(&log_nfo->addr.from))->sin_port = tcp_hdr->th_sport; + ((struct sockaddr_in *)(&log_nfo->addr.to))->sin_port = tcp_hdr->th_dport; + + check_packet(log_nfo); +} + +/* process UDP header and check it against our informatation about address objects */ +void +process_udp(int direction, struct udp_hdr *udp_hdr, struct flt_request *log_nfo) +{ + KdPrint(("[tdi_fw] udp %d -> %d\n", ntohs(udp_hdr->uh_sport), ntohs(udp_hdr->uh_dport))); + + ((struct sockaddr_in *)(&log_nfo->addr.from))->sin_port = udp_hdr->uh_sport; + ((struct sockaddr_in *)(&log_nfo->addr.to))->sin_port = udp_hdr->uh_dport; + + check_packet(log_nfo); +} + +/* process ICMP header */ +void +process_icmp(int direction, struct icmp_hdr *icmp_hdr, struct flt_request *log_nfo) +{ + KdPrint(("[tdi_fw] icmp (%d.%d)\n", icmp_hdr->icmp_type, icmp_hdr->icmp_code)); + + log_nfo->packet.icmp_type = icmp_hdr->icmp_type; + log_nfo->packet.icmp_code = icmp_hdr->icmp_code; + + check_packet(log_nfo); +} + +void +init_tcp_states(void) +{ + // init g_tcp_states sparse array (don't define RST flags) + +#define DEFINE_STATE_IN(state, index, flags_set, flags_not_set) \ + g_tcp_states[(state)][DIRECTION_IN][(index)].tcp_flags_set = (flags_set); \ + g_tcp_states[(state)][DIRECTION_IN][(index)].tcp_flags_not_set = (flags_not_set) + +#define DEFINE_STATE_OUT(state, index, flags_set, flags_not_set) \ + g_tcp_states[(state)][DIRECTION_OUT][(index)].tcp_flags_set = (flags_set); \ + g_tcp_states[(state)][DIRECTION_OUT][(index)].tcp_flags_not_set = (flags_not_set) + + // -connection establishment + + // --active (outgoing) connection + + // ---SYN retransmitting + 3rd positive answer + DEFINE_STATE_OUT(TCP_STATE_SYN_SENT, 0, TH_SYN, TH_ACK); + DEFINE_STATE_OUT(TCP_STATE_SYN_SENT, 1, TH_ACK, TH_SYN | TH_FIN); + + // ---positive answer + DEFINE_STATE_IN(TCP_STATE_SYN_SENT, 0, TH_SYN | TH_ACK, 0); + + // --passive (incoming) connection + + // ---positive answer + DEFINE_STATE_OUT(TCP_STATE_SYN_RCVD, 0, TH_SYN | TH_ACK, 0); + + // ---last connect step: ACK or FIN(ACK) + DEFINE_STATE_IN(TCP_STATE_SYN_RCVD, 0, TH_ACK, TH_FIN | TH_SYN); + DEFINE_STATE_IN(TCP_STATE_SYN_RCVD, 1, TH_FIN, TH_SYN); + + // -established in/out: ACK, FIN + DEFINE_STATE_IN(TCP_STATE_ESTABLISHED_IN, 0, TH_ACK, TH_FIN | TH_SYN); + DEFINE_STATE_IN(TCP_STATE_ESTABLISHED_IN, 1, TH_FIN, TH_SYN); + DEFINE_STATE_OUT(TCP_STATE_ESTABLISHED_IN, 0, TH_ACK, TH_FIN | TH_SYN); + DEFINE_STATE_OUT(TCP_STATE_ESTABLISHED_IN, 1, TH_FIN, TH_SYN); + + DEFINE_STATE_IN(TCP_STATE_ESTABLISHED_OUT, 0, TH_ACK, TH_FIN | TH_SYN); + DEFINE_STATE_IN(TCP_STATE_ESTABLISHED_OUT, 1, TH_FIN, TH_SYN); + + // --peer didn't receive 3rd ACK packet + DEFINE_STATE_IN(TCP_STATE_ESTABLISHED_OUT, 2, TH_ACK | TH_SYN, TH_FIN); + + DEFINE_STATE_OUT(TCP_STATE_ESTABLISHED_OUT, 0, TH_ACK, TH_FIN | TH_SYN); + DEFINE_STATE_OUT(TCP_STATE_ESTABLISHED_OUT, 1, TH_FIN, TH_SYN); + + // -connection closing + DEFINE_STATE_IN(TCP_STATE_FIN_WAIT1, 0, TH_FIN, TH_SYN); + DEFINE_STATE_IN(TCP_STATE_FIN_WAIT1, 1, TH_ACK, TH_FIN | TH_SYN); + DEFINE_STATE_OUT(TCP_STATE_FIN_WAIT1, 0, TH_FIN, TH_SYN); + DEFINE_STATE_OUT(TCP_STATE_FIN_WAIT1, 1, TH_ACK, TH_FIN | TH_SYN); + + DEFINE_STATE_IN(TCP_STATE_FIN_WAIT2, 0, TH_FIN, TH_SYN); + DEFINE_STATE_IN(TCP_STATE_FIN_WAIT2, 1, TH_ACK, TH_FIN | TH_SYN); + DEFINE_STATE_OUT(TCP_STATE_FIN_WAIT2, 0, TH_ACK, TH_FIN | TH_SYN); + + DEFINE_STATE_OUT(TCP_STATE_CLOSE_WAIT, 0, TH_ACK, TH_FIN | TH_SYN); + DEFINE_STATE_OUT(TCP_STATE_LAST_ACK, 0, TH_FIN, TH_SYN); + DEFINE_STATE_OUT(TCP_STATE_LAST_ACK, 1, TH_ACK, TH_FIN | TH_SYN); // answer on FIN retransmitting + DEFINE_STATE_IN(TCP_STATE_LAST_ACK, 0, TH_ACK, TH_FIN | TH_SYN); + DEFINE_STATE_IN(TCP_STATE_LAST_ACK, 1, TH_FIN, TH_SYN); // FIN retransmitting + + // -in "closed" state all incoming packets are allowed (except single SYN) + DEFINE_STATE_IN(TCP_STATE_CLOSED, 0, TH_ACK, 0); + DEFINE_STATE_IN(TCP_STATE_CLOSED, 1, TH_FIN, 0); + + // -and RST out too + DEFINE_STATE_OUT(TCP_STATE_CLOSED, 0, TH_RST, 0); + + // ??? I saw this packet sometimes (maybe there's a bug in my code?) + DEFINE_STATE_OUT(TCP_STATE_CLOSED, 1, TH_ACK, TH_FIN | TH_SYN); +} + +/* + * check packet against state in obj_tbl + * + * NOTE: this function is experimental ONLY!!! + * To make work it better I should specify interface information: IP address and network mask + */ +void +check_packet(struct flt_request *log_nfo) +{ + BOOLEAN correct = FALSE; + struct sockaddr_in *addr_from = (struct sockaddr_in *)&log_nfo->addr.from, + *addr_to = (struct sockaddr_in *)&log_nfo->addr.to; + + // check it + + if (log_nfo->proto == IPPROTO_TCP) { + + /* TCP protocol */ + + if ((log_nfo->packet.tcp_flags & TH_SYN) && + !(log_nfo->packet.tcp_flags & TH_ACK) && + log_nfo->direction == DIRECTION_IN) { + + // active incoming connection (SYN+ ACK-): check against listening address objects + correct = is_listen(addr_to->sin_addr.s_addr, addr_to->sin_port, IPPROTO_TCP) || + is_listen(0, addr_to->sin_port, IPPROTO_TCP); + + } else { + int state; + + // just check address & port against connection table + if (log_nfo->direction == DIRECTION_OUT) { + state = get_tcp_conn_state(addr_from->sin_addr.s_addr, addr_from->sin_port, + addr_to->sin_addr.s_addr, addr_to->sin_port); + if (state == TCP_STATE_NONE) // source addr can be 0 + state = get_tcp_conn_state(0, addr_from->sin_port, addr_to->sin_addr.s_addr, addr_to->sin_port); + } else { // DIRECTION_IN + state = get_tcp_conn_state(addr_to->sin_addr.s_addr, addr_to->sin_port, + addr_from->sin_addr.s_addr, addr_from->sin_port); + if (state == TCP_STATE_NONE) // dest addr can be 0 + state = get_tcp_conn_state(0, addr_to->sin_port, addr_from->sin_addr.s_addr, addr_from->sin_port); + } + + if (log_nfo->packet.tcp_flags & TH_RST) + correct = (state != TCP_STATE_NONE); + else { + + // check connection state & header flags against g_tcp_states + int i; + + for (i = 0; i < MAX_FLAGS; i++) { + UCHAR tcp_flags_set = g_tcp_states[state][log_nfo->direction][i].tcp_flags_set; + if (tcp_flags_set == 0) + break; + + if ((tcp_flags_set & log_nfo->packet.tcp_flags) == tcp_flags_set && + !(g_tcp_states[state][log_nfo->direction][i].tcp_flags_not_set & log_nfo->packet.tcp_flags)) { + correct = TRUE; + break; + } + } + } + + if (!correct && (log_nfo->packet.tcp_flags & TH_RST) && log_nfo->direction == DIRECTION_OUT) { + // don't log RSTs on opened ports + correct = is_listen(addr_from->sin_addr.s_addr, addr_from->sin_port, IPPROTO_TCP) || + is_listen(0, addr_from->sin_port, IPPROTO_TCP); + } + + if (!correct) + log_nfo->packet.tcp_state = state; + } + + } else if (log_nfo->proto == IPPROTO_UDP) { + + // UDP: check it against listening address objects + if (log_nfo->direction == DIRECTION_IN) { + // incoming datagram: check "to" addr & port + correct = is_listen(addr_to->sin_addr.s_addr, addr_to->sin_port, log_nfo->proto) || + is_listen(0, addr_to->sin_port, log_nfo->proto) || + (log_nfo->packet.is_broadcast && + (addr_to->sin_addr.s_addr == (ULONG)-1 || + is_bcast_listen(addr_to->sin_addr.s_addr, addr_to->sin_port, log_nfo->proto))); + + } else { // DIRECTION_OUT + // outgoing datagram: check "from" addr & port + correct = is_listen(addr_from->sin_addr.s_addr, addr_from->sin_port, log_nfo->proto) || + is_listen(0, addr_from->sin_port, log_nfo->proto) || + (log_nfo->packet.is_broadcast && is_bcast_listen(addr_from->sin_addr.s_addr, addr_from->sin_port, log_nfo->proto)); + } + } + + if (correct) + log_nfo->pid = (ULONG)-1; // don't log this packet +} + +#if _WIN32_WINNT >= 0x0500 +/* set or remove IP firewall hook */ +NTSTATUS +set_hook(PacketFilterExtensionPtr hook_fn) +{ + UNICODE_STRING ipfilter_name; + NTSTATUS status; + PFILE_OBJECT fileobj = NULL; + PDEVICE_OBJECT devobj; + PF_SET_EXTENSION_HOOK_INFO hook_nfo; + PIRP irp = NULL; + IO_STATUS_BLOCK isb; + + RtlInitUnicodeString(&ipfilter_name, DD_IPFLTRDRVR_DEVICE_NAME); + + status = IoGetDeviceObjectPointer( + &ipfilter_name, + STANDARD_RIGHTS_ALL, + &fileobj, + &devobj); + if (status != STATUS_SUCCESS) + goto done; + + hook_nfo.ExtensionPointer = hook_fn; + + irp = IoBuildDeviceIoControlRequest(IOCTL_PF_SET_EXTENSION_POINTER, + devobj, &hook_nfo, sizeof(hook_nfo), + NULL, 0, FALSE, NULL, &isb); + if (irp == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto done; + } + + status = IoCallDriver(devobj, irp); + irp = NULL; + +done: + /* cleanup */ + if (fileobj != NULL) + ObDereferenceObject(fileobj); + if (irp != NULL) + IoFreeIrp(irp); + + return status; +} + +/* firewall callback hook proc */ +PF_FORWARD_ACTION +hook_proc( + unsigned char *PacketHeader, + unsigned char *Packet, + unsigned int PacketLength, + unsigned int RecvInterfaceIndex, + unsigned int SendInterfaceIndex, + IPAddr RecvLinkNextHop, + IPAddr SendLinkNextHop) +{ + BOOLEAN log = FALSE, result = FALSE; + int direction, iface; + struct ip_hdr *ip_hdr; + struct flt_request log_nfo; + + direction = (RecvInterfaceIndex != INVALID_PF_IF_INDEX) ? DIRECTION_IN : DIRECTION_OUT; + iface = (direction == DIRECTION_IN) ? RecvInterfaceIndex : SendInterfaceIndex; + + KdPrint(("[tdi_fw] hook_proc: direction = %s; iface = 0x%x; len = %d\n", + direction == DIRECTION_IN ? "in" : "out", iface, PacketLength)); + + memset(&log_nfo, 0, sizeof(log_nfo)); + log_nfo.struct_size = sizeof(log_nfo); + log_nfo.direction = direction; + + // ...have not access to ethernet handler + + ip_hdr = (struct ip_hdr *)PacketHeader; + + if (ip_hdr->ip_dst == 0x0100007f) { // don't work with loopback interface + result = TRUE; + goto done; + } + + // check we've got the first fragment (don't work with another!) + if ((ntohs(ip_hdr->ip_off) & IP_OFFMASK) != 0 && (ip_hdr->ip_off & IP_DF) == 0) { + + KdPrint(("[tdi_fw] hook_proc: got not first fragment\n")); + + result = TRUE; + log = FALSE; + goto done; + } + + process_ip(direction, ip_hdr, &log_nfo); + + // no way to determine broadcasts without knowing interface IP/mask :-( + log_nfo.packet.is_broadcast = 1; // for datagram processing code + + result = process_transp(direction, ip_hdr->ip_p, ip_hdr, Packet, PacketLength, &log_nfo); + if (!result) + goto done; + + // using log_nfo->pid as signal to log it + log = (log_nfo.pid == 0); + + result = TRUE; // packet headers looks like valid + +done: + if (log) { + log_nfo.result = result ? FILTER_PACKET_LOG : FILTER_PACKET_BAD; + log_nfo.pid = (ULONG)-1; + log_request(&log_nfo); + } + + return result ? PF_FORWARD : PF_DROP; +} +#endif /* _WIN32_WINNT >= 0x0500 */ + +#endif /* USE_PACKET_ENGINE */ diff --git a/tdifw-1.4.4/drv/packet.h b/tdifw-1.4.4/drv/packet.h new file mode 100644 index 00000000..1eca7b9e --- /dev/null +++ b/tdifw-1.4.4/drv/packet.h @@ -0,0 +1,11 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs) +// +// $Id: packet.h,v 1.1 2003/05/14 16:33:34 dev Exp $ + +#ifndef _packet_h_ +#define _packet_h_ + +NTSTATUS init_packet(void); +void free_packet(void); + +#endif diff --git a/tdifw-1.4.4/drv/pid_pname.c b/tdifw-1.4.4/drv/pid_pname.c new file mode 100644 index 00000000..62360ee1 --- /dev/null +++ b/tdifw-1.4.4/drv/pid_pname.c @@ -0,0 +1,269 @@ +/* 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: pid_pname.c,v 1.3 2003/01/21 13:16:12 dev Exp $ + +/* + * Get process name by its pid and all related routines + */ + +#include + +#include "memtrack.h" +#include "pid_pname.h" + +/* process list entry */ +struct plist_entry { + struct plist_entry *next; + + // id & name + ULONG pid; + char *pname; + KEVENT *pname_event; + + int context; +}; + +/* process list */ +static struct { + struct plist_entry *head; + struct plist_entry *tail; + KSPIN_LOCK guard; +} g_plist; + +static struct plist_entry *add_ple(ULONG pid, KIRQL *irql); +static struct plist_entry *find_ple(ULONG pid, KIRQL *irql, struct plist_entry **prev); + +static VOID ProcessNotifyProc(IN HANDLE ParentId, IN HANDLE ProcessId, IN BOOLEAN Create); + +void +pid_pname_init(void) +{ + KeInitializeSpinLock(&g_plist.guard); + g_plist.head = g_plist.tail = NULL; + + PsSetCreateProcessNotifyRoutine(ProcessNotifyProc, FALSE); +} + +// free plist +void +pid_pname_free(void) +{ + KIRQL irql; + struct plist_entry *ple; + + PsSetCreateProcessNotifyRoutine(ProcessNotifyProc, TRUE); + + KeAcquireSpinLock(&g_plist.guard, &irql); + for (ple = g_plist.head; ple != NULL;) { + struct plist_entry *ple2 = ple->next; + if (ple->pname != NULL) + free(ple->pname); + free(ple); + ple = ple2; + } + g_plist.head = g_plist.tail = NULL; + KeReleaseSpinLock(&g_plist.guard, irql); +} + +// try to get pname by pid +BOOLEAN +pid_pname_resolve(ULONG pid, char *buf, int buf_size) +{ + BOOLEAN result; + KIRQL irql; + struct plist_entry *ple = find_ple(pid, &irql, NULL); + + if (ple == NULL) + return FALSE; + + if (ple->pname != NULL) { + if (buf_size > 0) { + strncpy(buf, ple->pname, buf_size); + buf[buf_size - 1] = '\0'; + } + result = TRUE; + } else + result = FALSE; + + KeReleaseSpinLock(&g_plist.guard, irql); + return result; +} + +// set pname_event by pid +NTSTATUS +pid_pname_set_event(ULONG pid, KEVENT *event) +{ + KIRQL irql; + struct plist_entry *ple = find_ple(pid, &irql, NULL); + + if (ple == NULL) { + // try to add + ple = add_ple(pid, &irql); + if (ple == NULL) + return STATUS_INSUFFICIENT_RESOURCES; + } + + ple->pname_event = event; + + KeReleaseSpinLock(&g_plist.guard, irql); + return STATUS_SUCCESS; +} + +NTSTATUS +pid_pname_set(ULONG pid, const char *pname, int context) +{ + KIRQL irql; + struct plist_entry *ple = find_ple(pid, &irql, NULL); + NTSTATUS status; + + if (ple == NULL) { + // try to add + ple = add_ple(pid, &irql); + if (ple == NULL) + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (ple->pname == NULL) { + ple->pname = (char *)malloc_np(strlen(pname) + 1); + if (ple->pname != NULL) { + strcpy(ple->pname, pname); + status = STATUS_SUCCESS; + } else { + KdPrint(("[tdi_fw] set_pid_name: malloc_np!\n")); + status = STATUS_INSUFFICIENT_RESOURCES; + } + + // and signal event we have name! (almost) + if (ple->pname_event != NULL) + KeSetEvent(ple->pname_event, IO_NO_INCREMENT, FALSE); + + } else + status = STATUS_SUCCESS; // already got pname + + + ple->context = context; + + KeReleaseSpinLock(&g_plist.guard, irql); + return status; +} + +int +pid_pname_get_context(ULONG pid) +{ + KIRQL irql; + int context; + struct plist_entry *ple = find_ple(pid, &irql, NULL); + if (ple == NULL) + return 0; + + context = ple->context; + + KeReleaseSpinLock(&g_plist.guard, irql); + return context; +} + + +struct plist_entry * +add_ple(ULONG pid, KIRQL *irql) +{ + struct plist_entry *ple; + + if (irql != NULL) + KeAcquireSpinLock(&g_plist.guard, irql); + + // add new entry to g_plist + ple = (struct plist_entry *)malloc_np(sizeof(*ple)); + if (ple != NULL) { + memset(ple, 0, sizeof(*ple)); + ple->pid = pid; + + // append + if (g_plist.tail != NULL) { + g_plist.tail->next = ple; + g_plist.tail = ple; + } else + g_plist.head = g_plist.tail = ple; + + } else { + KdPrint(("[tdi_fw] add_ple: malloc_np!\n")); + + if (irql != NULL) + KeReleaseSpinLock(&g_plist.guard, *irql); + } + + return ple; +} + +struct plist_entry * +find_ple(ULONG pid, KIRQL *irql, struct plist_entry **prev) +{ + struct plist_entry *ple, *prev_ple; + + if (irql != NULL) + KeAcquireSpinLock(&g_plist.guard, irql); + + prev_ple = NULL; + for (ple = g_plist.head; ple != NULL; ple = ple->next) { + if (ple->pid == pid) { + if (prev != NULL) + *prev = prev_ple; + return ple; + } + prev_ple = ple; + } + + if (irql != NULL) + KeReleaseSpinLock(&g_plist.guard, *irql); + + return NULL; +} + +// notify routine on process creation or removing +VOID +ProcessNotifyProc(IN HANDLE ParentId, IN HANDLE ProcessId, IN BOOLEAN Create) +{ + KIRQL irql; + struct plist_entry *ple, *prev_ple; + + if (Create) { + KdPrint(("[tdi_fw] ProcessNotifyProc: create process with pid:%u\n", ProcessId)); + + add_ple((ULONG)ProcessId, &irql); + KeReleaseSpinLock(&g_plist.guard, irql); + + } else { + // remove entry from plist + + KdPrint(("[tdi_fw] ProcessNotifyProc: remove process with pid:%u\n", ProcessId)); + + ple = find_ple((ULONG)ProcessId, &irql, &prev_ple); + if (ple == NULL) + return; + + if (prev_ple != NULL) + prev_ple->next = ple->next; + else + g_plist.head = ple->next; + + if (ple->next == NULL) + g_plist.tail = prev_ple; + + if (ple->pname != NULL) { + KdPrint(("[tdi_fw] ProcessNotifyProc: pname was %s\n", ple->pname)); + free(ple->pname); + } + + free(ple); + KeReleaseSpinLock(&g_plist.guard, irql); + } +} diff --git a/tdifw-1.4.4/drv/pid_pname.h b/tdifw-1.4.4/drv/pid_pname.h new file mode 100644 index 00000000..d1e957fb --- /dev/null +++ b/tdifw-1.4.4/drv/pid_pname.h @@ -0,0 +1,18 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs) +// +// $Id: pid_pname.h,v 1.1 2002/12/03 12:14:42 dev Exp $ + +#ifndef _pid_pname_h_ +#define _pid_pname_h_ + +void pid_pname_init(void); +void pid_pname_free(void); + +BOOLEAN pid_pname_resolve(ULONG pid, char *buf, int buf_size); + +NTSTATUS pid_pname_set_event(ULONG pid, KEVENT *event); + +NTSTATUS pid_pname_set(ULONG pid, const char *pname, int context); +int pid_pname_get_context(ULONG pid); + +#endif diff --git a/tdifw-1.4.4/drv/sids.c b/tdifw-1.4.4/drv/sids.c new file mode 100644 index 00000000..7f9ff1a8 --- /dev/null +++ b/tdifw-1.4.4/drv/sids.c @@ -0,0 +1,211 @@ +/* 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: sids.c,v 1.1 2005/03/15 15:44:26 vlad Exp $ + +/* + * Working with SID bitmasks + */ + +#include +#include "sock.h" + +#include "ipc.h" +#include "memtrack.h" +#include "sids.h" +#include "tdi_fw.h" + +struct sid_nfo { + ULONG sid_len; + char sid_data[]; +}; + +static struct { + char *buf; + struct sid_nfo *list[MAX_SIDS_COUNT]; + int count; + KSPIN_LOCK guard; +} g_sids; + +void +sids_init(void) +{ + KeInitializeSpinLock(&g_sids.guard); +} + +NTSTATUS +set_sid_list(char *buf, ULONG size) +{ + KIRQL irql; + NTSTATUS status; + ULONG pos; + int i; + + KeAcquireSpinLock(&g_sids.guard, &irql); + + // first, free information + if (g_sids.buf != NULL) { + free(g_sids.buf); + g_sids.buf = NULL; + } + memset(g_sids.list, 0, sizeof(g_sids.list)); + g_sids.count = 0; + + if (size != 0) { + // copy buffer + g_sids.buf = (char *)malloc_np(size); + if (g_sids.buf == NULL) { + KdPrint(("[tdi_fw] set_sid_list: malloc_np!\n")); + status = STATUS_INSUFFICIENT_RESOURCES; + goto done; + } + memcpy(g_sids.buf, buf, size); + + // parse buffer and find struct sid_nfo + for (pos = 0, i = 0; pos + sizeof(struct sid_nfo) < size && i < MAX_SIDS_COUNT; i++) { + struct sid_nfo *nfo = (struct sid_nfo *)&g_sids.buf[pos]; + + if (pos + sizeof(*nfo) + nfo->sid_len > size) + break; + + g_sids.list[i] = nfo; + + pos += sizeof(*nfo) + nfo->sid_len; + } + + g_sids.count = i; + + if (pos == size) + status = STATUS_SUCCESS; + else + status = STATUS_INVALID_PARAMETER; + } else + status = STATUS_SUCCESS; + +done: + KeReleaseSpinLock(&g_sids.guard, irql); + return status; +} + +#define SeLengthSid(sid) (8 + 4 * ((unsigned char *)(sid))[1]) + +int +get_sid_id(struct _SID_AND_ATTRIBUTES *sid_a, ULONG sid_a_size) +{ + // doing linear search (optimize?) + KIRQL irql; + int i, result = 0; + + KdPrint(("[tdi_fw] get_sid_id: (sid_size = %u/%u)\n", + SeLengthSid(sid_a->Sid), sid_a_size)); + + KeAcquireSpinLock(&g_sids.guard, &irql); + + for (i = 1; i < g_sids.count; i++) { + // comparing sids byte by byte (can't call RtlEqualSid() due to DISPATCH_LEVEL) + + KdPrint(("[tdi_fw] get_sid_id: sid #%d size %u\n", i, SeLengthSid((PSID)g_sids.list[i]->sid_data))); + + if (SeLengthSid(sid_a->Sid) == SeLengthSid((PSID)g_sids.list[i]->sid_data) && + memcmp(sid_a->Sid, (PSID)g_sids.list[i]->sid_data, SeLengthSid(sid_a->Sid)) == 0) { + result = i; + break; + } + } + + KeReleaseSpinLock(&g_sids.guard, irql); + + KdPrint(("[tdi_fw] get_sid_id: %d\n", result)); + return result; +} + +#define CURRENT_THREAD (HANDLE)-2 +#define CURRENT_PROCESS (HANDLE)-1 + +#define TOKEN_QUERY 0x0008 + +struct _SID_AND_ATTRIBUTES * +get_current_sid_a(ULONG *sid_a_size) // must be called at PASSIVE_LEVEL! +{ + NTSTATUS status; + HANDLE token; + ULONG size; + SID_AND_ATTRIBUTES *sid_a; + + *sid_a_size = 0; + + // open thread token + status = ZwOpenThreadToken(CURRENT_THREAD, TOKEN_QUERY, FALSE, &token); + if (status == STATUS_NO_TOKEN) { + // open process token + status = ZwOpenProcessToken(CURRENT_PROCESS, TOKEN_QUERY, &token); + } + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] get_current_sid_a: ZwOpen{Thread|Process}Token: 0x%x!\n")); + return NULL; + } + + size = sizeof(*sid_a) + 100; // default size + + sid_a = (SID_AND_ATTRIBUTES *)malloc_np(size); + if (sid_a == NULL) { + KdPrint(("[tdi_fw] get_current_sid_a: malloc_np!\n")); + goto done; + } + + status = ZwQueryInformationToken(token, TokenUser, sid_a, size, &size); + if (status == STATUS_BUFFER_TOO_SMALL) { + free(sid_a); + + sid_a = (SID_AND_ATTRIBUTES *)malloc_np(size); + if (sid_a == NULL) { + KdPrint(("[tdi_fw] get_current_sid_a: malloc_np!\n")); + goto done; + } + + status = ZwQueryInformationToken(token, TokenUser, sid_a, size, &size); + } + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] get_current_sid_a: ZwQueryInformationToken: 0x%x!\n")); + + free(sid_a); + sid_a = NULL; + goto done; + } + + // got sid & attributes! + + *sid_a_size = size; + +done: + ZwClose(token); + return sid_a; +} + +struct _SID_AND_ATTRIBUTES * +copy_sid_a(SID_AND_ATTRIBUTES *sid_a, ULONG sid_a_size) +{ + SID_AND_ATTRIBUTES *result; + + if (sid_a == NULL) + return NULL; + + result = (SID_AND_ATTRIBUTES *)malloc_np(sid_a_size); + if (result == NULL) + return NULL; + + memcpy(result, sid_a, sid_a_size); + + result->Sid = (char *)result + ((char *)(sid_a->Sid) - (char *)sid_a); + + return result; +} diff --git a/tdifw-1.4.4/drv/sids.h b/tdifw-1.4.4/drv/sids.h new file mode 100644 index 00000000..98bf619a --- /dev/null +++ b/tdifw-1.4.4/drv/sids.h @@ -0,0 +1,30 @@ +/* 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: sids.h,v 1.1 2005/03/15 15:44:26 vlad Exp $ + +#ifndef _sids_h_ +#define _sids_h_ + +/* SID stuff */ + +struct _SID_AND_ATTRIBUTES *get_current_sid_a(ULONG *sid_a_size); +struct _SID_AND_ATTRIBUTES *copy_sid_a(struct _SID_AND_ATTRIBUTES *sid_a, ULONG sid_a_size); + +void sids_init(void); + +int get_sid_id(struct _SID_AND_ATTRIBUTES *sid_a, ULONG sid_a_size); + +/* size can be NULL; buf is ULONG sid_len; SID ... */ +NTSTATUS set_sid_list(char *buf, ULONG size); + +#endif diff --git a/tdifw-1.4.4/drv/sock.c b/tdifw-1.4.4/drv/sock.c new file mode 100644 index 00000000..6a43035b --- /dev/null +++ b/tdifw-1.4.4/drv/sock.c @@ -0,0 +1,41 @@ +/* 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: sock.c,v 1.2 2002/12/03 12:14:28 dev Exp $ + +/* + * Some helpers from sockets + */ + +#include +#include +#include "sock.h" + +u_long +ntohl (u_long netlong) +{ + u_long result = 0; + ((char *)&result)[0] = ((char *)&netlong)[3]; + ((char *)&result)[1] = ((char *)&netlong)[2]; + ((char *)&result)[2] = ((char *)&netlong)[1]; + ((char *)&result)[3] = ((char *)&netlong)[0]; + return result; +} + +u_short +ntohs (u_short netshort) +{ + u_short result = 0; + ((char *)&result)[0] = ((char *)&netshort)[1]; + ((char *)&result)[1] = ((char *)&netshort)[0]; + return result; +} diff --git a/tdifw-1.4.4/drv/sock.h b/tdifw-1.4.4/drv/sock.h new file mode 100644 index 00000000..3215153a --- /dev/null +++ b/tdifw-1.4.4/drv/sock.h @@ -0,0 +1,86 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs) +// +// $Id: sock.h,v 1.2 2003/05/14 16:33:10 dev Exp $ + +#ifndef _sock_h_ +#define _sock_h_ + +/*--- some declarations from winsock.h ---*/ + +/* + * Basic system type definitions, taken from the BSD file sys/types.h. + */ +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long u_long; + +// some socket functions + +u_long ntohl(u_long netlong); +u_short ntohs(u_short netshort); + +u_long htonl(u_long netlong); +u_short htons(u_short netshort); + +/* + * Protocols + */ +#define IPPROTO_IP 0 /* dummy for IP */ +#define IPPROTO_ICMP 1 /* control message protocol */ +#define IPPROTO_TCP 6 /* tcp */ +#define IPPROTO_UDP 17 /* user datagram protocol */ + +/* + * Structure used by kernel to store most + * addresses. + */ +struct sockaddr { + u_short sa_family; /* address family */ + char sa_data[14]; /* up to 14 bytes of direct address */ +}; + +/* + * Internet address (old style... should be updated) + */ +struct in_addr { + union { + struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; + struct { u_short s_w1,s_w2; } S_un_w; + u_long S_addr; + } S_un; +#define s_addr S_un.S_addr + /* can be used for most tcp & ip code */ +#define s_host S_un.S_un_b.s_b2 + /* host on imp */ +#define s_net S_un.S_un_b.s_b1 + /* network */ +#define s_imp S_un.S_un_w.s_w2 + /* imp */ +#define s_impno S_un.S_un_b.s_b4 + /* imp # */ +#define s_lh S_un.S_un_b.s_b3 + /* logical host */ +}; + +/* + * Socket address, internet style. + */ +struct sockaddr_in { + short sin_family; + u_short sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +}; + +/* + * Address families. + */ +#define AF_INET 2 /* internetwork: UDP, TCP, etc. */ + +#define INADDR_ANY (u_long)0x00000000 +#define INADDR_LOOPBACK 0x7f000001 +#define INADDR_BROADCAST (u_long)0xffffffff +#define INADDR_NONE 0xffffffff + +#endif diff --git a/tdifw-1.4.4/drv/tdi_fw.c b/tdifw-1.4.4/drv/tdi_fw.c new file mode 100644 index 00000000..0304082c --- /dev/null +++ b/tdifw-1.4.4/drv/tdi_fw.c @@ -0,0 +1,985 @@ +/* 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: tdi_fw.c,v 1.13 2003/09/04 15:20:09 dev Exp $ + +/* + * TDI-based open source personal firewall (TdiFw) + */ + +#include +#include +#include "sock.h" + +#include "conn_state.h" +#include "dispatch.h" +#include "filter.h" +#include "memtrack.h" +#include "obj_tbl.h" +#include "tdi_fw.h" + +#define IOCTL_TRANSFER_TYPE(ioctl) ((ioctl) & 3) + +/* context for tdi_skip_complete */ +typedef struct { + PIO_COMPLETION_ROUTINE old_cr; /* old (original) completion routine */ + PVOID old_context; /* old (original) parameter for old_cr */ + PIO_COMPLETION_ROUTINE new_cr; /* new (replaced) completion routine */ + PVOID new_context; /* new (replaced) parameter for new_cr */ + PFILE_OBJECT fileobj; /* FileObject from IO_STACK_LOCATION */ + PDEVICE_OBJECT new_devobj; /* filter device object */ + UCHAR old_control; /* old (original) irps->Control */ +} TDI_SKIP_CTX; + +/* prototypes */ + +static NTSTATUS DeviceDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP irp); +static VOID OnUnload(IN PDRIVER_OBJECT DriverObject); + +#ifndef USE_TDI_HOOKING +static NTSTATUS c_n_a_device(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT *fltobj, + PDEVICE_OBJECT *oldobj, wchar_t *devname); +static void d_n_d_device(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT oldobj, + PDEVICE_OBJECT fltobj); +#else +static NTSTATUS hook_tcpip(DRIVER_OBJECT *old_DriverObject, BOOLEAN b_hook); +static NTSTATUS get_device_object(wchar_t *name, PDEVICE_OBJECT *devobj); +#endif + +static NTSTATUS tdi_skip_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context); + +/* device objects for: */ +PDEVICE_OBJECT + g_tcpfltobj = NULL, // \Device\Tcp + g_udpfltobj = NULL, // \Device\Udp + g_ipfltobj = NULL, // \Device\RawIp + g_devcontrol = NULL, // control device (exclusive access only!) + g_devnfo = NULL; // information device + +BOOLEAN g_got_log = FALSE; // got log app + +#ifndef USE_TDI_HOOKING +// original device objects +PDEVICE_OBJECT g_tcpoldobj, g_udpoldobj, g_ipoldobj; +#else +// original driver object +DRIVER_OBJECT g_old_DriverObject; +BOOLEAN g_hooked = FALSE; +#endif + +/* for IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER */ +typedef NTSTATUS TCPSendData_t(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp); +static TCPSendData_t *g_TCPSendData = NULL; +static TCPSendData_t new_TCPSendData; + +/* global traffic stats */ +unsigned __int64 g_traffic[TRAFFIC_MAX]; +KSPIN_LOCK g_traffic_guard; + +/* initialization */ +NTSTATUS +DriverEntry(IN PDRIVER_OBJECT theDriverObject, + IN PUNICODE_STRING theRegistryPath) +{ + NTSTATUS status = STATUS_SUCCESS; + int i; + UNICODE_STRING name, linkname; + + memtrack_init(); + KeInitializeSpinLock(&g_traffic_guard); + +#ifdef USE_TDI_HOOKING + KdPrint(("[tdi_fw] WARNING! Using unstable working mode: TDI hooking!\n")); +#endif + + status = ot_init(); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] DriverEntry: ot_init: 0x%x\n", status)); + goto done; + } + + status = filter_init(); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] DriverEntry: filter_init: 0x%x\n", status)); + goto done; + } + + status = conn_state_init(); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] DriverEntry: conn_state_init: 0x%x\n", status)); + goto done; + } + + for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) + theDriverObject->MajorFunction[i] = DeviceDispatch; + +#if DBG + // register UnLoad procedure + theDriverObject->DriverUnload = OnUnload; +#endif + + /* create control device and symbolic link */ + + RtlInitUnicodeString(&name, L"\\Device\\tdifw"); + + status = IoCreateDevice(theDriverObject, + 0, + &name, + 0, + 0, + TRUE, // exclusive! + &g_devcontrol); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] DriverEntry: IoCreateDevice(control): 0x%x!\n", status)); + goto done; + } + + RtlInitUnicodeString(&linkname, L"\\??\\tdifw"); + + status = IoCreateSymbolicLink(&linkname, &name); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] DriverEntry: IoCreateSymbolicLink: 0x%x!\n", status)); + goto done; + } + + RtlInitUnicodeString(&name, L"\\Device\\tdifw_nfo"); + + status = IoCreateDevice(theDriverObject, + 0, + &name, + 0, + 0, + FALSE, // not exclusive! + &g_devnfo); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] DriverEntry: IoCreateDevice(nfo): 0x%x!\n", status)); + goto done; + } + + RtlInitUnicodeString(&linkname, L"\\??\\tdifw_nfo"); + + status = IoCreateSymbolicLink(&linkname, &name); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] DriverEntry: IoCreateSymbolicLink: 0x%x!\n", status)); + goto done; + } + +#ifndef USE_TDI_HOOKING + + status = c_n_a_device(theDriverObject, &g_tcpfltobj, &g_tcpoldobj, L"\\Device\\Tcp"); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] DriverEntry: c_n_a_device: 0x%x\n", status)); + goto done; + } + + status = c_n_a_device(theDriverObject, &g_udpfltobj, &g_udpoldobj, L"\\Device\\Udp"); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] DriverEntry: c_n_a_device: 0x%x\n", status)); + goto done; + } + + status = c_n_a_device(theDriverObject, &g_ipfltobj, &g_ipoldobj, L"\\Device\\RawIp"); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] DriverEntry: c_n_a_device: 0x%x\n", status)); + goto done; + } + +#else /* USE_TDI_HOOKING */ + + /* get device objects for tcp/udp/ip */ + + status = get_device_object(L"\\Device\\Tcp", &g_tcpfltobj); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] DriverEntry: get_device_object(tcp): 0x%x\n", status)); + goto done; + } + + status = get_device_object(L"\\Device\\Udp", &g_udpfltobj); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] DriverEntry: get_device_object(udp): 0x%x\n", status)); + goto done; + } + + status = get_device_object(L"\\Device\\RawIp", &g_ipfltobj); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] DriverEntry: get_device_object(ip): 0x%x\n", status)); + goto done; + } + + /* hook tcpip */ + + status = hook_tcpip(&g_old_DriverObject, TRUE); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] DriverEntry: hook_driver: 0x%x\n", status)); + goto done; + } + g_hooked = TRUE; + +#endif /* USE_TDI_HOOKING */ + + status = STATUS_SUCCESS; + +done: + if (status != STATUS_SUCCESS) { + // cleanup + OnUnload(theDriverObject); + } + + return status; +} + +/* deinitialization */ +VOID +OnUnload(IN PDRIVER_OBJECT DriverObject) +{ +#ifndef USE_TDI_HOOKING + d_n_d_device(DriverObject, g_tcpoldobj, g_tcpfltobj); + d_n_d_device(DriverObject, g_udpoldobj, g_udpfltobj); + d_n_d_device(DriverObject, g_ipoldobj, g_ipfltobj); +#else + if (g_hooked) + hook_tcpip(&g_old_DriverObject, FALSE); +#endif + + // delete control device and symbolic link + if (g_devcontrol != NULL) { + UNICODE_STRING linkname; + + RtlInitUnicodeString(&linkname, L"\\??\\tdifw"); + IoDeleteSymbolicLink(&linkname); + + IoDeleteDevice(g_devcontrol); + } + + // delete info device and symbolic link + if (g_devnfo != NULL) { + UNICODE_STRING linkname; + + RtlInitUnicodeString(&linkname, L"\\??\\tdifw_nfo"); + IoDeleteSymbolicLink(&linkname); + + IoDeleteDevice(g_devnfo); + } + + filter_free(); + ot_free(); + conn_state_free(); // call after ot_free() + + memtrack_free(); +} + +#ifndef USE_TDI_HOOKING + +/* create & attach device */ +NTSTATUS +c_n_a_device(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT *fltobj, PDEVICE_OBJECT *oldobj, + wchar_t *devname) +{ + NTSTATUS status; + UNICODE_STRING str; + + /* create filter device */ + + status = IoCreateDevice(DriverObject, + 0, + NULL, + FILE_DEVICE_UNKNOWN, + 0, + TRUE, + fltobj); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] c_n_a_device: IoCreateDevice(%S): 0x%x\n", devname, status)); + return status; + } + + (*fltobj)->Flags |= DO_DIRECT_IO; + + RtlInitUnicodeString(&str, devname); + + status = IoAttachDevice(*fltobj, &str, oldobj); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] DriverEntry: IoAttachDevice(%S): 0x%x\n", devname, status)); + return status; + } + + KdPrint(("[tdi_fw] DriverEntry: %S fileobj: 0x%x\n", devname, *fltobj)); + + return STATUS_SUCCESS; +} + +/* detach & delete device */ +void +d_n_d_device(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT oldobj, PDEVICE_OBJECT fltobj) +{ + /* + * Detaching of a filter driver at runtime is a high-risk deal + */ + +#if 1 + // for extremal guys only! + if (oldobj != NULL && fltobj != NULL) { + int i; + for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) + DriverObject->MajorFunction[i] = g_tcpoldobj->DriverObject->MajorFunction[i]; + } +#endif + + if (oldobj != NULL) + IoDetachDevice(oldobj); + + if (fltobj != NULL) + IoDeleteDevice(fltobj); +} + +#else /* USE_TDI_HOOKING */ + +/* hook/unhook driver */ +NTSTATUS +hook_tcpip(DRIVER_OBJECT *old_DriverObject, BOOLEAN b_hook) +{ + UNICODE_STRING drv_name; + NTSTATUS status; + PDRIVER_OBJECT new_DriverObject; + int i; + + RtlInitUnicodeString(&drv_name, L"\\Driver\\Tcpip"); + + status = ObReferenceObjectByName(&drv_name, OBJ_CASE_INSENSITIVE, NULL, 0, + IoDriverObjectType, KernelMode, NULL, &new_DriverObject); + if (status != STATUS_SUCCESS) { + KdPrint(("[tdi_fw] hook_driver: ObReferenceObjectByName\n")); + return status; + } + + for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) { + if (b_hook) { + old_DriverObject->MajorFunction[i] = new_DriverObject->MajorFunction[i]; + new_DriverObject->MajorFunction[i] = DeviceDispatch; + } else + new_DriverObject->MajorFunction[i] = old_DriverObject->MajorFunction[i]; + } + + return STATUS_SUCCESS; +} + +/* get device object by its name */ +NTSTATUS +get_device_object(wchar_t *name, PDEVICE_OBJECT *devobj) +{ + UNICODE_STRING str; + NTSTATUS status; + PFILE_OBJECT fileobj; + + RtlInitUnicodeString(&str, name); + + status = IoGetDeviceObjectPointer(&str, FILE_ALL_ACCESS, &fileobj, devobj); + if (status == STATUS_SUCCESS) + ObDereferenceObject(fileobj); + + return status; +} + +#endif /* USE_TDI_HOOKING */ + +/* dispatch */ +NTSTATUS +DeviceDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP irp) +{ + PIO_STACK_LOCATION irps; + NTSTATUS status; + + // sanity check + if (irp == NULL) { + KdPrint(("[tdi_fw] DeviceDispatch: !irp\n")); + return STATUS_SUCCESS; + } + + irps = IoGetCurrentIrpStackLocation(irp); + + if (DeviceObject == g_tcpfltobj || DeviceObject == g_udpfltobj || + DeviceObject == g_ipfltobj) { + + /* + * This IRP is for filtered device + */ + + int result; + struct completion completion; + + memset(&completion, 0, sizeof(completion)); + + // Analyze MajorFunction + switch (irps->MajorFunction) { + + case IRP_MJ_CREATE: /* create fileobject */ + + result = tdi_create(irp, irps, &completion); + + status = tdi_dispatch_complete(DeviceObject, irp, result, + completion.routine, completion.context); + + break; + + case IRP_MJ_DEVICE_CONTROL: + + KdPrint(("[tdi_fw] DeviceDispatch: IRP_MJ_DEVICE_CONTROL, control 0x%x for 0x%08X\n", + irps->Parameters.DeviceIoControl.IoControlCode, irps->FileObject)); + + if (KeGetCurrentIrql() == PASSIVE_LEVEL) { + /* + * try to convert it to IRP_MJ_INTERNAL_DEVICE_CONTROL + * (works on PASSIVE_LEVEL only!) + */ + status = TdiMapUserRequest(DeviceObject, irp, irps); + + } else + status = STATUS_NOT_IMPLEMENTED; // set fake status + + if (status != STATUS_SUCCESS) { + void *buf = (irps->Parameters.DeviceIoControl.IoControlCode == IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER) ? + irps->Parameters.DeviceIoControl.Type3InputBuffer : NULL; + + // send IRP to original driver + status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW, NULL, NULL); + + if (buf != NULL && status == STATUS_SUCCESS) { + + g_TCPSendData = *(TCPSendData_t **)buf; + + KdPrint(("[tdi_fw] DeviceDispatch: IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER: TCPSendData = 0x%x\n", + g_TCPSendData)); + + *(TCPSendData_t **)buf = new_TCPSendData; + } + + break; + } + + // don't break! go to internal device control! + + case IRP_MJ_INTERNAL_DEVICE_CONTROL: { + /* + * Analyze ioctl for TDI driver + */ + int i; + + for (i = 0; g_tdi_ioctls[i].MinorFunction != 0; i++) + if (g_tdi_ioctls[i].MinorFunction == irps->MinorFunction) { + +#if DBG + // print description + KdPrint(("[tdi_fw] DeviceDispatch: %s (0x%x) for 0x%x\n", + g_tdi_ioctls[i].desc, + irps->MinorFunction, + irps->FileObject)); +#endif + + if (g_tdi_ioctls[i].fn == NULL) { + // send IRP to original driver + status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW, + NULL, NULL); + break; + } + + // call dispatch function + + result = g_tdi_ioctls[i].fn(irp, irps, &completion); + + // complete request + status = tdi_dispatch_complete(DeviceObject, irp, result, + completion.routine, completion.context); + + break; + } + + // if dispatch function hasn't been found + if (g_tdi_ioctls[i].MinorFunction == 0) { + // send IRP to original driver + status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW, NULL, NULL); + } + + break; + } + + case IRP_MJ_CLEANUP: /* cleanup fileobject */ + + result = tdi_cleanup(irp, irps, &completion); + + status = tdi_dispatch_complete(DeviceObject, irp, result, + completion.routine, completion.context); + break; + + case IRP_MJ_CLOSE: + KdPrint(("[tdi_fw] DeviceDispatch: IRP_MJ_CLOSE fileobj 0x%x\n", irps->FileObject)); + + // passthrough IRP + status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW, + completion.routine, completion.context); + + break; + + default: + KdPrint(("[tdi_fw] DeviceDispatch: major 0x%x, minor 0x%x for 0x%x\n", + irps->MajorFunction, irps->MinorFunction, irps->FileObject)); + + // passthrough IRP + status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW, + completion.routine, completion.context); + } + + } else if (DeviceObject == g_devcontrol) { + + /* + * this IRP is for control device + */ + + // set default status + status = STATUS_SUCCESS; + + if (irps->MajorFunction == IRP_MJ_CREATE) { + + // initialize for user-mode part (exclusive access - 1 user-mode logging part) + filter_init_2(); + + g_got_log = TRUE; + + } else if (irps->MajorFunction == IRP_MJ_CLOSE) { + + // cleanup for user-mode logging part + filter_free_2(); + + g_got_log = FALSE; + + } if (irps->MajorFunction == IRP_MJ_DEVICE_CONTROL) { + + /* + * control request + */ + + ULONG ioctl = irps->Parameters.DeviceIoControl.IoControlCode, + len = irps->Parameters.DeviceIoControl.InputBufferLength, + size = irps->Parameters.DeviceIoControl.OutputBufferLength; + char *out_buf; + + if (IOCTL_TRANSFER_TYPE(ioctl) == METHOD_NEITHER) { + // this type of transfer unsupported + out_buf = NULL; + } else + out_buf = (char *)irp->AssociatedIrp.SystemBuffer; + + // process control request + status = process_request(ioctl, out_buf, &len, size); + + irp->IoStatus.Information = len; + + } + + irp->IoStatus.Status = status; + + IoCompleteRequest(irp, IO_NO_INCREMENT); + + } else if (DeviceObject == g_devnfo) { + + /* + * this IRP is for information device + */ + + // set default status + status = STATUS_SUCCESS; + + if (irps->MajorFunction == IRP_MJ_DEVICE_CONTROL) { + + /* + * control request + */ + + ULONG ioctl = irps->Parameters.DeviceIoControl.IoControlCode, + len = irps->Parameters.DeviceIoControl.InputBufferLength, + size = irps->Parameters.DeviceIoControl.OutputBufferLength; + char *out_buf; + + if (IOCTL_TRANSFER_TYPE(ioctl) == METHOD_NEITHER) { + // this type of transfer unsupported + out_buf = NULL; + } else + out_buf = (char *)irp->AssociatedIrp.SystemBuffer; + + // process control request + status = process_nfo_request(ioctl, out_buf, &len, size); + + irp->IoStatus.Information = len; + + } + + irp->IoStatus.Status = status; + + IoCompleteRequest(irp, IO_NO_INCREMENT); + + } else { + + KdPrint(("[tdi_fw] DeviceDispatch: ioctl for unknown DeviceObject 0x%x\n", DeviceObject)); + +#ifndef USE_TDI_HOOKING + // ??? just complete IRP + status = irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(irp, IO_NO_INCREMENT); +#else + // call original handler + status = g_old_DriverObject.MajorFunction[irps->MajorFunction]( + DeviceObject, irp); +#endif + } + + return status; +} + +/* + * Dispatch routines call this function to complete their processing. + * They _MUST_ call this function anyway. + */ +NTSTATUS +tdi_dispatch_complete(PDEVICE_OBJECT devobj, PIRP irp, int filter, + PIO_COMPLETION_ROUTINE cr, PVOID context) +{ + PIO_STACK_LOCATION irps = IoGetCurrentIrpStackLocation(irp); + NTSTATUS status; + + if (filter == FILTER_DENY) { + + /* + * DENY: complete request with status "Access violation" + */ + + KdPrint(("[tdi_fw] tdi_dispatch_complete: [DROP!]" + " major 0x%x, minor 0x%x for devobj 0x%x; fileobj 0x%x\n", + irps->MajorFunction, + irps->MinorFunction, + devobj, + irps->FileObject)); + + if (irp->IoStatus.Status == STATUS_SUCCESS) { + // change status + status = irp->IoStatus.Status = STATUS_ACCESS_DENIED; + } else { + // set IRP status unchanged + status = irp->IoStatus.Status; + } + + IoCompleteRequest (irp, IO_NO_INCREMENT); + + } else if (filter == FILTER_ALLOW) { + + /* + * ALLOW: pass IRP to the next driver + */ + +#ifndef USE_TDI_HOOKING + + PDEVICE_OBJECT old_devobj = get_original_devobj(devobj, NULL); + + if (old_devobj == NULL) { + KdPrint(("[tdi_fw] tdi_send_irp_to_old_driver: Unknown DeviceObject 0x%x!\n", devobj)); + + status = irp->IoStatus.Status = STATUS_UNSUCCESSFUL; + IoCompleteRequest (irp, IO_NO_INCREMENT); + + return status; + } + +#endif + + KdPrint(("[tdi_fw] tdi_dispatch_complete: [ALLOW.]" + " major 0x%x, minor 0x%x for devobj 0x%x; fileobj 0x%x\n", + irps->MajorFunction, + irps->MinorFunction, + devobj, + irps->FileObject)); + +#ifndef USE_TDI_HOOKING + + if (cr == NULL || irp->CurrentLocation <= 1) { + /* + * we use _THIS_ way of sending IRP to old driver + * a) to avoid NO_MORE_STACK_LOCATIONS + * b) and if we haven't our completions - no need to copy stack locations! + */ + + // stay on this location after IoCallDriver + IoSkipCurrentIrpStackLocation(irp); + +#endif + + if (cr != NULL) { + /* + * set completion routine (this way is slow) + */ + + // save old completion routine and context + TDI_SKIP_CTX *ctx = (TDI_SKIP_CTX *)malloc_np(sizeof(*ctx)); + if (ctx == NULL) { + KdPrint(("[tdi_fw] tdi_send_irp_to_old_driver: malloc_np\n")); + + status = irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoCompleteRequest(irp, IO_NO_INCREMENT); + + return status; + } + + ctx->old_cr = irps->CompletionRoutine; + ctx->old_context = irps->Context; + ctx->new_cr = cr; + ctx->new_context = context; + ctx->fileobj = irps->FileObject; + ctx->new_devobj = devobj; + + ctx->old_control = irps->Control; + + IoSetCompletionRoutine(irp, tdi_skip_complete, ctx, TRUE, TRUE, TRUE); + } + +#ifndef USE_TDI_HOOKING + } else { + PIO_STACK_LOCATION irps = IoGetCurrentIrpStackLocation(irp), + next_irps = IoGetNextIrpStackLocation(irp); + + memcpy(next_irps, irps, sizeof(*irps)); + + if (cr != NULL) { + /* + * this way for completion is more quicker than used above + */ + + IoSetCompletionRoutine(irp, cr, context, TRUE, TRUE, TRUE); + } else + IoSetCompletionRoutine(irp, tdi_generic_complete, NULL, TRUE, TRUE, TRUE); + } +#endif + + /* call original driver */ + +#ifndef USE_TDI_HOOKING + status = IoCallDriver(old_devobj, irp); +#else + status = g_old_DriverObject.MajorFunction[irps->MajorFunction](devobj, irp); +#endif + + } else { /* FILTER_UNKNOWN */ + + /* + * UNKNOWN: just complete the request + */ + + status = irp->IoStatus.Status = STATUS_SUCCESS; // ??? + IoCompleteRequest (irp, IO_NO_INCREMENT); + } + + return status; +} + +/* + * completion routine for case if we use IoSkipCurrentIrpStackLocation way + * or we USE_TDI_HOOKING + */ +NTSTATUS +tdi_skip_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) +{ + TDI_SKIP_CTX *ctx = (TDI_SKIP_CTX *)Context; + NTSTATUS status; + PIO_STACK_LOCATION irps; + + if (Irp->IoStatus.Status != STATUS_SUCCESS) + KdPrint(("[tdi_fw] tdi_skip_complete: status 0x%x\n", Irp->IoStatus.Status)); + + // restore IRP for using in our completion + + Irp->CurrentLocation--; + Irp->Tail.Overlay.CurrentStackLocation--; + + irps = IoGetCurrentIrpStackLocation(Irp); + + KdPrint(("[tdi_fw] tdi_skip_complete: DeviceObject = 0x%x; FileObject = 0x%x\n", + DeviceObject, irps->FileObject)); + + DeviceObject = irps->DeviceObject; + + if (ctx->new_cr != NULL) { + // restore fileobject (it's NULL) + irps->FileObject = ctx->fileobj; + // set new device object in irps + irps->DeviceObject = ctx->new_devobj; + + // call new completion + status = ctx->new_cr(ctx->new_devobj, Irp, ctx->new_context); + + } else + status = STATUS_SUCCESS; + + /* patch IRP back */ + + // restore routine and context (and even control!) + irps->CompletionRoutine = ctx->old_cr; + irps->Context = ctx->old_context; + irps->Control = ctx->old_control; + + // restore device object + irps->DeviceObject = DeviceObject; + + Irp->CurrentLocation++; + Irp->Tail.Overlay.CurrentStackLocation++; + + if (ctx->old_cr != NULL) { + + if (status != STATUS_MORE_PROCESSING_REQUIRED) { + // call old completion (see the old control) + BOOLEAN b_call = FALSE; + + if (Irp->Cancel) { + // cancel + if (ctx->old_control & SL_INVOKE_ON_CANCEL) + b_call = TRUE; + } else { + if (Irp->IoStatus.Status >= STATUS_SUCCESS) { + // success + if (ctx->old_control & SL_INVOKE_ON_SUCCESS) + b_call = TRUE; + } else { + // error + if (ctx->old_control & SL_INVOKE_ON_ERROR) + b_call = TRUE; + } + } + + if (b_call) + status = ctx->old_cr(DeviceObject, Irp, ctx->old_context); + + } else { + + /* + * patch IRP to set IoManager to call completion next time + */ + + // restore Control + irps->Control = ctx->old_control; + + } + } + + free(ctx); + + return status; +} + + +/* get original device object by filtered */ +PDEVICE_OBJECT +get_original_devobj(PDEVICE_OBJECT flt_devobj, int *proto) +{ +#ifndef USE_TDI_HOOKING + PDEVICE_OBJECT result; + int ipproto; + + if (flt_devobj == g_tcpfltobj) { + result = g_tcpoldobj; + ipproto = IPPROTO_TCP; + } else if (flt_devobj == g_udpfltobj) { + result = g_udpoldobj; + ipproto = IPPROTO_UDP; + } else if (flt_devobj == g_ipfltobj) { + result = g_ipoldobj; + ipproto = IPPROTO_IP; + } else { + KdPrint(("[tdi_fw] get_original_devobj: Unknown DeviceObject 0x%x!\n", + flt_devobj)); + ipproto = IPPROTO_IP; // what else? + result = NULL; + } + + if (result != NULL && proto != NULL) + *proto = ipproto; + + return result; + +#else /* USE_TDI_HOOKING */ + + // just stub for original devobj; return proto by devobj + int ipproto; + + if (flt_devobj == g_tcpfltobj) + ipproto = IPPROTO_TCP; + else if (flt_devobj == g_udpfltobj) + ipproto = IPPROTO_UDP; + else if (flt_devobj == g_ipfltobj) + ipproto = IPPROTO_IP; + else { + KdPrint(("[tdi_fw] get_original_devobj: Unknown DeviceObject 0x%x!\n", + flt_devobj)); + ipproto = IPPROTO_IP; // what else? + flt_devobj = NULL; + } + + if (proto != NULL) + *proto = ipproto; + + return flt_devobj; + +#endif +} + +/* + * Completion routines must call this function at the end of their execution + */ +NTSTATUS +tdi_generic_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) +{ + KdPrint(("[tdi_fw] tdi_generic_complete: STATUS = 0x%x\n", Irp->IoStatus.Status)); + + if (Irp->PendingReturned) { + KdPrint(("[tdi_fw] tdi_generic_complete: PENDING\n")); + IoMarkIrpPending(Irp); + } + + return STATUS_SUCCESS; +} + +/* + * for IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER + */ +NTSTATUS +new_TCPSendData(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp) +{ + struct completion completion; + int result; + + KdPrint(("[tdi_fw] new_TCPSendData\n")); + +#if 1 + memset(&completion, 0, sizeof(completion)); + + result = tdi_send(Irp, IrpSp, &completion); + + // complete request + return tdi_dispatch_complete(IrpSp->DeviceObject, Irp, result, + completion.routine, completion.context); +#else + return g_TCPSendData(Irp, IrpSp); +#endif +} + +/* + * deny stub for dispatch table + */ +int +tdi_deny_stub(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) +{ + KdPrint(("[tdi_fw] tdi_deny_stub!\n")); + return FILTER_DENY; +} diff --git a/tdifw-1.4.4/drv/tdi_fw.h b/tdifw-1.4.4/drv/tdi_fw.h new file mode 100644 index 00000000..999b3ee1 --- /dev/null +++ b/tdifw-1.4.4/drv/tdi_fw.h @@ -0,0 +1,148 @@ +/* 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: tdi_fw.h,v 1.12 2005/03/14 18:28:27 vlad Exp $ + +#ifndef _tdi_fw_h_ +#define _tdi_fw_h_ + +extern PDEVICE_OBJECT g_tcpfltobj, g_udpfltobj, g_ipfltobj; + +#ifndef USE_TDI_HOOKING +// original (unhooked) device objects when don't using TDI hooking +extern PDEVICE_OBJECT g_tcpoldobj, g_udpoldobj, g_ipoldobj; +#endif + +extern BOOLEAN g_got_log; + +NTSTATUS tdi_dispatch_complete( + PDEVICE_OBJECT devobj, PIRP irp, int filter, PIO_COMPLETION_ROUTINE cr, PVOID context); + +NTSTATUS tdi_generic_complete( + IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context); + +PDEVICE_OBJECT get_original_devobj(PDEVICE_OBJECT flt_devobj, int *proto); + +// should be in ipc.h +NTSTATUS process_request(ULONG code, char *buf, ULONG *buf_len, ULONG buf_size); +NTSTATUS process_nfo_request(ULONG code, char *buf, ULONG *buf_len, ULONG buf_size); + +// used for incoming or outgoing connections +void update_conn_info(PDEVICE_OBJECT devobj, PFILE_OBJECT connobj); + +// traffic counters +extern unsigned __int64 g_traffic[TRAFFIC_MAX]; +extern KSPIN_LOCK g_traffic_guard; + +/* some Native API prototypes */ + +NTKERNELAPI +NTSTATUS +ObReferenceObjectByName ( + IN PUNICODE_STRING ObjectName, + IN ULONG Attributes, + IN PACCESS_STATE PassedAccessState OPTIONAL, + IN ACCESS_MASK DesiredAccess OPTIONAL, + IN POBJECT_TYPE ObjectType OPTIONAL, + IN KPROCESSOR_MODE AccessMode, + IN OUT PVOID ParseContext OPTIONAL, + OUT PVOID *Object +); + +NTSTATUS +NTAPI +ZwCreateEvent ( + OUT PHANDLE EventHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN EVENT_TYPE EventType, + IN BOOLEAN InitialState +); + +NTSTATUS +NTAPI +ZwOpenThreadToken ( + IN HANDLE ThreadHandle, + IN ACCESS_MASK DesiredAccess, + IN BOOLEAN OpenAsSelf, + OUT PHANDLE TokenHandle +); + +NTSTATUS +NTAPI +ZwOpenProcessToken ( + IN HANDLE ProcessHandle, + IN ACCESS_MASK DesiredAccess, + OUT PHANDLE TokenHandle +); + +typedef enum _TOKEN_INFORMATION_CLASS { + TokenUser = 1, + TokenGroups, + TokenPrivileges, + TokenOwner, + TokenPrimaryGroup, + TokenDefaultDacl, + TokenSource, + TokenType, + TokenImpersonationLevel, + TokenStatistics, + TokenRestrictedSids +} TOKEN_INFORMATION_CLASS; + +NTSTATUS +NTAPI +ZwQueryInformationToken ( + IN HANDLE TokenHandle, + IN TOKEN_INFORMATION_CLASS TokenInformationClass, + OUT PVOID TokenInformation, + IN ULONG Length, + OUT PULONG ResultLength +); + +typedef enum _TOKEN_TYPE { + TokenPrimary = 1, + TokenImpersonation +} TOKEN_TYPE; + +typedef struct _SID_AND_ATTRIBUTES { + PSID Sid; + ULONG Attributes; +} SID_AND_ATTRIBUTES, *PSID_AND_ATTRIBUTES; + +typedef struct _TOKEN_USER { + SID_AND_ATTRIBUTES User; +} TOKEN_USER, *PTOKEN_USER; + +NTSTATUS +NTAPI +ZwWaitForSingleObject( + IN HANDLE hObject, + IN BOOLEAN bAlertable, + IN PLARGE_INTEGER Timeout +); + +/* for compilation under NT4 DDK */ + +#ifndef EVENT_ALL_ACCESS +# define EVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3) +#endif + +#ifndef IoSkipCurrentIrpStackLocation +# define IoSkipCurrentIrpStackLocation(Irp) \ + (Irp)->CurrentLocation++; \ + (Irp)->Tail.Overlay.CurrentStackLocation++; +#endif + +extern POBJECT_TYPE IoDriverObjectType; + +#endif diff --git a/tdifw-1.4.4/install/MAKEFILE b/tdifw-1.4.4/install/MAKEFILE new file mode 100644 index 00000000..58189757 --- /dev/null +++ b/tdifw-1.4.4/install/MAKEFILE @@ -0,0 +1,7 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the driver components of the Windows NT DDK +# + +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/tdifw-1.4.4/install/SOURCES b/tdifw-1.4.4/install/SOURCES new file mode 100644 index 00000000..af34bc53 --- /dev/null +++ b/tdifw-1.4.4/install/SOURCES @@ -0,0 +1,16 @@ +TARGETNAME=install +TARGETPATH=obj +TARGETTYPE=PROGRAM + +SOURCES=tdi_install.c +UMTYPE=console +386_STDCALL=0 +USE_MSVCRT=1 + +!if "$(NTDEBUG)"=="ntsdnodbg" +NTDEBUG= +!endif + +!ifndef NTDEBUG +NTDEBUG=retail +!endif diff --git a/tdifw-1.4.4/install/tdi_install.c b/tdifw-1.4.4/install/tdi_install.c new file mode 100644 index 00000000..f5b34afa --- /dev/null +++ b/tdifw-1.4.4/install/tdi_install.c @@ -0,0 +1,299 @@ +/* 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. + */ + +// $Id: tdi_install.c,v 1.5 2002/12/05 13:03:05 dev Exp $ + +/* + * Installer for tdifw_drv.sys driver (Win >= 2k) + * Also sets loading order between tcpip.sys & netbt.sys + */ + +#include +#include + +static void install_driver(void); +static void uninstall_driver(void); + +int +main(int argc, char **argv) +{ + if (argc != 2) { + fprintf(stderr, "use: %s install|remove\n", argv[0]); + return 0; + } + + if (strcmp(argv[1], "install") == 0) + install_driver(); + else if (strcmp(argv[1], "remove") == 0) + uninstall_driver(); + else + fprintf(stderr, "Invalid command line! \"%s\" is not \"install\" or \"remove\"\n", argv[1]); + + return 0; +} + +#define DRIVER_NAME "tdifw_drv" +#define DRIVER_BINARY "system32\\drivers\\tdifw_drv.sys" +#define DRIVER_GROUP "PNP_TDI" +#define DRIVER_DEPENDS "tcpip\0\0" + +#define swap_dword(a, b) \ + do { \ + DWORD c =(a); \ + (a) = (b); \ + (b) = c; \ + } while(0) + +void +install_driver(void) +{ + SC_HANDLE sch, tdifw, tcpip, netbt; + DWORD tdifw_tag, tcpip_tag, netbt_tag, n, tags[100], type, i; + char buf[2048]; + QUERY_SERVICE_CONFIG *cfg = (QUERY_SERVICE_CONFIG *)buf; + HKEY reg_key; + LONG status; + int n_type; + BOOL has_tcpip, has_netbt, has_tdifw; + + // install driver like instdrv does + + sch = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (sch == NULL) { + fprintf(stderr, "OpenSCManager: %d\n", GetLastError()); + return; + } + + tdifw = CreateService(sch, + DRIVER_NAME, + DRIVER_NAME, + SERVICE_ALL_ACCESS, + SERVICE_KERNEL_DRIVER, + SERVICE_SYSTEM_START, + SERVICE_ERROR_NORMAL, + DRIVER_BINARY, + DRIVER_GROUP, + &tdifw_tag, + DRIVER_DEPENDS, + NULL, + NULL + ); + if (tdifw == NULL) { + fprintf(stderr, "CreateService: %d\n", GetLastError()); + return; + } + + // get tag for tcpip + + tcpip = OpenService(sch, "tcpip", SERVICE_ALL_ACCESS); + if (tcpip == NULL) { + fprintf(stderr, "OpenService(tcpip): %d\n", GetLastError()); + return; + } + + if (!QueryServiceConfig(tcpip, cfg, sizeof(buf), &n)) { + fprintf(stderr, "QueryServiceConfig(tcpip): %d\n", GetLastError()); + return; + } + tcpip_tag = cfg->dwTagId; + + // get tag for netbt + + netbt = OpenService(sch, "netbt", SERVICE_ALL_ACCESS); + if (netbt == NULL) { + fprintf(stderr, "OpenService(netbt): %d\n", GetLastError()); + return; + } + + if (!QueryServiceConfig(netbt, cfg, sizeof(buf), &n)) { + fprintf(stderr, "QueryServiceConfig(tcpip): %d\n", GetLastError()); + return; + } + netbt_tag = cfg->dwTagId; + + // change tags for all drivers + + // 1. tcpip + // 2. tdifw + // 3. netbt + + // get registry key + + status = RegOpenKey(HKEY_LOCAL_MACHINE, + "SYSTEM\\CurrentControlSet\\Control\\GroupOrderList", ®_key); + if (status != ERROR_SUCCESS) { + fprintf(stderr, "RegOpenKey: %d\n", status); + return; + } + + n = sizeof(tags) - sizeof(DWORD) * 3; // reserve space for 3 new items + status = RegQueryValueEx(reg_key, DRIVER_GROUP, NULL, &type, (LPBYTE)tags, &n); + if (status != ERROR_SUCCESS) { + fprintf(stderr, "RegQueryValueEx: %d\n", status); + return; + } + + // find each num + + n /= sizeof(DWORD); + + // try to find tag for tdifw if not found add it + has_tdifw = FALSE; + for (i = 1; i < n; i++) + if (tags[i] == tdifw_tag) { + has_tdifw = TRUE; + break; + } + + if (!has_tdifw) + tags[n++] = tdifw_tag; + + has_tcpip = has_netbt = FALSE; + n_type = 1; + + for (i = 1; i < n; i++) + if (tags[i] == tcpip_tag || tags[i] == netbt_tag || tags[i] == tdifw_tag) { + + switch (n_type) { + case 1: + // 1. tcpip + tags[i] = tcpip_tag; + has_tcpip = TRUE; + break; + case 2: + // 2. tdifw + tags[i] = tdifw_tag; + break; + case 3: + // 3. netbt_tag + tags[i] = netbt_tag; + has_netbt = TRUE; + break; + } + n_type++; + + } + + if (!has_tcpip) { + printf("Warning! tcpip with tag %d not found! Trying to restore registry GroupOrderList key...\n", tcpip_tag); + + // insert at the begin of list + memmove(&tags[1], &tags[2], (n - 1) * sizeof(DWORD)); + tags[1] = tcpip_tag; + n++; + } + if (!has_netbt) { + printf("Warning! netbt with tag %d not found! Trying to restore registry GroupOrderList key...\n", netbt_tag); + + // append to the end of list + tags[n++] = netbt_tag; + } + + // save number of entries + tags[0] = n - 1; + + // that's all. save new order + + status = RegSetValueEx(reg_key, DRIVER_GROUP, 0, type, (LPBYTE)tags, n * sizeof(DWORD)); + if (status != ERROR_SUCCESS) { + fprintf(stderr, "RegSetValueEx: %d\n", status); + return; + } + + // ok + RegCloseKey(reg_key); + CloseServiceHandle(tcpip); + CloseServiceHandle(tdifw); + CloseServiceHandle(netbt); + CloseServiceHandle(sch); + + printf("success\n"); +} + +void +uninstall_driver(void) +{ + SC_HANDLE sch, service; + DWORD tags[100], n, type, i, service_tag; + char buf[2048]; + HKEY reg_key; + QUERY_SERVICE_CONFIG *cfg = (QUERY_SERVICE_CONFIG *)buf; + int status; + + sch = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (sch == NULL) { + fprintf(stderr, "OpenSCManager: %d\n", GetLastError()); + return; + } + + // get tag for driver + + service = OpenService(sch, DRIVER_NAME, SERVICE_ALL_ACCESS); + if (service == NULL) { + fprintf(stderr, "OpenService: %d\n", GetLastError()); + return; + } + + if (!QueryServiceConfig(service, cfg, sizeof(buf), &n)) { + fprintf(stderr, "QueryServiceConfig(tcpip): %d\n", GetLastError()); + return; + } + service_tag = cfg->dwTagId; + + // remove tag from registry + + status = RegOpenKey(HKEY_LOCAL_MACHINE, + "SYSTEM\\CurrentControlSet\\Control\\GroupOrderList", ®_key); + if (status != ERROR_SUCCESS) { + fprintf(stderr, "RegOpenKey: %d\n", status); + return; + } + + n = sizeof(tags); + status = RegQueryValueEx(reg_key, DRIVER_GROUP, NULL, &type, (LPBYTE)tags, &n); + if (status != ERROR_SUCCESS) { + fprintf(stderr, "RegQueryValueEx: %d\n", status); + return; + } + + n /= sizeof(DWORD); + + for (i = 1; i < n; i++) + if (tags[i] == service_tag) { + memmove(&tags[i], &tags[i + 1], (n - i - 1) * sizeof(DWORD)); + n--; + break; + } + + tags[0] = n - 1; + + status = RegSetValueEx(reg_key, DRIVER_GROUP, 0, type, (LPBYTE)tags, n * sizeof(DWORD)); + if (status != ERROR_SUCCESS) { + fprintf(stderr, "RegSetValueEx: %d\n", status); + return; + } + + RegCloseKey(reg_key); + + // remove service + + if (!DeleteService(service)) { + if (GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE) { + fprintf(stderr, "DeleteService: %d\n", GetLastError()); + return; + } + printf("Restart Windows to changes has effect\n"); + } + + CloseServiceHandle(service); + CloseServiceHandle(sch); + + printf("success\n"); +} diff --git a/tdifw-1.4.4/ipc.h b/tdifw-1.4.4/ipc.h new file mode 100644 index 00000000..c2d8a2ce --- /dev/null +++ b/tdifw-1.4.4/ipc.h @@ -0,0 +1,215 @@ +/* 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: ipc.h,v 1.10 2003/09/04 15:20:08 dev Exp $ + +#ifndef _ipc_h_ +#define _ipc_h_ + +/* ioctls */ + +#define FILE_DEVICE_TDI_FW 0x8e86 + +#define IOCTL_CMD_GETREQUEST CTL_CODE(FILE_DEVICE_TDI_FW, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_CMD_CLEARCHAIN CTL_CODE(FILE_DEVICE_TDI_FW, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_CMD_APPENDRULE CTL_CODE(FILE_DEVICE_TDI_FW, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_CMD_SETCHAINPNAME CTL_CODE(FILE_DEVICE_TDI_FW, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_CMD_SETPNAME CTL_CODE(FILE_DEVICE_TDI_FW, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_CMD_ACTIVATECHAIN CTL_CODE(FILE_DEVICE_TDI_FW, 0x806, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_CMD_SET_SIDS CTL_CODE(FILE_DEVICE_TDI_FW, 0x807, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define FILE_DEVICE_TDI_FW_NFO 0x8e87 + +#define IOCTL_CMD_ENUM_LISTEN CTL_CODE(FILE_DEVICE_TDI_FW_NFO, 0x901, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_CMD_ENUM_TCP_CONN CTL_CODE(FILE_DEVICE_TDI_FW_NFO, 0x902, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_CMD_GET_COUNTERS CTL_CODE(FILE_DEVICE_TDI_FW_NFO, 0x903, METHOD_BUFFERED, FILE_ANY_ACCESS) + +/* + * direction type for filter + * for quick filter: + * if proto == IPPROTO_TCP (DIRECTION_IN - accept connections; DIRECTION_OUT - connect) + * if proto == IPPROTO_UDP (DIRECTION_IN - receive datagram; DIRECTION_OUT - send datagram) + */ +#define DIRECTION_IN 0 +#define DIRECTION_OUT 1 +#define DIRECTION_ANY -1 + +/* filter result */ +enum { + FILTER_ALLOW = 1, + FILTER_DENY, + FILTER_PACKET_LOG, + FILTER_PACKET_BAD, + FILTER_DISCONNECT +}; + +/* types of request */ +enum { + TYPE_CONNECT = 1, + TYPE_DATAGRAM, + TYPE_RESOLVE_PID, + TYPE_CONNECT_ERROR, + TYPE_LISTEN, + TYPE_NOT_LISTEN, + TYPE_CONNECT_CANCELED, + TYPE_CONNECT_RESET, + TYPE_CONNECT_TIMEOUT, + TYPE_CONNECT_UNREACH +}; + +#pragma pack(1) + +#define RULE_ID_SIZE 32 + +/* + * request for filter + */ +struct flt_request { + int struct_size; /* should be sizeof(flt_request) */ + + int type; /* see TYPE_xxx */ + ULONG status; /* for TYPE_CONNECT_xxx */ + + int result; /* see FILTER_xxx */ + int direction; /* see DIRECTION_xxx */ + int proto; /* see IPPROTO_xxx */ + + ULONG pid; + ULONG sid_a_size; + + /* addr */ + + struct { + struct sockaddr from; + struct sockaddr to; + int len; + } addr; + + /* info from packet filter (valid for FILTER_PACKET_LOG) */ + struct { + int is_broadcast; // 0 or 1 (for now unused) + UCHAR tcp_flags; + UCHAR icmp_type; + UCHAR icmp_code; + int tcp_state; // see TCP_STATE_xxx + } packet; + + /* info for logging */ + + ULONG log_skipped; + ULONG log_bytes_in; + ULONG log_bytes_out; + char log_rule_id[RULE_ID_SIZE]; + + /* for internal use (like private:) */ + + char *pname; + struct _SID_AND_ATTRIBUTES *sid_a; +}; + +// I think 128 is a good number :-) (better than 256 :)) +#define MAX_CHAINS_COUNT 128 + +// how many users can be assigned per rule? (MUST: MAX_SIDS_COUNT % 8 == 0 !!!) +#define MAX_SIDS_COUNT 128 + +/* + * IP rule for quick filter (addr & port are in network order) + */ +struct flt_rule { + union { + struct flt_rule *next; // for internal use + int chain; // useful for IOCTL_CMD_APPENDRULE + }; + int result; + int proto; + int direction; + ULONG addr_from; + ULONG mask_from; + USHORT port_from; + USHORT port2_from; /* if nonzero use port range from port_from */ + ULONG addr_to; + ULONG mask_to; + USHORT port_to; + USHORT port2_to; /* if nonzero use port range from port_to */ + int log; /* see RULE_LOG_xxx */ + + UCHAR sid_mask[MAX_SIDS_COUNT / 8]; /* SIDs bitmask */ + + char rule_id[RULE_ID_SIZE]; +}; + +#define RULE_LOG_NOLOG 0 +#define RULE_LOG_LOG 1 +#define RULE_LOG_COUNT 2 + +#define IPPROTO_ANY -1 + +/* + * Entry for listen info + */ +struct listen_nfo { + int ipproto; + ULONG addr; + USHORT port; + ULONG pid; +}; + +/* + * TCP states + */ +enum { + TCP_STATE_NONE, + TCP_STATE_SYN_SENT, + TCP_STATE_SYN_RCVD, + TCP_STATE_ESTABLISHED_IN, + TCP_STATE_ESTABLISHED_OUT, + TCP_STATE_FIN_WAIT1, + TCP_STATE_FIN_WAIT2, + TCP_STATE_TIME_WAIT, + TCP_STATE_CLOSE_WAIT, + TCP_STATE_LAST_ACK, + TCP_STATE_CLOSED, + + TCP_STATE_MAX +}; + +/* + * Entry for connection info + */ +struct tcp_conn_nfo { + int state; + ULONG laddr; + USHORT lport; + ULONG raddr; + USHORT rport; + ULONG pid; + ULONG bytes_in; + ULONG bytes_out; +}; + +/* + * traffic counters for IOCTL_CMD_GET_COUNTERS + */ +enum { + TRAFFIC_TOTAL_IN, + TRAFFIC_TOTAL_OUT, + TRAFFIC_COUNTED_IN, + TRAFFIC_COUNTED_OUT, + + TRAFFIC_MAX +}; + + +#pragma pack() + +#endif diff --git a/tdifw-1.4.4/net.h b/tdifw-1.4.4/net.h new file mode 100644 index 00000000..8dd087da --- /dev/null +++ b/tdifw-1.4.4/net.h @@ -0,0 +1,77 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs) +// +// $Id: net.h,v 1.2 2003/03/24 11:26:51 dev Exp $ + +#ifndef _net_h_ +#define _net_h_ + +/* + * network definitions + */ + +#pragma pack(1) + +struct ether_hdr { + UCHAR ether_dhost[6]; + UCHAR ether_shost[6]; + USHORT ether_type; +}; + +#define ETHERTYPE_IP 0x0800 /* IP protocol */ + +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + +struct ip_hdr { + UCHAR ip_hl:4; /* header length */ + UCHAR ip_v:4; /* version */ + UCHAR ip_tos; /* type of service */ + USHORT ip_len; /* total length */ + USHORT ip_id; /* identification */ + USHORT ip_off; /* fragment offset field */ + UCHAR ip_ttl; /* time to live */ + UCHAR ip_p; /* protocol */ + USHORT ip_sum; /* checksum */ + ULONG ip_src; /* source address */ + ULONG ip_dst; /* dest address */ +}; + +struct icmp_hdr { + UCHAR icmp_type; /* type of message */ + UCHAR icmp_code; /* type sub code */ + USHORT icmp_cksum; /* ones complement cksum of struct */ +}; + +struct udp_hdr { + USHORT uh_sport; /* source port */ + USHORT uh_dport; /* destination port */ + USHORT uh_ulen; /* udp length */ + USHORT uh_sum; /* udp checksum */ +}; + +#define TH_FIN 0x01 +#define TH_SYN 0x02 +#define TH_RST 0x04 +#define TH_PUSH 0x08 +#define TH_ACK 0x10 +#define TH_URG 0x20 + +struct tcp_hdr { + USHORT th_sport; /* source port */ + USHORT th_dport; /* destination port */ + ULONG th_seq; /* sequence number */ + ULONG th_ack; /* acknowledgement number */ + + UCHAR th_x2:4; /* (unused) */ + UCHAR th_off:4; /* data offset */ + UCHAR th_flags; + + USHORT th_win; /* window */ + USHORT th_sum; /* checksum */ + USHORT th_urp; /* urgent pointer */ +}; + +#pragma pack() + +#endif diff --git a/tdifw-1.4.4/svc/MAKEFILE b/tdifw-1.4.4/svc/MAKEFILE new file mode 100644 index 00000000..58189757 --- /dev/null +++ b/tdifw-1.4.4/svc/MAKEFILE @@ -0,0 +1,7 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the driver components of the Windows NT DDK +# + +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/tdifw-1.4.4/svc/SOURCES b/tdifw-1.4.4/svc/SOURCES new file mode 100644 index 00000000..19aaeedc --- /dev/null +++ b/tdifw-1.4.4/svc/SOURCES @@ -0,0 +1,19 @@ +TARGETNAME=tdifw +TARGETPATH=obj +TARGETTYPE=PROGRAM + +TARGETLIBS=$(SDK_LIB_PATH)\wsock32.lib $(SDK_LIB_PATH)\Winmm.lib iphlpapi.lib +USER_INCLUDES=.. + +SOURCES=tdifw_svc.c main.c flt_rule.c +UMTYPE=console +386_STDCALL=0 +USE_MSVCRT=1 + +!if "$(NTDEBUG)"=="ntsdnodbg" +NTDEBUG= +!endif + +!ifndef NTDEBUG +NTDEBUG=retail +!endif diff --git a/tdifw-1.4.4/svc/flt_rule.c b/tdifw-1.4.4/svc/flt_rule.c new file mode 100644 index 00000000..8311a99e --- /dev/null +++ b/tdifw-1.4.4/svc/flt_rule.c @@ -0,0 +1,373 @@ +/* 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. + */ + +// $Id: flt_rule.c,v 1.9 2003/09/04 15:20:10 dev Exp $ + +/* + * Rule parser + */ + +#include +#include + +#include "flt_rule.h" +#include "ipc.h" +#include "tdifw_svc.h" + +struct str_value { + const char *str; + int value; +}; + +static BOOL str_value(const struct str_value *sv, const char *str, int *result); +static int get_amp(char *str, u_long *addr, u_long *mask, u_short *port, u_short *port2); + +static char g_delim[] = " \t"; + +static struct str_value filter_sv[] = { + {"ALLOW", FILTER_ALLOW}, + {"DENY", FILTER_DENY}, + {NULL, 0} +}; + +static struct str_value proto_sv[] = { + {"TCP", IPPROTO_TCP}, + {"UDP", IPPROTO_UDP}, + {"RawIP", IPPROTO_IP}, + {"*", IPPROTO_ANY}, + {NULL, 0} +}; + +static struct str_value direction_sv[] = { + {"IN", DIRECTION_IN}, + {"OUT", DIRECTION_OUT}, + {"*", DIRECTION_ANY}, + {NULL, 0} +}; + +/* + * rule is like this: + * + * [:] ALLOW|DENY TCP|UDP|RawIP|* IN|OUT|* FROM TO [NOLOG|COUNT] + */ +int +parse_rule(char *str, struct flt_rule *rule) +{ + char *p = str, *p2; + int v; + + memset(rule, 0, sizeof(*rule)); + + // by default log using of all rules! + rule->log = RULE_LOG_LOG; + + // skip whitespace + while (strchr(g_delim, *p) != NULL) + p++; + + /* ALLOW|DENY */ + + if (*p == '\0') { + error("PARSE\tparse_rule: filter ALLOW or DENY is missing"); + return 0; + } + + p2 = strpbrk(p, g_delim); + if (p2 != NULL) { + *(p2++) = '\0'; + while (strchr(g_delim, *p2) != NULL) + p2++; + } + + if (p[strlen(p) - 1] == ':') { + // got name! + + p[strlen(p) - 1] = '\0'; // kill ending ':' + + strncpy(rule->rule_id, p, RULE_ID_SIZE); + // string can be not zero-terminated + + p = p2; + + /* ALLOW|DENY again */ + + if (p == NULL) { + error("PARSE\tparse_rule: filter ALLOW or DENY is missing"); + return 0; + } + + p2 = strpbrk(p, g_delim); + if (p2 != NULL) { + *(p2++) = '\0'; + while (strchr(g_delim, *p2) != NULL) + p2++; + } + + } + + if (!str_value(filter_sv, p, &v)) { + error("PARSE\tparse_rule: \"%s\" is not ALLOW or DENY filter", p); + return 0; + } + rule->result = v; + + p = p2; + + /* TCP|UDP|RawIP|* */ + + if (p == NULL) { + error("PARSE\tparse_rule: protocol TCP, UDP, RawIP or * is missing"); + return 0; + } + + p2 = strpbrk(p, g_delim); + if (p2 != NULL) { + *(p2++) = '\0'; + while (strchr(g_delim, *p2) != NULL) + p2++; + } + + if (!str_value(proto_sv, p, &v)) { + error("PARSE\tparse_rule: \"%s\" is not TCP, UDP, RawIP protocol or *", p); + return 0; + } + rule->proto = v; + + p = p2; + + /* IN|OUT|* */ + + if (p == NULL) { + error("PARSE\tparse_rule: direction IN, OUT or * is missing"); + return 0; + } + + p2 = strpbrk(p, g_delim); + if (p2 != NULL) { + *(p2++) = '\0'; + while (strchr(g_delim, *p2) != NULL) + p2++; + } + + if (!str_value(direction_sv, p, &v)) { + error("PARSE\tparse_rule: \"%s\" is not IN, OUT or * direction", p); + return 0; + } + rule->direction = v; + + p = p2; + + /* FROM */ + + if (p == NULL) { + error("PARSE\tparse_rule: keyword FROM is missing"); + return 0; + } + + p2 = strpbrk(p, g_delim); + if (p2 != NULL) { + *(p2++) = '\0'; + while (strchr(g_delim, *p2) != NULL) + p2++; + } + + if (_stricmp(p, "FROM") != 0) { + error("PARSE\tparse_rule: \"%s\" is not FROM keyword", p); + return 0; + } + + p = p2; + + /* */ + + if (p == NULL) { + error("PARSE\tparse_rule: from address is missing"); + return 0; + } + + p2 = strpbrk(p, g_delim); + if (p2 != NULL) { + *(p2++) = '\0'; + while (strchr(g_delim, *p2) != NULL) + p2++; + } + + if (!get_amp(p, &rule->addr_from, &rule->mask_from, &rule->port_from, &rule->port2_from)) { + error("PARSE\tparse_rule: invalid from address \"%s\"", p); + return 0; + } + + p = p2; + + /* TO */ + + if (p == NULL) { + error("PARSE\tparse_rule: keyword TO is missing"); + return 0; + } + + p2 = strpbrk(p, g_delim); + if (p2 != NULL) { + *(p2++) = '\0'; + while (strchr(g_delim, *p2) != NULL) + p2++; + } + + if (_stricmp(p, "TO") != 0) { + error("PARSE\tparse_rule: \"%s\" is not TO keyword"); + return 0; + } + + p = p2; + + /* */ + + if (p == NULL) { + error("PARSE\tparse_rule: to address is missing"); + return 0; + } + + p2 = strpbrk(p, g_delim); + if (p2 != NULL) { + *(p2++) = '\0'; + while (strchr(g_delim, *p2) != NULL) + p2++; + } + + if (!get_amp(p, &rule->addr_to, &rule->mask_to, &rule->port_to, &rule->port2_to)) { + error("PARSE\tparse_rule: invalid to address \"%s\"", p); + return 0; + } + + p = p2; + + /* NOLOG|COUNT */ + if (p != NULL) { + + if (_stricmp(p, "NOLOG") == 0) + rule->log = RULE_LOG_NOLOG; + else if (_stricmp(p, "COUNT") == 0) + rule->log = RULE_LOG_COUNT; + else { + error("PARSE\tparse_rule: invalid to address \"%s\"", p); + return 0; + } + + } + + return 1; +} + +BOOL +str_value(const struct str_value *sv, const char *str, int *result) +{ + while (sv->str != NULL) { + if (_stricmp(sv->str, str) == 0) { + *result = sv->value; + return TRUE; + } + sv++; + } + return FALSE; +} + +int +get_amp(char *string, u_long *addr, u_long *mask, u_short *port, u_short *port2) +{ + char *p, *addr_str = NULL, *mask_str = NULL, *port_str = NULL, + *port2_str = NULL, net_mask[10]; + int result = 0; + + addr_str = string; + + // "/mask" + p = strchr(string, '/'); + if (p != NULL) { + *(p++) = '\0'; + mask_str = p; + } + + // ":port" + p = strchr(mask_str ? mask_str : string, ':'); + if (p != NULL) { + *(p++) = '\0'; + port_str = p; + + // "-port2" + p = strchr(port_str, '-'); + if (p != NULL) { + *(p++) = '\0'; + port2_str = p; + } + } + + //
[/] + + *addr = inet_addr(addr_str); + if (*addr == INADDR_NONE && _stricmp(addr_str, "255.255.255.255") != 0) { + + *addr = get_host_by_name(addr_str, net_mask); + if (*addr == INADDR_NONE) { + // XXX try to resolve? + error("PARSE\tInvalid address: %s", addr_str); + return 0; + } + + if (*net_mask != '\0') { + // got mask in resolving + mask_str = net_mask; + } + + } + + if (mask_str != NULL) { + // + int n = atoi(mask_str); + if ((n == 0 && _stricmp(mask_str, "0") != 0) || n < 0 || n > 32) { + error("PARSE\tInvalid mask: %s", mask_str); + return 0; + } + if (n == 0) + *mask = 0; + else { + int i; + for (i = 1, *mask = 0x80000000; i < n; i++) + *mask |= *mask >> 1; + *mask = htonl(*mask); + } + } else + *mask = INADDR_NONE; // default mask 255.255.255.255 + + if (port_str != NULL) { + int n = atoi(port_str); + if ((n == 0 && _stricmp(port_str, "0") != 0) || n < 0 || n > 0xffff) { + error("PARSE\tInvalid port: %s", port_str); + return 0; + } + *port = (USHORT)n; + } else + *port = 0; + + if (port2_str != NULL) { + int n = atoi(port2_str); + if ((n == 0 && _stricmp(port2_str, "0") != 0) || n < 0 || n > 0xffff) { + error("PARSE\tInvalid port2: %s", port2_str); + return 0; + } + *port2 = (USHORT)n; + } else + *port2 = 0; + + // make network order + *port = htons(*port); + *port2 = htons(*port2); + + return 1; +} diff --git a/tdifw-1.4.4/svc/flt_rule.h b/tdifw-1.4.4/svc/flt_rule.h new file mode 100644 index 00000000..22763faf --- /dev/null +++ b/tdifw-1.4.4/svc/flt_rule.h @@ -0,0 +1,10 @@ +// $Id: flt_rule.h,v 1.1.1.1 2002/09/24 11:12:16 dev Exp $ + +#ifndef _flt_rule_h_ +#define _flt_rule_h_ + +#include "filter.h" + +int parse_rule(char *str, struct flt_rule *rule); + +#endif diff --git a/tdifw-1.4.4/svc/iphlpapi.h b/tdifw-1.4.4/svc/iphlpapi.h new file mode 100644 index 00000000..02683b3e --- /dev/null +++ b/tdifw-1.4.4/svc/iphlpapi.h @@ -0,0 +1,49 @@ +/* + +iphlpapi.h: declares the subset of iphlpapi needed to compile +Copyright (C) 2000 Andy Lutomirski + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License, version 2.1, as published by the Free Software +Foundation, with the exception that if this copy of the library +is distributed under the Lesser GNU Public License (as opposed +to the ordinary GPL), you may ignore section 6b, and that all +copies distributed without exercising section 3 must retain this +paragraph in its entirety. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef __IPHLPAPI_H__ +#ifndef __IPHLPAPI_FAKE_H__ +#define __IPHLPAPI_FAKE_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +DWORD __declspec(dllimport) __stdcall GetIfTable(PMIB_IFTABLE, DWORD*, BOOL); +DWORD __declspec(dllimport) __stdcall GetIpAddrTable(PMIB_IPADDRTABLE, DWORD*, BOOL); +DWORD __declspec(dllimport) __stdcall GetIpNetTable(PMIB_IPNETTABLE, DWORD*, BOOL); +DWORD __declspec(dllimport) __stdcall GetIpForwardTable(PMIB_IPFORWARDTABLE, DWORD*, BOOL); +DWORD __declspec(dllimport) __stdcall GetIfEntry(PMIB_IFROW); + + +#ifdef __cplusplus +} +#endif + +#endif +#endif \ No newline at end of file diff --git a/tdifw-1.4.4/svc/iphlpapi.lib b/tdifw-1.4.4/svc/iphlpapi.lib new file mode 100644 index 0000000000000000000000000000000000000000..2906fa50f5c937f001c3e791ca3258c00feaef17 GIT binary patch literal 2640 zcmcIm%}(1u5dQ1{fq*ImJ+>vN9Own|=aizh3Wfeegn&Xgn-GU4l2NQQRgZmvs^Tqr z>;v?I+6U;NhhEbgw@RFm=*-${HjV>H1uJ=XXXf+H>^HM(&p)lSs`clw<#=~XCeqnt zHenb~y7dW9Oef@QKL%hD(9eJ|9f+<1J_f8tf%9>| zo&b(%?9do&j32`YMiGSu-JeHX#)9E@6By1@$Ugrkmr8n+DEj71G4y0dv8g=wuNGNG zOO|bOHm7j~tOb(+|;f3@W>{bH$MRoaz>l+@p)$ydVt@URh*b4Z+cxU9t8p~)Ww z$NC)NCqx-$;g@jc@3CVuw?~FP)k+sKOwX?-zc+SDbul t@O*Xuho~-w_h*Ff?IHQoeSg6(VwcOI`7~sa2KG2dJ|n(Q^-J6B?;rC*{AmCH literal 0 HcmV?d00001 diff --git a/tdifw-1.4.4/svc/main.c b/tdifw-1.4.4/svc/main.c new file mode 100644 index 00000000..cfbcded3 --- /dev/null +++ b/tdifw-1.4.4/svc/main.c @@ -0,0 +1,474 @@ +/* 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. + */ + +// $Id: main.c,v 1.3 2003/05/16 14:06:24 dev Exp $ + +/* + * TdiFw helper service. + * SCM & command line related stuff: + */ + +#include +#include +#include +#include + +#include "tdifw_svc.h" + +#define CONFIG_SUBKEY "SYSTEM\\CurrentControlSet\\Services\\tdifw" + +BOOL g_console = TRUE; + +static SERVICE_STATUS ssStatus; // current status of the service +static SERVICE_STATUS_HANDLE sshStatusHandle; + +static void AddEventSource(const char *ident); + +static void install_service(const char *config); +static void remove_service(void); + +static VOID WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv); +static VOID WINAPI service_ctrl(DWORD dwCtrlCode); + +static BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint); + +static BOOL add_config_info(HANDLE schService, const char *config); + +int +main(int argc, char **argv) +{ + static SERVICE_TABLE_ENTRY dispatch_table[] = { + {"tdifw", service_main}, + {NULL, NULL} + }; + + _LEAK_CHECK; + + if (argc >= 2) { + const char *param = argv[1]; + + if (strcmp(param, "install") == 0) { + if (argc < 3) { + fprintf(stderr, "Use: tdifw install \n"); + return -1; + } + + install_service(argv[2]); + + } else if (strcmp(param, "remove") == 0) { + remove_service(); + } else if (strcmp(param, "debug") == 0) { + + if (argc < 3) { + fprintf(stderr, "Use: tdifw debug \n"); + return -1; + } + + if (start(argv[2])) { + printf("press enter to exit...\n"); + getchar(); + printf("exiting...\n"); + + stop(); + } + + } else if (strcmp(param, "listen") == 0) { // tdifw specific + enum_listen(); + } else if (strcmp(param, "conn") == 0) { // tdifw specific + enum_connect(); + } else { + fprintf(stderr, "Use: tdifw install|remove|debug|listen|conn\n"); + } + } else { + + g_console = FALSE; + + // run as service + if (!StartServiceCtrlDispatcher(dispatch_table)) + winerr("main: StartServiceCtrlDispatcher"); + + } + + return 0; +} + +void +install_service(const char *config) +{ + SC_HANDLE schService; + SC_HANDLE schSCManager; + + CHAR szPath[MAX_PATH]; + + AddEventSource("tdifw"); + + if (GetModuleFileName(NULL, szPath, sizeof(szPath)) == 0) { + winerr("install_service: GetModuleFileName"); + return; + } + + schSCManager = OpenSCManager( + NULL, // machine (NULL == local) + NULL, // database (NULL == default) + SC_MANAGER_ALL_ACCESS); // access required + + if (schSCManager != NULL) { + + schService = CreateService( + schSCManager, // SCManager database + "tdifw", // name of service + "TDI-based open source personal firewall", // name to display + SERVICE_ALL_ACCESS, // desired access + SERVICE_WIN32_OWN_PROCESS, // service type + SERVICE_AUTO_START, // start type + SERVICE_ERROR_NORMAL, // error control type + szPath, // service's binary + NULL, // no load ordering group + NULL, // no tag identifier + NULL, // dependencies + NULL, // LocalSystem account + NULL); // no password + + if (schService != NULL) { + printf("tdifw service has been installed\n"); + + if (!add_config_info(schService, config)) + fprintf(stderr, "Can't store config info! Service will use defaults.\n"); + + CloseServiceHandle(schService); + } else + winerr("install_service: CreateService"); + + CloseServiceHandle(schSCManager); + } + else + winerr("install_service: OpenSCManager"); +} + +void +remove_service(void) +{ + SC_HANDLE schService; + SC_HANDLE schSCManager; + + schSCManager = OpenSCManager( + NULL, // machine (NULL == local) + NULL, // database (NULL == default) + SC_MANAGER_ALL_ACCESS); // access required + + if (schSCManager != NULL) { + schService = OpenService(schSCManager, "tdifw", SERVICE_ALL_ACCESS); + + if (schService != NULL) { + + // try to stop the service + if (ControlService(schService, SERVICE_CONTROL_STOP, &ssStatus)) { + printf("stopping..."); + Sleep(1000); + + while(QueryServiceStatus( schService, &ssStatus)) { + if (ssStatus.dwCurrentState == SERVICE_STOP_PENDING) { + printf("."); + Sleep( 1000 ); + } + else + break; + } + + printf("\n"); + + if (ssStatus.dwCurrentState == SERVICE_STOPPED) + printf("stopped\n"); + else + printf("failed to stop\n"); + } + + // now remove the service + if (DeleteService(schService)) + printf("service has been removed\n"); + else + winerr("install_service: DeleteService"); + + CloseServiceHandle(schService); + } + else + winerr("install_service: OpenService"); + + CloseServiceHandle(schSCManager); + } + else + winerr("install_service: OpenSCManager"); +} + +VOID WINAPI +service_main(DWORD dwArgc, LPTSTR *lpszArgv) +{ + HKEY hkey = NULL; + char *config = NULL; + DWORD type, config_size, status; + + // register our service control handler: + // + sshStatusHandle = RegisterServiceCtrlHandler("tdifw", service_ctrl); + if (sshStatusHandle == 0) { + winerr("install_service: RegisterServiceCtrlHandler"); + goto cleanup; + } + + // SERVICE_STATUS members that don't change in example + // + ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + ssStatus.dwServiceSpecificExitCode = 0; + + // report the status to the service control manager. + // + if (!ReportStatusToSCMgr( + SERVICE_START_PENDING, // service state + NO_ERROR, // exit code + 3000)) // wait hint + goto cleanup; + + /* get config name from registry */ + + if ((status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, CONFIG_SUBKEY, 0, KEY_QUERY_VALUE, + &hkey)) != ERROR_SUCCESS) { + SetLastError(status); + winerr("RegOpenKeyEx"); + goto cleanup; + } + + if ((status = RegQueryValueEx(hkey, "config", 0, &type, NULL, &config_size)) != ERROR_SUCCESS) { + SetLastError(status); + winerr("RegOpenKeyEx"); + goto cleanup; + } + + if (type != REG_SZ) { + error("Invalid type for config value in registry"); + SetLastError(ERROR_INVALID_DATA); + goto cleanup; + } + + config = (char *)malloc(config_size); + if (config == NULL) { + liberr("malloc"); + goto cleanup; + } + + if ((status = RegQueryValueEx(hkey, "config", 0, NULL, config, &config_size)) != ERROR_SUCCESS) { + SetLastError(status); + winerr("RegOpenKeyEx"); + goto cleanup; + } + + if (start(config)) { + + // start success + + // report the status to the service control manager. + // + if (!ReportStatusToSCMgr( + SERVICE_RUNNING, // service state + NO_ERROR, // exit code + 0)) // wait hint + goto cleanup; + + wait(); + + SetLastError(0); + } + +cleanup: + + // try to report the stopped status to the service control manager. + // + if (sshStatusHandle != 0) + ReportStatusToSCMgr(SERVICE_STOPPED, GetLastError(), 0); + + if (hkey != NULL) + RegCloseKey(hkey); + free(config); +} + +// +// FUNCTION: ReportStatusToSCMgr() +// +// PURPOSE: Sets the current status of the service and +// reports it to the Service Control Manager +// +// PARAMETERS: +// dwCurrentState - the state of the service +// dwWin32ExitCode - error code to report +// dwWaitHint - worst case estimate to next checkpoint +// +// RETURN VALUE: +// TRUE - success +// FALSE - failure +// +// COMMENTS: +// +BOOL +ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint) +{ + static DWORD dwCheckPoint = 1; + + if (dwCurrentState == SERVICE_START_PENDING) + ssStatus.dwControlsAccepted = 0; + else + ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + + ssStatus.dwCurrentState = dwCurrentState; + ssStatus.dwWin32ExitCode = dwWin32ExitCode; + ssStatus.dwWaitHint = dwWaitHint; + + if (dwCurrentState == SERVICE_RUNNING || dwCurrentState == SERVICE_STOPPED) + ssStatus.dwCheckPoint = 0; + else + ssStatus.dwCheckPoint = dwCheckPoint++; + + // Report the status of the service to the service control manager. + // + + if (!SetServiceStatus(sshStatusHandle, &ssStatus)) { + winerr("install_service: SetServiceStatus"); + return FALSE; + } + + return TRUE; +} + +// +// FUNCTION: service_ctrl +// +// PURPOSE: This function is called by the SCM whenever +// ControlService() is called on this service. +// +// PARAMETERS: +// dwCtrlCode - type of control requested +// +// RETURN VALUE: +// none +// +// COMMENTS: +// +VOID WINAPI +service_ctrl(DWORD dwCtrlCode) +{ + // Handle the requested control code. + // + switch(dwCtrlCode) { + // Stop the service. + // + // SERVICE_STOP_PENDING should be reported before + // setting the Stop Event - hServerStopEvent - in + // ServiceStop(). This avoids a race condition + // which may result in a 1053 - The Service did not respond... + // error. + case SERVICE_CONTROL_STOP: + ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 0); + + // stop it! + stop(); + + return; + + // Update the service status. + // + case SERVICE_CONTROL_INTERROGATE: + break; + + // invalid control code + // + default: + break; + + } + + ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0); +} + +/* Taken from MSDN. */ +void +AddEventSource(const char *ident) +{ + HKEY hk; + DWORD dwData; + char szFilePath[_MAX_PATH]; + char key[_MAX_PATH]; + + // Add your source name as a subkey under the Application + // key in the EventLog registry key. + _snprintf(key, sizeof(key), "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s", ident); + + if (RegCreateKey(HKEY_LOCAL_MACHINE, key, &hk)) { + printf("Could not create the registry key."); + exit(-1); + } + + // Set the name of the message file. + GetModuleFileName(NULL, szFilePath, sizeof(szFilePath)); + // Add the name to the EventMessageFile subkey. + + if (RegSetValueEx(hk, // subkey handle + "EventMessageFile", // value name + 0, // must be zero + REG_EXPAND_SZ, // value type + (LPBYTE) szFilePath, // pointer to value data + strlen(szFilePath) + 1)) { // length of value data + printf("Could not set the event message file."); + exit(-1); + } + + // Set the supported event types in the TypesSupported subkey. + + dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | + EVENTLOG_INFORMATION_TYPE | EVENTLOG_AUDIT_SUCCESS | EVENTLOG_AUDIT_FAILURE; + + if (RegSetValueEx(hk, // subkey handle + "TypesSupported", // value name + 0, // must be zero + REG_DWORD, // value type + (LPBYTE) &dwData, // pointer to value data + sizeof(DWORD))){ // length of value data + printf("Could not set the supported types."); + exit(-1); + } + + RegCloseKey(hk); +} + +BOOL +add_config_info(HANDLE schService, const char *config) +{ + BOOL result = FALSE; + HKEY hkey = NULL; + DWORD status; + + if ((status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, CONFIG_SUBKEY, + 0, KEY_SET_VALUE, &hkey)) != ERROR_SUCCESS) { + SetLastError(status); + winerr("RegOpenKeyEx"); + goto done; + } + + if ((status = RegSetValueEx(hkey, "config", 0, REG_SZ, config, + strlen(config) + 1)) != ERROR_SUCCESS) { + SetLastError(status); + winerr("RegSetValueEx"); + goto done; + } + + result = TRUE; + +done: + if (hkey != NULL) + RegCloseKey(hkey); + return result; +} diff --git a/tdifw-1.4.4/svc/msg.h b/tdifw-1.4.4/svc/msg.h new file mode 100644 index 00000000..14d5d0a1 --- /dev/null +++ b/tdifw-1.4.4/svc/msg.h @@ -0,0 +1,45 @@ +// +// Values are 32 bit values layed out as follows: +// +// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +---+-+-+-----------------------+-------------------------------+ +// |Sev|C|R| Facility | Code | +// +---+-+-+-----------------------+-------------------------------+ +// +// where +// +// Sev - is the severity code +// +// 00 - Success +// 01 - Informational +// 10 - Warning +// 11 - Error +// +// C - is the Customer code flag +// +// R - is a reserved bit +// +// Facility - is the facility code +// +// Code - is the facility's status code +// +// +// Define the facility codes +// + + +// +// Define the severity codes +// + + +// +// MessageId: MSG +// +// MessageText: +// +// %1 +// +#define MSG ((WORD)0x00000001L) + diff --git a/tdifw-1.4.4/svc/msg.mc b/tdifw-1.4.4/svc/msg.mc new file mode 100644 index 00000000..edfbd8d1 --- /dev/null +++ b/tdifw-1.4.4/svc/msg.mc @@ -0,0 +1,6 @@ +MessageIdTypedef=WORD +MessageId=0x1 +SymbolicName=MSG +Language=English +%1 +. diff --git a/tdifw-1.4.4/svc/msg.rc b/tdifw-1.4.4/svc/msg.rc new file mode 100644 index 00000000..0885a897 --- /dev/null +++ b/tdifw-1.4.4/svc/msg.rc @@ -0,0 +1,2 @@ +LANGUAGE 0x9,0x1 +1 11 MSG00001.bin diff --git a/tdifw-1.4.4/svc/tdi_fw_svc.rc b/tdifw-1.4.4/svc/tdi_fw_svc.rc new file mode 100644 index 00000000..0a3e08df --- /dev/null +++ b/tdifw-1.4.4/svc/tdi_fw_svc.rc @@ -0,0 +1 @@ +#include "msg.rc" diff --git a/tdifw-1.4.4/svc/tdifw_svc.c b/tdifw-1.4.4/svc/tdifw_svc.c new file mode 100644 index 00000000..9b361d7d --- /dev/null +++ b/tdifw-1.4.4/svc/tdifw_svc.c @@ -0,0 +1,1716 @@ +/* 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. + */ + +// $Id: tdifw_svc.c,v 1.23 2003/09/04 15:20:10 dev Exp $ + +/* + * TdiFw helper service + */ + +#include +#include +#include +#include +#include +#include + +#include "iphlpapi.h" + +#include "flt_rule.h" +#include "ipc.h" +#include "net.h" +#include "tdifw_svc.h" +#include "thread.h" + +#include "msg.h" + +#define DISP_BUF_SIZE 0x10000 + +HANDLE g_device = INVALID_HANDLE_VALUE; + +static HANDLE g_event = NULL; +static HANDLE g_exit_event = NULL; + +static HANDLE g_pipe = INVALID_HANDLE_VALUE; + +static HANDLE g_dispatcher = NULL; +static char *g_disp_buf = NULL; + +static DWORD WINAPI dispatcher(LPVOID param); +static DWORD WINAPI restart_thread(LPVOID param); + +static void dispatch_request(struct flt_request *request); + +static FILE *g_logfile = NULL; +static const char *g_config_file = NULL; + +static BOOL load_config(const char *config); +static BOOL read_config(const char *config); +static BOOL add_rules_name(const char *main_name, const char *config, int chain); +static void add_rules(const char *config, char *buf, const char *name, int chain); +static BOOL get_pname_by_pid(u_long pid, char *buf, int buf_size); +static void prepare_addr(char *buf, int size, u_long addr); + +static void my_GetLongPathName(LPCSTR lpszShortPath, LPSTR lpszLongPath, + DWORD cchBuffer); + +static int compare_ln(const void *arg1, const void *arg2); +static int compare_tn(const void *arg1, const void *arg2); + +static void get_traffic_stats(unsigned __int64 *stats); + +// some config switches +static BOOL g_eventlog_allow = FALSE; +static BOOL g_eventlog_deny = FALSE; +static BOOL g_eventlog_error = FALSE; + +// wave files to play :-) +static char wave_deny_in[MAX_PATH]; +static char wave_deny_out[MAX_PATH]; + +static void log_msg(const char *msg, int type); + +enum { + MSGTYPE_ALLOW, + MSGTYPE_DENY, + MSGTYPE_ERROR +}; + +static char g_device_name[] = "\\\\.\\tdifw"; +static char g_nfo_device_name[] = "\\\\.\\tdifw_nfo"; + +// for dynamic linking with psapi.dll + +static void link_psapi(void); + +typedef BOOL WINAPI EnumProcesses_t(DWORD *, DWORD, DWORD *); +typedef BOOL WINAPI EnumProcessModules_t(HANDLE, HMODULE*, DWORD, DWORD*); +typedef BOOL WINAPI GetModuleFileNameEx_t(HANDLE, HMODULE, LPTSTR, DWORD); + +static HMODULE g_psapi = NULL; +static EnumProcesses_t *pEnumProcesses = NULL; +static EnumProcessModules_t *pEnumProcessModules = NULL; +static GetModuleFileNameEx_t *pGetModuleFileNameEx = NULL; + +// for routing code + +static ULONG get_if_ip(ULONG remote_ip); +static ULONG route_ip(ULONG remote_ip); +static ULONG get_if_index_ip(ULONG if_index); + +// for SID stuff + +static BOOL load_users(const char *config); +static void get_sid_mask(const char *config, const char *name, UCHAR *sid_mask); +static BOOL check_for_name(const char *config, const char *section, const char *value, const char *name); + +static const char *g_tcp_states[] = { + "?", + "SYN_SENT", + "SYN_RCVD", + "ESTABLISHED(in)", + "ESTABLISHED(out)", + "FIN_WAIT1", + "FIN_WAIT2", + "TIME_WAIT", + "CLOSE_WAIT", + "LAST_ACK", + "CLOSED" +}; + +int +start(const char *config) +{ + int result = FALSE; + WSADATA wsd; + DWORD thread_id; + + WSAStartup(MAKEWORD(1, 1), &wsd); + + // try to dynamically link psapi.dll + link_psapi(); + + /* connect with driver */ + + g_device = CreateFile(g_device_name, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (g_device == INVALID_HANDLE_VALUE) { + winerr(g_device_name); + goto done; + } + + /* open event for driver communication */ + + g_event = OpenEvent(SYNCHRONIZE, FALSE, "tdifw_request"); + if (g_event == NULL) { + winerr("start: OpenEvent"); + goto done; + } + + /* load config & rules */ + + if (!load_config(config)) + goto done; + + /* start dispatcher thread */ + + g_exit_event = CreateEvent(NULL, TRUE, FALSE, NULL); + if (g_exit_event == NULL) { + winerr("start: CreateEvent"); + goto done; + } + + g_disp_buf = (char *)malloc(DISP_BUF_SIZE); + if (g_disp_buf == NULL) { + liberr("start: malloc"); + goto done; + } + + log_msg("START", MSGTYPE_ALLOW); + + g_dispatcher = lib_CreateThread(NULL, 0, dispatcher, (LPVOID)config, 0, &thread_id); + if (g_dispatcher == NULL) { + winerr("start: lib_CreateThread"); + goto done; + } + + result = TRUE; + +done: + if (!result) + stop(); + + return result; +} + +void +stop(void) +{ + // collect statistics + if (g_logfile != NULL) { + // output traffic statistic! + unsigned __int64 traffic[TRAFFIC_MAX]; + char msg[200]; + + get_traffic_stats(traffic); + + sprintf(msg, "TRAFFIC\t%I64u/%I64u\t%I64u/%I64u", + traffic[TRAFFIC_TOTAL_OUT], traffic[TRAFFIC_TOTAL_IN], + traffic[TRAFFIC_COUNTED_OUT], traffic[TRAFFIC_COUNTED_IN]); + + log_msg(msg, MSGTYPE_ALLOW); + } + + // disconnect from driver + if (g_device != INVALID_HANDLE_VALUE) { + HANDLE old_h = g_device; + g_device = INVALID_HANDLE_VALUE; + CancelIo(old_h); + CloseHandle(old_h); + } + + // stop dispatcher thread + + if (g_exit_event != NULL) + SetEvent(g_exit_event); + + if (g_dispatcher != NULL) { + WaitForSingleObject(g_dispatcher, INFINITE); + g_dispatcher = NULL; + } + + // close logfile + if (g_logfile != NULL) { + log_msg("STOP", MSGTYPE_ALLOW); + + fprintf(g_logfile, "--- end ---\n"); + fclose(g_logfile); + g_logfile = NULL; + } + + if (g_exit_event != NULL) + CloseHandle(g_exit_event); + if (g_event != NULL) + CloseHandle(g_event); + + if (g_disp_buf != NULL) { + free(g_disp_buf); + g_disp_buf = NULL; + } +} + +void +wait(void) +{ + if (g_exit_event != NULL) + WaitForSingleObject(g_exit_event, INFINITE); +} + +/* output functions */ + +void +error(const char *fmt, ...) +{ + va_list ap; + char message[1024]; + + // prepare message + + va_start(ap, fmt); + + if (_vsnprintf(message, sizeof(message), fmt, ap) == -1) + message[sizeof(message) - 1] = '\0'; + + va_end(ap); + + // got message + log_msg(message, MSGTYPE_ERROR); +} + +void +winerr(const char *fn) +{ + char *win_msg = NULL; + DWORD code = GetLastError(); + + if (code == 0) + error("WINERR\t%s:", fn); + else { + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM,NULL,code, + MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPTSTR)&win_msg,0,NULL); + + if (win_msg != NULL) { + // kill \r\n + int len = strlen(win_msg); + + if (len >= 1) + win_msg[len-1]=0; + if (len >= 2) + win_msg[len-2]=0; + + error("WINERR\t%s: %s", fn, win_msg); + + LocalFree(win_msg); + } else + error("WINERR\t%s: %d (0x%x)", fn, code, code); + } + + // save error code + SetLastError(code); +} + +void +liberr(const char *fn) +{ + int code = errno; + if (code != 0) + error("LIBERR\t%s: %s", fn, strerror(code)); + else + error("LIBERR\t%s:", fn); +} + +/* dispatcher thread */ +DWORD WINAPI +dispatcher(LPVOID param) +{ + char *config = (char *)param; + HANDLE handles[2]; + DWORD i, n; + + handles[0] = g_event; + handles[1] = g_exit_event; + + for (;;) { + if (!DeviceIoControl(g_device, IOCTL_CMD_GETREQUEST, NULL, 0, + g_disp_buf, DISP_BUF_SIZE, &n, NULL)) { + winerr("dispatcher: DeviceIoControl"); + break; + } + + if (n == 0) { + DWORD wait; + + // if working with log file flush it! + if (g_logfile != NULL) + fflush(g_logfile); + + // wait for data + wait = WaitForMultipleObjects(2, handles, FALSE, INFINITE); + if (wait == WAIT_OBJECT_0 + 1) + break; + else if (wait != WAIT_OBJECT_0) { + winerr("dispatcher: WaitForSingleObject"); + break; + } + continue; + } + + for (i = 0; i < n;) { + struct flt_request *request; + + if (n - i < sizeof(*request)) + break; + + request = (struct flt_request *)(g_disp_buf + i); + + dispatch_request(request); + + i += request->struct_size; + } + } + + if (g_device != INVALID_HANDLE_VALUE) { + DWORD thread_id; + + error("dispatcher: unexpected exit!"); + + // restart tdifw_svc + lib_CreateThread(NULL, 0, restart_thread, config, 0, &thread_id); + } + + return 0; +} + +DWORD +WINAPI restart_thread(LPVOID param) +{ + char *config = (char *)param; + + error("restarting..."); + + stop(); + start(config); + + return 0; +} + +/* log filter request from filter driver */ +void +dispatch_request(struct flt_request *request) +{ + u_long from_ip, to_ip; + u_short from_port, to_port; + char msg[1024], pname_buf[MAX_PATH], addr_from[100], addr_to[100], *pname, uname[200]; + + if (request->log_skipped != 0) { + if (_snprintf(msg, sizeof(msg), "SKIP\t%u", request->log_skipped) == -1) + msg[sizeof(msg) - 1] = '\0'; + + log_msg(msg, MSGTYPE_ERROR); + } + + if (request->pid != (ULONG)-1) { + if (request->pname == NULL) { + // try to resolve pid to pname + if (get_pname_by_pid(request->pid, pname_buf + sizeof(DWORD), sizeof(pname_buf) - sizeof(DWORD))) { + // send message to driver to send process name next time + DWORD n; + + pname = pname_buf + sizeof(DWORD); + *(DWORD *)pname_buf = request->pid; + + if (!DeviceIoControl(g_device, IOCTL_CMD_SETPNAME, pname_buf, + sizeof(DWORD) + strlen(pname) + 1, + NULL, 0, &n, NULL)) + winerr("DeviceIoControl"); + + } else { + error("PROCESS\tCan't resolve pid %u!", request->pid); + sprintf(pname_buf, "pid:%u", request->pid); + pname = pname_buf; + } + + } else + pname = (char *)&request[1]; + } else + pname = ""; + + if (request->sid_a != NULL) { + SID_AND_ATTRIBUTES *sa; + char user[100], domain[100]; + DWORD size1, size2; + SID_NAME_USE type; + + if (request->pname != NULL) { + char *buf = (char *)&request[1]; + buf += strlen(buf) + 1; + sa = (SID_AND_ATTRIBUTES *)buf; + } else + sa = (SID_AND_ATTRIBUTES *)&request[1]; + + // convert sid from relative pointer to absolute + sa->Sid = (PSID)((char *)sa + (ULONG)sa->Sid); + + size1 = sizeof(user); + size2 = sizeof(domain); + + if (LookupAccountSid(NULL, sa->Sid, user, &size1, domain, &size2, &type)) { + + if (_snprintf(uname, sizeof(uname), "{%s\\%s}", domain, user) == -1) + uname[sizeof(uname) - 1] = '\0'; + + } else + strcpy(uname, "{??\\??}"); // TODO: convert SID to string + + } else + uname[0] = '\0'; + + // check is it request type "TYPE_RESOLVE_PID" + if (request->type == TYPE_RESOLVE_PID) { + + if (_snprintf(msg, sizeof(msg), "PROCESS\t%u\t%s\t%s", request->pid, pname, uname) == -1) + msg[sizeof(msg) - 1] = '\0'; + + log_msg(msg, MSGTYPE_ALLOW); + + } else if (request->type == TYPE_LISTEN || request->type == TYPE_NOT_LISTEN) { + + // using from_ip & from_port as listen_ip & listen_port + from_ip = ((struct sockaddr_in *)&request->addr.from)->sin_addr.s_addr; + from_port = ntohs(((struct sockaddr_in *)&request->addr.from)->sin_port); + + prepare_addr(addr_from, sizeof(addr_from), from_ip); + sprintf(addr_from + strlen(addr_from), ":%d", from_port); + + if (_snprintf(msg, sizeof(msg), + "%s\tTCP\t%s\t%s\t%s", + (request->type == TYPE_LISTEN) ? "LISTEN" : "NOT_LISTEN", + addr_from, pname, uname) == -1) + msg[sizeof(msg) - 1] = '\0'; + + log_msg(msg, MSGTYPE_ALLOW); + + } else { + + // prepare message + + from_ip = ((struct sockaddr_in *)&request->addr.from)->sin_addr.s_addr; + from_port = ntohs(((struct sockaddr_in *)&request->addr.from)->sin_port); + to_ip = ((struct sockaddr_in *)&request->addr.to)->sin_addr.s_addr; + to_port = ntohs(((struct sockaddr_in *)&request->addr.to)->sin_port); + + // prepare address "from" & "to" + if (from_ip == 0) { + // try to route "to" addr to get "from" + from_ip = get_if_ip(to_ip); + } + prepare_addr(addr_from, sizeof(addr_from), from_ip); + + if (to_ip == 0) { + // some kind of "reverse route" :-) + to_ip = get_if_ip(from_ip); + } + prepare_addr(addr_to, sizeof(addr_to), to_ip); + + // add ports if nonzero + if (from_port != 0) + sprintf(addr_from + strlen(addr_from), ":%d", from_port); + if (to_port != 0) + sprintf(addr_to + strlen(addr_to), ":%d", to_port); + + if (request->result == FILTER_ALLOW || request->result == FILTER_DENY || + request->result == FILTER_DISCONNECT) { + // log it! (TDI message) + char tdi_msg[100 + RULE_ID_SIZE], size_str[64], *direction; + + switch (request->result) { + case FILTER_ALLOW: + strcpy(tdi_msg, "ALLOW"); + break; + case FILTER_DENY: + strcpy(tdi_msg, "DENY"); + break; + default: + strcpy(tdi_msg, "CLOSED"); + } + + if (request->result != FILTER_DISCONNECT) { + int size; + + size_str[0] = '\0'; + + switch (request->type) { + case TYPE_CONNECT_CANCELED: + strcat(tdi_msg, "(CANCELED)"); + break; + case TYPE_CONNECT_RESET: + strcat(tdi_msg, "(RESET)"); + break; + case TYPE_CONNECT_TIMEOUT: + strcat(tdi_msg, "(TIMEOUT)"); + break; + case TYPE_CONNECT_UNREACH: + strcat(tdi_msg, "(UNREACH)"); + break; + case TYPE_CONNECT_ERROR: + sprintf(tdi_msg + strlen(tdi_msg), "(ERR:%x)", request->status); + break; + case TYPE_DATAGRAM: + size = (request->direction == DIRECTION_IN) ? request->log_bytes_in : request->log_bytes_out; + if (size != (ULONG)-1) + sprintf(size_str, "%u", size); + + break; + } + + strcat(tdi_msg, "\t["); + size = strlen(tdi_msg); + memcpy(tdi_msg + size, request->log_rule_id, RULE_ID_SIZE); + tdi_msg[size + RULE_ID_SIZE + 1] = '\0'; // string can be not zero-terminated + strcat(tdi_msg, "]"); + + } else + sprintf(size_str, "%u/%u", request->log_bytes_out, request->log_bytes_in); + + switch (request->direction) { + case DIRECTION_IN: + direction = "IN"; + break; + case DIRECTION_OUT: + direction = "OUT"; + break; + case DIRECTION_ANY: + direction = "*"; + break; + default: + direction = "?"; + } + + if (_snprintf(msg, sizeof(msg), + "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s", + tdi_msg, + (request->proto == IPPROTO_TCP) ? "TCP" : + (request->proto == IPPROTO_UDP ? "UDP" : "RawIP"), + direction, + addr_from, addr_to, size_str, pname, uname) == -1) + msg[sizeof(msg) - 1] = '\0'; + + // sound it! + if (request->result == FILTER_DENY) { + + // TODO: try to sound it by voice of user! + + if (request->direction == DIRECTION_IN && wave_deny_in[0] != '\0') + PlaySound(wave_deny_in, NULL, SND_FILENAME | SND_ASYNC); + if (request->direction == DIRECTION_OUT && wave_deny_out[0] != '\0') + PlaySound(wave_deny_out, NULL, SND_FILENAME | SND_ASYNC); + } + + log_msg(msg, request->result == FILTER_ALLOW ? MSGTYPE_ALLOW : MSGTYPE_DENY); + + } else if (request->result == FILTER_PACKET_LOG || request->result == FILTER_PACKET_BAD) { + char flags[50], proto[50]; + + // prepare TCP flags or ICMP type + if (request->result == FILTER_PACKET_LOG) { + if (request->proto == IPPROTO_TCP) { + UCHAR th_flags = request->packet.tcp_flags; + + flags[0] = (th_flags & TH_FIN) ? 'F' : '-'; + flags[1] = (th_flags & TH_SYN) ? 'S' : '-'; + flags[2] = (th_flags & TH_RST) ? 'R' : '-'; + flags[3] = (th_flags & TH_PUSH) ? 'P' : '-'; + flags[4] = (th_flags & TH_ACK) ? 'A' : '-'; + flags[5] = (th_flags & TH_URG) ? 'U' : '-'; + flags[6] = '\0'; + + } else if (request->proto == IPPROTO_ICMP) + sprintf(flags, "%d.%d", request->packet.icmp_type, request->packet.icmp_code); + else + flags[0] = '\0'; + } else + flags[0] = '\0'; + + switch (request->proto) { + case IPPROTO_TCP: + if (request->packet.tcp_state > TCP_STATE_NONE && + request->packet.tcp_state < TCP_STATE_MAX) + sprintf(proto, "TCP(%s)", g_tcp_states[request->packet.tcp_state]); + else + strcpy(proto, "TCP"); + break; + case IPPROTO_UDP: + strcpy(proto, "UDP"); + break; + case IPPROTO_ICMP: + strcpy(proto, "ICMP"); + break; + default: + sprintf(proto, "%d", request->proto); + } + + + // log it! (packet filter message) + if (_snprintf(msg, sizeof(msg), + "%s\t%s\t%s\t%s\t%s\t%u\t%s", + (request->result == FILTER_PACKET_LOG) ? "PACKET" : "BAD_PACKET", + proto, + (request->direction == DIRECTION_IN) ? "IN" : "OUT", + addr_from, addr_to, + (request->direction == DIRECTION_IN) ? request->log_bytes_in : request->log_bytes_out, + flags) == -1) + msg[sizeof(msg) - 1] = '\0'; + + log_msg(msg, request->result == FILTER_PACKET_LOG ? MSGTYPE_ALLOW : MSGTYPE_DENY); + + } + } +} + +/* + * write message to console, log file or probably to eventlog + * don't call any error functions! avoid infinite recurse. + */ +void +log_msg(const char *msg, int type) +{ + static int file_day = 0; // for midnight checking + static ULONG event_num = 1; // number of event in log file + + // if working in console mode write message to console + if (g_console) { + if (type == MSGTYPE_ERROR) + fprintf(stderr, "%s\n", msg); + else + printf("%s\n", msg); + } + + + // write to eventlog if filter is set + if ((type == MSGTYPE_ALLOW && g_eventlog_allow) || + (type == MSGTYPE_DENY && g_eventlog_deny) || + (type == MSGTYPE_ERROR && g_eventlog_error)) { + + HANDLE hEventSource; + const char *strings; + + // write message to event log + // Use event logging to log the error. + // + hEventSource = RegisterEventSource(NULL, "tdifw_svc"); + + if (hEventSource != NULL) { + WORD event_type; + + strings = msg; // write message without timestamp + + event_type = (type == MSGTYPE_ALLOW) ? EVENTLOG_AUDIT_SUCCESS : + (type == MSGTYPE_DENY ? EVENTLOG_AUDIT_FAILURE : EVENTLOG_ERROR_TYPE); + + if (!ReportEvent(hEventSource, // handle of event source + event_type, + 0, // event category + MSG, // event ID + NULL, // current user's SID + 1, // strings in lpszStrings + 0, // no bytes of raw data + &strings, // array of error strings + NULL)) { // no raw data + +#ifdef _DEBUG + MessageBeep(0); + OutputDebugString("log_msg: ReportEvent\n"); +#endif + } + + DeregisterEventSource(hEventSource); + } + } else { + + // write message to log + + time_t tv; + struct tm *tm; + char tm_msg[1024]; + + time(&tv); + tm = localtime(&tv); + if (tm == NULL) { +#ifdef _DEBUG + MessageBeep(0); + OutputDebugString("log_msg: localtime\n"); +#endif + return; + } + + if (g_logfile == NULL || tm->tm_mday != file_day) { + char fname[MAX_PATH]; + + if (g_logfile != NULL) { + // wow! we're working at midnight! change log file! + fprintf(g_logfile, "--- midnight ---\n"); + fclose(g_logfile); + g_logfile = NULL; + } + + file_day = tm->tm_mday; + + // open logfile + + if (_snprintf(fname, sizeof(fname), + "%s\\system32\\LogFiles\\tdifw\\%04d%02d%02d.log", + getenv("SystemRoot"), + tm->tm_year + 1900, + tm->tm_mon + 1, + tm->tm_mday) == -1) { + +#ifdef _DEBUG + MessageBeep(0); + OutputDebugString("log_msg: _snprintf overflow!\n"); +#endif + return; + } + + g_logfile = fopen(fname, "a"); + if (g_logfile == NULL) { +#ifdef _DEBUG + MessageBeep(0); + OutputDebugString("log_msg: fopen error!\n"); +#endif + return; + } + + fprintf(g_logfile, "--- begin ---\n"); + } + + if (_snprintf(tm_msg, sizeof(tm_msg), + "%010u %02d:%02d:%02d\t%s", + event_num++, tm->tm_hour, tm->tm_min, tm->tm_sec, msg) == -1) + tm_msg[sizeof(tm_msg) - 1] = '\0'; + + // write it to log file + fprintf(g_logfile, "%s\n", tm_msg); + fflush(g_logfile); + + } +} + +#define MAX_SECTION_SIZE (16 * 1024) + +BOOL +load_config(const char *config) +{ + char *section = NULL, *p; + DWORD n, pids[100]; + BOOL result = FALSE; + int line, chain, i; + + if (!read_config(config)) + return FALSE; + + // save config to global variable (for get_host_by_name) + g_config_file = config; + + // load information about users + if (!load_users(config)) { + error("start: Fatal error! Can't load users information!\n"); + goto done; + } + + // parse & add rules default and process-related + + section = (char *)malloc(MAX_SECTION_SIZE); + if (section == NULL) { + liberr("malloc"); + goto done; + } + + GetPrivateProfileSection("_main_", section, MAX_SECTION_SIZE, config); + + // get lines + chain = 0; + for (p = section, line = 1; *p != '\0'; p += strlen(p) + 1, line++) { + char *p2; + + if (*p == ';' || *p == '#') + continue; // comment + + p2 = strchr(p, '='); + if (p2 == NULL) { + error("%s:[_main_]:%d: invalid line format (no '=' character)", config, line); + continue; + } + + if (chain >= MAX_CHAINS_COUNT) { + error("%s:[_main_]:%d: too many rules lines!", config, line); + break; + } + + *p2 = '\0'; // temporary kill '=' + add_rules_name(p, config, chain); + *p2 = '='; // recover '=' to make strlen(p) works right + + chain++; + } + + // try to get names for existing processes + if (pEnumProcesses != NULL && pEnumProcesses(pids, sizeof(pids), &n)) { + DWORD i; + + n /= sizeof(DWORD); + + for (i = 0; i < n; i++) { + char pname[MAX_PATH]; + if (get_pname_by_pid(pids[i], pname + sizeof(DWORD), sizeof(pname) - sizeof(DWORD))) { + // send information to driver about pid and pname + + DWORD nn; + + *(DWORD *)pname = pids[i]; + + if (!DeviceIoControl(g_device, IOCTL_CMD_SETPNAME, pname, + sizeof(DWORD) + strlen(pname + sizeof(DWORD)) + 1, + NULL, 0, &nn, NULL)) + winerr("DeviceIoControl"); + } + } + } + + // activate all chains! + for (i = 0; i < chain; i++) { + // activate chain #i + if (!DeviceIoControl(g_device, IOCTL_CMD_ACTIVATECHAIN, &i, sizeof(i), + NULL, 0, &n, NULL)) + winerr("start: DeviceIoControl"); + } + + result = TRUE; +done: + free(section); + return result; +} + +BOOL +read_config(const char *config) +{ + static char config_name[] = "_config_"; + static char signature_name[] = "_signature_"; + static char signature_value[] = "$tdi_fw$"; + + char buf[100]; + + // first, check config file signature + GetPrivateProfileString(signature_name, signature_name, "", buf, sizeof(buf), config); + if (strcmp(buf, signature_value) != 0) { + error("\"%s\": invalid configuration file", config); + return FALSE; + } + + // get g_eventlog_xxx values + + g_eventlog_allow = GetPrivateProfileInt(config_name, "eventlog_allow", FALSE, config); + g_eventlog_deny = GetPrivateProfileInt(config_name, "eventlog_deny", FALSE, config); + g_eventlog_error = GetPrivateProfileInt(config_name, "eventlog_error", FALSE, config); + + // read wave_deny_in/out values + + GetPrivateProfileString(config_name, "wave_deny_in", "", wave_deny_in, sizeof(wave_deny_in), config); + GetPrivateProfileString(config_name, "wave_deny_out", "", wave_deny_out, sizeof(wave_deny_out), config); + + return TRUE; +} + +BOOL +add_rules_name(const char *main_name, const char *config, int chain) +{ + char buf[1024], *p, *p2, *section = NULL; + BOOL result = FALSE; + DWORD n; + + section = (char *)malloc(MAX_SECTION_SIZE); + if (section == NULL) { + liberr("malloc"); + goto done; + } + + /* 1. get ruleset string */ + + GetPrivateProfileString("_main_", main_name, "", buf, sizeof(buf), config); + + if (*buf == '\0') + goto done; // no rules + + // reset all rules for chain + if (!DeviceIoControl(g_device, IOCTL_CMD_CLEARCHAIN, &chain, sizeof(chain), + NULL, 0, &n, NULL)) { + winerr("DeviceIoControl"); + goto done; + } + + if (chain != 0) { + // set chain name + int len = sizeof(int) + MAX_PATH; + char *data = (char *)malloc(len); + if (data == NULL) { + liberr("malloc"); + goto done; + } + + *(int *)data = chain; + + // try to extract environment variables from p to main_name + ExpandEnvironmentStrings(main_name, data + sizeof(int), MAX_PATH); + + len = sizeof(int) + strlen(data + sizeof(int)) + 1; + + if (!DeviceIoControl(g_device, IOCTL_CMD_SETCHAINPNAME, data, len, + NULL, 0, &n, NULL)) { + winerr("DeviceIoControl"); + free(data); + goto done; + } + free(data); + } + + /* 2. for each word in main_name string */ + + p = buf; + + while (p != NULL) { + + p2 = strchr(p, ' '); + if (p2 != NULL) { + while (*p2 == ' ') + *(p2++) = '\0'; + } + + if (*p != '\0') { + // get section by name in p + GetPrivateProfileSection(p, section, MAX_SECTION_SIZE, config); + if (*section == '\0') + error("\"%s\": unexistant or empty section %s", config, p); + else + add_rules(config, section, p, chain); + } + + p = p2; + } + + result = TRUE; + +done: + free(section); + return result; +} + +void +add_rules(const char *config, char *buf, const char *name, int chain) +{ + char *p, *p2; + int n_str; + DWORD n; + UCHAR sid_mask[MAX_SIDS_COUNT / 8]; + + get_sid_mask(config, name, sid_mask); + + for (p = buf, n_str = 1; *p != '\0'; p = p2, n_str++) { + struct flt_rule rule; + char num[10]; + + p2 = p + strlen(p) + 1; + + if (*buf == ';' || *buf == '\0') + continue; // empty line or comment + + memset(&rule, 0, sizeof(rule)); + + // parse it! + if (!parse_rule(p, &rule)) { + error("Error in line #%d of section [%s]", n_str, name); + continue; + } + + // set chain + rule.chain = chain; + + // set SID mask + memcpy(rule.sid_mask, sid_mask, sizeof(rule.sid_mask)); + + if (rule.rule_id[0] == '\0') { + // set default rule name: name of section + n_str + strncpy(rule.rule_id, name, RULE_ID_SIZE); // string can be not zero-terminated + sprintf(num, ":%d", n_str); + if (strlen(name) + strlen(num) < RULE_ID_SIZE) + memcpy(rule.rule_id + strlen(name), num, strlen(num)); // string can be not zero-terminated + else + memcpy(rule.rule_id + RULE_ID_SIZE - strlen(num), num, strlen(num)); + } + + // append rule + if (!DeviceIoControl(g_device, IOCTL_CMD_APPENDRULE, &rule, sizeof(rule), + NULL, 0, &n, NULL)) { + winerr("start: DeviceIoControl"); + break; + } + } +} + +BOOL +get_pname_by_pid(u_long pid, char *buf, int buf_size) +{ + BOOL result; + HANDLE h_process; + HMODULE h_module = NULL; + DWORD n; + + if (g_psapi == NULL) + return FALSE; // failed to load psapi.dll + + // try to resolve pid to pname + + h_process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); + if (h_process != NULL) + pEnumProcessModules(h_process, &h_module, sizeof(h_module), &n); + + // on error write pid + if (h_module == NULL || + pGetModuleFileNameEx(h_process, h_module, buf, buf_size) == 0) { + + // for "System" process last error value is: + // * ERROR_PARTIAL_COPY (on 2k) + // * ERROR_NOACCESS (on NT4) + // ??? on other (TODO: think about another way) + if (GetLastError() == ERROR_PARTIAL_COPY || + GetLastError() == ERROR_NOACCESS) { + + strncpy(buf, "System", buf_size); + buf[buf_size - 1] = '\0'; + + result = TRUE; + + } else { + *buf = '\0'; + result = FALSE; + } + + } else { + if (strchr(buf, '~') != NULL) { // XXX is it a right way? + // try to convert long name to short name + char long_name[MAX_PATH]; + + my_GetLongPathName(buf, long_name, sizeof(long_name)); + + strncpy(buf, long_name, buf_size - 1); + buf[buf_size - 1] = '\0'; + } + + result = TRUE; + } + + if (h_process != NULL) + CloseHandle(h_process); + return result; +} + +void +prepare_addr(char *buf, int size, u_long addr) +{ + // XXX do we need resolving? + strcpy(buf, inet_ntoa(*(struct in_addr *)&addr)); +} + +ULONG +get_host_by_name(const char *hostname, char *net_mask) +{ + // resolve address using config file (DNS way is insecure) + char addr[100], *p; + + GetPrivateProfileString("_hosts_", hostname, "", addr, sizeof(addr), g_config_file); + + if (*addr == '\0') + return INADDR_NONE; + + p = strchr(addr, '/'); + if (p != NULL) { + // got net_mask! + *(p++) = '\0'; + + // max size is 2 characters! (/xx) + strncpy(net_mask, p, 2); + net_mask[2] = '\0'; + } else + *net_mask = '\0'; + + return inet_addr(addr); +} + +// GetLongPathName is in Windows 2000 only so make it for NT4 +void +my_GetLongPathName(LPCSTR lpszShortPath, LPSTR lpszLongPath, DWORD cchBuffer) +{ + LPSTR p_l; + LPCSTR p_s; + + lpszLongPath[0] = lpszShortPath[0]; + lpszLongPath[1] = lpszShortPath[1]; + lpszLongPath[2] = lpszShortPath[2]; + lpszLongPath[3] = '\0'; + + p_l = &lpszLongPath[3]; + p_s = &lpszShortPath[3]; + + for (;;) { + LPSTR p_s2; + HANDLE search; + WIN32_FIND_DATA ffd; + size_t len; + BOOL overflow; + + p_s2 = strchr(p_s, '\\'); + + if (p_s2 != NULL) + len = p_s2 - p_s; + else + len = strlen(p_s); + + // check the buffer + if (len + (p_l - lpszLongPath) >= cchBuffer) { + // buffer overflow: copy and exit + len = cchBuffer - (p_l - lpszLongPath) - 1; + overflow = TRUE; + } else + overflow = FALSE; + + memcpy(p_l, p_s, len); + p_l[len] = '\0'; + + if (overflow) + break; + + search = FindFirstFile(lpszLongPath, &ffd); + if (search != INVALID_HANDLE_VALUE) { + + len = strlen(ffd.cFileName); + if (p_s2 != NULL) + len++; + + // check the buffer + if (len + (p_l - lpszLongPath) >= cchBuffer) { + // buffer overflow: copy and exit + len = cchBuffer - (p_l - lpszLongPath) - 1; + p_s2 = NULL; + } + + memcpy(p_l, ffd.cFileName, len); + + if (p_s2 != NULL) { + p_l[len - 1] = '\\'; + p_l[len] = '\0'; + } else + p_l[len] = '\0'; + + FindClose(search); + } + + if (p_s2 == NULL) + break; + + p_l += strlen(p_l); + p_s = p_s2 + 1; + } + + // ignore status +} + +// dynamically try to link with psapi.lib +void +link_psapi(void) +{ + g_psapi = LoadLibrary("psapi.dll"); + if (g_psapi == NULL) { + // no psapi.dll - log it! + error("INIT\tCan't load psapi.dll!"); + return; + } + + pEnumProcesses = (EnumProcesses_t *)GetProcAddress(g_psapi, "EnumProcesses"); + pEnumProcessModules = (EnumProcessModules_t *)GetProcAddress(g_psapi, "EnumProcessModules"); +#ifdef UNICODE + pGetModuleFileNameEx = (GetModuleFileNameEx_t *)GetProcAddress(g_psapi, "GetModuleFileNameExW"); +#else + pGetModuleFileNameEx = (GetModuleFileNameEx_t *)GetProcAddress(g_psapi, "GetModuleFileNameExA"); +#endif + + if (pEnumProcesses == NULL || pEnumProcessModules == NULL || pGetModuleFileNameEx == NULL) { + // invalid psapi.dll? + error("INIT\tInvalid psapi.dll!"); + + FreeLibrary(g_psapi); + g_psapi = NULL; + } +} + +#define PRINT_IP_ADDR(addr) \ + ((UCHAR *)&(addr))[0], ((UCHAR *)&(addr))[1], ((UCHAR *)&(addr))[2], ((UCHAR *)&(addr))[3] + +// enum listening objects +void +enum_listen(void) +{ + ULONG size; + struct listen_nfo *ln = NULL; + int i, n; + + // try to dynamically link psapi.dll + link_psapi(); + + /* connect with driver */ + + g_device = CreateFile(g_nfo_device_name, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (g_device == INVALID_HANDLE_VALUE) { + winerr(g_nfo_device_name); + goto done; + } + + /* get list of listening objects */ + + size = sizeof(*ln) * 0x10000 * 3; // this size is good enough :-) + ln = (struct listen_nfo *)malloc(size); + if (ln == NULL) { + perror("malloc"); + goto done; + } + + if (!DeviceIoControl(g_device, IOCTL_CMD_ENUM_LISTEN, NULL, 0, + ln, size, &size, NULL)) { + winerr("DeviceIoControl"); + goto done; + } + + n = size / sizeof(*ln); + + // sort this list! + qsort(ln, n, sizeof(*ln), compare_ln); + + printf("IPProto\tAddress:Port\tProcess (pid)\n"); + printf("-------\t------------\t---------------------------------------------\n"); + + for (i = 0; i < n ; i++) { + char *proto, pname[MAX_PATH]; + + if (ln[i].ipproto == IPPROTO_TCP) + proto = "TCP"; + else if (ln[i].ipproto == IPPROTO_UDP) + proto = "UDP"; + else if (ln[i].ipproto == IPPROTO_IP) + proto = "RawIP"; + else + proto = "?"; + + // resolve pid! + if (!get_pname_by_pid(ln[i].pid, pname, sizeof(pname))) + pname[0] = '\0'; + + printf("%s\t%d.%d.%d.%d:%d\t%s (%d)\n", + proto, PRINT_IP_ADDR(ln[i].addr), ntohs(ln[i].port), pname, ln[i].pid); + } + +done: + free(ln); + if (g_device != INVALID_HANDLE_VALUE) + CloseHandle(g_device); +} + +int +compare_ln(const void *arg1, const void *arg2) +{ + const struct listen_nfo *l1 = (const struct listen_nfo *)arg1; + const struct listen_nfo *l2 = (const struct listen_nfo *)arg2; + int result; + + result = l1->ipproto - l2->ipproto; + if (result == 0) { + result = ntohl(l2->addr) - ntohl(l1->addr); + if (result == 0) + result = ntohs(l1->port) - ntohs(l2->port); + } + return result; +} + +// enum connections +void +enum_connect(void) +{ + ULONG size; + struct tcp_conn_nfo *tn = NULL; + int i, n; + unsigned __int64 traffic[TRAFFIC_MAX]; + + // try to dynamically link psapi.dll + link_psapi(); + + /* connect with driver */ + + g_device = CreateFile(g_nfo_device_name, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (g_device == INVALID_HANDLE_VALUE) { + winerr(g_nfo_device_name); + goto done; + } + + /* get list of listening objects */ + + size = sizeof(*tn) * 0x10000 * 3; // this size is good enough :-) + tn = (struct tcp_conn_nfo *)malloc(size); + if (tn == NULL) { + perror("malloc"); + goto done; + } + + if (!DeviceIoControl(g_device, IOCTL_CMD_ENUM_TCP_CONN, NULL, 0, + tn, size, &size, NULL)) { + winerr("DeviceIoControl"); + goto done; + } + + n = size / sizeof(*tn); + + // sort this list! + qsort(tn, n, sizeof(*tn), compare_tn); + + for (i = 0; i < n ; i++) { + char pname[MAX_PATH]; + + if (tn[i].state >= TCP_STATE_MAX) + tn[i].state = 0; + + // resolve pid! + if (!get_pname_by_pid(tn[i].pid, pname, sizeof(pname))) + pname[0] = '\0'; + + printf("%s\t%d.%d.%d.%d:%d\t%d.%d.%d.%d:%d\t%s (%d)\t%u/%u\n", + g_tcp_states[tn[i].state], + PRINT_IP_ADDR(tn[i].laddr), ntohs(tn[i].lport), + PRINT_IP_ADDR(tn[i].raddr), ntohs(tn[i].rport), + pname, tn[i].pid, + tn[i].bytes_out, tn[i].bytes_in); + } + + // output traffic counters + get_traffic_stats(traffic); + + printf( + "\n" + "Traffic counters (out/in):\n" + " Total: %I64u/%I64u\n" + " Counted: %I64u/%I64u\n", + traffic[TRAFFIC_TOTAL_OUT], traffic[TRAFFIC_TOTAL_IN], + traffic[TRAFFIC_COUNTED_OUT], traffic[TRAFFIC_COUNTED_IN]); + +done: + free(tn); + if (g_device != INVALID_HANDLE_VALUE) + CloseHandle(g_device); +} + +int +compare_tn(const void *arg1, const void *arg2) +{ + const struct tcp_conn_nfo *t1 = (const struct tcp_conn_nfo *)arg1; + const struct tcp_conn_nfo *t2 = (const struct tcp_conn_nfo *)arg2; + int result; + + result = t1->state - t2->state; + if (result == 0) { + result = ntohl(t1->laddr) - ntohl(t2->laddr); + if (result == 0) { + result = ntohs(t1->lport) - ntohs(t2->lport); + if (result == 0) { + result = ntohl(t1->raddr) - ntohl(t2->raddr); + if (result == 0) + result = ntohs(t1->rport) - ntohs(t2->rport); + } + } + } + + return result; +} + +/* + * Routing functions help us to get local IP address of outgoing datagrams or connections + * Warning! This functions are slow! + * TODO: make it faster! + */ + +ULONG +get_if_ip(ULONG remote_ip) +{ + ULONG if_index; + + // get interface index by routing table + if_index = route_ip(remote_ip); + if (if_index == (ULONG)-1) + return 0; + + return get_if_index_ip(if_index); +} + +ULONG +route_ip(ULONG remote_ip) +{ + ULONG size, i, bestmetric, result; + MIB_IPFORWARDTABLE *buf; + + // get route table + + size = 0; + if (GetIpForwardTable(NULL, &size, FALSE) != ERROR_INSUFFICIENT_BUFFER) + return (ULONG)-1; + + buf = (MIB_IPFORWARDTABLE *)malloc(size); + if (buf == NULL) + return (ULONG)-1; + + if (GetIpForwardTable(buf, &size, FALSE) != NO_ERROR) { + free(buf); + return (ULONG)-1; + } + + // find optimal route + + bestmetric = (ULONG)-1; + result = (ULONG)-1; + + for (i = 0; i < buf->dwNumEntries; i++) { + if ((remote_ip & buf->table[i].dwForwardMask) == + (buf->table[i].dwForwardDest & buf->table[i].dwForwardMask)) { + + if (bestmetric == (ULONG)-1 || buf->table[i].dwForwardMetric1 > bestmetric) { + bestmetric = buf->table[i].dwForwardMetric1; + result = buf->table[i].dwForwardIfIndex; + } + + } + } + + free(buf); + return result; +} + +ULONG +get_if_index_ip(ULONG if_index) +{ + ULONG size, i; + MIB_IPADDRTABLE *buf; + ULONG result; + + size = 0; + if (GetIpAddrTable(NULL, &size, FALSE) != ERROR_INSUFFICIENT_BUFFER) + return (ULONG)-1; + + buf = (MIB_IPADDRTABLE *)malloc(size); + if (buf == NULL) + return (ULONG)-1; + + if (GetIpAddrTable(buf, &size, FALSE) != NO_ERROR) { + free(buf); + return (ULONG)-1; + } + + result = 0; + for (i = 0; i < buf->dwNumEntries; i++) + if (buf->table[i].dwIndex == if_index) { + result = buf->table[i].dwAddr; + break; + } + + free(buf); + return result; +} + +BOOL +load_users(const char *config) +{ + BOOL result = FALSE; + char *section, *p, *all_sids = NULL; + int line, sid_id; + ULONG all_sids_len = 0, all_sids_size = 0, n; + + section = (char *)malloc(MAX_SECTION_SIZE); + if (section == NULL) { + liberr("malloc"); + return FALSE; + } + GetPrivateProfileSection("_users_", section, MAX_SECTION_SIZE, config); + + // get lines + sid_id = 0; + for (p = section, line = 1; *p != '\0'; p += strlen(p) + 1, line++) { + char *p2, *p3, *authority, *principal, domain[100]; + char *sid_buf; + DWORD sid_size, domain_size; + SID_NAME_USE use; + + if (*p == ';') + continue; // comment + + p2 = strchr(p, '='); + if (p2 == NULL) { + error("%s:[_users_]:%d: invalid line format (no '=' character)", config, line); + continue; + } + + *p2 = '\0'; // temporary kill '=' + + if (sid_id != 0) { + // not _default_ + p3 = strchr(p, '\\'); + if (p3 != NULL) { + *p3 = '\0'; + + authority = p; + if (strcmp(authority, "NT AUTHORITY") == 0) + authority = NULL; + + principal = p3 + 1; + + } else { + authority = NULL; + principal = p; + } + + // try to get SID size + sid_size = 0; + domain_size = 0; + + LookupAccountName(authority, principal, NULL, &sid_size, NULL, &domain_size, &use); + } else + sid_size = 0; // empty SID for default + + if (all_sids_len + sid_size + sizeof(ULONG) >= all_sids_size) { + all_sids_size += sid_size + sizeof(ULONG) + 4096; + all_sids = (char *)realloc(all_sids, all_sids_size); + if (all_sids == NULL) { + liberr("malloc"); + goto done; + } + } + sid_buf = all_sids + all_sids_len; + all_sids_len += sid_size + sizeof(ULONG); + + *(ULONG *)sid_buf = sid_size; + + if (sid_id != 0) { + domain_size = sizeof(domain); + + if (!LookupAccountName(authority, principal, + (PSID)(sid_buf + sizeof(ULONG)), &sid_size, domain, &domain_size, &use)) { + winerr("LookupAccountName"); + error("%s:[_users_]:%d: Error getting SID for %s\\%s", config, line, + (authority != NULL ? authority : "."), principal); + goto done; + } + if (use != SidTypeUser && use != SidTypeWellKnownGroup) { + error("%s:[_users_]:%d: Invalid SID type (%d) for %s\\%s", config, line, use, + (authority != NULL ? authority : "."), principal); + goto done; + } + + if (p3 != NULL) + *p3 = '\\'; + } + + *p2 = '='; // recover '=' to make strlen(p) works right + sid_id++; + } + + // send all_sids buffer to filter driver! + result = DeviceIoControl(g_device, IOCTL_CMD_SET_SIDS, all_sids, all_sids_len, + NULL, 0, &n, NULL); + if (!result) + winerr("DeviceIoControl"); + +done: + free(all_sids); + free(section); + return result; +} + +void +get_sid_mask(const char *config, const char *name, UCHAR *sid_mask) +{ + BOOL result = FALSE; + char *section, *p; + int line, sid_id; + + memset(sid_mask, 0, MAX_SIDS_COUNT / 8); + + section = (char *)malloc(MAX_SECTION_SIZE); + if (section == NULL) { + liberr("malloc"); + return; + } + GetPrivateProfileSection("_users_", section, MAX_SECTION_SIZE, config); + + // get lines + sid_id = 0; + for (p = section, line = 1; *p != '\0'; p += strlen(p) + 1, line++) { + char *p2; + + if (*p == ';') + continue; // comment + + p2 = strchr(p, '='); + if (p2 == NULL) { + error("%s:[_users_]:%d: invalid line format (no '=' character)", config, line); + continue; + } + + *p2 = '\0'; // temporary kill '=' + + if (check_for_name(config, "_users_", p, name)) + sid_mask[sid_id / 8] |= 1 << (sid_id % 8); // set bit in mask + + *p2 = '='; // recover '=' to make strlen(p) works right + + sid_id++; + } + + free(section); +} + +BOOL +check_for_name(const char *config, const char *section, const char *value, const char *name) +{ + char buf[1024], *p, *p2; + + GetPrivateProfileString(section, value, "", buf, sizeof(buf), config); + + p = buf; + + if (strcmp(p, "*") == 0) + return TRUE; // * + + while (p != NULL) { + + p2 = strchr(p, ' '); + if (p2 != NULL) { + while (*p2 == ' ') + *(p2++) = '\0'; + } + + if (*p != '\0' && strcmp(p, name) == 0) + return TRUE; + + p = p2; + } + + return FALSE; +} + +void +get_traffic_stats(unsigned __int64 *stats) +{ + DWORD n; + memset(stats, 0, sizeof(unsigned __int64) * TRAFFIC_MAX); + DeviceIoControl(g_device, IOCTL_CMD_GET_COUNTERS, NULL, 0, stats, sizeof(unsigned __int64) * TRAFFIC_MAX, &n, NULL); +} diff --git a/tdifw-1.4.4/svc/tdifw_svc.h b/tdifw-1.4.4/svc/tdifw_svc.h new file mode 100644 index 00000000..b4dac592 --- /dev/null +++ b/tdifw-1.4.4/svc/tdifw_svc.h @@ -0,0 +1,42 @@ +// $Id: tdifw_svc.h,v 1.5 2003/05/14 16:34:01 dev Exp $ + +#ifndef _tdifw_svc_h_ +#define _tdifw_svc_h_ + +#include "ipc.h" + +int start(const char *config); +void stop(void); + +void wait(void); + +void error(const char *fmt, ...); +void winerr(const char *fn); +void liberr(const char *fn); + +#ifdef _DEBUG +# define DEBUG(_x_) error _x_ +#else +# define DEBUG(_x_) +#endif + +// debug heap support +#ifdef _MSC_VER +# define _CRTDBG_MAP_ALLOC +# include +#endif + +#ifdef _MSC_VER +# define _LEAK_CHECK _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF) +#else +# define _LEAK_CHECK +#endif + +extern BOOL g_console; + +ULONG get_host_by_name(const char *hostname, char *net_mask); + +void enum_listen(void); +void enum_connect(void); + +#endif diff --git a/tdifw-1.4.4/svc/thread.h b/tdifw-1.4.4/svc/thread.h new file mode 100644 index 00000000..1c4374e3 --- /dev/null +++ b/tdifw-1.4.4/svc/thread.h @@ -0,0 +1,19 @@ +// $Id: thread.h,v 1.1.1.1 2002/09/24 11:12:17 dev Exp $ + +#ifndef _thread_h_ +#define _thread_h_ + +#include + +#define lib_CreateThread( \ + lpThreadAttributes, \ + dwStackSize, \ + lpStartAddress, \ + lpParameter, \ + dwCreationFlags, \ + lpThreadId) \ + \ + (HANDLE)_beginthreadex((lpThreadAttributes), (unsigned)(dwStackSize), (lpStartAddress), \ + (lpParameter), (unsigned)(dwCreationFlags), (unsigned *)(lpThreadId)) + +#endif