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
124
125
126
127
128
129
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
namespace HyperBooru;
public enum WellKnownSidType {
NullSid,
WorldSid,
LocalSid,
CreatorOwnerSid,
CreatorGroupSid
}
public class SecurityIdentifier {
public SidStruct SidStruct { get; private init; }
private static readonly Regex SddlRegex =
new(@"^S(-[0-9]+){2,}$", RegexOptions.Compiled);
private static readonly Dictionary<WellKnownSidType, string> wellKnownSidTypes = new() {
{ HyperBooru.WellKnownSidType.NullSid, "S-1-0-0" },
{ HyperBooru.WellKnownSidType.WorldSid, "S-1-1-0" },
{ HyperBooru.WellKnownSidType.LocalSid, "S-1-2-0" },
{ HyperBooru.WellKnownSidType.CreatorOwnerSid, "S-1-3-0" },
{ HyperBooru.WellKnownSidType.CreatorGroupSid, "S-1-3-1" }
};
public SecurityIdentifier(WellKnownSidType wellKnownSidType)
: this(wellKnownSidTypes[wellKnownSidType]) {}
public SecurityIdentifier(string sddlForm) {
var match = SddlRegex.Match(sddlForm);
if(!match.Success)
throw new ArgumentException();
var values = match.Groups[1].Captures
.Select(v => uint.Parse(v.Value.Replace("-", "")))
.ToArray();
// Extremely roundabound way of converting a 32-bit
// integer to a 48-bit big-endian integer
byte[] identifierAuthority = BitConverter.GetBytes(values[1]);
if(BitConverter.IsLittleEndian)
Array.Reverse(identifierAuthority);
identifierAuthority = new byte[2]
.Concat(identifierAuthority)
.ToArray();
SidStruct = new SidStruct {
Revision = (byte) values[0],
SubAuthorityCount = (byte) (values.Count() - 2),
IdentifierAuthority = identifierAuthority,
SubAuthorities = values.Skip(2).ToArray()
};
}
public SecurityIdentifier(SidStruct sidStruct) =>
SidStruct = sidStruct;
public SecurityIdentifier(byte[] binaryForm) {
IntPtr p = IntPtr.Zero;
try {
p = Marshal.AllocHGlobal(binaryForm.Length);
Marshal.Copy(binaryForm, 0, p, binaryForm.Length);
SidStruct = Marshal.PtrToStructure<SidStruct>(p);
} finally {
Marshal.FreeHGlobal(p);
}
}
public byte[] BinaryForm {
get {
var size = Marshal.SizeOf(typeof(SidStruct));
byte[] array = new byte[size];
IntPtr p = IntPtr.Zero;
try {
p = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(SidStruct, p, true);
Marshal.Copy(p, array, 0, size);
} finally {
Marshal.FreeHGlobal(p);
}
return array;
}
}
public override string ToString() {
var identifierAuthority = new BigInteger(SidStruct.IdentifierAuthority, true, true);
var subAuthorities = string.Join('-',SidStruct.SubAuthorities.Select(sa => sa.ToString()));
if(!string.IsNullOrEmpty(subAuthorities))
subAuthorities = "-" + subAuthorities;
return $"S-{SidStruct.Revision}-{identifierAuthority}{subAuthorities}";
}
public static bool operator ==(SecurityIdentifier? x, SecurityIdentifier? y) =>
x?.SidStruct.Equals(y?.SidStruct) ?? false;
public static bool operator !=(SecurityIdentifier? x, SecurityIdentifier? y) =>
!(x == y);
public override bool Equals(object? obj) =>
obj is null ? false : this == (SecurityIdentifier) obj;
public override int GetHashCode() =>
SidStruct.GetHashCode();
}
[StructLayout(LayoutKind.Sequential)]
public struct SidStruct {
public byte Revision;
public byte SubAuthorityCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] IdentifierAuthority;
[MarshalAs(UnmanagedType.ByValArray)]
public uint[] SubAuthorities;
}
public class SecurityIdentifierConverter : ValueConverter<SecurityIdentifier, byte[]> {
public SecurityIdentifierConverter()
: base(
v => v.BinaryForm,
v => new SecurityIdentifier(v)) {}
}
|