using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; using System.Text.RegularExpressions; namespace HyperBooru; public static class WellKnownSid { public static readonly SecurityIdentifier NullSid = new("S-1-0-0"); public static readonly SecurityIdentifier WorldSid = new("S-1-1-0"); public static readonly SecurityIdentifier LocalSid = new("S-1-2-0"); public static readonly SecurityIdentifier CreatorOwnerSid = new("S-1-3-0"); public static readonly SecurityIdentifier CreatorGroupSid = new("S-1-3-1"); private static readonly (string name, SecurityIdentifier sid)[] nameMap = new[] { ( "Everyone", WorldSid ), ( "LOCAL", LocalSid ), ( "CREATOR OWNER", CreatorOwnerSid ), ( "CREATOR GROUP", CreatorGroupSid ) }; public static SecurityIdentifier? TranslateName(string name) { try { return nameMap .First(x => x.name.ToLower() == name.ToLower().Trim()) .sid; } catch(InvalidOperationException) { return null; } } public static string? TranslateName(SecurityIdentifier sid) { try { return nameMap.First(x => x.sid == sid).name; } catch(InvalidOperationException) { return null; } } } public class SecurityIdentifier { public SidStruct SidStruct { get; private init; } private static readonly Regex SddlRegex = new(@"^S(-[0-9]+){2,}$", RegexOptions.Compiled); 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 string SddlForm { get { 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 override string ToString() => SddlForm; 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 static bool operator ==(SidStruct? x, SidStruct? y) => x?.Equals(y) ?? false; public static bool operator !=(SidStruct? x, SidStruct? y) => !(x == y); public override bool Equals([NotNullWhen(true)] object? obj) { if(obj is null || obj is not SidStruct) return false; var sid = (SidStruct) obj; return Revision == sid.Revision && SubAuthorityCount == sid.SubAuthorityCount && Enumerable.SequenceEqual(IdentifierAuthority, sid.IdentifierAuthority) && Enumerable.SequenceEqual(SubAuthorities, sid.SubAuthorities); } public override int GetHashCode() => ( Revision, SubAuthorityCount, IdentifierAuthority .Select(v => (uint) v) .Aggregate(0, (a, v) => HashCode.Combine(a, v)), SubAuthorities .Aggregate(0, (a, v) => HashCode.Combine(a, v))) .GetHashCode(); } public class SecurityIdentifierConverter : ValueConverter { public SecurityIdentifierConverter() : base( v => v.BinaryForm, v => new SecurityIdentifier(v)) {} }