using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Security.AccessControl; using Vanara.Extensions; using static Vanara.PInvoke.AdvApi32; namespace Vanara.PInvoke { /// Extension methods for PACE, PACL and PSECURITY_DESCRIPTOR. public static class WinNTExtensions { /// Gets the number of ACEs held by an ACL. /// The pointer to the ACL structure to query. /// The ace count. public static uint AceCount(this PACL pACL) => IsValidAcl(pACL) && GetAclInformation(pACL, out ACL_SIZE_INFORMATION si) ? si.AceCount : 0; /// Gets the total number of bytes allocated to the ACL. /// The pointer to the ACL structure to query. /// The total of the free and used bytes in the ACL. public static uint BytesAllocated(this PACL pACL) => IsValidAcl(pACL) && GetAclInformation(pACL, out ACL_SIZE_INFORMATION si) ? si.AclBytesFree + si.AclBytesInUse : 0; /// The GetAce function obtains a pointer to an access control entry (ACE) in an access control list (ACL). /// A pointer to an ACL that contains the ACE to be retrieved. /// /// The index of the ACE to be retrieved. A value of zero corresponds to the first ACE in the ACL, a value of one to the second ACE, /// and so on. /// /// A pointer to the ACE. public static PACE GetAce(this PACL pAcl, uint aceIndex) { Win32Error.ThrowLastErrorIfFalse(AdvApi32.GetAce(pAcl, aceIndex, out var acePtr)); return acePtr; } /// Enumerates the ACEs in an ACL. /// A pointer to an ACL that contains the ACE to be retrieved. /// A sequence of PACE values from the ACL. public static IEnumerable EnumerateAces(this PACL pAcl) { for (var i = 0U; i < pAcl.AceCount(); i++) yield return GetAce(pAcl, i); } /// Gets the Flags for an ACE, if defined. /// A pointer to an ACE. /// The Flags value, if this is an object ACE, otherwise . /// pAce public static AdvApi32.ObjectAceFlags? GetFlags(this PACE pAce) { if (pAce.IsNull) throw new ArgumentNullException(nameof(pAce)); return !pAce.IsObjectAce() ? null : pAce.DangerousGetHandle().ToStructure().Flags; } /// Gets the header for an ACE. /// A pointer to an ACE. /// The value. /// pAce public static ACE_HEADER GetHeader(this PACE pAce) => !pAce.IsNull ? pAce.DangerousGetHandle().ToStructure() : throw new ArgumentNullException(nameof(pAce)); /// Gets the InheritedObjectType for an ACE, if defined. /// A pointer to an ACE. /// The InheritedObjectType value, if this is an object ACE, otherwise . /// pAce public static Guid? GetInheritedObjectType(this PACE pAce) { if (pAce.IsNull) throw new ArgumentNullException(nameof(pAce)); return !pAce.IsObjectAce() ? null : pAce.DangerousGetHandle().ToStructure().InheritedObjectType; } /// /// The function gets the integrity level of the token. Integrity level is only available on Windows Vista and newer operating /// systems, thus GetIntegrityLevel throws an exception if it is called on systems prior to Windows Vista. /// /// Returns the integrity level of the token. public static MANDATORY_LEVEL GetIntegrityLevel(this HTOKEN token) { // Marshal the TOKEN_MANDATORY_LABEL struct from native to .NET object. var tokenIL = GetTokenInformation(token, TOKEN_INFORMATION_CLASS.TokenIntegrityLevel); // Integrity Level SIDs are in the form of S-1-16-0xXXXX. (e.g. S-1-16-0x1000 stands for low integrity level SID). There is one and only one subauthority. return (MANDATORY_LEVEL)GetSidSubAuthority(tokenIL.Label.Sid, 0); } /// /// The function gets the integrity level of the security descriptor. Integrity level is only available on Windows Vista and newer /// operating systems, thus GetIntegrityLevel throws an exception if it is called on systems prior to Windows Vista. /// /// Returns the integrity level of the security descriptor. public static MANDATORY_LEVEL GetIntegrityLevel(this PSECURITY_DESCRIPTOR pSD) { if (GetSecurityDescriptorSacl(pSD, out var present, out var sacl, out _) && present) { // Marshal the TOKEN_MANDATORY_LABEL struct from native to .NET object. var pACE = sacl.EnumerateAces().FirstOrDefault(a => a.GetAceType() == (AceType)0x11 /*SYSTEM_MANDATORY_LABEL_ACE_TYPE*/); if (!pACE.IsNull) { using var psid = pACE.GetSid(); // Integrity Level SIDs are in the form of S-1-16-0xXXXX. (e.g. S-1-16-0x1000 stands for low integrity level SID). There is one and only one subauthority. return (MANDATORY_LEVEL)GetSidSubAuthority(psid, 0); } } return MANDATORY_LEVEL.MandatoryLevelUntrusted; } /// Gets the mask for an ACE. /// A pointer to an ACE. /// The ACCESS_MASK value. /// pAce public static uint GetMask(this PACE pAce) => !pAce.IsNull ? pAce.DangerousGetHandle().ToStructure().Mask : throw new ArgumentNullException(nameof(pAce)); /// Gets the ObjectType for an ACE, if defined. /// A pointer to an ACE. /// The ObjectType value, if this is an object ACE, otherwise . /// pAce public static Guid? GetObjectType(this PACE pAce) { if (pAce.IsNull) throw new ArgumentNullException(nameof(pAce)); return !pAce.IsObjectAce() ? null : pAce.DangerousGetHandle().ToStructure().ObjectType; } /// Gets the SID for an ACE. /// A pointer to an ACE. /// The SID value. /// pAce public static SafePSID GetSid(this PACE pAce) { if (pAce.IsNull) throw new ArgumentNullException(nameof(pAce)); var offset = Marshal.SizeOf(typeof(ACE_HEADER)) + sizeof(uint); if (pAce.IsObjectAce()) offset += sizeof(uint) + Marshal.SizeOf(typeof(Guid)) * 2; return SafePSID.CreateFromPtr(pAce.DangerousGetHandle().Offset(offset)); } /// Gets the AceType for an ACE, if defined. /// A pointer to an ACE. /// The AceType value. public static AceType GetAceType(this PACE pAce) => GetHeader(pAce).AceType; /// Determines if a ACE is an object ACE. /// A pointer to an ACE. /// if is this is an object ACE; otherwise, . /// pAce /// pAce - Unknown ACE type. public static bool IsObjectAce(this PACE pAce) { if (pAce.IsNull) throw new ArgumentNullException(nameof(pAce)); var aceType = (byte)GetHeader(pAce).AceType; if (aceType > 0x15) throw new ArgumentOutOfRangeException(nameof(pAce), "Unknown ACE type."); return (aceType >= 0x5 && aceType <= 0x8) || aceType == 0xB || aceType == 0xC || aceType == 0xF || aceType == 0x10; } /// Determines whether the security descriptor is self-relative. /// The pointer to the SECURITY_DESCRIPTOR structure to query. /// true if it is self-relative; otherwise, false. public static bool IsSelfRelative(this PSECURITY_DESCRIPTOR pSD) => GetSecurityDescriptorControl(pSD, out var ctrl, out _) ? ctrl.IsFlagSet(SECURITY_DESCRIPTOR_CONTROL.SE_SELF_RELATIVE) : throw Win32Error.GetLastError().GetException(); /// Validates an access control list (ACL). /// The pointer to the ACL structure to query. /// true if the ACL is valid; otherwise, false. public static bool IsValidAcl(this PACL pAcl) => IsValidAcl(pAcl); /// Determines whether the components of a security descriptor are valid. /// The pointer to the SECURITY_DESCRIPTOR structure to query. /// /// true if the components of the security descriptor are valid. If any of the components of the security descriptor are not /// valid, the return value is false. /// public static bool IsValidSecurityDescriptor(this PSECURITY_DESCRIPTOR pSD) => IsValidSecurityDescriptor(pSD); /// Gets the size, in bytes, of an ACE. /// The pointer to the ACE structure to query. /// The size, in bytes, of the ACE. public static uint Length(this PACE pAce) => GetHeader(pAce).AceSize; /// Gets the size, in bytes, of an ACL. If the ACL is not valid, 0 is returned. /// The pointer to the ACL structure to query. /// The size, in bytes, of an ACL. If the ACL is not valid, 0 is returned. public static uint Length(this PACL pACL) => IsValidAcl(pACL) && GetAclInformation(pACL, out ACL_SIZE_INFORMATION si) ? si.AclBytesInUse : 0; /// Gets the size, in bytes, of a security descriptor. If it is not valid, 0 is returned. /// The pointer to the SECURITY_DESCRIPTOR structure to query. /// The size, in bytes, of a security descriptor. If it is not valid, 0 is returned. public static uint Length(this PSECURITY_DESCRIPTOR pSD) => IsValidSecurityDescriptor(pSD) ? GetSecurityDescriptorLength(pSD) : 0U; } }