summaryrefslogtreecommitdiff
path: root/src/Agent.Windows/Network
diff options
context:
space:
mode:
Diffstat (limited to 'src/Agent.Windows/Network')
-rw-r--r--src/Agent.Windows/Network/LoopbackExemption.cs123
-rw-r--r--src/Agent.Windows/Network/NetworkAllowanceSnapshot.cs27
-rw-r--r--src/Agent.Windows/Network/TaskNetworkPolicy.cs83
-rw-r--r--src/Agent.Windows/Network/WfpDynamicSession.cs208
4 files changed, 68 insertions, 373 deletions
diff --git a/src/Agent.Windows/Network/LoopbackExemption.cs b/src/Agent.Windows/Network/LoopbackExemption.cs
deleted file mode 100644
index a3c68ba..0000000
--- a/src/Agent.Windows/Network/LoopbackExemption.cs
+++ /dev/null
@@ -1,123 +0,0 @@
-using System.ComponentModel;
-using System.Runtime.InteropServices;
-using System.Security.Principal;
-using Windows.Win32;
-using Windows.Win32.Foundation;
-using Windows.Win32.Security;
-using Windows.Win32.System.Memory;
-
-namespace Agent.Windows.Network;
-
-internal static unsafe class LoopbackExemption
-{
- private const string MutexName = "Local\\AgentSandboxMcpLoopbackExemption";
-
- public static bool Contains(SecurityIdentifier sid)
- {
- using MutexLease mutex = AcquireMutex();
- return Read().Any(item => item.Sid.Equals(sid));
- }
-
- public static bool Ensure(SecurityIdentifier sid)
- {
- using MutexLease mutex = AcquireMutex();
- List<Entry> entries = Read();
- if (entries.Any(item => item.Sid.Equals(sid))) return false;
- entries.Add(new(sid, 0));
- Write(entries);
- return true;
- }
-
- public static void Remove(SecurityIdentifier sid)
- {
- using MutexLease mutex = AcquireMutex();
- List<Entry> entries = Read();
- if (entries.RemoveAll(item => item.Sid.Equals(sid)) == 0) return;
- Write(entries);
- }
-
- private static MutexLease AcquireMutex()
- {
- var mutex = new Mutex(false, MutexName);
- try
- {
- try
- {
- if (!mutex.WaitOne(TimeSpan.FromSeconds(30))) throw new TimeoutException("Timed out waiting for the loopback exemption lock.");
- }
- catch (AbandonedMutexException)
- {
- // Ownership is granted to this thread when the prior owner died.
- }
- return new(mutex);
- }
- catch
- {
- mutex.Dispose();
- throw;
- }
- }
-
- private static List<Entry> Read()
- {
- uint result = PInvoke.NetworkIsolationGetAppContainerConfig(out uint count, out SID_AND_ATTRIBUTES* native);
- ThrowIfError(result, "NetworkIsolationGetAppContainerConfig");
- try
- {
- var entries = new List<Entry>(checked((int)count));
- for (uint index = 0; index < count; index++)
- {
- SID_AND_ATTRIBUTES item = native[index];
- entries.Add(new(new SecurityIdentifier((nint)item.Sid.Value), item.Attributes));
- }
- return entries;
- }
- finally
- {
- HANDLE heap = PInvoke.GetProcessHeap();
- for (uint index = 0; index < count; index++)
- if (native[index].Sid.Value is not null) _ = PInvoke.HeapFree(heap, HEAP_FLAGS.HEAP_NONE, native[index].Sid.Value);
- if (native is not null) _ = PInvoke.HeapFree(heap, HEAP_FLAGS.HEAP_NONE, native);
- }
- }
-
- private static void Write(IReadOnlyList<Entry> entries)
- {
- var native = new SID_AND_ATTRIBUTES[entries.Count];
- var allocations = new List<nint>(entries.Count);
- try
- {
- for (int index = 0; index < entries.Count; index++)
- {
- byte[] bytes = new byte[entries[index].Sid.BinaryLength];
- entries[index].Sid.GetBinaryForm(bytes, 0);
- void* allocation = NativeMemory.Alloc((nuint)bytes.Length);
- Marshal.Copy(bytes, 0, (nint)allocation, bytes.Length);
- allocations.Add((nint)allocation);
- native[index].Sid = new PSID(allocation);
- native[index].Attributes = entries[index].Attributes;
- }
- ThrowIfError(PInvoke.NetworkIsolationSetAppContainerConfig(native), "NetworkIsolationSetAppContainerConfig");
- }
- finally
- {
- foreach (nint allocation in allocations) NativeMemory.Free((void*)allocation);
- }
- }
-
- private static void ThrowIfError(uint result, string operation)
- {
- if (result != 0) throw new Win32Exception(unchecked((int)result), $"{operation} failed with Win32 error 0x{result:X8}.");
- }
-
- private sealed record Entry(SecurityIdentifier Sid, uint Attributes);
-
- private sealed class MutexLease(Mutex mutex) : IDisposable
- {
- public void Dispose()
- {
- mutex.ReleaseMutex();
- mutex.Dispose();
- }
- }
-}
diff --git a/src/Agent.Windows/Network/NetworkAllowanceSnapshot.cs b/src/Agent.Windows/Network/NetworkAllowanceSnapshot.cs
new file mode 100644
index 0000000..5feb457
--- /dev/null
+++ b/src/Agent.Windows/Network/NetworkAllowanceSnapshot.cs
@@ -0,0 +1,27 @@
+using System.Net;
+
+namespace Agent.Windows.Network;
+
+public sealed record NetworkEndpoint(IPAddress Address, ushort Port);
+
+public static class NetworkAllowanceSnapshot
+{
+ public static async Task<IReadOnlyList<NetworkEndpoint>> ResolveAsync(IEnumerable<(string Host, IReadOnlyList<int> Ports)> allowances,
+ CancellationToken cancellationToken)
+ {
+ var endpoints = new HashSet<NetworkEndpoint>();
+ foreach ((string host, IReadOnlyList<int> ports) in allowances)
+ {
+ IPAddress[] addresses = await Dns.GetHostAddressesAsync(host, cancellationToken).ConfigureAwait(false);
+ foreach (int port in ports)
+ {
+ if (port is < 1 or > 65535) throw new ArgumentOutOfRangeException(nameof(allowances), $"Invalid network port {port}.");
+ foreach (IPAddress address in addresses.Where(static item => item.AddressFamily is
+ System.Net.Sockets.AddressFamily.InterNetwork or System.Net.Sockets.AddressFamily.InterNetworkV6))
+ endpoints.Add(new(address, (ushort)port));
+ }
+ }
+ return endpoints.OrderBy(static item => item.Address.ToString(), StringComparer.Ordinal)
+ .ThenBy(static item => item.Port).ToArray();
+ }
+}
diff --git a/src/Agent.Windows/Network/TaskNetworkPolicy.cs b/src/Agent.Windows/Network/TaskNetworkPolicy.cs
index 9f78b3d..4bcd8b2 100644
--- a/src/Agent.Windows/Network/TaskNetworkPolicy.cs
+++ b/src/Agent.Windows/Network/TaskNetworkPolicy.cs
@@ -1,79 +1,78 @@
+using System.Net;
using System.Security.Principal;
+using Agent.Admin.Client;
using Agent.Windows.Recovery;
using Agent.Windows.Security;
namespace Agent.Windows.Network;
-public sealed class TaskNetworkPolicy : IDisposable
+public sealed class TaskNetworkPolicy : IAsyncDisposable
{
- private readonly WfpDynamicSession _session;
+ private readonly IElevatedNetworkSession _session;
private readonly TaskResourceJournal _journal;
private readonly ISecurityAuditSink _audit;
- private readonly Guid _entryId;
private readonly SecurityIdentifier _appContainerSid;
private readonly Guid? _loopbackEntryId;
- private readonly bool _loopbackAdded;
private readonly Guid? _sessionId;
private bool _disposed;
- private TaskNetworkPolicy(WfpDynamicSession session, TaskResourceJournal journal, ISecurityAuditSink audit, Guid entryId,
- SecurityIdentifier appContainerSid, Guid? loopbackEntryId, bool loopbackAdded, Guid? sessionId)
+ private TaskNetworkPolicy(IElevatedNetworkSession session, TaskResourceJournal journal, ISecurityAuditSink audit,
+ SecurityIdentifier appContainerSid, Guid? loopbackEntryId, Guid? sessionId)
{
- _session = session; _journal = journal; _audit = audit; _entryId = entryId;
- _appContainerSid = appContainerSid; _loopbackEntryId = loopbackEntryId; _loopbackAdded = loopbackAdded;
+ _session = session;
+ _journal = journal;
+ _audit = audit;
+ _appContainerSid = appContainerSid;
+ _loopbackEntryId = loopbackEntryId;
_sessionId = sessionId;
}
- public static TaskNetworkPolicy Apply(TaskResourceJournal journal, SecurityIdentifier appContainerSid, IReadOnlyList<NetworkEndpoint> allowances,
- ISecurityAuditSink audit, Guid? sessionId = null, bool enableLoopback = true)
+ public static async Task<TaskNetworkPolicy> ApplyAsync(IElevatedNetworkSession session, TaskResourceJournal journal,
+ SecurityIdentifier appContainerSid, string networkAccess, IReadOnlyList<NetworkEndpoint> allowances,
+ ISecurityAuditSink audit, Guid? sessionId, CancellationToken cancellationToken)
{
- bool originallyExempt = enableLoopback && LoopbackExemption.Contains(appContainerSid);
- Guid? loopbackEntryId = enableLoopback ? journal.Prepare("network.loopback_exemption", appContainerSid.Value,
+ bool needsLoopback = allowances.Any(static item => IPAddress.IsLoopback(item.Address));
+ bool originallyExempt = needsLoopback && await session.InspectLoopbackAsync(appContainerSid.Value, cancellationToken).ConfigureAwait(false);
+ Guid? loopbackEntryId = needsLoopback ? journal.Prepare("network.loopback_exemption", appContainerSid.Value,
new Dictionary<string, string> { ["originally_present"] = originallyExempt.ToString() }) : null;
- bool loopbackAdded = false;
- Guid entryId = journal.Prepare("wfp.dynamic", appContainerSid.Value, new Dictionary<string, string>
- {
- ["session"] = "dynamic", ["allowances"] = string.Join(',', allowances.Select(static item => $"{item.Address}:{item.Port}"))
- });
- WfpDynamicSession session = WfpDynamicSession.Open();
try
{
- if (enableLoopback)
- {
- loopbackAdded = LoopbackExemption.Ensure(appContainerSid);
- journal.MarkApplied(loopbackEntryId!.Value, new Dictionary<string, string> { ["added"] = loopbackAdded.ToString() });
- }
- IReadOnlyList<ulong> ids = session.ApplyDefaultBlock(appContainerSid, allowances);
- journal.MarkApplied(entryId, new Dictionary<string, string> { ["filter_ids"] = string.Join(',', ids) });
+ AdminNetworkEndpoint[] endpoints = allowances.Select(static item => new AdminNetworkEndpoint(item.Address.ToString(), item.Port)).ToArray();
+ await session.ApplyAsync(appContainerSid.Value, networkAccess, endpoints, originallyExempt, cancellationToken).ConfigureAwait(false);
+ if (loopbackEntryId is not null) journal.MarkApplied(loopbackEntryId.Value);
audit.Write(new(DateTimeOffset.UtcNow, journal.TaskId, "network.rule.apply", "applied", new Dictionary<string, string>
{
- ["ipv4_ipv6_default_block"] = "true", ["filter_count"] = ids.Count.ToString(), ["session"] = "dynamic"
+ ["ipv4_ipv6_default_block"] = "true", ["endpoint_count"] = endpoints.Length.ToString(), ["session"] = "dynamic"
}, SessionId: sessionId));
- if (enableLoopback) audit.Write(new(DateTimeOffset.UtcNow, journal.TaskId, "network.loopback.apply", loopbackAdded ? "added" : "preserved",
- new Dictionary<string, string> { ["app_container_sid"] = appContainerSid.Value }, SessionId: sessionId));
- return new(session, journal, audit, entryId, appContainerSid, loopbackEntryId, loopbackAdded, sessionId);
+ if (loopbackEntryId is not null)
+ audit.Write(new(DateTimeOffset.UtcNow, journal.TaskId, "network.loopback.apply", originallyExempt ? "preserved" : "added",
+ new Dictionary<string, string> { ["app_container_sid"] = appContainerSid.Value }, SessionId: sessionId));
+ return new(session, journal, audit, appContainerSid, loopbackEntryId, sessionId);
}
catch
{
- session.Dispose();
- journal.MarkCleaned(entryId);
- if (loopbackAdded) LoopbackExemption.Remove(appContainerSid);
- if (loopbackEntryId is not null) journal.MarkCleaned(loopbackEntryId.Value);
+ bool cleaned = false;
+ try { await session.CleanupAsync(CancellationToken.None).ConfigureAwait(false); cleaned = true; } catch { }
+ if (cleaned && loopbackEntryId is not null) journal.MarkCleaned(loopbackEntryId.Value);
+ try { await session.DisposeAsync().ConfigureAwait(false); } catch { }
throw;
}
}
- public void Dispose()
+ public async ValueTask DisposeAsync()
{
if (_disposed) return;
- _session.RemovePolicy();
- _session.Dispose();
- _journal.MarkCleaned(_entryId);
- if (_loopbackAdded) LoopbackExemption.Remove(_appContainerSid);
- if (_loopbackEntryId is not null) _journal.MarkCleaned(_loopbackEntryId.Value);
- _audit.Write(new(DateTimeOffset.UtcNow, _journal.TaskId, "network.rule.remove", "removed", new Dictionary<string, string> { ["residual_filters"] = "0" }, SessionId: _sessionId));
- if (_loopbackEntryId is not null) _audit.Write(new(DateTimeOffset.UtcNow, _journal.TaskId, "network.loopback.remove", "removed",
- new Dictionary<string, string> { ["residual_exemptions"] = LoopbackExemption.Contains(_appContainerSid) ? "preexisting" : "0" }, SessionId: _sessionId));
+ try
+ {
+ await _session.CleanupAsync(CancellationToken.None).ConfigureAwait(false);
+ if (_loopbackEntryId is not null) _journal.MarkCleaned(_loopbackEntryId.Value);
+ }
+ finally { await _session.DisposeAsync().ConfigureAwait(false); }
+ _audit.Write(new(DateTimeOffset.UtcNow, _journal.TaskId, "network.rule.remove", "removed",
+ new Dictionary<string, string> { ["residual_filters"] = "0" }, SessionId: _sessionId));
+ if (_loopbackEntryId is not null)
+ _audit.Write(new(DateTimeOffset.UtcNow, _journal.TaskId, "network.loopback.remove", "restored",
+ new Dictionary<string, string> { ["app_container_sid"] = _appContainerSid.Value }, SessionId: _sessionId));
_disposed = true;
}
}
diff --git a/src/Agent.Windows/Network/WfpDynamicSession.cs b/src/Agent.Windows/Network/WfpDynamicSession.cs
deleted file mode 100644
index ef8ba8e..0000000
--- a/src/Agent.Windows/Network/WfpDynamicSession.cs
+++ /dev/null
@@ -1,208 +0,0 @@
-using System.ComponentModel;
-using System.Buffers.Binary;
-using System.Net;
-using System.Runtime.InteropServices;
-using System.Security.Principal;
-using Windows.Win32;
-using Windows.Win32.Foundation;
-using Windows.Win32.NetworkManagement.WindowsFilteringPlatform;
-using Windows.Win32.Security;
-
-namespace Agent.Windows.Network;
-
-public sealed record NetworkEndpoint(IPAddress Address, ushort Port);
-
-public static class NetworkAllowanceSnapshot
-{
- public static async Task<IReadOnlyList<NetworkEndpoint>> ResolveAsync(IEnumerable<(string Host, IReadOnlyList<int> Ports)> allowances, CancellationToken cancellationToken)
- {
- var endpoints = new HashSet<NetworkEndpoint>();
- foreach ((string host, IReadOnlyList<int> ports) in allowances)
- {
- IPAddress[] addresses = await Dns.GetHostAddressesAsync(host, cancellationToken).ConfigureAwait(false);
- foreach (int port in ports)
- {
- if (port is < 1 or > 65535) throw new ArgumentOutOfRangeException(nameof(allowances), $"Invalid network port {port}.");
- foreach (IPAddress address in addresses.Where(static item => item.AddressFamily is System.Net.Sockets.AddressFamily.InterNetwork or System.Net.Sockets.AddressFamily.InterNetworkV6))
- endpoints.Add(new(address, (ushort)port));
- }
- }
- return endpoints.OrderBy(static item => item.Address.ToString(), StringComparer.Ordinal).ThenBy(static item => item.Port).ToArray();
- }
-}
-
-public sealed unsafe class WfpDynamicSession : IDisposable
-{
- private const FWPM_FILTER_FLAGS FwpmFilterFlagClearActionRight = (FWPM_FILTER_FLAGS)0x00000008;
- private readonly FwpmEngineClose0SafeHandle _engine;
- private Guid? _subLayerKey;
- private readonly List<ulong> _filterIds = [];
-
- private WfpDynamicSession(FwpmEngineClose0SafeHandle engine) => _engine = engine;
- public static WfpDynamicSession Open(string displayName = "Agent Sandbox MCP dynamic task session")
- {
- FWPM_SESSION0 session = default;
- session.flags = PInvoke.FWPM_SESSION_FLAG_DYNAMIC;
- uint result = PInvoke.FwpmEngineOpen0(10, null, session, out FwpmEngineClose0SafeHandle engine);
- ThrowIfError(result, "FwpmEngineOpen0");
- return new(engine);
- }
-
- public void ProbeWriteTransaction()
- {
- uint result = PInvoke.FwpmTransactionBegin0(_engine, 0);
- ThrowIfError(result, "FwpmTransactionBegin0");
- result = PInvoke.FwpmTransactionAbort0(_engine);
- ThrowIfError(result, "FwpmTransactionAbort0");
- }
-
- public IReadOnlyList<ulong> ApplyDefaultBlock(SecurityIdentifier appContainerSid, IReadOnlyList<NetworkEndpoint>? allowedEndpoints = null)
- {
- if (_subLayerKey is not null) throw new InvalidOperationException("WFP policy is already applied in this session.");
- byte[] sidBytes = new byte[appContainerSid.BinaryLength];
- appContainerSid.GetBinaryForm(sidBytes, 0);
- uint result = PInvoke.FwpmTransactionBegin0(_engine, 0);
- ThrowIfError(result, "FwpmTransactionBegin0");
- bool committed = false;
- try
- {
- Guid subLayerKey = Guid.NewGuid();
- fixed (char* subLayerName = "Agent Sandbox MCP task sublayer")
- {
- FWPM_SUBLAYER0 subLayer = default;
- subLayer.subLayerKey = subLayerKey;
- subLayer.displayData.name = new PWSTR(subLayerName);
- subLayer.weight = 0x7FFF;
- ThrowIfError(PInvoke.FwpmSubLayerAdd0(_engine, in subLayer, default), "FwpmSubLayerAdd0");
- }
-
- fixed (byte* sidPointer = sidBytes)
- {
- foreach (NetworkEndpoint endpoint in allowedEndpoints ?? []) _filterIds.Add(AddPackagePermit(endpoint, subLayerKey, (SID*)sidPointer));
- _filterIds.Add(AddPackageBlock(PInvoke.FWPM_LAYER_ALE_AUTH_CONNECT_V4, subLayerKey, (SID*)sidPointer, "Agent Sandbox MCP IPv4 default block"));
- _filterIds.Add(AddPackageBlock(PInvoke.FWPM_LAYER_ALE_AUTH_CONNECT_V6, subLayerKey, (SID*)sidPointer, "Agent Sandbox MCP IPv6 default block"));
- }
- ThrowIfError(PInvoke.FwpmTransactionCommit0(_engine), "FwpmTransactionCommit0");
- committed = true;
- _subLayerKey = subLayerKey;
- return _filterIds.ToArray();
- }
- finally
- {
- if (!committed)
- {
- _filterIds.Clear();
- _ = PInvoke.FwpmTransactionAbort0(_engine);
- }
- }
- }
-
- public void RemovePolicy()
- {
- if (_subLayerKey is null) return;
- ThrowIfError(PInvoke.FwpmTransactionBegin0(_engine, 0), "FwpmTransactionBegin0(remove)");
- bool committed = false;
- try
- {
- foreach (ulong filterId in _filterIds.AsEnumerable().Reverse()) ThrowIfError(PInvoke.FwpmFilterDeleteById0(_engine, filterId), "FwpmFilterDeleteById0");
- Guid subLayerKey = _subLayerKey.Value;
- ThrowIfError(PInvoke.FwpmSubLayerDeleteByKey0(_engine, in subLayerKey), "FwpmSubLayerDeleteByKey0");
- ThrowIfError(PInvoke.FwpmTransactionCommit0(_engine), "FwpmTransactionCommit0(remove)");
- committed = true;
- _filterIds.Clear();
- _subLayerKey = null;
- }
- finally
- {
- if (!committed) _ = PInvoke.FwpmTransactionAbort0(_engine);
- }
- }
-
- public void Dispose()
- {
- _engine.Dispose();
- }
-
- private ulong AddPackageBlock(Guid layerKey, Guid subLayerKey, SID* sid, string displayName)
- {
- FWPM_FILTER_CONDITION0 condition = default;
- condition.fieldKey = PInvoke.FWPM_CONDITION_ALE_PACKAGE_ID;
- condition.matchType = FWP_MATCH_TYPE.FWP_MATCH_EQUAL;
- condition.conditionValue.type = FWP_DATA_TYPE.FWP_SID;
- condition.conditionValue.sid = sid;
- fixed (char* name = displayName)
- {
- FWPM_FILTER0 filter = default;
- filter.filterKey = Guid.NewGuid();
- filter.displayData.name = new PWSTR(name);
- filter.layerKey = layerKey;
- filter.subLayerKey = subLayerKey;
- filter.weight.type = FWP_DATA_TYPE.FWP_UINT8;
- filter.weight.uint8 = 8;
- filter.numFilterConditions = 1;
- filter.filterCondition = &condition;
- filter.action.type = FWP_ACTION_TYPE.FWP_ACTION_BLOCK;
- ThrowIfError(PInvoke.FwpmFilterAdd0(_engine, in filter, default, out ulong id), "FwpmFilterAdd0");
- return id;
- }
- }
-
- private ulong AddPackagePermit(NetworkEndpoint endpoint, Guid subLayerKey, SID* sid)
- {
- FWPM_FILTER_CONDITION0* conditions = stackalloc FWPM_FILTER_CONDITION0[3];
- conditions[0].fieldKey = PInvoke.FWPM_CONDITION_ALE_PACKAGE_ID;
- conditions[0].matchType = FWP_MATCH_TYPE.FWP_MATCH_EQUAL;
- conditions[0].conditionValue.type = FWP_DATA_TYPE.FWP_SID;
- conditions[0].conditionValue.sid = sid;
- conditions[1].fieldKey = PInvoke.FWPM_CONDITION_IP_REMOTE_ADDRESS;
- conditions[1].matchType = FWP_MATCH_TYPE.FWP_MATCH_EQUAL;
- Guid layer;
- FWP_BYTE_ARRAY16 ipv6 = default;
- if (endpoint.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
- {
- layer = PInvoke.FWPM_LAYER_ALE_AUTH_CONNECT_V4;
- conditions[1].conditionValue.type = FWP_DATA_TYPE.FWP_UINT32;
- // FWP_UINT32 uses host-order numeric values. For 127.0.0.1 the
- // numeric value is 0x7F000001; parse the network-order octets into
- // that value rather than reinterpreting their in-memory byte order.
- conditions[1].conditionValue.uint32 = BinaryPrimitives.ReadUInt32BigEndian(endpoint.Address.GetAddressBytes());
- }
- else
- {
- layer = PInvoke.FWPM_LAYER_ALE_AUTH_CONNECT_V6;
- ipv6.byteArray16 = endpoint.Address.GetAddressBytes();
- conditions[1].conditionValue.type = FWP_DATA_TYPE.FWP_BYTE_ARRAY16_TYPE;
- conditions[1].conditionValue.byteArray16 = &ipv6;
- }
- conditions[2].fieldKey = PInvoke.FWPM_CONDITION_IP_REMOTE_PORT;
- conditions[2].matchType = FWP_MATCH_TYPE.FWP_MATCH_EQUAL;
- conditions[2].conditionValue.type = FWP_DATA_TYPE.FWP_UINT16;
- conditions[2].conditionValue.uint16 = endpoint.Port;
- string displayName = $"Agent Sandbox MCP allow {endpoint.Address}:{endpoint.Port}";
- fixed (char* name = displayName)
- {
- FWPM_FILTER0 filter = default;
- filter.filterKey = Guid.NewGuid();
- filter.displayData.name = new PWSTR(name);
- filter.layerKey = layer;
- filter.subLayerKey = subLayerKey;
- filter.weight.type = FWP_DATA_TYPE.FWP_UINT8;
- filter.weight.uint8 = 15;
- filter.numFilterConditions = 3;
- filter.filterCondition = conditions;
- // A plain permit is a soft permit and can be overridden by a block
- // from another sub-layer (for example Windows Firewall). The exact
- // task allowance must be a hard permit; our higher-weight match in
- // this sub-layer still prevents the task default-block from running.
- filter.flags = FwpmFilterFlagClearActionRight;
- filter.action.type = FWP_ACTION_TYPE.FWP_ACTION_PERMIT;
- ThrowIfError(PInvoke.FwpmFilterAdd0(_engine, in filter, default, out ulong id), "FwpmFilterAdd0(permit)");
- return id;
- }
- }
-
- private static void ThrowIfError(uint result, string operation)
- {
- if (result != 0) throw new Win32Exception(unchecked((int)result), $"{operation} failed with WFP/Win32 error 0x{result:X8}.");
- }
-}