Overhaul pending of classifications and make implementation compatible with connect-redirect

This commit is contained in:
Odd Stranne
2021-05-24 12:19:38 +02:00
parent 89080c5d14
commit eed015cd6e
9 changed files with 673 additions and 427 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -2,7 +2,7 @@
#include "firewall.h"
#include "context.h"
#include "identifiers.h"
#include "asyncbind.h"
#include "pending.h"
#include "callouts.h"
#include "logging.h"
#include "../util.h"
@@ -315,26 +315,30 @@ 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
);
@@ -343,13 +347,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
);
}
@@ -453,11 +456,12 @@ CalloutClassifyBind
}
case PROCESS_SPLIT_VERDICT::UNKNOWN:
{
ClassifyUnknownBind
PendClassification
(
context,
context->PendedClassifications,
HANDLE(MetaValues->processId),
Filter->filterId,
FixedValues->layerId,
ClassifyContext,
ClassifyOut
);
@@ -513,12 +517,12 @@ LocalAddress(const IN6_ADDR *addr)
void
RewriteConnection
(
CONTEXT *Context,
const FWPS_INCOMING_VALUES0 *FixedValues,
const FWPS_INCOMING_METADATA_VALUES0 *MetaValues,
UINT64 FilterId,
const void *ClassifyContext,
FWPS_CLASSIFY_OUT0 *ClassifyOut,
CONTEXT *Context
FWPS_CLASSIFY_OUT0 *ClassifyOut
)
{
UNREFERENCED_PARAMETER(MetaValues);
@@ -781,18 +785,37 @@ CalloutClassifyConnect
const auto verdict = callbacks.QueryProcess(HANDLE(MetaValues->processId), callbacks.Context);
if (verdict == PROCESS_SPLIT_VERDICT::DO_SPLIT)
switch (verdict)
{
RewriteConnection
(
FixedValues,
MetaValues,
Filter->filterId,
ClassifyContext,
ClassifyOut,
context
);
}
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
);
break;
}
};
}
bool IsAleReauthorize

View File

@@ -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"
@@ -18,33 +19,6 @@ struct IP_ADDRESSES_MGMT
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.
@@ -81,9 +55,7 @@ struct CONTEXT
IP_ADDRESSES_MGMT IpAddresses;
PENDED_BIND_MGMT PendedBinds;
procbroker::CONTEXT *ProcessEventBroker;
pending::CONTEXT *PendedClassifications;
eventing::CONTEXT *Eventing;

View File

@@ -5,7 +5,7 @@
#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"
@@ -982,10 +982,7 @@ Initialize
RtlZeroMemory(context, sizeof(*context));
InitializeListHead(&context->PendedBinds.Records);
context->Callbacks = *Callbacks;
context->ProcessEventBroker = ProcessEventBroker;
context->Eventing = Eventing;
auto status = WdfSpinLockCreate(WDF_NO_OBJECT_ATTRIBUTES, &context->IpAddresses.Lock);
@@ -999,17 +996,6 @@ Initialize
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))
@@ -1018,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);
@@ -1049,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:
{
@@ -1079,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);
@@ -1127,11 +1119,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);

537
src/firewall/pending.cpp Normal file
View File

@@ -0,0 +1,537 @@
#include "pending.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;
}
};
// TODO: replace with call to reusable function.
ClassifyOut->actionType = FWP_ACTION_PERMIT;
ClassifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
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
View 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

View File

@@ -204,12 +204,12 @@
<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\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" />
@@ -236,7 +236,6 @@
<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\constants.h" />
<ClInclude Include="firewall\context.h" />
@@ -245,6 +244,7 @@
<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" />

View File

@@ -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>
@@ -48,6 +45,9 @@
<ClCompile Include="firewall\logging.cpp">
<Filter>firewall</Filter>
</ClCompile>
<ClCompile Include="firewall\pending.cpp">
<Filter>firewall</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Inf Include="mullvad-split-tunnel.inf" />
@@ -131,9 +131,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>
@@ -153,6 +150,9 @@
<ClInclude Include="firewall\logging.h">
<Filter>firewall</Filter>
</ClInclude>
<ClInclude Include="firewall\pending.h">
<Filter>firewall</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="firewall">