diff options
| author | Jake Mannens <jake@asger.xyz> | 2023-09-28 03:14:35 +1000 |
|---|---|---|
| committer | Jake Mannens <jake@asger.xyz> | 2023-09-28 03:14:35 +1000 |
| commit | bedcb6b176130fc2c6bd4657c8af4d407b64c970 (patch) | |
| tree | 974a14bb03913e3a8083a633d9de61742c86e0a1 /SecurityIdentifier.cs | |
| parent | bc82b2dc2f7405c0fd4d179830412ea8209137b1 (diff) | |
Updated DB schema and configured ACLs to use SIDs
Diffstat (limited to 'SecurityIdentifier.cs')
| -rw-r--r-- | SecurityIdentifier.cs | 129 |
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 |
