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 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(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 { public SecurityIdentifierConverter() : base( v => v.BinaryForm, v => new SecurityIdentifier(v)) {} }