mirror of https://github.com/dahall/Vanara.git
186 lines
10 KiB
C#
186 lines
10 KiB
C#
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
|
|
{
|
|
/// <summary>Extension methods for PACE, PACL and PSECURITY_DESCRIPTOR.</summary>
|
|
public static class WinNTExtensions
|
|
{
|
|
/// <summary>Gets the number of ACEs held by an ACL.</summary>
|
|
/// <param name="pACL">The pointer to the ACL structure to query.</param>
|
|
/// <returns>The ace count.</returns>
|
|
public static uint AceCount(this PACL pACL) => IsValidAcl(pACL) && GetAclInformation(pACL, out ACL_SIZE_INFORMATION si) ? si.AceCount : 0;
|
|
|
|
/// <summary>Gets the total number of bytes allocated to the ACL.</summary>
|
|
/// <param name="pACL">The pointer to the ACL structure to query.</param>
|
|
/// <returns>The total of the free and used bytes in the ACL.</returns>
|
|
public static uint BytesAllocated(this PACL pACL) => IsValidAcl(pACL) && GetAclInformation(pACL, out ACL_SIZE_INFORMATION si) ? si.AclBytesFree + si.AclBytesInUse : 0;
|
|
|
|
/// <summary>The <c>GetAce</c> function obtains a pointer to an access control entry (ACE) in an access control list (ACL).</summary>
|
|
/// <param name="pAcl">A pointer to an ACL that contains the ACE to be retrieved.</param>
|
|
/// <param name="aceIndex">
|
|
/// 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.
|
|
/// </param>
|
|
/// <returns>A pointer to the ACE.</returns>
|
|
public static PACE GetAce(this PACL pAcl, uint aceIndex)
|
|
{
|
|
Win32Error.ThrowLastErrorIfFalse(AdvApi32.GetAce(pAcl, aceIndex, out var acePtr));
|
|
return acePtr;
|
|
}
|
|
|
|
/// <summary>Enumerates the ACEs in an ACL.</summary>
|
|
/// <param name="pAcl">A pointer to an ACL that contains the ACE to be retrieved.</param>
|
|
/// <returns>A sequence of PACE values from the ACL.</returns>
|
|
public static IEnumerable<PACE> EnumerateAces(this PACL pAcl)
|
|
{
|
|
for (var i = 0U; i < pAcl.AceCount(); i++)
|
|
yield return GetAce(pAcl, i);
|
|
}
|
|
|
|
/// <summary>Gets the Flags for an ACE, if defined.</summary>
|
|
/// <param name="pAce">A pointer to an ACE.</param>
|
|
/// <returns>The Flags value, if this is an object ACE, otherwise <see langword="null"/>.</returns>
|
|
/// <exception cref="ArgumentNullException">pAce</exception>
|
|
public static AdvApi32.ObjectAceFlags? GetFlags(this PACE pAce)
|
|
{
|
|
if (pAce.IsNull) throw new ArgumentNullException(nameof(pAce));
|
|
return !pAce.IsObjectAce() ? null : pAce.DangerousGetHandle().ToStructure<ACCESS_ALLOWED_OBJECT_ACE>().Flags;
|
|
}
|
|
|
|
/// <summary>Gets the header for an ACE.</summary>
|
|
/// <param name="pAce">A pointer to an ACE.</param>
|
|
/// <returns>The <see cref="ACE_HEADER"/> value.</returns>
|
|
/// <exception cref="ArgumentNullException">pAce</exception>
|
|
public static ACE_HEADER GetHeader(this PACE pAce) => !pAce.IsNull ? pAce.DangerousGetHandle().ToStructure<ACE_HEADER>() : throw new ArgumentNullException(nameof(pAce));
|
|
|
|
/// <summary>Gets the InheritedObjectType for an ACE, if defined.</summary>
|
|
/// <param name="pAce">A pointer to an ACE.</param>
|
|
/// <returns>The InheritedObjectType value, if this is an object ACE, otherwise <see langword="null"/>.</returns>
|
|
/// <exception cref="ArgumentNullException">pAce</exception>
|
|
public static Guid? GetInheritedObjectType(this PACE pAce)
|
|
{
|
|
if (pAce.IsNull) throw new ArgumentNullException(nameof(pAce));
|
|
return !pAce.IsObjectAce() ? null : pAce.DangerousGetHandle().ToStructure<ACCESS_ALLOWED_OBJECT_ACE>().InheritedObjectType;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <returns>Returns the integrity level of the token.</returns>
|
|
public static MANDATORY_LEVEL GetIntegrityLevel(this HTOKEN token)
|
|
{
|
|
// Marshal the TOKEN_MANDATORY_LABEL struct from native to .NET object.
|
|
var tokenIL = GetTokenInformation<TOKEN_MANDATORY_LABEL>(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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <returns>Returns the integrity level of the security descriptor.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>Gets the mask for an ACE.</summary>
|
|
/// <param name="pAce">A pointer to an ACE.</param>
|
|
/// <returns>The ACCESS_MASK value.</returns>
|
|
/// <exception cref="ArgumentNullException">pAce</exception>
|
|
public static uint GetMask(this PACE pAce) => !pAce.IsNull ? pAce.DangerousGetHandle().ToStructure<ACCESS_ALLOWED_ACE>().Mask : throw new ArgumentNullException(nameof(pAce));
|
|
|
|
/// <summary>Gets the ObjectType for an ACE, if defined.</summary>
|
|
/// <param name="pAce">A pointer to an ACE.</param>
|
|
/// <returns>The ObjectType value, if this is an object ACE, otherwise <see langword="null"/>.</returns>
|
|
/// <exception cref="ArgumentNullException">pAce</exception>
|
|
public static Guid? GetObjectType(this PACE pAce)
|
|
{
|
|
if (pAce.IsNull) throw new ArgumentNullException(nameof(pAce));
|
|
return !pAce.IsObjectAce() ? null : pAce.DangerousGetHandle().ToStructure<ACCESS_ALLOWED_OBJECT_ACE>().ObjectType;
|
|
}
|
|
|
|
/// <summary>Gets the SID for an ACE.</summary>
|
|
/// <param name="pAce">A pointer to an ACE.</param>
|
|
/// <returns>The SID value.</returns>
|
|
/// <exception cref="ArgumentNullException">pAce</exception>
|
|
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));
|
|
}
|
|
|
|
/// <summary>Gets the AceType for an ACE, if defined.</summary>
|
|
/// <param name="pAce">A pointer to an ACE.</param>
|
|
/// <returns>The AceType value.</returns>
|
|
public static AceType GetAceType(this PACE pAce) => GetHeader(pAce).AceType;
|
|
|
|
/// <summary>Determines if a ACE is an object ACE.</summary>
|
|
/// <param name="pAce">A pointer to an ACE.</param>
|
|
/// <returns><see langword="true"/> if is this is an object ACE; otherwise, <see langword="false"/>.</returns>
|
|
/// <exception cref="ArgumentNullException">pAce</exception>
|
|
/// <exception cref="ArgumentOutOfRangeException">pAce - Unknown ACE type.</exception>
|
|
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;
|
|
}
|
|
|
|
/// <summary>Determines whether the security descriptor is self-relative.</summary>
|
|
/// <param name="pSD">The pointer to the SECURITY_DESCRIPTOR structure to query.</param>
|
|
/// <returns><c>true</c> if it is self-relative; otherwise, <c>false</c>.</returns>
|
|
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();
|
|
|
|
/// <summary>Validates an access control list (ACL).</summary>
|
|
/// <param name="pAcl">The pointer to the ACL structure to query.</param>
|
|
/// <returns><c>true</c> if the ACL is valid; otherwise, <c>false</c>.</returns>
|
|
public static bool IsValidAcl(this PACL pAcl) => IsValidAcl(pAcl);
|
|
|
|
/// <summary>Determines whether the components of a security descriptor are valid.</summary>
|
|
/// <param name="pSD">The pointer to the SECURITY_DESCRIPTOR structure to query.</param>
|
|
/// <returns>
|
|
/// <c>true</c> 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 <c>false</c>.
|
|
/// </returns>
|
|
public static bool IsValidSecurityDescriptor(this PSECURITY_DESCRIPTOR pSD) => IsValidSecurityDescriptor(pSD);
|
|
|
|
/// <summary>Gets the size, in bytes, of an ACE.</summary>
|
|
/// <param name="pAce">The pointer to the ACE structure to query.</param>
|
|
/// <returns>The size, in bytes, of the ACE.</returns>
|
|
public static uint Length(this PACE pAce) => GetHeader(pAce).AceSize;
|
|
|
|
/// <summary>Gets the size, in bytes, of an ACL. If the ACL is not valid, 0 is returned.</summary>
|
|
/// <param name="pACL">The pointer to the ACL structure to query.</param>
|
|
/// <returns>The size, in bytes, of an ACL. If the ACL is not valid, 0 is returned.</returns>
|
|
public static uint Length(this PACL pACL) => IsValidAcl(pACL) && GetAclInformation(pACL, out ACL_SIZE_INFORMATION si) ? si.AclBytesInUse : 0;
|
|
|
|
/// <summary>Gets the size, in bytes, of a security descriptor. If it is not valid, 0 is returned.</summary>
|
|
/// <param name="pSD">The pointer to the SECURITY_DESCRIPTOR structure to query.</param>
|
|
/// <returns>The size, in bytes, of a security descriptor. If it is not valid, 0 is returned.</returns>
|
|
public static uint Length(this PSECURITY_DESCRIPTOR pSD) => IsValidSecurityDescriptor(pSD) ? GetSecurityDescriptorLength(pSD) : 0U;
|
|
}
|
|
} |