From c09b2ec246c61df5b4929e7bf8397402df31db67 Mon Sep 17 00:00:00 2001 From: David Hall Date: Fri, 2 Aug 2019 13:42:47 -0600 Subject: [PATCH] Completed unit testing and fixes for aclapi.h functions --- PInvoke/Security/AdvApi32/AclApi.cs | 48 +++++- UnitTests/PInvoke/Security/AdvApi32/AclApiTests.cs | 161 +++++++++++++++++++++ UnitTests/PInvoke/Security/Security.csproj | 1 + 3 files changed, 203 insertions(+), 7 deletions(-) create mode 100644 UnitTests/PInvoke/Security/AdvApi32/AclApiTests.cs diff --git a/PInvoke/Security/AdvApi32/AclApi.cs b/PInvoke/Security/AdvApi32/AclApi.cs index 06457829..aa0cc7b6 100644 --- a/PInvoke/Security/AdvApi32/AclApi.cs +++ b/PInvoke/Security/AdvApi32/AclApi.cs @@ -18,6 +18,41 @@ namespace Vanara.PInvoke [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Unicode)] public delegate void FN_PROGRESS(string pObjectName, uint Status, ref PROG_INVOKE_SETTING pInvokeSetting, IntPtr Args, [MarshalAs(UnmanagedType.Bool)] bool SecuritySet); + /// Flags to control the behavior of . + [PInvokeData("aclapi.h", MSDNShortId = "caa711c3-301b-4ed7-b1f4-dc6a48563905")] + public enum TREE_SEC_INFO + { + /// + /// The security information is set on the object specified by the pObjectName parameter and the tree of child objects of that + /// object. If ACLs are specified in either the pDacl or pSacl parameters, the security descriptors are associated with the + /// object. The security descriptors are propagated to the tree of child objects based on their inheritance properties. + /// + TREE_SEC_INFO_SET = 0x00000001, + + /// + /// The security information is reset on the object specified by the pObjectName parameter and the tree of child objects of that + /// object. Any existing security information is removed from all objects on the tree. + /// + /// If any object in the tree does not grant appropriate permissions to the caller to modify the security descriptor on the + /// object, then the propagation of security information on that particular node of the tree and its objects is skipped. The + /// operation continues on the rest of the tree under the object specified by the pObjectName parameter. + /// + /// + TREE_SEC_INFO_RESET = 0x00000002, + + /// + /// The security information is reset on the object specified by the pObjectName parameter and the tree of child objects of that + /// object. Any existing inherited security information is removed from all objects on the tree. Security information that was + /// explicitly set on objects in the tree is unchanged. + /// + /// If any object in the tree does not grant appropriate permissions to the caller to modify the security descriptor on the + /// object, then the propagation of security information on that particular node of the tree and its objects is skipped. The + /// operation continues on the rest of the tree under the object specified by the pObjectName parameter. + /// + /// + TREE_SEC_INFO_RESET_KEEP_EXPLICIT = 0x00000003, + } + /// /// /// The BuildExplicitAccessWithName function initializes an EXPLICIT_ACCESS structure with data specified by the caller. The @@ -903,8 +938,7 @@ namespace Vanara.PInvoke // https://docs.microsoft.com/en-us/windows/desktop/api/aclapi/nf-aclapi-gettrusteenamea LPSTR GetTrusteeNameA( PTRUSTEE_A pTrustee ); [DllImport(Lib.AdvApi32, SetLastError = false, CharSet = CharSet.Auto)] [PInvokeData("aclapi.h", MSDNShortId = "9d3ce528-fb28-4e2e-bf7f-7d84c697fcb6")] - [return: MarshalAs(UnmanagedType.LPStr)] - public static extern string GetTrusteeName(in TRUSTEE pTrustee); + public static extern StrPtrAuto GetTrusteeName(in TRUSTEE pTrustee); /// /// @@ -1143,8 +1177,8 @@ namespace Vanara.PInvoke /// [DllImport(Lib.AdvApi32, CharSet = CharSet.Auto)] [PInvokeData("Aclapi.h", MSDNShortId = "aa379579")] - public static extern Win32Error SetNamedSecurityInfo(string pObjectName, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID ppsidOwner, - PSID ppsidGroup, PACL ppDacl, PACL ppSacl); + public static extern Win32Error SetNamedSecurityInfo(string pObjectName, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, [Optional] PSID ppsidOwner, + [Optional] PSID ppsidGroup, [Optional] PACL ppDacl, [Optional] PACL ppSacl); /// /// @@ -1256,7 +1290,7 @@ namespace Vanara.PInvoke // SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID psidOwner, PSID psidGroup, PACL pDacl, PACL pSacl ); [DllImport(Lib.AdvApi32, SetLastError = false, ExactSpelling = true)] [PInvokeData("aclapi.h", MSDNShortId = "f1781ba9-81eb-46f9-b530-c390b67d65de")] - public static extern Win32Error SetSecurityInfo(IntPtr handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID psidOwner, PSID psidGroup, PACL pDacl, PACL pSacl); + public static extern Win32Error SetSecurityInfo(IntPtr handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, [Optional] PSID psidOwner, [Optional] PSID psidGroup, [Optional] PACL pDacl, [Optional] PACL pSacl); /// /// @@ -1374,7 +1408,7 @@ namespace Vanara.PInvoke [DllImport(Lib.AdvApi32, SetLastError = false, CharSet = CharSet.Auto)] [PInvokeData("aclapi.h", MSDNShortId = "adae7d07-a452-409e-b1a1-e9f86f873e39")] public static extern Win32Error TreeResetNamedSecurityInfo(string pObjectName, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID pOwner, PSID pGroup, PACL pDacl, PACL pSacl, - [MarshalAs(UnmanagedType.Bool)] bool KeepExplicit, FN_PROGRESS fnProgress, PROG_INVOKE_SETTING ProgressInvokeSetting, IntPtr Args); + [MarshalAs(UnmanagedType.Bool)] bool KeepExplicit, [Optional] FN_PROGRESS fnProgress, PROG_INVOKE_SETTING ProgressInvokeSetting, [Optional] IntPtr Args); /// /// @@ -1527,7 +1561,7 @@ namespace Vanara.PInvoke [DllImport(Lib.AdvApi32, SetLastError = false, CharSet = CharSet.Auto)] [PInvokeData("aclapi.h", MSDNShortId = "caa711c3-301b-4ed7-b1f4-dc6a48563905")] public static extern Win32Error TreeSetNamedSecurityInfo(string pObjectName, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID pOwner, PSID pGroup, - PACL pDacl, PACL pSacl, uint dwAction, FN_PROGRESS fnProgress, PROG_INVOKE_SETTING ProgressInvokeSetting, IntPtr Args); + PACL pDacl, PACL pSacl, TREE_SEC_INFO dwAction, [Optional] FN_PROGRESS fnProgress, PROG_INVOKE_SETTING ProgressInvokeSetting, [Optional] IntPtr Args); /// A to hold the array of instances returned from . public class SafeInheritedFromArray : SafeHGlobalHandle diff --git a/UnitTests/PInvoke/Security/AdvApi32/AclApiTests.cs b/UnitTests/PInvoke/Security/AdvApi32/AclApiTests.cs new file mode 100644 index 00000000..4b3d3cfd --- /dev/null +++ b/UnitTests/PInvoke/Security/AdvApi32/AclApiTests.cs @@ -0,0 +1,161 @@ +using NUnit.Framework; +using System; +using static Vanara.PInvoke.AdvApi32; + +namespace Vanara.PInvoke.Tests +{ + [TestFixture()] + public class AclApiTests + { + public const SECURITY_INFORMATION SecInfoAll = SECURITY_INFORMATION.OWNER_SECURITY_INFORMATION | SECURITY_INFORMATION.GROUP_SECURITY_INFORMATION | SECURITY_INFORMATION.DACL_SECURITY_INFORMATION | SECURITY_INFORMATION.SACL_SECURITY_INFORMATION; + public static readonly string localAdmins = $"{Environment.MachineName}\\Administrators"; + public static readonly SafePSECURITY_DESCRIPTOR pSd; + public static readonly string userName = $"{Environment.UserDomainName}\\{Environment.UserName}"; + + static AclApiTests() + { + using (new PrivBlock("SeSecurityPrivilege")) + pSd = AdvApi32Tests.GetSD(AdvApi32Tests.fn, SecInfoAll); + } + + [Test] + public void BuildExplicitAccessWithNameTest() + { + EXPLICIT_ACCESS ea = default; + Assert.That(() => BuildExplicitAccessWithName(out ea, userName, 0x10000000, ACCESS_MODE.SET_ACCESS, INHERIT_FLAGS.SUB_CONTAINERS_AND_OBJECTS_INHERIT), Throws.Nothing); + Assert.That(ea.grfAccessMode, Is.Not.Zero); + ea.WriteValues(); + } + + [Test] + public void BuildSecurityDescriptorTest() + { + SafePSECURITY_DESCRIPTOR pSd = null; + Assert.That(() => + { + BuildTrusteeWithName(out var trustee, userName); + BuildTrusteeWithName(out var grpTrustee, localAdmins); + BuildExplicitAccessWithName(out var ea, userName, 0x10000000, ACCESS_MODE.SET_ACCESS, INHERIT_FLAGS.SUB_CONTAINERS_AND_OBJECTS_INHERIT); + BuildSecurityDescriptor(trustee, grpTrustee, 1, new[] { ea }, 0, null, PSECURITY_DESCRIPTOR.NULL, out var sz, out pSd); + }, Throws.Nothing); + Assert.That(pSd, Is.Not.Null); + Assert.That(pSd.IsInvalid, Is.False); + pSd.Dispose(); + } + + [Test] + public void BuildTrusteeWithObjectsAndNameTest() + { + Assert.That(() => BuildTrusteeWithObjectsAndName(out var t, default, SE_OBJECT_TYPE.SE_FILE_OBJECT, "", "", "Name"), Throws.Nothing); + } + + [Test] + public void BuildTrusteeWithObjectsAndSidTest() + { + Assert.That(() => BuildTrusteeWithObjectsAndSid(out var t, default, default, default, PSID.NULL), Throws.Nothing); + } + + [Test] + public void BuildTrusteeWithSidTest() + { + Assert.That(() => BuildTrusteeWithSid(out var t, PSID.NULL), Throws.Nothing); + } + + [Test] + public void GetAuditedPermissionsFromAclTest() + { + Assert.That(GetSecurityDescriptorSacl(pSd, out var ok, out var pSacl, out _), ResultIs.Successful); + BuildTrusteeWithName(out var trustee, userName); + Assert.That(GetAuditedPermissionsFromAcl(pSacl, trustee, out var smask, out var fmask), ResultIs.Successful); + (smask, fmask).WriteValues(); + } + + [Test] + public void GetExplicitEntriesFromAclTest() + { + Assert.That(GetSecurityDescriptorDacl(pSd, out var ok, out var pDacl, out _), ResultIs.Successful); + Assert.That(GetExplicitEntriesFromAcl(pDacl, out var cnt, out var memList), ResultIs.Successful); + using (memList) + Assert.That(() => memList.ToArray((int)cnt).WriteValues(), Throws.Nothing); + } + + [Test] + public void GetSetSecurityInfoTest() + { + using (var tmp = new TempFile(Kernel32.FileAccess.FILE_ALL_ACCESS, System.IO.FileShare.Read)) + { + Assert.That(GetSecurityInfo(tmp.hFile.DangerousGetHandle(), SE_OBJECT_TYPE.SE_FILE_OBJECT, SECURITY_INFORMATION.OWNER_SECURITY_INFORMATION, out var owner, out _, out _, out _, out var plsd), ResultIs.Successful); + Assert.That(SetSecurityInfo(tmp.hFile.DangerousGetHandle(), SE_OBJECT_TYPE.SE_FILE_OBJECT, SECURITY_INFORMATION.OWNER_SECURITY_INFORMATION, owner), ResultIs.Successful); + } + } + + [Test] + public void GetTrusteeFormTest() + { + BuildTrusteeWithName(out var trustee, userName); + Assert.That(GetTrusteeForm(trustee), ResultIs.Value(TRUSTEE_FORM.TRUSTEE_IS_NAME)); + } + + [Test] + public void GetTrusteeNameTest() + { + BuildTrusteeWithName(out var trustee, userName); + var s = GetTrusteeName(trustee); + Assert.That(s.ToString(), ResultIs.Value(userName)); + } + + [Test] + public void GetTrusteeTypeTest() + { + BuildTrusteeWithName(out var trustee, userName); + Assert.That(GetTrusteeType(trustee), ResultIs.Value(TRUSTEE_TYPE.TRUSTEE_IS_UNKNOWN)); + } + + [Test] + public void LookupSecurityDescriptorPartsTest() + { + Assert.That(LookupSecurityDescriptorParts(out var ptOwner, out var ptGrp, out var cnt, out var plEntries, out var acnt, out var plAEntries, pSd), ResultIs.Successful); + ptOwner.ToStructure().WriteValues(); + ptGrp.ToStructure().WriteValues(); + plEntries.ToArray((int)cnt).WriteValues(); + plAEntries.ToArray((int)acnt).WriteValues(); + } + + [Test] + public void SetEntriesInAclTest() + { + Assert.That(GetSecurityDescriptorDacl(pSd, out _, out var pDacl, out _), ResultIs.Successful); + BuildExplicitAccessWithName(out var ea, $"{Environment.MachineName}\\Invalid", 0x10000000, ACCESS_MODE.SET_ACCESS, 0); + var entries = new[] { ea }; + Assert.That(SetEntriesInAcl((uint)entries.Length, entries, pDacl, out var pNewAcl), ResultIs.FailureCode(Win32Error.ERROR_NONE_MAPPED)); + } + + [Test] + public void TreeResetNamedSecurityInfoTest() + { + var counter = 0; + using (new PrivBlock("SeSecurityPrivilege")) + { + Assert.That(GetNamedSecurityInfo(AdvApi32Tests.fn, SE_OBJECT_TYPE.SE_FILE_OBJECT, SecInfoAll, out var pOwnSid, out var pGrpSid, out var dacl, out var sacl, out var plsd), ResultIs.Successful); + Assert.That(TreeResetNamedSecurityInfo(@"C:\Temp\Temp\", SE_OBJECT_TYPE.SE_FILE_OBJECT, SecInfoAll, pOwnSid, pGrpSid, dacl, sacl, false, OnProgress, PROG_INVOKE_SETTING.ProgressInvokeEveryObject), ResultIs.Successful); + } + Assert.That(counter, Is.GreaterThan(0)); + + void OnProgress(string pObjectName, uint Status, ref PROG_INVOKE_SETTING pInvokeSetting, IntPtr Args, bool SecuritySet) { counter++; } + } + + [Test] + public void TreeSetNamedSecurityInfoTest() + { + var counter = 0; + using (new PrivBlock("SeSecurityPrivilege")) + { + Assert.That(GetNamedSecurityInfo(AdvApi32Tests.fn, SE_OBJECT_TYPE.SE_FILE_OBJECT, SecInfoAll, out var pOwnSid, out var pGrpSid, out var dacl, out var sacl, out var plsd), ResultIs.Successful); + Assert.That(TreeSetNamedSecurityInfo(@"C:\Temp\Temp\", SE_OBJECT_TYPE.SE_FILE_OBJECT, SecInfoAll, pOwnSid, pGrpSid, dacl, sacl, TREE_SEC_INFO.TREE_SEC_INFO_SET, OnProgress, PROG_INVOKE_SETTING.ProgressInvokeEveryObject), ResultIs.Successful); + } + Assert.That(counter, Is.GreaterThan(0)); + + void OnProgress(string pObjectName, uint Status, ref PROG_INVOKE_SETTING pInvokeSetting, IntPtr Args, bool SecuritySet) { counter++; } + } + } +} \ No newline at end of file diff --git a/UnitTests/PInvoke/Security/Security.csproj b/UnitTests/PInvoke/Security/Security.csproj index 4e19fda0..b1eb61c9 100644 --- a/UnitTests/PInvoke/Security/Security.csproj +++ b/UnitTests/PInvoke/Security/Security.csproj @@ -40,6 +40,7 @@ +