diff --git a/PInvoke/Security/AdvApi32/PSID.cs b/PInvoke/Security/AdvApi32/PSID.cs index 2b60092a..d0cdd0e5 100644 --- a/PInvoke/Security/AdvApi32/PSID.cs +++ b/PInvoke/Security/AdvApi32/PSID.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using Vanara.Extensions; @@ -13,6 +14,7 @@ namespace Vanara.PInvoke { /// Class representation of the native SID structure. /// + [DebuggerDisplay("{DebugString}")] public class SafePSID : SafeMemoryHandle, IEquatable, IEquatable, IEquatable, ICloneable, ISecurityObject { /// Equivalent to a NULL pointer to a SID. @@ -67,6 +69,27 @@ namespace Vanara.PInvoke /// The SID length, in bytes. public int Length => IsValidSid ? GetLengthSid(this) : 0; + /// Gets the string used to display in the debugger. + 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})"; + } + } + + /// Creates a SID for predefined capability. + /// + /// Member of the KnownSIDCapability enumeration that specifies which capability the SID will identify. + /// + /// A instance. + public static SafePSID CreateCapability(KnownSIDCapability capability) => + Init(KnownSIDAuthority.SECURITY_APP_PACKAGE_AUTHORITY, KnownSIDRelativeID.SECURITY_CAPABILITY_BASE_RID, (int)capability); + /// Copies the specified SID from a memory pointer to a instance. /// The SID pointer. This value remains the responsibility of the caller to release. /// A instance. @@ -191,9 +214,46 @@ namespace Vanara.PInvoke /// Returns a that represents this instance. /// A that represents this instance. - public override string ToString() + public override string ToString() => ToString(null); + + /// Converts the value of this security identifier (SID) to its equivalent string representation according to the provided format specifier. + /// + /// A single format specifier that indicates how to format the value of this security identifier (SID). The format parameter can be + /// "B", "D", "N", or "P". If format is null or an empty string (""), "D" is used. + /// + /// The value of this security identifier (SID), in the specified format. + /// SID value is not a valid SID. - pSid + /// The value of format is not null, an empty string (""), "B", "D", "N", or "P". + /// + /// The following table shows the accepted format specifiers for the format parameter. + /// + /// + /// Specifier + /// Format of return value + /// + /// + /// "B" + /// + /// Binary hex dump representation of the SID. + /// + /// + /// + /// "D" + /// SDDL representation of the SID. + /// + /// + /// "N" + /// The NT4 style name (domain\username) corresponding to the SID. + /// + /// + /// "P" + /// The internet style name (UPN) corresponding to the SID. + /// + /// + /// + public string ToString(string format) { - try { return ConvertSidToStringSid(this); } + try { return ((PSID)handle).ToString(format); } catch { return !IsInvalid && !IsClosed ? "Invalid" : "0"; } } @@ -295,4 +355,113 @@ namespace Vanara.PInvoke } } } + + /// Extension methods for PSID instances. + public static class PSIDExtensions + { + /// Determines equality of two PSID instances. + /// The first PSID. + /// The second PSID. + /// if the SID structures are equal; otherwise. + public static bool Equals(this PSID psid1, PSID psid2) => AdvApi32.EqualSid(psid1, psid2); + + /// Gets the binary form of the SID structure. + /// The SID structure pointer. + /// The binary form (byte array) of the SID structure. + public static byte[] GetBinaryForm(this PSID pSid) => pSid.IsValidSid() ? ((IntPtr)pSid).ToArray(pSid.Length()) : (new byte[0]); + + /// + /// 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. + /// + /// A pointer to the SID structure to validate. This parameter cannot be NULL. + /// + /// If the SID structure is valid, the return value is . If the SID structure is not valid, the return value is . + /// + public static bool IsValidSid(this PSID pSid) => AdvApi32.IsValidSid(pSid); + + /// Returns the length, in bytes, of a valid security identifier (SID). + /// A pointer to the SID structure whose length is returned. The structure is assumed to be valid. + /// + /// 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. + /// + public static int Length(this PSID pSid) => AdvApi32.IsValidSid(pSid) ? AdvApi32.GetLengthSid(pSid) : 0; + + /// Converts the value of a security identifier (SID) to its equivalent string representation according to the provided format specifier. + /// A pointer to a valid SID structure. + /// + /// A single format specifier that indicates how to format the value of this security identifier (SID). The format parameter can be + /// "B", "D", "N", or "P". If format is null or an empty string (""), "D" is used. + /// + /// The value of this security identifier (SID), in the specified format. + /// SID value is not a valid SID. - pSid + /// The value of format is not null, an empty string (""), "B", "D", "N", or "P". + /// + /// The following table shows the accepted format specifiers for the format parameter. + /// + /// + /// Specifier + /// Format of return value + /// + /// + /// "B" + /// + /// Binary hex dump representation of the SID. + /// + /// + /// + /// "D" + /// SDDL representation of the SID. + /// + /// + /// "N" + /// The NT4 style name (domain\username) corresponding to the SID. + /// + /// + /// "P" + /// The internet style name (UPN) corresponding to the SID. + /// + /// + /// + 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 "": + return AdvApi32.ConvertSidToStringSid(pSid); + + 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().Name; + } + } + catch (Exception) + { + goto case "D"; + } + } + + default: + throw new FormatException(); + } + } + } } \ No newline at end of file