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;
}
}