summaryrefslogtreecommitdiff
path: root/SecurityIdentifier.cs
diff options
context:
space:
mode:
authorJake Mannens <jake@asger.xyz>2023-09-28 03:14:35 +1000
committerJake Mannens <jake@asger.xyz>2023-09-28 03:14:35 +1000
commitbedcb6b176130fc2c6bd4657c8af4d407b64c970 (patch)
tree974a14bb03913e3a8083a633d9de61742c86e0a1 /SecurityIdentifier.cs
parentbc82b2dc2f7405c0fd4d179830412ea8209137b1 (diff)
Updated DB schema and configured ACLs to use SIDs
Diffstat (limited to 'SecurityIdentifier.cs')
-rw-r--r--SecurityIdentifier.cs129
1 files changed, 129 insertions, 0 deletions
diff --git a/SecurityIdentifier.cs b/SecurityIdentifier.cs
new file mode 100644
index 0000000..81d5ae7
--- /dev/null
+++ b/SecurityIdentifier.cs
@@ -0,0 +1,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);
+
+ 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)) {}
+} \ No newline at end of file