mirror of
https://github.com/amnezia-vpn/win-split-tunnel.git
synced 2026-05-17 08:16:00 +03:00
Merge branch 'connect-redirect-localhost'
This commit is contained in:
@@ -20,7 +20,10 @@ Line wrap the file at 100 chars. Th
|
||||
* **Security**: in case of vulnerabilities.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Changed
|
||||
Use improved model to determine when to split traffic. This has the following effects:
|
||||
- TCP client sockets connecting to localhost can now be used successfully.
|
||||
- Routing now works as expected, e.g. when being connected to multiple LANs.
|
||||
|
||||
## [1.0.2.0] - 2021-05-04
|
||||
### Changed
|
||||
|
||||
@@ -119,13 +119,13 @@ From the point of view of the driver, all DNS requests are made by a particular
|
||||
|
||||
This can be mitigated for individual apps if they can be configured to use DoT/DoH.
|
||||
|
||||
## Localhost communications
|
||||
## Localhost UDP communications
|
||||
|
||||
Excluded apps aren't allowed to bind to `inaddr_any`. If a client socket isn't explicitly bound prior to calling `connect()`, the socket will momentarily be seen in the system as binding/bound towards `inaddr_any`, before the correct binding is realized. The driver sees the initial bind and redirects it to the primary network interface.
|
||||
Excluded apps aren't allowed to bind to `inaddr_any`. In certain cases, if a client socket isn't explicitly bound, the socket will momentarily be seen in the system as binding/bound towards `inaddr_any`, before the correct binding is realized. The driver sees the initial bind and redirects it to the primary network interface.
|
||||
|
||||
This means that, for excluded apps, if a socket isn't explicitly bound to `127.0.0.1` before connecting, it won't be able to talk to localhost.
|
||||
This means that, for excluded apps, if a UDP socket isn't explicitly bound to `127.0.0.1` before sending, it won't be able to talk to localhost.
|
||||
|
||||
Because explicitly bound client sockets are rare, it can be expected that most excluded apps are affected. No generally applicable mitigations are available.
|
||||
Because explicitly bound UDP client sockets are rare, it can be expected that most excluded apps are affected. No generally applicable mitigations are available.
|
||||
|
||||
## Multicast reception
|
||||
|
||||
|
||||
@@ -1,283 +0,0 @@
|
||||
#include "asyncbind.h"
|
||||
#include "../util.h"
|
||||
|
||||
#include "../trace.h"
|
||||
#include "asyncbind.tmh"
|
||||
|
||||
namespace firewall
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const ULONGLONG RECORD_MAX_LIFETIME_MS = 10000;
|
||||
|
||||
bool
|
||||
FailBindRequest
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut,
|
||||
UINT64 ClassifyHandle,
|
||||
UINT64 FilterId
|
||||
)
|
||||
{
|
||||
DbgPrint("Failing bind request from process %p\n", ProcessId);
|
||||
|
||||
//
|
||||
// There doesn't seem to be any support in WFP for blocking a bind request.
|
||||
// Specifying `FWP_ACTION_BLOCK` will just resume request processing.
|
||||
// So the best we can do is rewrite the bind to do as little harm as possible.
|
||||
//
|
||||
|
||||
FWPS_BIND_REQUEST0 *bindRequest = NULL;
|
||||
|
||||
auto status = FwpsAcquireWritableLayerDataPointer0
|
||||
(
|
||||
ClassifyHandle,
|
||||
FilterId,
|
||||
0,
|
||||
(PVOID*)&bindRequest,
|
||||
ClassifyOut
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DbgPrint("FwpsAcquireWritableLayerDataPointer0() failed 0x%X\n", status);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ClassifyOut->actionType = FWP_ACTION_PERMIT;
|
||||
ClassifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
|
||||
|
||||
//
|
||||
// This sets the port to 0, as well.
|
||||
//
|
||||
INETADDR_SETLOOPBACK((PSOCKADDR)&(bindRequest->localAddressAndPort));
|
||||
|
||||
FwpsApplyModifiedLayerData0(ClassifyHandle, (PVOID*)&bindRequest, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ReauthPendedBindRequest
|
||||
(
|
||||
PENDED_BIND *Record
|
||||
)
|
||||
{
|
||||
DbgPrint("Requesting re-auth for bind request from process %p\n", Record->ProcessId);
|
||||
|
||||
FwpsCompleteClassify0(Record->ClassifyHandle, 0, NULL);
|
||||
FwpsReleaseClassifyHandle0(Record->ClassifyHandle);
|
||||
|
||||
ExFreePoolWithTag(Record, ST_POOL_TAG);
|
||||
}
|
||||
|
||||
void
|
||||
FailPendedBindRequest
|
||||
(
|
||||
PENDED_BIND *Record
|
||||
)
|
||||
{
|
||||
const auto status = FailBindRequest(Record->ProcessId, &Record->ClassifyOut,
|
||||
Record->ClassifyHandle, Record->FilterId);
|
||||
|
||||
if (!status)
|
||||
{
|
||||
//
|
||||
// At this point there are basically two options:
|
||||
//
|
||||
// #1 Leak the bind request to prevent it from successfully binding to the tunnel interface.
|
||||
// #2 Request a re-auth of the bind request.
|
||||
//
|
||||
// We choose to implement #2 in order to retry the processing.
|
||||
//
|
||||
|
||||
ReauthPendedBindRequest(Record);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
FwpsCompleteClassify0(Record->ClassifyHandle, 0, &Record->ClassifyOut);
|
||||
FwpsReleaseClassifyHandle0(Record->ClassifyHandle);
|
||||
|
||||
ExFreePoolWithTag(Record, ST_POOL_TAG);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
NTSTATUS
|
||||
PendBindRequest
|
||||
(
|
||||
CONTEXT *Context,
|
||||
HANDLE ProcessId,
|
||||
void *ClassifyContext,
|
||||
UINT64 FilterId,
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
)
|
||||
{
|
||||
DbgPrint("Pending bind request from process %p\n", ProcessId);
|
||||
|
||||
auto record = (PENDED_BIND*)
|
||||
ExAllocatePoolWithTag(NonPagedPool, sizeof(PENDED_BIND), ST_POOL_TAG);
|
||||
|
||||
if (record == NULL)
|
||||
{
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
UINT64 classifyHandle;
|
||||
|
||||
auto status = FwpsAcquireClassifyHandle0(ClassifyContext, 0, &classifyHandle);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
ExFreePoolWithTag(record, ST_POOL_TAG);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
status = FwpsPendClassify0(classifyHandle, FilterId, 0, ClassifyOut);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
FwpsReleaseClassifyHandle0(classifyHandle);
|
||||
|
||||
ExFreePoolWithTag(record, ST_POOL_TAG);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
record->ProcessId = ProcessId;
|
||||
record->Timestamp = KeQueryInterruptTime();
|
||||
record->ClassifyHandle = classifyHandle;
|
||||
record->ClassifyOut = *ClassifyOut;
|
||||
record->FilterId = FilterId;
|
||||
|
||||
WdfWaitLockAcquire(Context->PendedBinds.Lock, NULL);
|
||||
|
||||
InsertTailList(&Context->PendedBinds.Records, &record->ListEntry);
|
||||
|
||||
WdfWaitLockRelease(Context->PendedBinds.Lock);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void
|
||||
FailBindRequest
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
void *ClassifyContext,
|
||||
UINT64 FilterId,
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
)
|
||||
{
|
||||
UINT64 classifyHandle = 0;
|
||||
|
||||
auto status = FwpsAcquireClassifyHandle0
|
||||
(
|
||||
ClassifyContext,
|
||||
0,
|
||||
&classifyHandle
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DbgPrint("FwpsAcquireClassifyHandle0() failed 0x%X\n", status);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
FailBindRequest(ProcessId, ClassifyOut, classifyHandle, FilterId);
|
||||
|
||||
FwpsReleaseClassifyHandle0(classifyHandle);
|
||||
}
|
||||
|
||||
void
|
||||
HandleProcessEvent
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
bool Arriving,
|
||||
void *Context
|
||||
)
|
||||
{
|
||||
auto context = (CONTEXT*)Context;
|
||||
|
||||
auto timeNow = KeQueryInterruptTime();
|
||||
|
||||
static const ULONGLONG MS_TO_100NS_FACTOR = 10000;
|
||||
|
||||
auto maxAge = RECORD_MAX_LIFETIME_MS * MS_TO_100NS_FACTOR;
|
||||
|
||||
//
|
||||
// Iterate over all pended bind requests.
|
||||
//
|
||||
// Fail all requests that are too old.
|
||||
// Re-auth all requests that belong to the arriving process.
|
||||
//
|
||||
|
||||
WdfWaitLockAcquire(context->PendedBinds.Lock, NULL);
|
||||
|
||||
for (auto rawRecord = context->PendedBinds.Records.Flink;
|
||||
rawRecord != &context->PendedBinds.Records;
|
||||
/* no post-condition */)
|
||||
{
|
||||
auto record = (PENDED_BIND*)rawRecord;
|
||||
|
||||
rawRecord = rawRecord->Flink;
|
||||
|
||||
auto timeDelta = timeNow - record->Timestamp;
|
||||
|
||||
if (timeDelta > maxAge)
|
||||
{
|
||||
RemoveEntryList(&record->ListEntry);
|
||||
|
||||
FailPendedBindRequest(record);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (record->ProcessId != ProcessId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
RemoveEntryList(&record->ListEntry);
|
||||
|
||||
if (Arriving)
|
||||
{
|
||||
ReauthPendedBindRequest(record);
|
||||
}
|
||||
else
|
||||
{
|
||||
FailPendedBindRequest(record);
|
||||
}
|
||||
}
|
||||
|
||||
WdfWaitLockRelease(context->PendedBinds.Lock);
|
||||
}
|
||||
|
||||
void
|
||||
FailPendedBinds
|
||||
(
|
||||
CONTEXT *Context
|
||||
)
|
||||
{
|
||||
auto context = (CONTEXT*)Context;
|
||||
|
||||
for (auto rawRecord = context->PendedBinds.Records.Flink;
|
||||
rawRecord != &context->PendedBinds.Records;
|
||||
/* no post-condition */)
|
||||
{
|
||||
auto record = (PENDED_BIND*)rawRecord;
|
||||
|
||||
rawRecord = rawRecord->Flink;
|
||||
|
||||
RemoveEntryList(&record->ListEntry);
|
||||
|
||||
FailPendedBindRequest(record);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace firewall
|
||||
@@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "wfp.h"
|
||||
#include <wdf.h>
|
||||
#include "context.h"
|
||||
|
||||
namespace firewall
|
||||
{
|
||||
|
||||
NTSTATUS
|
||||
PendBindRequest
|
||||
(
|
||||
CONTEXT *Context,
|
||||
HANDLE ProcessId,
|
||||
void *ClassifyContext,
|
||||
UINT64 FilterId,
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
);
|
||||
|
||||
void
|
||||
FailBindRequest
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
void *ClassifyContext,
|
||||
UINT64 FilterId,
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
);
|
||||
|
||||
void
|
||||
HandleProcessEvent
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
bool Arriving,
|
||||
void *Context
|
||||
);
|
||||
|
||||
void
|
||||
FailPendedBinds
|
||||
(
|
||||
CONTEXT *Context
|
||||
);
|
||||
|
||||
} // namespace firewall
|
||||
@@ -2,8 +2,10 @@
|
||||
#include "firewall.h"
|
||||
#include "context.h"
|
||||
#include "identifiers.h"
|
||||
#include "asyncbind.h"
|
||||
#include "pending.h"
|
||||
#include "callouts.h"
|
||||
#include "logging.h"
|
||||
#include "classify.h"
|
||||
#include "../util.h"
|
||||
|
||||
#include "../trace.h"
|
||||
@@ -133,8 +135,13 @@ UnregisterCallout
|
||||
//
|
||||
// RewriteBind()
|
||||
//
|
||||
// This is where the splitting happens.
|
||||
// Move socket binds from tunnel interface to the internet connected interface.
|
||||
// Implements redirection for non-TCP socket binds.
|
||||
//
|
||||
// If a bind is attempted (or implied) with target inaddr_any or the tunnel interface,
|
||||
// we rewrite the bind to move it to the internet interface.
|
||||
//
|
||||
// This has the unfortunate effect that client sockets which are not explicitly bound
|
||||
// to localhost are prevented from connecting to localhost.
|
||||
//
|
||||
void
|
||||
RewriteBind
|
||||
@@ -183,21 +190,7 @@ RewriteBind
|
||||
goto Cleanup_handle;
|
||||
}
|
||||
|
||||
//
|
||||
// According to documentation, FwpsAcquireWritableLayerDataPointer0() will update the
|
||||
// `actionType` and `rights` fields with poorly chosen values:
|
||||
//
|
||||
// ```
|
||||
// classifyOut->actionType = FWP_ACTION_BLOCK
|
||||
// classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE
|
||||
// ```
|
||||
//
|
||||
// However, in practice it seems to not make any changes to those fields.
|
||||
// But if it did we'd want to ensure the fields have sane values.
|
||||
//
|
||||
|
||||
ClassifyOut->actionType = FWP_ACTION_CONTINUE;
|
||||
ClassifyOut->rights |= FWPS_RIGHT_ACTION_WRITE;
|
||||
ClassificationReset(ClassifyOut);
|
||||
|
||||
//
|
||||
// There's a list with redirection history.
|
||||
@@ -225,62 +218,44 @@ RewriteBind
|
||||
|
||||
const bool ipv4 = FixedValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V4;
|
||||
|
||||
WdfWaitLockAcquire(Context->IpAddresses.Lock, NULL);
|
||||
WdfSpinLockAcquire(Context->IpAddresses.Lock);
|
||||
|
||||
if (ipv4)
|
||||
{
|
||||
auto bindTarget = (SOCKADDR_IN*)&(bindRequest->localAddressAndPort);
|
||||
|
||||
DbgPrint("Bind request eligible for splitting: %d.%d.%d.%d:%d\n",
|
||||
bindTarget->sin_addr.S_un.S_un_b.s_b1,
|
||||
bindTarget->sin_addr.S_un.S_un_b.s_b2,
|
||||
bindTarget->sin_addr.S_un.S_un_b.s_b3,
|
||||
bindTarget->sin_addr.S_un.S_un_b.s_b4,
|
||||
ntohs(bindTarget->sin_port)
|
||||
);
|
||||
|
||||
if (IN4_IS_ADDR_UNSPECIFIED(&(bindTarget->sin_addr))
|
||||
|| IN4_ADDR_EQUAL(&(bindTarget->sin_addr), &(Context->IpAddresses.Addresses.TunnelIpv4)))
|
||||
{
|
||||
DbgPrint("SPLITTING\n");
|
||||
const auto newTarget = &Context->IpAddresses.Addresses.InternetIpv4;
|
||||
|
||||
bindTarget->sin_addr = Context->IpAddresses.Addresses.InternetIpv4;
|
||||
LogBindRedirect(HANDLE(MetaValues->processId), bindTarget, newTarget);
|
||||
|
||||
ClassifyOut->actionType = FWP_ACTION_PERMIT;
|
||||
ClassifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
|
||||
bindTarget->sin_addr = *newTarget;
|
||||
|
||||
ClassificationApplyHardPermit(ClassifyOut);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto bindTarget = (SOCKADDR_IN6*)&(bindRequest->localAddressAndPort);
|
||||
|
||||
DbgPrint("Bind request eligible for splitting: [%X:%X:%X:%X:%X:%X:%X:%X]:%d\n",
|
||||
ntohs(bindTarget->sin6_addr.u.Word[0]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[1]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[2]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[3]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[4]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[5]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[6]),
|
||||
ntohs(bindTarget->sin6_addr.u.Word[7]),
|
||||
ntohs(bindTarget->sin6_port)
|
||||
);
|
||||
|
||||
static const IN6_ADDR IN6_ADDR_ANY = { 0 };
|
||||
|
||||
if (IN6_ADDR_EQUAL(&(bindTarget->sin6_addr), &IN6_ADDR_ANY)
|
||||
|| IN6_ADDR_EQUAL(&(bindTarget->sin6_addr), &(Context->IpAddresses.Addresses.TunnelIpv6)))
|
||||
{
|
||||
DbgPrint("SPLITTING\n");
|
||||
const auto newTarget = &Context->IpAddresses.Addresses.InternetIpv6;
|
||||
|
||||
bindTarget->sin6_addr = Context->IpAddresses.Addresses.InternetIpv6;
|
||||
LogBindRedirect(HANDLE(MetaValues->processId), bindTarget, newTarget);
|
||||
|
||||
ClassifyOut->actionType = FWP_ACTION_PERMIT;
|
||||
ClassifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
|
||||
bindTarget->sin6_addr = *newTarget;
|
||||
|
||||
ClassificationApplyHardPermit(ClassifyOut);
|
||||
}
|
||||
}
|
||||
|
||||
WdfWaitLockRelease(Context->IpAddresses.Lock);
|
||||
WdfSpinLockRelease(Context->IpAddresses.Lock);
|
||||
|
||||
Cleanup_data:
|
||||
|
||||
@@ -291,33 +266,37 @@ Cleanup_data:
|
||||
// This is the correct logic according to documentation.
|
||||
//
|
||||
|
||||
FwpsApplyModifiedLayerData0(classifyHandle, (PVOID*)&bindRequest, 0);
|
||||
FwpsApplyModifiedLayerData0(classifyHandle, bindRequest, 0);
|
||||
|
||||
Cleanup_handle:
|
||||
|
||||
FwpsReleaseClassifyHandle0(classifyHandle);
|
||||
}
|
||||
|
||||
//
|
||||
// PendClassification()
|
||||
//
|
||||
// This function is used when, for an incoming request, we don't know what the correct action is.
|
||||
// I.e. when the process making the request hasn't been categorized yet.
|
||||
//
|
||||
void
|
||||
ClassifyUnknownBind
|
||||
PendClassification
|
||||
(
|
||||
CONTEXT *Context,
|
||||
pending::CONTEXT *Context,
|
||||
HANDLE ProcessId,
|
||||
UINT64 FilterId,
|
||||
UINT16 LayerId,
|
||||
const void *ClassifyContext,
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
)
|
||||
{
|
||||
//
|
||||
// Pend the bind and wait for process to become known and classified.
|
||||
//
|
||||
|
||||
auto status = PendBindRequest
|
||||
auto status = pending::PendRequest
|
||||
(
|
||||
Context,
|
||||
ProcessId,
|
||||
const_cast<void*>(ClassifyContext),
|
||||
FilterId,
|
||||
LayerId,
|
||||
ClassifyOut
|
||||
);
|
||||
|
||||
@@ -326,13 +305,12 @@ ClassifyUnknownBind
|
||||
return;
|
||||
}
|
||||
|
||||
DbgPrint("Could not pend bind request from process %p, blocking instead\n", ProcessId);
|
||||
|
||||
FailBindRequest
|
||||
pending::FailRequest
|
||||
(
|
||||
ProcessId,
|
||||
const_cast<void*>(ClassifyContext),
|
||||
FilterId,
|
||||
LayerId,
|
||||
ClassifyOut
|
||||
);
|
||||
}
|
||||
@@ -351,8 +329,7 @@ ClassifyUnknownBind
|
||||
//
|
||||
// ===
|
||||
//
|
||||
// Entry point for splitting traffic.
|
||||
// Check whether the binding process is marked for having its traffic split.
|
||||
// Entry point for splitting non-TCP socket binds.
|
||||
//
|
||||
// FWPS_LAYER_ALE_BIND_REDIRECT_V4
|
||||
// FWPS_LAYER_ALE_BIND_REDIRECT_V6
|
||||
@@ -374,8 +351,17 @@ CalloutClassifyBind
|
||||
|
||||
NT_ASSERT
|
||||
(
|
||||
FixedValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V4
|
||||
|| FixedValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V6
|
||||
(
|
||||
FixedValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V4
|
||||
&& FixedValues->incomingValue[FWPS_FIELD_ALE_BIND_REDIRECT_V4_IP_PROTOCOL] \
|
||||
.value.uint8 != IPPROTO_TCP
|
||||
)
|
||||
||
|
||||
(
|
||||
FixedValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V6
|
||||
&& FixedValues->incomingValue[FWPS_FIELD_ALE_BIND_REDIRECT_V6_IP_PROTOCOL] \
|
||||
.value.uint8 != IPPROTO_TCP
|
||||
)
|
||||
);
|
||||
|
||||
NT_ASSERT
|
||||
@@ -428,11 +414,359 @@ CalloutClassifyBind
|
||||
}
|
||||
case PROCESS_SPLIT_VERDICT::UNKNOWN:
|
||||
{
|
||||
ClassifyUnknownBind
|
||||
PendClassification
|
||||
(
|
||||
context,
|
||||
context->PendedClassifications,
|
||||
HANDLE(MetaValues->processId),
|
||||
Filter->filterId,
|
||||
FixedValues->layerId,
|
||||
ClassifyContext,
|
||||
ClassifyOut
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bool
|
||||
LocalAddress(const IN_ADDR *addr)
|
||||
{
|
||||
return IN4_IS_ADDR_LOOPBACK(addr) // 127/8
|
||||
|| IN4_IS_ADDR_LINKLOCAL(addr) // 169.254/16
|
||||
|| IN4_IS_ADDR_RFC1918(addr) // 10/8, 172.16/12, 192.168/16
|
||||
|| IN4_IS_ADDR_MC_LINKLOCAL(addr) // 224.0.0/24
|
||||
|| IN4_IS_ADDR_MC_ADMINLOCAL(addr) // 239.255/16
|
||||
|| IN4_IS_ADDR_MC_SITELOCAL(addr) // 239/8
|
||||
|| IN4_IS_ADDR_BROADCAST(addr) // 255.255.255.255
|
||||
;
|
||||
}
|
||||
|
||||
bool
|
||||
IN6_IS_ADDR_ULA(const IN6_ADDR *a)
|
||||
{
|
||||
return (a->s6_bytes[0] == 0xfd);
|
||||
|
||||
}
|
||||
|
||||
bool
|
||||
IN6_IS_ADDR_MC_NON_GLOBAL(const IN6_ADDR *a)
|
||||
{
|
||||
return IN6_IS_ADDR_MULTICAST(a)
|
||||
&& !IN6_IS_ADDR_MC_GLOBAL(a);
|
||||
}
|
||||
|
||||
bool
|
||||
LocalAddress(const IN6_ADDR *addr)
|
||||
{
|
||||
return IN6_IS_ADDR_LOOPBACK(addr) // ::1/128
|
||||
|| IN6_IS_ADDR_LINKLOCAL(addr) // fe80::/10
|
||||
|| IN6_IS_ADDR_SITELOCAL(addr) // fec0::/10
|
||||
|| IN6_IS_ADDR_ULA(addr) // fd00::/8
|
||||
|| IN6_IS_ADDR_MC_NON_GLOBAL(addr) // ff00::/8 && !(ffxe::/16)
|
||||
;
|
||||
}
|
||||
|
||||
//
|
||||
// RewriteConnection()
|
||||
//
|
||||
// See comment on CalloutClassifyConnect().
|
||||
//
|
||||
void
|
||||
RewriteConnection
|
||||
(
|
||||
CONTEXT *Context,
|
||||
const FWPS_INCOMING_VALUES0 *FixedValues,
|
||||
const FWPS_INCOMING_METADATA_VALUES0 *MetaValues,
|
||||
UINT64 FilterId,
|
||||
const void *ClassifyContext,
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(MetaValues);
|
||||
|
||||
WdfSpinLockAcquire(Context->IpAddresses.Lock);
|
||||
|
||||
const auto ipAddresses = Context->IpAddresses.Addresses;
|
||||
|
||||
WdfSpinLockRelease(Context->IpAddresses.Lock);
|
||||
|
||||
//
|
||||
// Identify the specific cases we're interested in or abort.
|
||||
//
|
||||
|
||||
const bool ipv4 = FixedValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V4;
|
||||
|
||||
if (ipv4)
|
||||
{
|
||||
const auto rawLocalAddress = RtlUlongByteSwap(FixedValues->incomingValue[
|
||||
FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_LOCAL_ADDRESS].value.uint32);
|
||||
|
||||
const auto rawRemoteAddress = RtlUlongByteSwap(FixedValues->incomingValue[
|
||||
FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_REMOTE_ADDRESS].value.uint32);
|
||||
|
||||
auto localAddress = reinterpret_cast<const IN_ADDR*>(&rawLocalAddress);
|
||||
auto remoteAddress = reinterpret_cast<const IN_ADDR*>(&rawRemoteAddress);
|
||||
|
||||
const auto shouldRedirect = IN4_ADDR_EQUAL(localAddress, &ipAddresses.TunnelIpv4)
|
||||
|| !LocalAddress(remoteAddress);
|
||||
|
||||
const auto localPort = FixedValues->incomingValue[
|
||||
FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_LOCAL_PORT].value.uint16;
|
||||
|
||||
const auto remotePort = FixedValues->incomingValue[
|
||||
FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_REMOTE_PORT].value.uint16;
|
||||
|
||||
if (!shouldRedirect)
|
||||
{
|
||||
LogConnectRedirectPass
|
||||
(
|
||||
HANDLE(MetaValues->processId),
|
||||
localAddress,
|
||||
localPort,
|
||||
remoteAddress,
|
||||
remotePort
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
LogConnectRedirect
|
||||
(
|
||||
HANDLE(MetaValues->processId),
|
||||
localAddress,
|
||||
localPort,
|
||||
&ipAddresses.InternetIpv4,
|
||||
remoteAddress,
|
||||
remotePort
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto localAddress = reinterpret_cast<const IN6_ADDR*>(FixedValues->incomingValue[
|
||||
FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_IP_LOCAL_ADDRESS].value.byteArray16);
|
||||
|
||||
auto remoteAddress = reinterpret_cast<const IN6_ADDR*>(FixedValues->incomingValue[
|
||||
FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_IP_REMOTE_ADDRESS].value.byteArray16);
|
||||
|
||||
const auto shouldRedirect = IN6_ADDR_EQUAL(localAddress, &ipAddresses.TunnelIpv6)
|
||||
|| !LocalAddress(remoteAddress);
|
||||
|
||||
const auto localPort = FixedValues->incomingValue[
|
||||
FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_IP_LOCAL_PORT].value.uint16;
|
||||
|
||||
const auto remotePort = FixedValues->incomingValue[
|
||||
FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_IP_REMOTE_PORT].value.uint16;
|
||||
|
||||
if (!shouldRedirect)
|
||||
{
|
||||
LogConnectRedirectPass
|
||||
(
|
||||
HANDLE(MetaValues->processId),
|
||||
localAddress,
|
||||
localPort,
|
||||
remoteAddress,
|
||||
remotePort
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
LogConnectRedirect
|
||||
(
|
||||
HANDLE(MetaValues->processId),
|
||||
localAddress,
|
||||
localPort,
|
||||
&ipAddresses.InternetIpv6,
|
||||
remoteAddress,
|
||||
remotePort
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// Patch local address to force connection off of tunnel interface.
|
||||
//
|
||||
|
||||
UINT64 classifyHandle = 0;
|
||||
|
||||
auto status = FwpsAcquireClassifyHandle0
|
||||
(
|
||||
const_cast<void*>(ClassifyContext),
|
||||
0,
|
||||
&classifyHandle
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DbgPrint("FwpsAcquireClassifyHandle0() failed 0x%X\n", status);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
FWPS_CONNECT_REQUEST0 *connectRequest = NULL;
|
||||
|
||||
status = FwpsAcquireWritableLayerDataPointer0
|
||||
(
|
||||
classifyHandle,
|
||||
FilterId,
|
||||
0,
|
||||
(PVOID*)&connectRequest,
|
||||
ClassifyOut
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DbgPrint("FwpsAcquireWritableLayerDataPointer0() failed 0x%X\n", status);
|
||||
|
||||
goto Cleanup_handle;
|
||||
}
|
||||
|
||||
ClassificationReset(ClassifyOut);
|
||||
|
||||
//
|
||||
// There's a list with redirection history.
|
||||
//
|
||||
// This only ever comes into play if several callouts are fighting to redirect the connection.
|
||||
//
|
||||
// To prevent recursion, we need to check if we're on the list, and abort if so.
|
||||
//
|
||||
|
||||
for (auto history = connectRequest->previousVersion;
|
||||
history != NULL;
|
||||
history = history->previousVersion)
|
||||
{
|
||||
if (history->modifierFilterId == FilterId)
|
||||
{
|
||||
DbgPrint("Aborting connection processing because already redirected by us\n");
|
||||
|
||||
goto Cleanup_data;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Rewrite connection.
|
||||
//
|
||||
|
||||
if (ipv4)
|
||||
{
|
||||
auto localDetails = (SOCKADDR_IN*)&connectRequest->localAddressAndPort;
|
||||
localDetails->sin_addr = ipAddresses.InternetIpv4;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto localDetails = (SOCKADDR_IN6*)&connectRequest->localAddressAndPort;
|
||||
localDetails->sin6_addr = ipAddresses.InternetIpv6;
|
||||
}
|
||||
|
||||
ClassificationApplyHardPermit(ClassifyOut);
|
||||
|
||||
Cleanup_data:
|
||||
|
||||
FwpsApplyModifiedLayerData0(classifyHandle, connectRequest, 0);
|
||||
|
||||
Cleanup_handle:
|
||||
|
||||
FwpsReleaseClassifyHandle0(classifyHandle);
|
||||
}
|
||||
|
||||
//
|
||||
// CalloutClassifyConnect()
|
||||
//
|
||||
// Adjust properties on new TCP connections.
|
||||
//
|
||||
// If an app is marked for splitting, and if a new connection is explicitly made on the
|
||||
// tunnel interface, or can be assumed to be routed through the tunnel interface,
|
||||
// then move the connection to the Internet connected interface (LAN interface usually).
|
||||
//
|
||||
// FWPS_LAYER_ALE_CONNECT_REDIRECT_V4
|
||||
// FWPS_LAYER_ALE_CONNECT_REDIRECT_V6
|
||||
//
|
||||
void
|
||||
CalloutClassifyConnect
|
||||
(
|
||||
const FWPS_INCOMING_VALUES0 *FixedValues,
|
||||
const FWPS_INCOMING_METADATA_VALUES0 *MetaValues,
|
||||
void *LayerData,
|
||||
const void *ClassifyContext,
|
||||
const FWPS_FILTER1 *Filter,
|
||||
UINT64 FlowContext,
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(LayerData);
|
||||
UNREFERENCED_PARAMETER(FlowContext);
|
||||
|
||||
NT_ASSERT
|
||||
(
|
||||
(
|
||||
FixedValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V4
|
||||
&& FixedValues->incomingValue[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_PROTOCOL] \
|
||||
.value.uint8 == IPPROTO_TCP
|
||||
)
|
||||
||
|
||||
(
|
||||
FixedValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V6
|
||||
&& FixedValues->incomingValue[FWPS_FIELD_ALE_CONNECT_REDIRECT_V6_IP_PROTOCOL] \
|
||||
.value.uint8 == IPPROTO_TCP
|
||||
)
|
||||
);
|
||||
|
||||
NT_ASSERT
|
||||
(
|
||||
Filter->providerContext != NULL
|
||||
&& Filter->providerContext->type == FWPM_GENERAL_CONTEXT
|
||||
&& Filter->providerContext->dataBuffer->size == sizeof(CONTEXT*)
|
||||
);
|
||||
|
||||
auto context = *(CONTEXT**)Filter->providerContext->dataBuffer->data;
|
||||
|
||||
if (0 == (ClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE))
|
||||
{
|
||||
DbgPrint("Aborting connect-redirect processing because hard permit/block already applied\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (ClassifyOut->actionType == FWP_ACTION_NONE)
|
||||
{
|
||||
ClassifyOut->actionType = FWP_ACTION_CONTINUE;
|
||||
}
|
||||
|
||||
if (!FWPS_IS_METADATA_FIELD_PRESENT(MetaValues, FWPS_METADATA_FIELD_PROCESS_ID))
|
||||
{
|
||||
DbgPrint("Failed to classify connection because PID was not provided\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const CALLBACKS &callbacks = context->Callbacks;
|
||||
|
||||
const auto verdict = callbacks.QueryProcess(HANDLE(MetaValues->processId), callbacks.Context);
|
||||
|
||||
switch (verdict)
|
||||
{
|
||||
case PROCESS_SPLIT_VERDICT::DO_SPLIT:
|
||||
{
|
||||
RewriteConnection
|
||||
(
|
||||
context,
|
||||
FixedValues,
|
||||
MetaValues,
|
||||
Filter->filterId,
|
||||
ClassifyContext,
|
||||
ClassifyOut
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
case PROCESS_SPLIT_VERDICT::UNKNOWN:
|
||||
{
|
||||
PendClassification
|
||||
(
|
||||
context->PendedClassifications,
|
||||
HANDLE(MetaValues->processId),
|
||||
Filter->filterId,
|
||||
FixedValues->layerId,
|
||||
ClassifyContext,
|
||||
ClassifyOut
|
||||
);
|
||||
@@ -482,14 +816,85 @@ bool IsAleReauthorize
|
||||
return ((flags & FWP_CONDITION_FLAG_IS_REAUTHORIZE) != 0);
|
||||
}
|
||||
|
||||
struct AuthLayerValueIndices
|
||||
{
|
||||
SIZE_T LocalAddress;
|
||||
SIZE_T LocalPort;
|
||||
SIZE_T RemoteAddress;
|
||||
SIZE_T RemotePort;
|
||||
};
|
||||
|
||||
bool
|
||||
GetAuthLayerValueIndices
|
||||
(
|
||||
UINT16 LayerId,
|
||||
AuthLayerValueIndices *Indices
|
||||
)
|
||||
{
|
||||
switch (LayerId)
|
||||
{
|
||||
case FWPS_LAYER_ALE_AUTH_CONNECT_V4:
|
||||
{
|
||||
*Indices =
|
||||
{
|
||||
FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS,
|
||||
FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT,
|
||||
FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS,
|
||||
FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4:
|
||||
{
|
||||
*Indices =
|
||||
{
|
||||
FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_ADDRESS,
|
||||
FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_PORT,
|
||||
FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_REMOTE_ADDRESS,
|
||||
FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_REMOTE_PORT
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
case FWPS_LAYER_ALE_AUTH_CONNECT_V6:
|
||||
{
|
||||
*Indices =
|
||||
{
|
||||
FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_LOCAL_ADDRESS,
|
||||
FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_LOCAL_PORT,
|
||||
FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_REMOTE_ADDRESS,
|
||||
FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_REMOTE_PORT
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6:
|
||||
{
|
||||
*Indices =
|
||||
{
|
||||
FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_LOCAL_ADDRESS,
|
||||
FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_LOCAL_PORT,
|
||||
FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_REMOTE_ADDRESS,
|
||||
FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_REMOTE_PORT
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// CalloutPermitSplitApps()
|
||||
//
|
||||
// For processes being split, the bind will have already been moved off the
|
||||
// tunnel interface.
|
||||
//
|
||||
// For processes being split, binds and connections will have already been aptly redirected.
|
||||
// So now it's only a matter of approving the connection.
|
||||
//
|
||||
// The reason we have to explicitly approve these connections is because otherwise
|
||||
// the default filters with lower weights would block all non-tunnel connections.
|
||||
//
|
||||
// FWPS_LAYER_ALE_AUTH_CONNECT_V4
|
||||
// FWPS_LAYER_ALE_AUTH_CONNECT_V6
|
||||
// FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4
|
||||
@@ -507,12 +912,8 @@ CalloutPermitSplitApps
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
)
|
||||
{
|
||||
#if !DBG
|
||||
UNREFERENCED_PARAMETER(FixedValues);
|
||||
#endif
|
||||
UNREFERENCED_PARAMETER(LayerData);
|
||||
UNREFERENCED_PARAMETER(ClassifyContext);
|
||||
UNREFERENCED_PARAMETER(Filter);
|
||||
UNREFERENCED_PARAMETER(FlowContext);
|
||||
|
||||
NT_ASSERT
|
||||
@@ -534,7 +935,7 @@ CalloutPermitSplitApps
|
||||
|
||||
if (0 == (ClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE))
|
||||
{
|
||||
DbgPrint("Aborting connection processing because hard permit/block already applied\n");
|
||||
DbgPrint("Aborting auth processing because hard permit/block already applied\n");
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -546,7 +947,7 @@ CalloutPermitSplitApps
|
||||
|
||||
if (!FWPS_IS_METADATA_FIELD_PRESENT(MetaValues, FWPS_METADATA_FIELD_PROCESS_ID))
|
||||
{
|
||||
DbgPrint("Failed to classify connection because PID was not provided\n");
|
||||
DbgPrint("Failed to complete auth processing because PID was not provided\n");
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -555,23 +956,82 @@ CalloutPermitSplitApps
|
||||
|
||||
const auto verdict = callbacks.QueryProcess(HANDLE(MetaValues->processId), callbacks.Context);
|
||||
|
||||
if (verdict == PROCESS_SPLIT_VERDICT::DO_SPLIT)
|
||||
{
|
||||
DbgPrint("APPROVING CONNECTION\n");
|
||||
//
|
||||
// If the process is not marked for splitting we should just abort
|
||||
// and not attempt to classify the connection.
|
||||
//
|
||||
|
||||
ClassifyOut->actionType = FWP_ACTION_PERMIT;
|
||||
ClassifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
|
||||
if (verdict != PROCESS_SPLIT_VERDICT::DO_SPLIT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Include extensive logging.
|
||||
//
|
||||
|
||||
AuthLayerValueIndices indices = {0};
|
||||
|
||||
const auto status = GetAuthLayerValueIndices(FixedValues->layerId, &indices);
|
||||
|
||||
NT_ASSERT(status);
|
||||
|
||||
if (!status)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto localPort = FixedValues->incomingValue[indices.LocalPort].value.uint16;
|
||||
const auto remotePort = FixedValues->incomingValue[indices.RemotePort].value.uint16;
|
||||
|
||||
const bool ipv4 = FixedValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4
|
||||
|| FixedValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4;
|
||||
|
||||
if (ipv4)
|
||||
{
|
||||
const auto rawLocalAddress = RtlUlongByteSwap(FixedValues->incomingValue[
|
||||
indices.LocalAddress].value.uint32);
|
||||
|
||||
const auto rawRemoteAddress = RtlUlongByteSwap(FixedValues->incomingValue[
|
||||
indices.RemoteAddress].value.uint32);
|
||||
|
||||
auto localAddress = reinterpret_cast<const IN_ADDR*>(&rawLocalAddress);
|
||||
auto remoteAddress = reinterpret_cast<const IN_ADDR*>(&rawRemoteAddress);
|
||||
|
||||
LogPermitConnection
|
||||
(
|
||||
HANDLE(MetaValues->processId),
|
||||
localAddress,
|
||||
localPort,
|
||||
remoteAddress,
|
||||
remotePort,
|
||||
(FixedValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if DBG
|
||||
if (IsAleReauthorize(FixedValues))
|
||||
{
|
||||
DbgPrint("[CalloutPermitSplitApps] Reauthorized connection (PID: %p) is not explicitly "\
|
||||
"approved by callout\n", HANDLE(MetaValues->processId));
|
||||
}
|
||||
#endif
|
||||
auto localAddress = reinterpret_cast<const IN6_ADDR*>(FixedValues->incomingValue[
|
||||
indices.LocalAddress].value.byteArray16);
|
||||
|
||||
auto remoteAddress = reinterpret_cast<const IN6_ADDR*>(FixedValues->incomingValue[
|
||||
indices.RemoteAddress].value.byteArray16);
|
||||
|
||||
LogPermitConnection
|
||||
(
|
||||
HANDLE(MetaValues->processId),
|
||||
localAddress,
|
||||
localPort,
|
||||
remoteAddress,
|
||||
remotePort,
|
||||
(FixedValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6)
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// Apply classification.
|
||||
//
|
||||
|
||||
ClassificationApplyHardPermit(ClassifyOut);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -583,6 +1043,11 @@ CalloutPermitSplitApps
|
||||
// These connections need to be blocked to ensure the process exists on
|
||||
// only one side of the tunnel.
|
||||
//
|
||||
// Additionally, block any processes which have not been evaluated.
|
||||
//
|
||||
// This normally isn't required because earlier callouts re-auth until the process
|
||||
// is categorized, but it makes sense as a safety measure.
|
||||
//
|
||||
// FWPS_LAYER_ALE_AUTH_CONNECT_V4
|
||||
// FWPS_LAYER_ALE_AUTH_CONNECT_V6
|
||||
// FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4
|
||||
@@ -600,12 +1065,8 @@ CalloutBlockSplitApps
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
)
|
||||
{
|
||||
#if !DBG
|
||||
UNREFERENCED_PARAMETER(FixedValues);
|
||||
#endif
|
||||
UNREFERENCED_PARAMETER(LayerData);
|
||||
UNREFERENCED_PARAMETER(ClassifyContext);
|
||||
UNREFERENCED_PARAMETER(Filter);
|
||||
UNREFERENCED_PARAMETER(FlowContext);
|
||||
|
||||
NT_ASSERT
|
||||
@@ -627,7 +1088,7 @@ CalloutBlockSplitApps
|
||||
|
||||
if (0 == (ClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE))
|
||||
{
|
||||
DbgPrint("Aborting connection processing because hard permit/block already applied\n");
|
||||
DbgPrint("Aborting auth processing because hard permit/block already applied\n");
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -639,7 +1100,7 @@ CalloutBlockSplitApps
|
||||
|
||||
if (!FWPS_IS_METADATA_FIELD_PRESENT(MetaValues, FWPS_METADATA_FIELD_PROCESS_ID))
|
||||
{
|
||||
DbgPrint("Failed to classify connection because PID was not provided\n");
|
||||
DbgPrint("Failed to complete auth processing because PID was not provided\n");
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -648,23 +1109,85 @@ CalloutBlockSplitApps
|
||||
|
||||
const auto verdict = callbacks.QueryProcess(HANDLE(MetaValues->processId), callbacks.Context);
|
||||
|
||||
if (verdict == PROCESS_SPLIT_VERDICT::DO_SPLIT)
|
||||
{
|
||||
DbgPrint("BLOCKING CONNECTION\n");
|
||||
//
|
||||
// Block any processes which have not yet been evaluated.
|
||||
// This is a safety measure to prevent race conditions.
|
||||
//
|
||||
|
||||
ClassifyOut->actionType = FWP_ACTION_BLOCK;
|
||||
ClassifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
|
||||
const auto shouldBlock = (verdict == PROCESS_SPLIT_VERDICT::DO_SPLIT)
|
||||
|| (verdict == PROCESS_SPLIT_VERDICT::UNKNOWN);
|
||||
|
||||
if (!shouldBlock)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Include extensive logging.
|
||||
//
|
||||
|
||||
AuthLayerValueIndices indices = {0};
|
||||
|
||||
const auto status = GetAuthLayerValueIndices(FixedValues->layerId, &indices);
|
||||
|
||||
NT_ASSERT(status);
|
||||
|
||||
if (!status)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto localPort = FixedValues->incomingValue[indices.LocalPort].value.uint16;
|
||||
const auto remotePort = FixedValues->incomingValue[indices.RemotePort].value.uint16;
|
||||
|
||||
const bool ipv4 = FixedValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4
|
||||
|| FixedValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4;
|
||||
|
||||
if (ipv4)
|
||||
{
|
||||
const auto rawLocalAddress = RtlUlongByteSwap(FixedValues->incomingValue[
|
||||
indices.LocalAddress].value.uint32);
|
||||
|
||||
const auto rawRemoteAddress = RtlUlongByteSwap(FixedValues->incomingValue[
|
||||
indices.RemoteAddress].value.uint32);
|
||||
|
||||
auto localAddress = reinterpret_cast<const IN_ADDR*>(&rawLocalAddress);
|
||||
auto remoteAddress = reinterpret_cast<const IN_ADDR*>(&rawRemoteAddress);
|
||||
|
||||
LogBlockConnection
|
||||
(
|
||||
HANDLE(MetaValues->processId),
|
||||
localAddress,
|
||||
localPort,
|
||||
remoteAddress,
|
||||
remotePort,
|
||||
(FixedValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if DBG
|
||||
if (IsAleReauthorize(FixedValues))
|
||||
{
|
||||
DbgPrint("[CalloutBlockSplitApps] Reauthorized connection (PID: %p) is not explicitly "\
|
||||
"blocked by callout\n", HANDLE(MetaValues->processId));
|
||||
}
|
||||
#endif
|
||||
auto localAddress = reinterpret_cast<const IN6_ADDR*>(FixedValues->incomingValue[
|
||||
indices.LocalAddress].value.byteArray16);
|
||||
|
||||
auto remoteAddress = reinterpret_cast<const IN6_ADDR*>(FixedValues->incomingValue[
|
||||
indices.RemoteAddress].value.byteArray16);
|
||||
|
||||
LogBlockConnection
|
||||
(
|
||||
HANDLE(MetaValues->processId),
|
||||
localAddress,
|
||||
localPort,
|
||||
remoteAddress,
|
||||
remotePort,
|
||||
(FixedValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6)
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// Apply classification.
|
||||
//
|
||||
|
||||
ClassificationApplyHardBlock(ClassifyOut);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
@@ -732,6 +1255,69 @@ UnregisterCalloutClassifyBind
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// RegisterCalloutClassifyConnectTx()
|
||||
//
|
||||
// Register callout with WFP. In all applicable layers.
|
||||
//
|
||||
// "Tx" (in transaction) suffix means there is no clean-up in failure paths.
|
||||
//
|
||||
NTSTATUS
|
||||
RegisterCalloutClassifyConnectTx
|
||||
(
|
||||
PDEVICE_OBJECT DeviceObject,
|
||||
HANDLE WfpSession
|
||||
)
|
||||
{
|
||||
auto status = RegisterCalloutTx
|
||||
(
|
||||
DeviceObject,
|
||||
WfpSession,
|
||||
CalloutClassifyConnect,
|
||||
&ST_FW_CALLOUT_CLASSIFY_CONNECT_IPV4_KEY,
|
||||
&FWPM_LAYER_ALE_CONNECT_REDIRECT_V4,
|
||||
L"Mullvad Split Tunnel Connect Redirect Callout (IPv4)",
|
||||
L"Adjusts properties on new network connections"
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
status = RegisterCalloutTx
|
||||
(
|
||||
DeviceObject,
|
||||
WfpSession,
|
||||
CalloutClassifyConnect,
|
||||
&ST_FW_CALLOUT_CLASSIFY_CONNECT_IPV6_KEY,
|
||||
&FWPM_LAYER_ALE_CONNECT_REDIRECT_V6,
|
||||
L"Mullvad Split Tunnel Connect Redirect Callout (IPv6)",
|
||||
L"Adjusts properties on new network connections"
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
UnregisterCalloutClassifyConnect();
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
UnregisterCalloutClassifyConnect
|
||||
(
|
||||
)
|
||||
{
|
||||
auto s1 = UnregisterCallout(&ST_FW_CALLOUT_CLASSIFY_CONNECT_IPV4_KEY);
|
||||
auto s2 = UnregisterCallout(&ST_FW_CALLOUT_CLASSIFY_CONNECT_IPV6_KEY);
|
||||
|
||||
RETURN_IF_UNSUCCESSFUL(s1);
|
||||
RETURN_IF_UNSUCCESSFUL(s2);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// RegisterCalloutPermitSplitAppsTx()
|
||||
//
|
||||
|
||||
@@ -17,6 +17,18 @@ UnregisterCalloutClassifyBind
|
||||
(
|
||||
);
|
||||
|
||||
NTSTATUS
|
||||
RegisterCalloutClassifyConnectTx
|
||||
(
|
||||
PDEVICE_OBJECT DeviceObject,
|
||||
HANDLE WfpSession
|
||||
);
|
||||
|
||||
NTSTATUS
|
||||
UnregisterCalloutClassifyConnect
|
||||
(
|
||||
);
|
||||
|
||||
NTSTATUS
|
||||
RegisterCalloutPermitSplitAppsTx
|
||||
(
|
||||
|
||||
49
src/firewall/classify.cpp
Normal file
49
src/firewall/classify.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "classify.h"
|
||||
|
||||
namespace firewall
|
||||
{
|
||||
|
||||
void
|
||||
ClassificationReset
|
||||
(
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
)
|
||||
{
|
||||
//
|
||||
// According to documentation, FwpsAcquireWritableLayerDataPointer0() will update the
|
||||
// `actionType` and `rights` fields with poorly chosen values:
|
||||
//
|
||||
// ```
|
||||
// classifyOut->actionType = FWP_ACTION_BLOCK
|
||||
// classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE
|
||||
// ```
|
||||
//
|
||||
// However, in practice it seems to not make any changes to those fields.
|
||||
// But if it did we'd want to ensure the fields have sane values.
|
||||
//
|
||||
|
||||
ClassifyOut->actionType = FWP_ACTION_CONTINUE;
|
||||
ClassifyOut->rights |= FWPS_RIGHT_ACTION_WRITE;
|
||||
}
|
||||
|
||||
void
|
||||
ClassificationApplyHardPermit
|
||||
(
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
)
|
||||
{
|
||||
ClassifyOut->actionType = FWP_ACTION_PERMIT;
|
||||
ClassifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
|
||||
}
|
||||
|
||||
void
|
||||
ClassificationApplyHardBlock
|
||||
(
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
)
|
||||
{
|
||||
ClassifyOut->actionType = FWP_ACTION_BLOCK;
|
||||
ClassifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
|
||||
}
|
||||
|
||||
} // namespace firewall
|
||||
27
src/firewall/classify.h
Normal file
27
src/firewall/classify.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "wfp.h"
|
||||
|
||||
namespace firewall
|
||||
{
|
||||
|
||||
void
|
||||
ClassificationReset
|
||||
(
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
);
|
||||
|
||||
void
|
||||
ClassificationApplyHardPermit
|
||||
(
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
)
|
||||
;
|
||||
|
||||
void
|
||||
ClassificationApplyHardBlock
|
||||
(
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
);
|
||||
|
||||
} // namespace firewall
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <wdf.h>
|
||||
#include "firewall.h"
|
||||
#include "mode.h"
|
||||
#include "pending.h"
|
||||
#include "../ipaddr.h"
|
||||
#include "../procbroker/procbroker.h"
|
||||
#include "../eventing/eventing.h"
|
||||
@@ -13,38 +14,11 @@ namespace firewall
|
||||
|
||||
struct IP_ADDRESSES_MGMT
|
||||
{
|
||||
WDFWAITLOCK Lock;
|
||||
WDFSPINLOCK Lock;
|
||||
ST_IP_ADDRESSES Addresses;
|
||||
SPLITTING_MODE SplittingMode;
|
||||
};
|
||||
|
||||
struct PENDED_BIND
|
||||
{
|
||||
LIST_ENTRY ListEntry;
|
||||
|
||||
// Process that is trying to bind.
|
||||
HANDLE ProcessId;
|
||||
|
||||
// Timestamp when record was created.
|
||||
ULONGLONG Timestamp;
|
||||
|
||||
// Handle used to trigger re-auth or resume request processing.
|
||||
UINT64 ClassifyHandle;
|
||||
|
||||
// Classification data for when we don't want a re-auth
|
||||
// but instead wish to break and deny the bind.
|
||||
FWPS_CLASSIFY_OUT0 ClassifyOut;
|
||||
|
||||
// The filter that triggered the classification.
|
||||
UINT64 FilterId;
|
||||
};
|
||||
|
||||
struct PENDED_BIND_MGMT
|
||||
{
|
||||
WDFWAITLOCK Lock;
|
||||
LIST_ENTRY Records;
|
||||
};
|
||||
|
||||
struct TRANSACTION_MGMT
|
||||
{
|
||||
// Lock that is held for the duration of a transaction.
|
||||
@@ -61,6 +35,8 @@ struct ACTIVE_FILTERS
|
||||
{
|
||||
bool BindRedirectIpv4;
|
||||
bool BindRedirectIpv6;
|
||||
bool ConnectRedirectIpv4;
|
||||
bool ConnectRedirectIpv6;
|
||||
bool PermitNonTunnelIpv4;
|
||||
bool PermitNonTunnelIpv6;
|
||||
bool BlockTunnelIpv4;
|
||||
@@ -79,9 +55,7 @@ struct CONTEXT
|
||||
|
||||
IP_ADDRESSES_MGMT IpAddresses;
|
||||
|
||||
PENDED_BIND_MGMT PendedBinds;
|
||||
|
||||
procbroker::CONTEXT *ProcessEventBroker;
|
||||
pending::CONTEXT *PendedClassifications;
|
||||
|
||||
eventing::CONTEXT *Eventing;
|
||||
|
||||
|
||||
@@ -14,7 +14,9 @@ RegisterFilterBindRedirectIpv4Tx
|
||||
{
|
||||
//
|
||||
// Create filter that references callout.
|
||||
// Not specifying any conditions makes it apply to all traffic.
|
||||
//
|
||||
// Use `protocol != TCP` as the sole condition.
|
||||
// This is because TCP traffic is better dealt with in connect-redirect.
|
||||
//
|
||||
|
||||
FWPM_FILTER0 filter = { 0 };
|
||||
@@ -35,6 +37,16 @@ RegisterFilterBindRedirectIpv4Tx
|
||||
filter.action.calloutKey = ST_FW_CALLOUT_CLASSIFY_BIND_IPV4_KEY;
|
||||
filter.providerContextKey = ST_FW_PROVIDER_CONTEXT_KEY;
|
||||
|
||||
FWPM_FILTER_CONDITION0 cond;
|
||||
|
||||
cond.fieldKey = FWPM_CONDITION_IP_PROTOCOL;
|
||||
cond.matchType = FWP_MATCH_NOT_EQUAL;
|
||||
cond.conditionValue.type = FWP_UINT8;
|
||||
cond.conditionValue.uint8 = IPPROTO_TCP;
|
||||
|
||||
filter.filterCondition = &cond;
|
||||
filter.numFilterConditions = 1;
|
||||
|
||||
return FwpmFilterAdd0(WfpSession, &filter, NULL, NULL);
|
||||
}
|
||||
|
||||
@@ -55,7 +67,9 @@ RegisterFilterBindRedirectIpv6Tx
|
||||
{
|
||||
//
|
||||
// Create filter that references callout.
|
||||
// Not specifying any conditions makes it apply to all traffic.
|
||||
//
|
||||
// Use `protocol != TCP` as the sole condition.
|
||||
// This is because TCP traffic is better dealt with in connect-redirect.
|
||||
//
|
||||
|
||||
FWPM_FILTER0 filter = { 0 };
|
||||
@@ -76,6 +90,16 @@ RegisterFilterBindRedirectIpv6Tx
|
||||
filter.action.calloutKey = ST_FW_CALLOUT_CLASSIFY_BIND_IPV6_KEY;
|
||||
filter.providerContextKey = ST_FW_PROVIDER_CONTEXT_KEY;
|
||||
|
||||
FWPM_FILTER_CONDITION0 cond;
|
||||
|
||||
cond.fieldKey = FWPM_CONDITION_IP_PROTOCOL;
|
||||
cond.matchType = FWP_MATCH_NOT_EQUAL;
|
||||
cond.conditionValue.type = FWP_UINT8;
|
||||
cond.conditionValue.uint8 = IPPROTO_TCP;
|
||||
|
||||
filter.filterCondition = &cond;
|
||||
filter.numFilterConditions = 1;
|
||||
|
||||
return FwpmFilterAdd0(WfpSession, &filter, NULL, NULL);
|
||||
}
|
||||
|
||||
@@ -88,6 +112,116 @@ RemoveFilterBindRedirectIpv6Tx
|
||||
return FwpmFilterDeleteByKey0(WfpSession, &ST_FW_FILTER_CLASSIFY_BIND_IPV6_KEY);
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
RegisterFilterConnectRedirectIpv4Tx
|
||||
(
|
||||
HANDLE WfpSession
|
||||
)
|
||||
{
|
||||
//
|
||||
// Create filter that references callout.
|
||||
//
|
||||
// Use `protocol == TCP` as the sole condition.
|
||||
//
|
||||
// This is because the source address for non-TCP traffic can't be updated in connect-redirect.
|
||||
// So that traffic is instead dealt with in bind-redirect.
|
||||
//
|
||||
|
||||
FWPM_FILTER0 filter = { 0 };
|
||||
|
||||
const auto filterName = L"Mullvad Split Tunnel Connect Redirect Filter (IPv4)";
|
||||
const auto filterDescription = L"Adjusts properties on new network connections";
|
||||
|
||||
filter.filterKey = ST_FW_FILTER_CLASSIFY_CONNECT_IPV4_KEY;
|
||||
filter.displayData.name = const_cast<wchar_t*>(filterName);
|
||||
filter.displayData.description = const_cast<wchar_t*>(filterDescription);
|
||||
filter.flags = FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT | FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT;
|
||||
filter.providerKey = const_cast<GUID*>(&ST_FW_PROVIDER_KEY);
|
||||
filter.layerKey = FWPM_LAYER_ALE_CONNECT_REDIRECT_V4;
|
||||
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||
filter.weight.type = FWP_UINT64;
|
||||
filter.weight.uint64 = const_cast<UINT64*>(&ST_MAX_FILTER_WEIGHT);
|
||||
filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN;
|
||||
filter.action.calloutKey = ST_FW_CALLOUT_CLASSIFY_CONNECT_IPV4_KEY;
|
||||
filter.providerContextKey = ST_FW_PROVIDER_CONTEXT_KEY;
|
||||
|
||||
FWPM_FILTER_CONDITION0 cond;
|
||||
|
||||
cond.fieldKey = FWPM_CONDITION_IP_PROTOCOL;
|
||||
cond.matchType = FWP_MATCH_EQUAL;
|
||||
cond.conditionValue.type = FWP_UINT8;
|
||||
cond.conditionValue.uint8 = IPPROTO_TCP;
|
||||
|
||||
filter.filterCondition = &cond;
|
||||
filter.numFilterConditions = 1;
|
||||
|
||||
return FwpmFilterAdd0(WfpSession, &filter, NULL, NULL);
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
RemoveFilterConnectRedirectIpv4Tx
|
||||
(
|
||||
HANDLE WfpSession
|
||||
)
|
||||
{
|
||||
return FwpmFilterDeleteByKey0(WfpSession, &ST_FW_FILTER_CLASSIFY_CONNECT_IPV4_KEY);
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
RegisterFilterConnectRedirectIpv6Tx
|
||||
(
|
||||
HANDLE WfpSession
|
||||
)
|
||||
{
|
||||
//
|
||||
// Create filter that references callout.
|
||||
//
|
||||
// Use `protocol == TCP` as the sole condition.
|
||||
//
|
||||
// This is because the source address for non-TCP traffic can't be updated in connect-redirect.
|
||||
// So that traffic is instead dealt with in bind-redirect.
|
||||
//
|
||||
|
||||
FWPM_FILTER0 filter = { 0 };
|
||||
|
||||
const auto filterName = L"Mullvad Split Tunnel Connect Redirect Filter (IPv6)";
|
||||
const auto filterDescription = L"Adjusts properties on new network connections";
|
||||
|
||||
filter.filterKey = ST_FW_FILTER_CLASSIFY_CONNECT_IPV6_KEY;
|
||||
filter.displayData.name = const_cast<wchar_t*>(filterName);
|
||||
filter.displayData.description = const_cast<wchar_t*>(filterDescription);
|
||||
filter.flags = FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT | FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT;
|
||||
filter.providerKey = const_cast<GUID*>(&ST_FW_PROVIDER_KEY);
|
||||
filter.layerKey = FWPM_LAYER_ALE_CONNECT_REDIRECT_V6;
|
||||
filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY;
|
||||
filter.weight.type = FWP_UINT64;
|
||||
filter.weight.uint64 = const_cast<UINT64*>(&ST_MAX_FILTER_WEIGHT);
|
||||
filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN;
|
||||
filter.action.calloutKey = ST_FW_CALLOUT_CLASSIFY_CONNECT_IPV6_KEY;
|
||||
filter.providerContextKey = ST_FW_PROVIDER_CONTEXT_KEY;
|
||||
|
||||
FWPM_FILTER_CONDITION0 cond;
|
||||
|
||||
cond.fieldKey = FWPM_CONDITION_IP_PROTOCOL;
|
||||
cond.matchType = FWP_MATCH_EQUAL;
|
||||
cond.conditionValue.type = FWP_UINT8;
|
||||
cond.conditionValue.uint8 = IPPROTO_TCP;
|
||||
|
||||
filter.filterCondition = &cond;
|
||||
filter.numFilterConditions = 1;
|
||||
|
||||
return FwpmFilterAdd0(WfpSession, &filter, NULL, NULL);
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
RemoveFilterConnectRedirectIpv6Tx
|
||||
(
|
||||
HANDLE WfpSession
|
||||
)
|
||||
{
|
||||
return FwpmFilterDeleteByKey0(WfpSession, &ST_FW_FILTER_CLASSIFY_CONNECT_IPV6_KEY);
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
RegisterFilterPermitNonTunnelIpv4Tx
|
||||
(
|
||||
|
||||
@@ -45,6 +45,45 @@ RemoveFilterBindRedirectIpv6Tx
|
||||
HANDLE WfpSession
|
||||
);
|
||||
|
||||
//
|
||||
// RegisterFilterConnectRedirectIpv4Tx()
|
||||
//
|
||||
// Register filter, with linked callout, that will pass all connection requests through
|
||||
// the connection callout for validation/redirection.
|
||||
//
|
||||
// The callout will look for and amend broken localhost client connections.
|
||||
//
|
||||
// "Tx" (in transaction) suffix means there's no clean-up in failure paths.
|
||||
//
|
||||
NTSTATUS
|
||||
RegisterFilterConnectRedirectIpv4Tx
|
||||
(
|
||||
HANDLE WfpSession
|
||||
);
|
||||
|
||||
NTSTATUS
|
||||
RemoveFilterConnectRedirectIpv4Tx
|
||||
(
|
||||
HANDLE WfpSession
|
||||
);
|
||||
|
||||
//
|
||||
// RegisterFilterConnectRedirectIpv6Tx()
|
||||
//
|
||||
// Refer comment on corresponding function for IPv4.
|
||||
//
|
||||
NTSTATUS
|
||||
RegisterFilterConnectRedirectIpv6Tx
|
||||
(
|
||||
HANDLE WfpSession
|
||||
);
|
||||
|
||||
NTSTATUS
|
||||
RemoveFilterConnectRedirectIpv6Tx
|
||||
(
|
||||
HANDLE WfpSession
|
||||
);
|
||||
|
||||
//
|
||||
// RegisterFilterPermitNonTunnelIpv4Tx()
|
||||
//
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
#include "filters.h"
|
||||
#include "callouts.h"
|
||||
#include "constants.h"
|
||||
#include "asyncbind.h"
|
||||
#include "pending.h"
|
||||
#include "logging.h"
|
||||
#include "../util.h"
|
||||
#include "../eventing/builder.h"
|
||||
#include "firewall.h"
|
||||
@@ -129,7 +130,8 @@ UnregisterCallouts
|
||||
{
|
||||
auto s1 = UnregisterCalloutBlockSplitApps();
|
||||
auto s2 = UnregisterCalloutPermitSplitApps();
|
||||
auto s3 = UnregisterCalloutClassifyBind();
|
||||
auto s3 = UnregisterCalloutClassifyConnect();
|
||||
auto s4 = UnregisterCalloutClassifyBind();
|
||||
|
||||
if (!NT_SUCCESS(s1))
|
||||
{
|
||||
@@ -147,11 +149,18 @@ UnregisterCallouts
|
||||
|
||||
if (!NT_SUCCESS(s3))
|
||||
{
|
||||
DbgPrint("Could not unregister bind-redirect callout\n");
|
||||
DbgPrint("Could not unregister connect-redirect callout\n");
|
||||
|
||||
return s3;
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(s4))
|
||||
{
|
||||
DbgPrint("Could not unregister bind-redirect callout\n");
|
||||
|
||||
return s4;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -179,6 +188,15 @@ RegisterCallouts
|
||||
return status;
|
||||
}
|
||||
|
||||
status = RegisterCalloutClassifyConnectTx(DeviceObject, WfpSession);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DbgPrint("Could not register connect-redirect callout\n");
|
||||
|
||||
goto Unregister_callouts;
|
||||
}
|
||||
|
||||
status = RegisterCalloutPermitSplitAppsTx(DeviceObject, WfpSession);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
@@ -320,6 +338,8 @@ ResetStructure
|
||||
{
|
||||
ActiveFilters->BindRedirectIpv4 = false;
|
||||
ActiveFilters->BindRedirectIpv6 = false;
|
||||
ActiveFilters->ConnectRedirectIpv4 = false;
|
||||
ActiveFilters->ConnectRedirectIpv6 = false;
|
||||
ActiveFilters->BlockTunnelIpv4 = false;
|
||||
ActiveFilters->BlockTunnelIpv6 = false;
|
||||
ActiveFilters->PermitNonTunnelIpv4 = false;
|
||||
@@ -357,6 +377,12 @@ RegisterFiltersForModeTx
|
||||
&ActiveFilters->BindRedirectIpv4
|
||||
);
|
||||
|
||||
RFFM_SUCCEED_OR_RETURN
|
||||
(
|
||||
RegisterFilterConnectRedirectIpv4Tx(WfpSession),
|
||||
&ActiveFilters->ConnectRedirectIpv4
|
||||
);
|
||||
|
||||
RFFM_SUCCEED_OR_RETURN
|
||||
(
|
||||
RegisterFilterPermitNonTunnelIpv4Tx(WfpSession, &IpAddresses->TunnelIpv4),
|
||||
@@ -375,6 +401,12 @@ RegisterFiltersForModeTx
|
||||
&ActiveFilters->BindRedirectIpv6
|
||||
);
|
||||
|
||||
RFFM_SUCCEED_OR_RETURN
|
||||
(
|
||||
RegisterFilterConnectRedirectIpv6Tx(WfpSession),
|
||||
&ActiveFilters->ConnectRedirectIpv6
|
||||
);
|
||||
|
||||
RFFM_SUCCEED_OR_RETURN
|
||||
(
|
||||
RegisterFilterPermitNonTunnelIpv6Tx(WfpSession, &IpAddresses->TunnelIpv6),
|
||||
@@ -397,6 +429,12 @@ RegisterFiltersForModeTx
|
||||
&ActiveFilters->BindRedirectIpv4
|
||||
);
|
||||
|
||||
RFFM_SUCCEED_OR_RETURN
|
||||
(
|
||||
RegisterFilterConnectRedirectIpv4Tx(WfpSession),
|
||||
&ActiveFilters->ConnectRedirectIpv4
|
||||
);
|
||||
|
||||
RFFM_SUCCEED_OR_RETURN
|
||||
(
|
||||
RegisterFilterPermitNonTunnelIpv4Tx(WfpSession, &IpAddresses->TunnelIpv4),
|
||||
@@ -419,6 +457,12 @@ RegisterFiltersForModeTx
|
||||
&ActiveFilters->BindRedirectIpv4
|
||||
);
|
||||
|
||||
RFFM_SUCCEED_OR_RETURN
|
||||
(
|
||||
RegisterFilterConnectRedirectIpv4Tx(WfpSession),
|
||||
&ActiveFilters->ConnectRedirectIpv4
|
||||
);
|
||||
|
||||
RFFM_SUCCEED_OR_RETURN
|
||||
(
|
||||
RegisterFilterPermitNonTunnelIpv4Tx(WfpSession, &IpAddresses->TunnelIpv4),
|
||||
@@ -451,6 +495,12 @@ RegisterFiltersForModeTx
|
||||
&ActiveFilters->BindRedirectIpv4
|
||||
);
|
||||
|
||||
RFFM_SUCCEED_OR_RETURN
|
||||
(
|
||||
RegisterFilterConnectRedirectIpv4Tx(WfpSession),
|
||||
&ActiveFilters->ConnectRedirectIpv4
|
||||
);
|
||||
|
||||
RFFM_SUCCEED_OR_RETURN
|
||||
(
|
||||
RegisterFilterPermitNonTunnelIpv4Tx(WfpSession, &IpAddresses->TunnelIpv4),
|
||||
@@ -479,6 +529,12 @@ RegisterFiltersForModeTx
|
||||
&ActiveFilters->BindRedirectIpv6
|
||||
);
|
||||
|
||||
RFFM_SUCCEED_OR_RETURN
|
||||
(
|
||||
RegisterFilterConnectRedirectIpv6Tx(WfpSession),
|
||||
&ActiveFilters->ConnectRedirectIpv6
|
||||
);
|
||||
|
||||
RFFM_SUCCEED_OR_RETURN
|
||||
(
|
||||
RegisterFilterPermitNonTunnelIpv6Tx(WfpSession, &IpAddresses->TunnelIpv6),
|
||||
@@ -501,6 +557,12 @@ RegisterFiltersForModeTx
|
||||
&ActiveFilters->BindRedirectIpv6
|
||||
);
|
||||
|
||||
RFFM_SUCCEED_OR_RETURN
|
||||
(
|
||||
RegisterFilterConnectRedirectIpv6Tx(WfpSession),
|
||||
&ActiveFilters->ConnectRedirectIpv6
|
||||
);
|
||||
|
||||
RFFM_SUCCEED_OR_RETURN
|
||||
(
|
||||
RegisterFilterPermitNonTunnelIpv6Tx(WfpSession, &IpAddresses->TunnelIpv6),
|
||||
@@ -533,6 +595,13 @@ RegisterFiltersForModeTx
|
||||
&ActiveFilters->BindRedirectIpv6
|
||||
);
|
||||
|
||||
RFFM_SUCCEED_OR_RETURN
|
||||
(
|
||||
RegisterFilterConnectRedirectIpv6Tx(WfpSession),
|
||||
&ActiveFilters->ConnectRedirectIpv6
|
||||
);
|
||||
|
||||
|
||||
RFFM_SUCCEED_OR_RETURN
|
||||
(
|
||||
RegisterFilterPermitNonTunnelIpv6Tx(WfpSession, &IpAddresses->TunnelIpv6),
|
||||
@@ -625,6 +694,22 @@ RemoveActiveFiltersTx
|
||||
);
|
||||
}
|
||||
|
||||
if (ActiveFilters->ConnectRedirectIpv4)
|
||||
{
|
||||
RAF_SUCCEED_OR_RETURN
|
||||
(
|
||||
RemoveFilterConnectRedirectIpv4Tx(WfpSession)
|
||||
);
|
||||
}
|
||||
|
||||
if (ActiveFilters->ConnectRedirectIpv6)
|
||||
{
|
||||
RAF_SUCCEED_OR_RETURN
|
||||
(
|
||||
RemoveFilterConnectRedirectIpv6Tx(WfpSession)
|
||||
);
|
||||
}
|
||||
|
||||
if (ActiveFilters->BlockTunnelIpv4)
|
||||
{
|
||||
RAF_SUCCEED_OR_RETURN
|
||||
@@ -897,34 +982,20 @@ Initialize
|
||||
|
||||
RtlZeroMemory(context, sizeof(*context));
|
||||
|
||||
InitializeListHead(&context->PendedBinds.Records);
|
||||
|
||||
context->Callbacks = *Callbacks;
|
||||
context->ProcessEventBroker = ProcessEventBroker;
|
||||
context->Eventing = Eventing;
|
||||
|
||||
auto status = WdfWaitLockCreate(WDF_NO_OBJECT_ATTRIBUTES, &context->IpAddresses.Lock);
|
||||
auto status = WdfSpinLockCreate(WDF_NO_OBJECT_ATTRIBUTES, &context->IpAddresses.Lock);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DbgPrint("WdfWaitLockCreate() failed 0x%X\n", status);
|
||||
DbgPrint("WdfSpinLockCreate() failed 0x%X\n", status);
|
||||
|
||||
context->IpAddresses.Lock = NULL;
|
||||
|
||||
goto Abort;
|
||||
}
|
||||
|
||||
status = WdfWaitLockCreate(WDF_NO_OBJECT_ATTRIBUTES, &context->PendedBinds.Lock);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DbgPrint("WdfWaitLockCreate() failed 0x%X\n", status);
|
||||
|
||||
context->PendedBinds.Lock = NULL;
|
||||
|
||||
goto Abort_delete_ip_lock;
|
||||
}
|
||||
|
||||
status = WdfWaitLockCreate(WDF_NO_OBJECT_ATTRIBUTES, &context->Transaction.Lock);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
@@ -933,14 +1004,31 @@ Initialize
|
||||
|
||||
context->Transaction.Lock = NULL;
|
||||
|
||||
goto Abort_delete_bind_lock;
|
||||
goto Abort_delete_ip_lock;
|
||||
}
|
||||
|
||||
status = pending::Initialize
|
||||
(
|
||||
&context->PendedClassifications,
|
||||
ProcessEventBroker
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DbgPrint("pending::Initialize failed 0x%X\n", status);
|
||||
|
||||
context->PendedClassifications = NULL;
|
||||
|
||||
goto Abort_delete_transaction_lock;
|
||||
}
|
||||
|
||||
status = CreateWfpSession(&context->WfpSession);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
goto Abort_delete_transaction_lock;
|
||||
context->WfpSession = NULL;
|
||||
|
||||
goto Abort_teardown_pending;
|
||||
}
|
||||
|
||||
status = ConfigureWfpTx(context->WfpSession, context);
|
||||
@@ -964,21 +1052,10 @@ Initialize
|
||||
goto Abort_unregister_callouts;
|
||||
}
|
||||
|
||||
status = procbroker::Subscribe(ProcessEventBroker, HandleProcessEvent, context);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
goto Abort_teardown_appfilters;
|
||||
}
|
||||
|
||||
*Context = context;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
Abort_teardown_appfilters:
|
||||
|
||||
appfilters::TearDown(&context->AppFiltersContext);
|
||||
|
||||
Abort_unregister_callouts:
|
||||
|
||||
{
|
||||
@@ -994,14 +1071,14 @@ Abort_destroy_session:
|
||||
|
||||
DestroyWfpSession(context->WfpSession);
|
||||
|
||||
Abort_teardown_pending:
|
||||
|
||||
pending::TearDown(&context->PendedClassifications);
|
||||
|
||||
Abort_delete_transaction_lock:
|
||||
|
||||
WdfObjectDelete(context->Transaction.Lock);
|
||||
|
||||
Abort_delete_bind_lock:
|
||||
|
||||
WdfObjectDelete(context->PendedBinds.Lock);
|
||||
|
||||
Abort_delete_ip_lock:
|
||||
|
||||
WdfObjectDelete(context->IpAddresses.Lock);
|
||||
@@ -1034,6 +1111,20 @@ TearDown
|
||||
CONTEXT **Context
|
||||
)
|
||||
{
|
||||
const bool preConditions =
|
||||
(
|
||||
(Context != NULL)
|
||||
&& (*Context != NULL)
|
||||
&& ((*Context)->SplittingEnabled == false)
|
||||
);
|
||||
|
||||
NT_ASSERT(preConditions);
|
||||
|
||||
if (!preConditions)
|
||||
{
|
||||
return STATUS_INVALID_DISPOSITION;
|
||||
}
|
||||
|
||||
auto context = *Context;
|
||||
|
||||
*Context = NULL;
|
||||
@@ -1042,11 +1133,7 @@ TearDown
|
||||
// Clean up adjacent systems.
|
||||
//
|
||||
|
||||
procbroker::CancelSubscription(context->ProcessEventBroker, HandleProcessEvent);
|
||||
|
||||
FailPendedBinds(context);
|
||||
|
||||
WdfObjectDelete(context->PendedBinds.Lock);
|
||||
pending::TearDown(&context->PendedClassifications);
|
||||
|
||||
appfilters::TearDown(&context->AppFiltersContext);
|
||||
|
||||
@@ -1177,6 +1264,8 @@ EnableSplitting
|
||||
Context->SplittingEnabled = true;
|
||||
Context->ActiveFilters = activeFilters;
|
||||
|
||||
LogActivatedSplittingMode(Context->IpAddresses.SplittingMode);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
Abort:
|
||||
@@ -1345,15 +1434,19 @@ RegisterUpdatedIpAddresses
|
||||
goto Abort;
|
||||
}
|
||||
|
||||
WdfWaitLockAcquire(Context->IpAddresses.Lock, NULL);
|
||||
auto intermediateNonPagedAddresses = *IpAddresses;
|
||||
|
||||
Context->IpAddresses.Addresses = *IpAddresses;
|
||||
WdfSpinLockAcquire(Context->IpAddresses.Lock);
|
||||
|
||||
Context->IpAddresses.Addresses = intermediateNonPagedAddresses;
|
||||
Context->IpAddresses.SplittingMode = newMode;
|
||||
|
||||
WdfWaitLockRelease(Context->IpAddresses.Lock);
|
||||
WdfSpinLockRelease(Context->IpAddresses.Lock);
|
||||
|
||||
Context->ActiveFilters = newActiveFilters;
|
||||
|
||||
LogActivatedSplittingMode(newMode);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
Abort:
|
||||
|
||||
@@ -20,6 +20,14 @@ DEFINE_GUID(ST_FW_CALLOUT_CLASSIFY_BIND_IPV4_KEY,
|
||||
DEFINE_GUID(ST_FW_CALLOUT_CLASSIFY_BIND_IPV6_KEY,
|
||||
0x53fb3120, 0xb6a4, 0x462b, 0xbf, 0xfc, 0x69, 0x78, 0xaa, 0xda, 0x1d, 0xa2);
|
||||
|
||||
// {A4E010B5-DC3F-474A-B7C2-2F3269945F41}
|
||||
DEFINE_GUID(ST_FW_CALLOUT_CLASSIFY_CONNECT_IPV4_KEY,
|
||||
0xa4e010b5, 0xdc3f, 0x474a, 0xb7, 0xc2, 0x2f, 0x32, 0x69, 0x94, 0x5f, 0x41);
|
||||
|
||||
// {6B634022-B3D3-4667-88BA-BF5028858F52}
|
||||
DEFINE_GUID(ST_FW_CALLOUT_CLASSIFY_CONNECT_IPV6_KEY,
|
||||
0x6b634022, 0xb3d3, 0x4667, 0x88, 0xba, 0xbf, 0x50, 0x28, 0x85, 0x8f, 0x52);
|
||||
|
||||
// {33F3EDCC-EB5E-41CF-9250-702C94A28E39}
|
||||
DEFINE_GUID(ST_FW_CALLOUT_PERMIT_SPLIT_APPS_IPV4_CONN_KEY,
|
||||
0x33f3edcc, 0xeb5e, 0x41cf, 0x92, 0x50, 0x70, 0x2c, 0x94, 0xa2, 0x8e, 0x39);
|
||||
@@ -60,6 +68,14 @@ DEFINE_GUID(ST_FW_FILTER_CLASSIFY_BIND_IPV4_KEY,
|
||||
DEFINE_GUID(ST_FW_FILTER_CLASSIFY_BIND_IPV6_KEY,
|
||||
0x2f607222, 0xb2eb, 0x443c, 0xb6, 0xe0, 0x64, 0x10, 0x67, 0x37, 0x54, 0x78);
|
||||
|
||||
// {4207F127-CC80-477E-ADDF-26F76585E073}
|
||||
DEFINE_GUID(ST_FW_FILTER_CLASSIFY_CONNECT_IPV4_KEY,
|
||||
0x4207f127, 0xcc80, 0x477e, 0xad, 0xdf, 0x26, 0xf7, 0x65, 0x85, 0xe0, 0x73);
|
||||
|
||||
// {9A87F137-5112-4427-B315-4F87B3E84DCC}
|
||||
DEFINE_GUID(ST_FW_FILTER_CLASSIFY_CONNECT_IPV6_KEY,
|
||||
0x9a87f137, 0x5112, 0x4427, 0xb3, 0x15, 0x4f, 0x87, 0xb3, 0xe8, 0x4d, 0xcc);
|
||||
|
||||
// {66CED079-C270-4B4D-A45C-D11711C0D600}
|
||||
DEFINE_GUID(ST_FW_FILTER_PERMIT_SPLIT_APPS_IPV4_CONN_KEY,
|
||||
0x66ced079, 0xc270, 0x4b4d, 0xa4, 0x5c, 0xd1, 0x17, 0x11, 0xc0, 0xd6, 0x0);
|
||||
|
||||
333
src/firewall/logging.cpp
Normal file
333
src/firewall/logging.cpp
Normal file
@@ -0,0 +1,333 @@
|
||||
#include "logging.h"
|
||||
#include "../util.h"
|
||||
|
||||
#include "../trace.h"
|
||||
#include "logging.tmh"
|
||||
|
||||
namespace firewall
|
||||
{
|
||||
|
||||
void
|
||||
LogBindRedirect
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const SOCKADDR_IN *Target,
|
||||
const IN_ADDR *Override
|
||||
)
|
||||
{
|
||||
char targetString[32];
|
||||
char overrideString[32];
|
||||
|
||||
RtlIpv4AddressToStringA(&Target->sin_addr, targetString);
|
||||
RtlIpv4AddressToStringA(Override, overrideString);
|
||||
|
||||
const auto port = ntohs(Target->sin_port);
|
||||
|
||||
DbgPrint
|
||||
(
|
||||
"[BIND][%p] Rewriting Non-TCP bind request %s:%d into %s:%d\n",
|
||||
ProcessId,
|
||||
targetString,
|
||||
port,
|
||||
overrideString,
|
||||
port
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
LogBindRedirect
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const SOCKADDR_IN6 *Target,
|
||||
const IN6_ADDR *Override
|
||||
)
|
||||
{
|
||||
char targetString[64];
|
||||
char overrideString[64];
|
||||
|
||||
RtlIpv6AddressToStringA(&Target->sin6_addr, targetString);
|
||||
RtlIpv6AddressToStringA(Override, overrideString);
|
||||
|
||||
const auto port = ntohs(Target->sin6_port);
|
||||
|
||||
DbgPrint
|
||||
(
|
||||
"[BIND][%p] Rewriting Non-TCP bind request [%s]:%d into [%s]:%d\n",
|
||||
ProcessId,
|
||||
targetString,
|
||||
port,
|
||||
overrideString,
|
||||
port
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
LogConnectRedirectPass
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const IN_ADDR *LocalAddress,
|
||||
USHORT LocalPort,
|
||||
const IN_ADDR *RemoteAddress,
|
||||
USHORT RemotePort
|
||||
)
|
||||
{
|
||||
char localAddrString[32];
|
||||
char remoteAddrString[32];
|
||||
|
||||
RtlIpv4AddressToStringA(LocalAddress, localAddrString);
|
||||
RtlIpv4AddressToStringA(RemoteAddress, remoteAddrString);
|
||||
|
||||
DbgPrint
|
||||
(
|
||||
"[CONN][%p] Passing on opportunity to redirect %s:%d -> %s:%d\n",
|
||||
ProcessId,
|
||||
localAddrString,
|
||||
LocalPort,
|
||||
remoteAddrString,
|
||||
RemotePort
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
LogConnectRedirectPass
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const IN6_ADDR *LocalAddress,
|
||||
USHORT LocalPort,
|
||||
const IN6_ADDR *RemoteAddress,
|
||||
USHORT RemotePort
|
||||
)
|
||||
{
|
||||
char localAddrString[64];
|
||||
char remoteAddrString[64];
|
||||
|
||||
RtlIpv6AddressToStringA(LocalAddress, localAddrString);
|
||||
RtlIpv6AddressToStringA(RemoteAddress, remoteAddrString);
|
||||
|
||||
DbgPrint
|
||||
(
|
||||
"[CONN][%p] Passing on opportunity to redirect [%s]:%d -> [%s]:%d\n",
|
||||
ProcessId,
|
||||
localAddrString,
|
||||
LocalPort,
|
||||
remoteAddrString,
|
||||
RemotePort
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
LogConnectRedirect
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const IN_ADDR *LocalAddress,
|
||||
USHORT LocalPort,
|
||||
const IN_ADDR *LocalAddressOverride,
|
||||
const IN_ADDR *RemoteAddress,
|
||||
USHORT RemotePort
|
||||
)
|
||||
{
|
||||
char localAddrString[32];
|
||||
char localAddrOverrideString[32];
|
||||
char remoteAddrString[32];
|
||||
|
||||
RtlIpv4AddressToStringA(LocalAddress, localAddrString);
|
||||
RtlIpv4AddressToStringA(LocalAddressOverride, localAddrOverrideString);
|
||||
RtlIpv4AddressToStringA(RemoteAddress, remoteAddrString);
|
||||
|
||||
DbgPrint
|
||||
(
|
||||
"[CONN][%p] Rewriting connection on %s:%d as %s:%d -> %s:%d\n",
|
||||
ProcessId,
|
||||
localAddrString,
|
||||
LocalPort,
|
||||
localAddrOverrideString,
|
||||
LocalPort,
|
||||
remoteAddrString,
|
||||
RemotePort
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
LogConnectRedirect
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const IN6_ADDR *LocalAddress,
|
||||
USHORT LocalPort,
|
||||
const IN6_ADDR *LocalAddressOverride,
|
||||
const IN6_ADDR *RemoteAddress,
|
||||
USHORT RemotePort
|
||||
)
|
||||
{
|
||||
char localAddrString[64];
|
||||
char localAddrOverrideString[64];
|
||||
char remoteAddrString[64];
|
||||
|
||||
RtlIpv6AddressToStringA(LocalAddress, localAddrString);
|
||||
RtlIpv6AddressToStringA(LocalAddressOverride, localAddrOverrideString);
|
||||
RtlIpv6AddressToStringA(RemoteAddress, remoteAddrString);
|
||||
|
||||
DbgPrint
|
||||
(
|
||||
"[CONN][%p] Rewriting connection on [%s]:%d as [%s]:%d -> [%s]:%d\n",
|
||||
ProcessId,
|
||||
localAddrString,
|
||||
LocalPort,
|
||||
localAddrOverrideString,
|
||||
LocalPort,
|
||||
remoteAddrString,
|
||||
RemotePort
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
LogPermitConnection
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const IN_ADDR *LocalAddress,
|
||||
USHORT LocalPort,
|
||||
const IN_ADDR *RemoteAddress,
|
||||
USHORT RemotePort,
|
||||
bool outgoing
|
||||
)
|
||||
{
|
||||
char localAddrString[32];
|
||||
char remoteAddrString[32];
|
||||
|
||||
RtlIpv4AddressToStringA(LocalAddress, localAddrString);
|
||||
RtlIpv4AddressToStringA(RemoteAddress, remoteAddrString);
|
||||
|
||||
const auto direction = outgoing
|
||||
? "->"
|
||||
: "<-";
|
||||
|
||||
DbgPrint
|
||||
(
|
||||
"[PRMT][%p] %s:%d %s %s:%d\n",
|
||||
ProcessId,
|
||||
localAddrString,
|
||||
LocalPort,
|
||||
direction,
|
||||
remoteAddrString,
|
||||
RemotePort
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
LogPermitConnection
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const IN6_ADDR *LocalAddress,
|
||||
USHORT LocalPort,
|
||||
const IN6_ADDR *RemoteAddress,
|
||||
USHORT RemotePort,
|
||||
bool outgoing
|
||||
)
|
||||
{
|
||||
char localAddrString[64];
|
||||
char remoteAddrString[64];
|
||||
|
||||
RtlIpv6AddressToStringA(LocalAddress, localAddrString);
|
||||
RtlIpv6AddressToStringA(RemoteAddress, remoteAddrString);
|
||||
|
||||
const auto direction = outgoing
|
||||
? "->"
|
||||
: "<-";
|
||||
|
||||
DbgPrint
|
||||
(
|
||||
"[PRMT][%p] [%s]:%d %s [%s]:%d\n",
|
||||
ProcessId,
|
||||
localAddrString,
|
||||
LocalPort,
|
||||
direction,
|
||||
remoteAddrString,
|
||||
RemotePort
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
LogBlockConnection
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const IN_ADDR *LocalAddress,
|
||||
USHORT LocalPort,
|
||||
const IN_ADDR *RemoteAddress,
|
||||
USHORT RemotePort,
|
||||
bool outgoing
|
||||
)
|
||||
{
|
||||
char localAddrString[32];
|
||||
char remoteAddrString[32];
|
||||
|
||||
RtlIpv4AddressToStringA(LocalAddress, localAddrString);
|
||||
RtlIpv4AddressToStringA(RemoteAddress, remoteAddrString);
|
||||
|
||||
const auto direction = outgoing
|
||||
? "->"
|
||||
: "<-";
|
||||
|
||||
DbgPrint
|
||||
(
|
||||
"[BLCK][%p] %s:%d %s %s:%d\n",
|
||||
ProcessId,
|
||||
localAddrString,
|
||||
LocalPort,
|
||||
direction,
|
||||
remoteAddrString,
|
||||
RemotePort
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
LogBlockConnection
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const IN6_ADDR *LocalAddress,
|
||||
USHORT LocalPort,
|
||||
const IN6_ADDR *RemoteAddress,
|
||||
USHORT RemotePort,
|
||||
bool outgoing
|
||||
)
|
||||
{
|
||||
char localAddrString[64];
|
||||
char remoteAddrString[64];
|
||||
|
||||
RtlIpv6AddressToStringA(LocalAddress, localAddrString);
|
||||
RtlIpv6AddressToStringA(RemoteAddress, remoteAddrString);
|
||||
|
||||
const auto direction = outgoing
|
||||
? "->"
|
||||
: "<-";
|
||||
|
||||
DbgPrint
|
||||
(
|
||||
"[BLCK][%p] [%s]:%d %s [%s]:%d\n",
|
||||
ProcessId,
|
||||
localAddrString,
|
||||
LocalPort,
|
||||
direction,
|
||||
remoteAddrString,
|
||||
RemotePort
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
LogActivatedSplittingMode
|
||||
(
|
||||
SPLITTING_MODE Mode
|
||||
)
|
||||
{
|
||||
//
|
||||
// This only works because SPLITTING_MODE::MODE_1 is defined as 1, etc.
|
||||
//
|
||||
|
||||
NT_ASSERT
|
||||
(
|
||||
static_cast<SIZE_T>(SPLITTING_MODE::MODE_1) == 1
|
||||
&& static_cast<SIZE_T>(SPLITTING_MODE::MODE_9) == 9
|
||||
);
|
||||
|
||||
DbgPrint("Activated splitting mode: %d\n", Mode);
|
||||
}
|
||||
|
||||
}; // namespace firewall
|
||||
117
src/firewall/logging.h
Normal file
117
src/firewall/logging.h
Normal file
@@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#include "wfp.h"
|
||||
#include "mode.h"
|
||||
|
||||
namespace firewall
|
||||
{
|
||||
|
||||
void
|
||||
LogBindRedirect
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const SOCKADDR_IN *Target,
|
||||
const IN_ADDR *Override
|
||||
);
|
||||
|
||||
void
|
||||
LogBindRedirect
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const SOCKADDR_IN6 *Target,
|
||||
const IN6_ADDR *Override
|
||||
);
|
||||
|
||||
void
|
||||
LogConnectRedirectPass
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const IN_ADDR *LocalAddress,
|
||||
USHORT LocalPort,
|
||||
const IN_ADDR *RemoteAddress,
|
||||
USHORT RemotePort
|
||||
);
|
||||
|
||||
void
|
||||
LogConnectRedirectPass
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const IN6_ADDR *LocalAddress,
|
||||
USHORT LocalPort,
|
||||
const IN6_ADDR *RemoteAddress,
|
||||
USHORT RemotePort
|
||||
);
|
||||
|
||||
void
|
||||
LogConnectRedirect
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const IN_ADDR *LocalAddress,
|
||||
USHORT LocalPort,
|
||||
const IN_ADDR *LocalAddressOverride,
|
||||
const IN_ADDR *RemoteAddress,
|
||||
USHORT RemotePort
|
||||
);
|
||||
|
||||
void
|
||||
LogConnectRedirect
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const IN6_ADDR *LocalAddress,
|
||||
USHORT LocalPort,
|
||||
const IN6_ADDR *LocalAddressOverride,
|
||||
const IN6_ADDR *RemoteAddress,
|
||||
USHORT RemotePort
|
||||
);
|
||||
|
||||
void
|
||||
LogPermitConnection
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const IN_ADDR *LocalAddress,
|
||||
USHORT LocalPort,
|
||||
const IN_ADDR *RemoteAddress,
|
||||
USHORT RemotePort,
|
||||
bool outgoing
|
||||
);
|
||||
|
||||
void
|
||||
LogPermitConnection
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const IN6_ADDR *LocalAddress,
|
||||
USHORT LocalPort,
|
||||
const IN6_ADDR *RemoteAddress,
|
||||
USHORT RemotePort,
|
||||
bool outgoing
|
||||
);
|
||||
|
||||
void
|
||||
LogBlockConnection
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const IN_ADDR *LocalAddress,
|
||||
USHORT LocalPort,
|
||||
const IN_ADDR *RemoteAddress,
|
||||
USHORT RemotePort,
|
||||
bool outgoing
|
||||
);
|
||||
|
||||
void
|
||||
LogBlockConnection
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
const IN6_ADDR *LocalAddress,
|
||||
USHORT LocalPort,
|
||||
const IN6_ADDR *RemoteAddress,
|
||||
USHORT RemotePort,
|
||||
bool outgoing
|
||||
);
|
||||
|
||||
void
|
||||
LogActivatedSplittingMode
|
||||
(
|
||||
SPLITTING_MODE Mode
|
||||
);
|
||||
|
||||
}; // namespace firewall
|
||||
535
src/firewall/pending.cpp
Normal file
535
src/firewall/pending.cpp
Normal file
@@ -0,0 +1,535 @@
|
||||
#include "pending.h"
|
||||
#include "classify.h"
|
||||
#include "../util.h"
|
||||
|
||||
#include "../trace.h"
|
||||
#include "pending.tmh"
|
||||
|
||||
namespace firewall::pending
|
||||
{
|
||||
|
||||
struct PENDED_CLASSIFICATION
|
||||
{
|
||||
LIST_ENTRY ListEntry;
|
||||
|
||||
// Process that's making the request.
|
||||
HANDLE ProcessId;
|
||||
|
||||
// Timestamp when record was created.
|
||||
ULONGLONG Timestamp;
|
||||
|
||||
// Handle used to trigger re-auth or resume processing.
|
||||
UINT64 ClassifyHandle;
|
||||
|
||||
// Result of classification is recorded here.
|
||||
FWPS_CLASSIFY_OUT0 ClassifyOut;
|
||||
|
||||
// Filter that triggered the classification.
|
||||
UINT64 FilterId;
|
||||
|
||||
// Layer in which classification is occurring.
|
||||
UINT16 LayerId;
|
||||
};
|
||||
|
||||
struct CONTEXT
|
||||
{
|
||||
procbroker::CONTEXT *ProcessEventBroker;
|
||||
|
||||
WDFSPINLOCK Lock;
|
||||
|
||||
// PENDED_CLASSIFICATION
|
||||
LIST_ENTRY Classifications;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const ULONGLONG RECORD_MAX_LIFETIME_MS = 10000;
|
||||
|
||||
bool
|
||||
AssertCompatibleLayer
|
||||
(
|
||||
UINT16 LayerId
|
||||
)
|
||||
{
|
||||
NT_ASSERT
|
||||
(
|
||||
LayerId == FWPS_LAYER_ALE_BIND_REDIRECT_V4
|
||||
|| LayerId == FWPS_LAYER_ALE_BIND_REDIRECT_V6
|
||||
|| LayerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V4
|
||||
|| LayerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V6
|
||||
);
|
||||
|
||||
if (LayerId == FWPS_LAYER_ALE_BIND_REDIRECT_V4
|
||||
|| LayerId == FWPS_LAYER_ALE_BIND_REDIRECT_V6
|
||||
|| LayerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V4
|
||||
|| LayerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V6)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DbgPrint("Invalid layer id %d specified in call to 'pending' module\n", LayerId);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const char*
|
||||
LayerToString
|
||||
(
|
||||
UINT16 LayerId
|
||||
)
|
||||
{
|
||||
char *string = "undefined";
|
||||
|
||||
switch (LayerId)
|
||||
{
|
||||
case FWPS_LAYER_ALE_BIND_REDIRECT_V4:
|
||||
{
|
||||
string = "FWPS_LAYER_ALE_BIND_REDIRECT_V4";
|
||||
break;
|
||||
}
|
||||
case FWPS_LAYER_ALE_BIND_REDIRECT_V6:
|
||||
{
|
||||
string = "FWPS_LAYER_ALE_BIND_REDIRECT_V6";
|
||||
break;
|
||||
}
|
||||
case FWPS_LAYER_ALE_CONNECT_REDIRECT_V4:
|
||||
{
|
||||
string = "FWPS_LAYER_ALE_CONNECT_REDIRECT_V4";
|
||||
break;
|
||||
}
|
||||
case FWPS_LAYER_ALE_CONNECT_REDIRECT_V6:
|
||||
{
|
||||
string = "FWPS_LAYER_ALE_CONNECT_REDIRECT_V6";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
FailRequest
|
||||
(
|
||||
UINT64 FilterId,
|
||||
UINT16 LayerId,
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut,
|
||||
UINT64 ClassifyHandle
|
||||
)
|
||||
{
|
||||
//
|
||||
// There doesn't seem to be any support in WFP for blocking requests in the redirect layers.
|
||||
// Specifying `FWP_ACTION_BLOCK` will just resume request processing.
|
||||
// So the best we can do is rewrite the request to do as little harm as possible.
|
||||
//
|
||||
|
||||
PVOID requestData = NULL;
|
||||
|
||||
auto status = FwpsAcquireWritableLayerDataPointer0
|
||||
(
|
||||
ClassifyHandle,
|
||||
FilterId,
|
||||
0,
|
||||
&requestData,
|
||||
ClassifyOut
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DbgPrint("FwpsAcquireWritableLayerDataPointer0() failed\n");
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
switch (LayerId)
|
||||
{
|
||||
case FWPS_LAYER_ALE_BIND_REDIRECT_V4:
|
||||
case FWPS_LAYER_ALE_BIND_REDIRECT_V6:
|
||||
{
|
||||
auto bindRequest = reinterpret_cast<FWPS_BIND_REQUEST0*>(requestData);
|
||||
|
||||
//
|
||||
// This sets the port to 0, as well.
|
||||
//
|
||||
INETADDR_SETLOOPBACK((PSOCKADDR)&(bindRequest->localAddressAndPort));
|
||||
|
||||
break;
|
||||
}
|
||||
case FWPS_LAYER_ALE_CONNECT_REDIRECT_V4:
|
||||
case FWPS_LAYER_ALE_CONNECT_REDIRECT_V6:
|
||||
{
|
||||
auto connectRequest = reinterpret_cast<FWPS_CONNECT_REQUEST0*>(requestData);
|
||||
|
||||
INETADDR_SETLOOPBACK((PSOCKADDR)&(connectRequest->localAddressAndPort));
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
ClassificationApplyHardPermit(ClassifyOut);
|
||||
FwpsApplyModifiedLayerData0(ClassifyHandle, requestData, 0);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void
|
||||
ReauthPendedRequest
|
||||
(
|
||||
PENDED_CLASSIFICATION *Record
|
||||
)
|
||||
{
|
||||
DbgPrint
|
||||
(
|
||||
"Requesting re-auth for pended request in layer %s for process %p\n",
|
||||
LayerToString(Record->LayerId),
|
||||
Record->ProcessId
|
||||
);
|
||||
|
||||
FwpsCompleteClassify0(Record->ClassifyHandle, 0, NULL);
|
||||
FwpsReleaseClassifyHandle0(Record->ClassifyHandle);
|
||||
|
||||
ExFreePoolWithTag(Record, ST_POOL_TAG);
|
||||
}
|
||||
|
||||
void
|
||||
FailPendedRequest
|
||||
(
|
||||
PENDED_CLASSIFICATION *Record,
|
||||
bool ReauthOnFailure = true
|
||||
)
|
||||
{
|
||||
DbgPrint
|
||||
(
|
||||
"Failing pended request in layer %s for process %p\n",
|
||||
LayerToString(Record->LayerId),
|
||||
Record->ProcessId
|
||||
);
|
||||
|
||||
const auto status = FailRequest
|
||||
(
|
||||
Record->FilterId,
|
||||
Record->LayerId,
|
||||
&Record->ClassifyOut,
|
||||
Record->ClassifyHandle
|
||||
);
|
||||
|
||||
if (NT_SUCCESS(status))
|
||||
{
|
||||
FwpsCompleteClassify0(Record->ClassifyHandle, 0, &Record->ClassifyOut);
|
||||
FwpsReleaseClassifyHandle0(Record->ClassifyHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
DbgPrint("FailRequest() failed 0x%X\n", status);
|
||||
|
||||
if (ReauthOnFailure)
|
||||
{
|
||||
ReauthPendedRequest(Record);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ExFreePoolWithTag(Record, ST_POOL_TAG);
|
||||
}
|
||||
|
||||
//
|
||||
// FailAllPendedRequests()
|
||||
//
|
||||
// This function is used during tear down.
|
||||
// So we don't have the luxury of re-authing requests that can't be failed.
|
||||
//
|
||||
void
|
||||
FailAllPendedRequests
|
||||
(
|
||||
CONTEXT *Context
|
||||
)
|
||||
{
|
||||
for (auto rawRecord = Context->Classifications.Flink;
|
||||
rawRecord != &Context->Classifications;
|
||||
/* no post-condition */)
|
||||
{
|
||||
auto record = (PENDED_CLASSIFICATION*)rawRecord;
|
||||
|
||||
rawRecord = rawRecord->Flink;
|
||||
|
||||
RemoveEntryList(&record->ListEntry);
|
||||
|
||||
FailPendedRequest(record, false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HandleProcessEvent
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
bool Arriving,
|
||||
void *Context
|
||||
)
|
||||
{
|
||||
auto context = (CONTEXT*)Context;
|
||||
|
||||
auto timeNow = KeQueryInterruptTime();
|
||||
|
||||
static const ULONGLONG MS_TO_100NS_FACTOR = 10000;
|
||||
|
||||
auto maxAge = RECORD_MAX_LIFETIME_MS * MS_TO_100NS_FACTOR;
|
||||
|
||||
//
|
||||
// Iterate over all pended bind requests.
|
||||
//
|
||||
// Fail all requests that are too old.
|
||||
// Re-auth all requests that belong to the arriving process.
|
||||
//
|
||||
|
||||
WdfSpinLockAcquire(context->Lock);
|
||||
|
||||
for (auto rawRecord = context->Classifications.Flink;
|
||||
rawRecord != &context->Classifications;
|
||||
/* no post-condition */)
|
||||
{
|
||||
auto record = (PENDED_CLASSIFICATION*)rawRecord;
|
||||
|
||||
rawRecord = rawRecord->Flink;
|
||||
|
||||
auto timeDelta = timeNow - record->Timestamp;
|
||||
|
||||
if (timeDelta > maxAge)
|
||||
{
|
||||
RemoveEntryList(&record->ListEntry);
|
||||
|
||||
FailPendedRequest(record);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (record->ProcessId != ProcessId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
RemoveEntryList(&record->ListEntry);
|
||||
|
||||
if (Arriving)
|
||||
{
|
||||
ReauthPendedRequest(record);
|
||||
}
|
||||
else
|
||||
{
|
||||
FailPendedRequest(record, false);
|
||||
}
|
||||
}
|
||||
|
||||
WdfSpinLockRelease(context->Lock);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
NTSTATUS
|
||||
Initialize
|
||||
(
|
||||
CONTEXT **Context,
|
||||
procbroker::CONTEXT *ProcessEventBroker
|
||||
)
|
||||
{
|
||||
auto context = (CONTEXT*)ExAllocatePoolWithTag(NonPagedPool, sizeof(CONTEXT), ST_POOL_TAG);
|
||||
|
||||
if (context == NULL)
|
||||
{
|
||||
DbgPrint("ExAllocatePoolWithTag() failed\n");
|
||||
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
RtlZeroMemory(context, sizeof(*context));
|
||||
|
||||
context->ProcessEventBroker = ProcessEventBroker;
|
||||
|
||||
auto status = WdfSpinLockCreate(WDF_NO_OBJECT_ATTRIBUTES, &context->Lock);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DbgPrint("WdfSpinLockCreate() failed\n");
|
||||
|
||||
goto Abort;
|
||||
}
|
||||
|
||||
InitializeListHead(&context->Classifications);
|
||||
|
||||
//
|
||||
// Everything is initialized.
|
||||
// Register with process event broker.
|
||||
//
|
||||
|
||||
status = procbroker::Subscribe(ProcessEventBroker, HandleProcessEvent, context);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DbgPrint("Could not register with process event broker\n");
|
||||
|
||||
goto Abort_delete_lock;
|
||||
}
|
||||
|
||||
*Context = context;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
Abort_delete_lock:
|
||||
|
||||
WdfObjectDelete(context->Lock);
|
||||
|
||||
Abort:
|
||||
|
||||
ExFreePoolWithTag(context, ST_POOL_TAG);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void
|
||||
TearDown
|
||||
(
|
||||
CONTEXT **Context
|
||||
)
|
||||
{
|
||||
auto context = *Context;
|
||||
|
||||
*Context = NULL;
|
||||
|
||||
procbroker::CancelSubscription(context->ProcessEventBroker, HandleProcessEvent);
|
||||
|
||||
FailAllPendedRequests(context);
|
||||
|
||||
WdfObjectDelete(context->Lock);
|
||||
|
||||
ExFreePoolWithTag(context, ST_POOL_TAG);
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
PendRequest
|
||||
(
|
||||
CONTEXT *Context,
|
||||
HANDLE ProcessId,
|
||||
void *ClassifyContext,
|
||||
UINT64 FilterId,
|
||||
UINT16 LayerId,
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
)
|
||||
{
|
||||
if (!AssertCompatibleLayer(LayerId))
|
||||
{
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
DbgPrint
|
||||
(
|
||||
"Pending request in layer %s for process %p\n",
|
||||
LayerToString(LayerId),
|
||||
ProcessId
|
||||
);
|
||||
|
||||
auto record = (PENDED_CLASSIFICATION*)
|
||||
ExAllocatePoolWithTag(NonPagedPool, sizeof(PENDED_CLASSIFICATION), ST_POOL_TAG);
|
||||
|
||||
if (record == NULL)
|
||||
{
|
||||
DbgPrint("ExAllocatePoolWithTag() failed\n");
|
||||
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
UINT64 classifyHandle;
|
||||
|
||||
auto status = FwpsAcquireClassifyHandle0(ClassifyContext, 0, &classifyHandle);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DbgPrint("FwpsAcquireClassifyHandle0() failed\n");
|
||||
|
||||
goto Abort;
|
||||
}
|
||||
|
||||
status = FwpsPendClassify0(classifyHandle, FilterId, 0, ClassifyOut);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DbgPrint("FwpsPendClassify0() failed\n");
|
||||
|
||||
FwpsReleaseClassifyHandle0(classifyHandle);
|
||||
|
||||
goto Abort;
|
||||
}
|
||||
|
||||
record->ProcessId = ProcessId;
|
||||
record->Timestamp = KeQueryInterruptTime();
|
||||
record->ClassifyHandle = classifyHandle;
|
||||
record->ClassifyOut = *ClassifyOut;
|
||||
record->LayerId = LayerId;
|
||||
record->FilterId = FilterId;
|
||||
|
||||
WdfSpinLockAcquire(Context->Lock);
|
||||
|
||||
InsertTailList(&Context->Classifications, &record->ListEntry);
|
||||
|
||||
WdfSpinLockRelease(Context->Lock);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
Abort:
|
||||
|
||||
ExFreePoolWithTag(record, ST_POOL_TAG);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
FailRequest
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
void *ClassifyContext,
|
||||
UINT64 FilterId,
|
||||
UINT16 LayerId,
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
)
|
||||
{
|
||||
if (!AssertCompatibleLayer(LayerId))
|
||||
{
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
DbgPrint
|
||||
(
|
||||
"Failing request in layer %s for process %p\n",
|
||||
LayerToString(LayerId),
|
||||
ProcessId
|
||||
);
|
||||
|
||||
UINT64 classifyHandle = 0;
|
||||
|
||||
auto status = FwpsAcquireClassifyHandle0
|
||||
(
|
||||
ClassifyContext,
|
||||
0,
|
||||
&classifyHandle
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DbgPrint("FwpsAcquireClassifyHandle0() failed\n");
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
status = FailRequest(FilterId, LayerId, ClassifyOut, classifyHandle);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
FwpsReleaseClassifyHandle0(classifyHandle);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace firewall::pending
|
||||
52
src/firewall/pending.h
Normal file
52
src/firewall/pending.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "wfp.h"
|
||||
#include <wdf.h>
|
||||
#include "../procbroker/procbroker.h"
|
||||
|
||||
//
|
||||
// This module is currently used for pending redirection classifications,
|
||||
// but could plausibly be extended to handle other types of classifications,
|
||||
// as and when the need arises.
|
||||
//
|
||||
|
||||
namespace firewall::pending
|
||||
{
|
||||
|
||||
struct CONTEXT;
|
||||
|
||||
NTSTATUS
|
||||
Initialize
|
||||
(
|
||||
CONTEXT **Context,
|
||||
procbroker::CONTEXT *ProcessEventBroker
|
||||
);
|
||||
|
||||
void
|
||||
TearDown
|
||||
(
|
||||
CONTEXT **Context
|
||||
);
|
||||
|
||||
NTSTATUS
|
||||
PendRequest
|
||||
(
|
||||
CONTEXT *Context,
|
||||
HANDLE ProcessId,
|
||||
void *ClassifyContext,
|
||||
UINT64 FilterId,
|
||||
UINT16 LayerId,
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
);
|
||||
|
||||
NTSTATUS
|
||||
FailRequest
|
||||
(
|
||||
HANDLE ProcessId,
|
||||
void *ClassifyContext,
|
||||
UINT64 FilterId,
|
||||
UINT16 LayerId,
|
||||
FWPS_CLASSIFY_OUT0 *ClassifyOut
|
||||
);
|
||||
|
||||
} // namespace firewall::pending
|
||||
@@ -204,11 +204,13 @@
|
||||
<ClCompile Include="eventing\builder.cpp" />
|
||||
<ClCompile Include="eventing\eventing.cpp" />
|
||||
<ClCompile Include="firewall\appfilters.cpp" />
|
||||
<ClCompile Include="firewall\asyncbind.cpp" />
|
||||
<ClCompile Include="firewall\callouts.cpp" />
|
||||
<ClCompile Include="firewall\classify.cpp" />
|
||||
<ClCompile Include="firewall\filters.cpp" />
|
||||
<ClCompile Include="firewall\firewall.cpp" />
|
||||
<ClCompile Include="firewall\logging.cpp" />
|
||||
<ClCompile Include="firewall\mode.cpp" />
|
||||
<ClCompile Include="firewall\pending.cpp" />
|
||||
<ClCompile Include="ioctl.cpp" />
|
||||
<ClCompile Include="ipaddr.cpp" />
|
||||
<ClCompile Include="procbroker\procbroker.cpp" />
|
||||
@@ -235,14 +237,16 @@
|
||||
<ClInclude Include="eventing\context.h" />
|
||||
<ClInclude Include="eventing\eventing.h" />
|
||||
<ClInclude Include="firewall\appfilters.h" />
|
||||
<ClInclude Include="firewall\asyncbind.h" />
|
||||
<ClInclude Include="firewall\callouts.h" />
|
||||
<ClInclude Include="firewall\classify.h" />
|
||||
<ClInclude Include="firewall\constants.h" />
|
||||
<ClInclude Include="firewall\context.h" />
|
||||
<ClInclude Include="firewall\filters.h" />
|
||||
<ClInclude Include="firewall\firewall.h" />
|
||||
<ClInclude Include="firewall\identifiers.h" />
|
||||
<ClInclude Include="firewall\logging.h" />
|
||||
<ClInclude Include="firewall\mode.h" />
|
||||
<ClInclude Include="firewall\pending.h" />
|
||||
<ClInclude Include="firewall\wfp.h" />
|
||||
<ClInclude Include="ioctl.h" />
|
||||
<ClInclude Include="ipaddr.h" />
|
||||
|
||||
@@ -33,9 +33,6 @@
|
||||
<ClCompile Include="procbroker\procbroker.cpp">
|
||||
<Filter>procbroker</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="firewall\asyncbind.cpp">
|
||||
<Filter>firewall</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="firewall\mode.cpp">
|
||||
<Filter>firewall</Filter>
|
||||
</ClCompile>
|
||||
@@ -45,6 +42,15 @@
|
||||
<ClCompile Include="firewall\appfilters.cpp">
|
||||
<Filter>firewall</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="firewall\logging.cpp">
|
||||
<Filter>firewall</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="firewall\pending.cpp">
|
||||
<Filter>firewall</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="firewall\classify.cpp">
|
||||
<Filter>firewall</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Inf Include="mullvad-split-tunnel.inf" />
|
||||
@@ -128,9 +134,6 @@
|
||||
<ClInclude Include="procbroker\procbroker.h">
|
||||
<Filter>procbroker</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="firewall\asyncbind.h">
|
||||
<Filter>firewall</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="procmgmt\callbacks.h">
|
||||
<Filter>procmgmt</Filter>
|
||||
</ClInclude>
|
||||
@@ -147,6 +150,15 @@
|
||||
<Filter>firewall</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="trace.h" />
|
||||
<ClInclude Include="firewall\logging.h">
|
||||
<Filter>firewall</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="firewall\pending.h">
|
||||
<Filter>firewall</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="firewall\classify.h">
|
||||
<Filter>firewall</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="firewall">
|
||||
|
||||
Reference in New Issue
Block a user