mirror of https://github.com/dahall/Vanara.git
535 lines
25 KiB
C#
535 lines
25 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security.Principal;
|
|
using Vanara.Extensions;
|
|
using Vanara.InteropServices;
|
|
using static Vanara.PInvoke.Kernel32;
|
|
|
|
namespace Vanara.PInvoke
|
|
{
|
|
public static partial class AdvApi32
|
|
{
|
|
/// <summary>Class representation of the native SID structure.</summary>
|
|
/// <seealso cref="SafeHGlobalHandle"/>
|
|
[DebuggerDisplay("{DebugString}")]
|
|
public class SafePSID : SafeMemoryHandle<LocalMemoryMethods>, IEquatable<SafePSID>, IEquatable<PSID>, IEquatable<IntPtr>, ICloneable, ISecurityObject
|
|
{
|
|
/// <summary>Equivalent to a NULL pointer to a SID.</summary>
|
|
public static readonly SafePSID Null = new SafePSID(0);
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="SafePSID"/> class.</summary>
|
|
/// <param name="psid">The existing <see cref="SafePSID"/> instance to duplicate.</param>
|
|
public SafePSID(PSID psid) : base(psid.IsNull ? 0 : GetLengthSid(psid)) { if (!psid.IsNull) CopySid(Size, handle, psid); }
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="SafePSID"/> class.</summary>
|
|
/// <param name="size">The size of memory to allocate, in bytes.</param>
|
|
public SafePSID(SizeT size) : base(size)
|
|
{
|
|
}
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="SafePSID"/> class.</summary>
|
|
/// <param name="sidBytes">An array of bytes that contain a valid Sid.</param>
|
|
public SafePSID(byte[] sidBytes) : base(sidBytes?.Length ?? 0) => Marshal.Copy(sidBytes, 0, handle, Size);
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="SafePSID"/> class.</summary>
|
|
/// <param name="sidValue">The string SID value.</param>
|
|
public SafePSID(string sidValue) : this(ConvertStringSidToSid(sidValue))
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="SafePSID"/> class from a
|
|
/// <see cref="System.Security.Principal.SecurityIdentifier"/> instance.
|
|
/// </summary>
|
|
/// <param name="si">The <see cref="System.Security.Principal.SecurityIdentifier"/> instance.</param>
|
|
public SafePSID(System.Security.Principal.SecurityIdentifier si) : this(si is null ? null : GetBytes(si))
|
|
{
|
|
}
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="SafePSID"/> class.</summary>
|
|
private SafePSID() : base() { }
|
|
|
|
/// <summary>Gets a pointer to the SID_IDENTIFIER_AUTHORITY structure in this security identifier (SID).</summary>
|
|
/// <returns>A pointer to the SID_IDENTIFIER_AUTHORITY structure for this SID structure.</returns>
|
|
public PSID_IDENTIFIER_AUTHORITY Authority => GetSidIdentifierAuthority(this);
|
|
|
|
/// <summary>Gets the SID for the current user</summary>
|
|
/// <value>The current user's SID.</value>
|
|
public static SafePSID Current
|
|
{
|
|
get
|
|
{
|
|
using var identity = WindowsIdentity.GetCurrent();
|
|
|
|
return new SafePSID(identity.User);
|
|
}
|
|
}
|
|
|
|
/// <summary>A SID representing the Everyone Group (S-1-1-0).</summary>
|
|
public static SafePSID Everyone => CreateWellKnown(WELL_KNOWN_SID_TYPE.WinWorldSid);
|
|
|
|
/// <summary>
|
|
/// Verifies that the revision number is within a known range, and that the number of subauthorities is less than the maximum.
|
|
/// </summary>
|
|
/// <value><c>true</c> if this instance is a valid SID; otherwise, <c>false</c>.</value>
|
|
public bool IsValidSid => IsValidSid(this);
|
|
|
|
/// <summary>Gets the length, in bytes, of the SID.</summary>
|
|
/// <value>The SID length, in bytes.</value>
|
|
public int Length => IsValidSid ? GetLengthSid(this) : 0;
|
|
|
|
/// <summary>Enumerates the subauthorities in a security identifier (SID). The subauthority values are relative identifiers (RID).</summary>
|
|
/// <returns>A sequence of subauthorities.</returns>
|
|
public IEnumerable<uint> SubAuthorities => ((PSID)this).GetSubAuthorities();
|
|
|
|
/// <summary>Gets the string used to display in the debugger.</summary>
|
|
internal string DebugString
|
|
{
|
|
get
|
|
{
|
|
if (IsInvalid || IsClosed) return "NULL";
|
|
if (!IsValidSid) return "Invalid";
|
|
var d = ToString("D");
|
|
var n = ToString("P");
|
|
return d == n ? $"({d})" : $"{n} ({d})";
|
|
}
|
|
}
|
|
|
|
/// <summary>Creates a SID for predefined capability.</summary>
|
|
/// <param name="capability">
|
|
/// Member of the KnownSIDCapability enumeration that specifies which capability the SID will identify.
|
|
/// </param>
|
|
/// <returns>A <see cref="SafePSID"/> instance.</returns>
|
|
public static SafePSID CreateCapability(KnownSIDCapability capability) =>
|
|
Init(KnownSIDAuthority.SECURITY_APP_PACKAGE_AUTHORITY, KnownSIDRelativeID.SECURITY_CAPABILITY_BASE_RID, (int)capability);
|
|
|
|
/// <summary>Copies the specified SID from a memory pointer to a <see cref="SafePSID"/> instance.</summary>
|
|
/// <param name="psid">The SID pointer. This value remains the responsibility of the caller to release.</param>
|
|
/// <returns>A <see cref="SafePSID"/> instance.</returns>
|
|
public static SafePSID CreateFromPtr(IntPtr psid) => new SafePSID(psid);
|
|
|
|
/// <summary>Creates a SID for predefined aliases.</summary>
|
|
/// <param name="WellKnownSidType">Member of the WELL_KNOWN_SID_TYPE enumeration that specifies what the SID will identify.</param>
|
|
/// <param name="DomainSid">
|
|
/// A pointer to a SID that identifies the domain to use when creating the SID. Pass <c>PSID.NULL</c> to use the local computer.
|
|
/// </param>
|
|
/// <returns>A <see cref="SafePSID"/> instance.</returns>
|
|
public static SafePSID CreateWellKnown(WELL_KNOWN_SID_TYPE WellKnownSidType, PSID DomainSid = default)
|
|
{
|
|
var sz = 0U;
|
|
CreateWellKnownSid(WellKnownSidType, DomainSid, Null, ref sz);
|
|
if (sz == 0) Win32Error.ThrowLastError();
|
|
var newSid = new SafePSID((int)sz);
|
|
if (!CreateWellKnownSid(WellKnownSidType, DomainSid, newSid, ref sz))
|
|
Win32Error.ThrowLastError();
|
|
return newSid;
|
|
}
|
|
|
|
/// <summary>Extracts the user account SID associated with a security token.</summary>
|
|
/// <param name="hToken">The security token handle.</param>
|
|
/// <returns>A <see cref="SafePSID"/> instance of the user account SID associated with a security token.</returns>
|
|
public static SafePSID FromToken(HTOKEN hToken)
|
|
{
|
|
using var hTok = new SafeHTOKEN((IntPtr)hToken, false);
|
|
return hTok.GetInfo<TOKEN_USER>(TOKEN_INFORMATION_CLASS.TokenUser).User.Sid;
|
|
}
|
|
|
|
/// <summary>Performs an explicit conversion from <see cref="SafePSID"/> to <see cref="IntPtr"/>.</summary>
|
|
/// <param name="psid">The SafePSID instance.</param>
|
|
/// <returns>The result of the conversion.</returns>
|
|
public static explicit operator IntPtr(SafePSID psid) => psid.DangerousGetHandle();
|
|
|
|
/// <summary>Performs an explicit conversion from <see cref="SafePSID"/> to <see cref="PSID"/>.</summary>
|
|
/// <param name="psid">The SafePSID instance.</param>
|
|
/// <returns>The result of the conversion.</returns>
|
|
public static implicit operator PSID(SafePSID psid) => psid.DangerousGetHandle();
|
|
|
|
/// <summary>Performs an implicit conversion from <see cref="PSID"/> to <see cref="SafePSID"/>.</summary>
|
|
/// <param name="psid">The psid.</param>
|
|
/// <returns>The result of the conversion.</returns>
|
|
public static implicit operator SafePSID(PSID psid) => new SafePSID(psid);
|
|
|
|
/// <summary>Initializes a new <see cref="SafePSID"/> instance from a SID authority and subauthorities.</summary>
|
|
/// <param name="sidAuthority">The SID authority.</param>
|
|
/// <param name="subAuth0">The first subauthority.</param>
|
|
/// <param name="subAuthorities1to7">Up to seven other subauthorities.</param>
|
|
/// <returns>A new <see cref="SafePSID"/> instance.</returns>
|
|
/// <exception cref="System.ArgumentOutOfRangeException">
|
|
/// <paramref name="sidAuthority"/> is null or an invalid length or more than 8 total subauthorities were submitted.
|
|
/// </exception>
|
|
public static SafePSID Init(PSID_IDENTIFIER_AUTHORITY sidAuthority, int subAuth0, params int[] subAuthorities1to7)
|
|
{
|
|
if (sidAuthority == null)
|
|
throw new ArgumentOutOfRangeException(nameof(sidAuthority));
|
|
if (subAuthorities1to7.Length > 7)
|
|
throw new ArgumentOutOfRangeException(nameof(subAuthorities1to7));
|
|
AllocateAndInitializeSid(sidAuthority, (byte)(subAuthorities1to7.Length + 1),
|
|
subAuth0,
|
|
subAuthorities1to7.Length > 0 ? subAuthorities1to7[0] : 0,
|
|
subAuthorities1to7.Length > 1 ? subAuthorities1to7[1] : 0,
|
|
subAuthorities1to7.Length > 2 ? subAuthorities1to7[2] : 0,
|
|
subAuthorities1to7.Length > 3 ? subAuthorities1to7[3] : 0,
|
|
subAuthorities1to7.Length > 4 ? subAuthorities1to7[4] : 0,
|
|
subAuthorities1to7.Length > 5 ? subAuthorities1to7[5] : 0,
|
|
subAuthorities1to7.Length > 6 ? subAuthorities1to7[6] : 0,
|
|
out var res);
|
|
return new SafePSID(res);
|
|
}
|
|
|
|
/// <summary>Implements the operator !=.</summary>
|
|
/// <param name="psid1">The psid1.</param>
|
|
/// <param name="psid2">The psid2.</param>
|
|
/// <returns>The result of the operator.</returns>
|
|
public static bool operator !=(SafePSID psid1, SafePSID psid2) => !(psid1 == psid2);
|
|
|
|
/// <summary>Implements the operator ==.</summary>
|
|
/// <param name="psid1">The psid1.</param>
|
|
/// <param name="psid2">The psid2.</param>
|
|
/// <returns>The result of the operator.</returns>
|
|
public static bool operator ==(SafePSID psid1, SafePSID psid2)
|
|
{
|
|
if (ReferenceEquals(psid1, psid2)) return true;
|
|
if (Equals(null, psid1) || Equals(null, psid2)) return false;
|
|
return psid1.Equals(psid2);
|
|
}
|
|
|
|
/// <summary>Clones this instance.</summary>
|
|
/// <returns>A copy of the current <see cref="SafePSID"/>.</returns>
|
|
public SafePSID Clone() => CreateFromPtr(handle);
|
|
|
|
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
|
|
/// <param name="other">An object to compare with this object.</param>
|
|
/// <returns>true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.</returns>
|
|
public bool Equals(SafePSID other) => other != null && (ReferenceEquals(this, other) || EqualSid(this, other));
|
|
|
|
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
|
|
/// <param name="other">An object to compare with this object.</param>
|
|
/// <returns>true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.</returns>
|
|
public bool Equals(PSID other) => Equals(other.DangerousGetHandle());
|
|
|
|
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
|
|
/// <param name="other">An object to compare with this object.</param>
|
|
/// <returns>true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.</returns>
|
|
public bool Equals(IntPtr other) => EqualSid(handle, other);
|
|
|
|
/// <summary>Determines whether the specified <see cref="object"/> is equal to the current <see cref="object"/>.</summary>
|
|
/// <param name="obj">The object to compare with the current object.</param>
|
|
/// <returns>true if the specified <see cref="object"/> is equal to the current <see cref="object"/>; otherwise, false.</returns>
|
|
public override bool Equals(object obj) => obj switch
|
|
{
|
|
SafePSID psid2 => Equals(psid2),
|
|
PSID psidh => Equals(psidh),
|
|
IntPtr ptr => Equals(ptr),
|
|
_ => false
|
|
};
|
|
|
|
/// <summary>Gets the binary form of this SafePSID.</summary>
|
|
/// <returns>An array of bytes containing the Sid.</returns>
|
|
public byte[] GetBinaryForm() => GetBytes(0, Size);
|
|
|
|
/// <summary>Returns a hash code for this instance.</summary>
|
|
/// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns>
|
|
public override int GetHashCode() => base.GetHashCode();
|
|
|
|
/// <summary>Returns a <see cref="string"/> that represents this instance.</summary>
|
|
/// <returns>A <see cref="string"/> that represents this instance.</returns>
|
|
public override string ToString() => ToString(null);
|
|
|
|
/// <summary>Converts the value of this security identifier (SID) to its equivalent string representation according to the provided format specifier.</summary>
|
|
/// <param name="format">
|
|
/// A single format specifier that indicates how to format the value of this security identifier (SID). The format parameter can be
|
|
/// "B" (binary), "D" (sddl), "N" (name), or "P" (upn). If format is null or an empty string (""), "D" is used.
|
|
/// </param>
|
|
/// <returns>The value of this security identifier (SID), in the specified format.</returns>
|
|
/// <exception cref="ArgumentException">SID value is not a valid SID. - pSid</exception>
|
|
/// <exception cref="FormatException">The value of format is not null, an empty string (""), "B" (binary), "D" (sddl), "N" (name), or "P" (upn).</exception>
|
|
/// <remarks>
|
|
/// <para>The following table shows the accepted format specifiers for the format parameter.</para>
|
|
/// <list type="table">
|
|
/// <item>
|
|
/// <description>Specifier</description>
|
|
/// <description>Format of return value</description>
|
|
/// </item>
|
|
/// <item>
|
|
/// <description>"B"</description>
|
|
/// <description>
|
|
/// <para>Binary hex dump representation of the SID.</para>
|
|
/// </description>
|
|
/// </item>
|
|
/// <item>
|
|
/// <description>"D"</description>
|
|
/// <description>SDDL representation of the SID.</description>
|
|
/// </item>
|
|
/// <item>
|
|
/// <description>"N"</description>
|
|
/// <description>The NT4 style name (domain\username) corresponding to the SID.</description>
|
|
/// </item>
|
|
/// <item>
|
|
/// <description>"P"</description>
|
|
/// <description>The internet style name (UPN) corresponding to the SID.</description>
|
|
/// </item>
|
|
/// </list>
|
|
/// </remarks>
|
|
public string ToString(string format)
|
|
{
|
|
try { return ((PSID)handle).ToString(format); }
|
|
catch { return !IsInvalid && !IsClosed ? "Invalid" : "0"; }
|
|
}
|
|
|
|
/// <summary>Creates a new object that is a copy of the current instance.</summary>
|
|
/// <returns>A new object that is a copy of this instance.</returns>
|
|
object ICloneable.Clone() => Clone();
|
|
|
|
private static byte[] GetBytes(System.Security.Principal.SecurityIdentifier si)
|
|
{
|
|
var b = new byte[si.BinaryLength];
|
|
si.GetBinaryForm(b, 0);
|
|
return b;
|
|
}
|
|
}
|
|
|
|
/// <summary>Provides an array of SID pointers whose memory is disposed after use.</summary>
|
|
/// <seealso cref="Vanara.PInvoke.SafeHANDLE"/>
|
|
/// <seealso cref="System.Collections.Generic.IReadOnlyList{T}"/>
|
|
public class SafePSIDArray : SafeHANDLE, IReadOnlyList<PSID>
|
|
{
|
|
private List<SafePSID> items;
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="SafePSIDArray"/> class and assigns an existing handle.</summary>
|
|
/// <param name="preexistingHandle">An <see cref="IntPtr"/> object that represents the pre-existing handle to use.</param>
|
|
/// <param name="count">The count of PSID array values pointed to by <paramref name="preexistingHandle"/>.</param>
|
|
/// <param name="ownsHandle">
|
|
/// <see langword="true"/> to reliably release the handle during the finalization phase; otherwise, <see langword="false"/> (not
|
|
/// recommended). If <see langword="true"/>, the individually allocated values for each PSID will also be released.
|
|
/// </param>
|
|
public SafePSIDArray(IntPtr preexistingHandle, int count, bool ownsHandle = true) : base(preexistingHandle, ownsHandle)
|
|
{
|
|
if (ownsHandle)
|
|
Count = count;
|
|
else
|
|
items = new List<SafePSID>(handle.ToIEnum<IntPtr>(count).Select(p => new SafePSID(p)));
|
|
}
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="SafePSIDArray"/> class.</summary>
|
|
/// <param name="pSIDs">A list of <see cref="SafePSID"/> instances.</param>
|
|
public SafePSIDArray(IEnumerable<SafePSID> pSIDs) : this(pSIDs?.Select(p => (PSID)p))
|
|
{
|
|
}
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="SafePSIDArray"/> class.</summary>
|
|
/// <param name="pSIDs">A list of <see cref="SafePSID"/> instances.</param>
|
|
public SafePSIDArray(IEnumerable<PSID> pSIDs) : base()
|
|
{
|
|
if (pSIDs is null) throw new ArgumentNullException(nameof(pSIDs));
|
|
items = pSIDs.Select(p => new SafePSID(p)).ToList();
|
|
SetHandle(items.Select(p => (IntPtr)p).MarshalToPtr<IntPtr>(i => LocalAlloc(LMEM.LPTR, i).DangerousGetHandle(), out _));
|
|
}
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="SafePSIDArray"/> class.</summary>
|
|
private SafePSIDArray() : base() { }
|
|
|
|
/// <summary>Gets or sets the length of the array. This value must be set in order to interact with the elements.</summary>
|
|
/// <value>The length.</value>
|
|
public int Count
|
|
{
|
|
get => items?.Count ?? throw new InvalidOperationException("The length must be set before using this function.");
|
|
set
|
|
{
|
|
if (items != null) throw new InvalidOperationException("The length can only be set for partially initialized arrays.");
|
|
items = new List<SafePSID>();
|
|
foreach (var psid in handle.ToIEnum<IntPtr>(value))
|
|
{
|
|
items.Add(new SafePSID(psid));
|
|
LocalFree(psid);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Gets the <see cref="PSID"/> at the specified index.</summary>
|
|
/// <value>The <see cref="PSID"/>.</value>
|
|
/// <param name="index">The index.</param>
|
|
/// <returns>The PSID at the specified index.</returns>
|
|
/// <exception cref="InvalidOperationException">The length must be set before using this function.</exception>
|
|
public PSID this[int index] => items?[index] ?? throw new InvalidOperationException("The length must be set before using this function.");
|
|
|
|
/// <summary>Performs an implicit conversion from <see cref="SafePSIDArray"/> to <see cref="PSID"/>[].</summary>
|
|
/// <param name="a">The <see cref="SafePSIDArray"/> instance.</param>
|
|
/// <returns>The result of the conversion.</returns>
|
|
public static implicit operator PSID[](SafePSIDArray a) => a.items.ConvertAll(p => (PSID)p).ToArray();
|
|
|
|
/// <summary>Returns an enumerator that iterates through the collection.</summary>
|
|
/// <returns>A <see cref="IEnumerator{PSID}"/> that can be used to iterate through the collection.</returns>
|
|
public IEnumerator<PSID> GetEnumerator() => items.ConvertAll(p => (PSID)p).GetEnumerator();
|
|
|
|
/// <inheritdoc/>
|
|
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<SafePSID>)items).GetEnumerator();
|
|
|
|
/// <inheritdoc/>
|
|
protected override bool InternalReleaseHandle()
|
|
{
|
|
if (items != null)
|
|
foreach (var p in items)
|
|
p.Dispose();
|
|
return LocalFree(handle) == HLOCAL.NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Extension methods for PSID instances.</summary>
|
|
public static class PSIDExtensions
|
|
{
|
|
/// <summary>Determines equality of two PSID instances.</summary>
|
|
/// <param name="psid1">The first PSID.</param>
|
|
/// <param name="psid2">The second PSID.</param>
|
|
/// <returns><see langword="true"/> if the SID structures are equal; <see langword="false"/> otherwise.</returns>
|
|
public static bool Equals(this PSID psid1, PSID psid2) => AdvApi32.EqualSid(psid1, psid2);
|
|
|
|
/// <summary>
|
|
/// The <c>GetAuthority</c> function returns a pointer to the SID_IDENTIFIER_AUTHORITY structure in a specified security identifier (SID).
|
|
/// </summary>
|
|
/// <param name="pSid">
|
|
/// <para>A pointer to the SID structure for which a pointer to the SID_IDENTIFIER_AUTHORITY structure is returned.</para>
|
|
/// <para>
|
|
/// This function does not handle SID structures that are not valid. Call the IsValidSid function to verify that the <c>SID</c>
|
|
/// structure is valid before you call this function.
|
|
/// </para>
|
|
/// </param>
|
|
/// <returns>
|
|
/// <para>
|
|
/// If the function succeeds, the return value is a pointer to the SID_IDENTIFIER_AUTHORITY structure for the specified SID structure.
|
|
/// </para>
|
|
/// <para>
|
|
/// If the function fails, the return value is undefined. The function fails if the SID structure pointed to by the pSid parameter
|
|
/// is not valid. To get extended error information, call GetLastError.
|
|
/// </para>
|
|
/// </returns>
|
|
/// <remarks>
|
|
/// This function uses a 32-bit RID value. For applications that require a larger RID value, use CreateWellKnownSid and related functions.
|
|
/// </remarks>
|
|
public static AdvApi32.PSID_IDENTIFIER_AUTHORITY GetAuthority(this PSID pSid) => AdvApi32.GetSidIdentifierAuthority(pSid);
|
|
|
|
/// <summary>Gets the binary form of the SID structure.</summary>
|
|
/// <param name="pSid">The SID structure pointer.</param>
|
|
/// <returns>The binary form (byte array) of the SID structure.</returns>
|
|
public static byte[] GetBinaryForm(this PSID pSid) => pSid.IsValidSid() ? ((IntPtr)pSid).ToArray<byte>(pSid.Length()) : (new byte[0]);
|
|
|
|
/// <summary>
|
|
/// The <c>GetDomainSid</c> function receives a security identifier (SID) and returns a SID representing the domain of that SID.
|
|
/// </summary>
|
|
/// <param name="pSid">A pointer to the SID to examine.</param>
|
|
/// <returns>An allocated safe pointer to a SID representing the domain.</returns>
|
|
public static AdvApi32.SafePSID GetDomainSid(this PSID pSid)
|
|
{
|
|
Win32Error.ThrowLastErrorIfFalse(AdvApi32.GetWindowsAccountDomainSid(pSid, out var pDomSid));
|
|
return pDomSid;
|
|
}
|
|
|
|
/// <summary>Enumerates the subauthorities in a security identifier (SID). The subauthority values are relative identifiers (RID).</summary>
|
|
/// <param name="pSid">A pointer to the SID structure from which a pointer to a subauthority is to be returned.</param>
|
|
/// <returns>A sequence of subauthorities.</returns>
|
|
public static IEnumerable<uint> GetSubAuthorities(this PSID pSid)
|
|
{
|
|
for (uint i = 0; i < AdvApi32.GetSidSubAuthorityCount(pSid); i++)
|
|
yield return AdvApi32.GetSidSubAuthority(pSid, i);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validates a security identifier (SID) by verifying that the revision number is within a known range, and that the number of
|
|
/// subauthorities is less than the maximum.
|
|
/// </summary>
|
|
/// <param name="pSid">A pointer to the SID structure to validate. This parameter cannot be NULL.</param>
|
|
/// <returns>
|
|
/// If the SID structure is valid, the return value is <see langword="true"/>. If the SID structure is not valid, the return value is <see langword="false"/>.
|
|
/// </returns>
|
|
public static bool IsValidSid(this PSID pSid) => AdvApi32.IsValidSid(pSid);
|
|
|
|
/// <summary>Returns the length, in bytes, of a valid security identifier (SID).</summary>
|
|
/// <param name="pSid">A pointer to the SID structure whose length is returned. The structure is assumed to be valid.</param>
|
|
/// <returns>
|
|
/// If the SID structure is valid, the return value is the length, in bytes, of the SID structure. If the SID structure is not valid,
|
|
/// the return value is 0.
|
|
/// </returns>
|
|
public static int Length(this PSID pSid) => AdvApi32.IsValidSid(pSid) ? AdvApi32.GetLengthSid(pSid) : 0;
|
|
|
|
/// <summary>Converts the value of a security identifier (SID) to its equivalent string representation according to the provided format specifier.</summary>
|
|
/// <param name="pSid">A pointer to a valid SID structure.</param>
|
|
/// <param name="format">
|
|
/// A single format specifier that indicates how to format the value of this security identifier (SID). The format parameter can be
|
|
/// "B" (binary), "D" (sddl), "N" (name), or "P" (upn). If format is null or an empty string (""), "D" is used.
|
|
/// </param>
|
|
/// <returns>The value of this security identifier (SID), in the specified format.</returns>
|
|
/// <exception cref="ArgumentException">SID value is not a valid SID. - pSid</exception>
|
|
/// <exception cref="FormatException">The value of format is not null, an empty string (""), "B", "D", "N", or "P".</exception>
|
|
/// <remarks>
|
|
/// <para>The following table shows the accepted format specifiers for the format parameter.</para>
|
|
/// <list type="table">
|
|
/// <item>
|
|
/// <description>Specifier</description>
|
|
/// <description>Format of return value</description>
|
|
/// </item>
|
|
/// <item>
|
|
/// <description>"B"</description>
|
|
/// <description>
|
|
/// <para>Binary hex dump representation of the SID.</para>
|
|
/// </description>
|
|
/// </item>
|
|
/// <item>
|
|
/// <description>"D"</description>
|
|
/// <description>SDDL representation of the SID.</description>
|
|
/// </item>
|
|
/// <item>
|
|
/// <description>"N"</description>
|
|
/// <description>The NT4 style name (domain\username) corresponding to the SID.</description>
|
|
/// </item>
|
|
/// <item>
|
|
/// <description>"P"</description>
|
|
/// <description>The internet style name (UPN) corresponding to the SID.</description>
|
|
/// </item>
|
|
/// </list>
|
|
/// </remarks>
|
|
public static string ToString(this PSID pSid, string format)
|
|
{
|
|
if (!pSid.IsValidSid())
|
|
throw new ArgumentException("SID value is not a valid SID.", nameof(pSid));
|
|
switch (format)
|
|
{
|
|
case "B":
|
|
var len = pSid.Length();
|
|
return pSid.GetBinaryForm().ToHexDumpString(len, len, 0).Trim(' ', '\r', '\n');
|
|
|
|
case "D":
|
|
case null:
|
|
case "":
|
|
try { return AdvApi32.ConvertSidToStringSid(pSid); }
|
|
catch { goto case "B"; }
|
|
|
|
case "N":
|
|
case "P":
|
|
using (var hPol = AdvApi32.LsaOpenPolicy(AdvApi32.LsaPolicyRights.POLICY_ALL_ACCESS))
|
|
{
|
|
var flag = format == "P" ? AdvApi32.LsaLookupSidsFlags.LSA_LOOKUP_PREFER_INTERNET_NAMES : 0;
|
|
try
|
|
{
|
|
AdvApi32.LsaLookupSids2(hPol, flag, 1, new[] { pSid }, out var memDoms, out var memNames).ThrowIfFailed();
|
|
memDoms.Dispose();
|
|
using (memNames)
|
|
{
|
|
return memNames.ToStructure<AdvApi32.LSA_TRANSLATED_NAME>().Name;
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
goto case "D";
|
|
}
|
|
}
|
|
|
|
default:
|
|
throw new FormatException();
|
|
}
|
|
}
|
|
}
|
|
} |