Vanara/PInvoke/Security/AdvApi32/WinNT.Extensions.cs

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