1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
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.Admin.Host;
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();
}
}
}
|