summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJordan Borean <jborean93@gmail.com>2018-12-14 03:00:46 +0100
committerGitHub <noreply@github.com>2018-12-14 03:00:46 +0100
commit4019d4f6d175b8a68d42615a212375e950a11eae (patch)
tree5966b692f340526211049f7a7dc61339a1017598
parentansible-test: do not upgrade homebrew to speed up tests (#49914) (diff)
downloadansible-4019d4f6d175b8a68d42615a212375e950a11eae.tar.xz
ansible-4019d4f6d175b8a68d42615a212375e950a11eae.zip
Windows Privileges - moved util code to it's own C# util (#48897)
* Windows Privileges - moved util code to it's own C# util * Rename Enabler class to PrivilegeEnabler to remove ambiguity * rename Utils to PrivilegeUtil * fix missing util name changes
-rw-r--r--changelogs/fragments/win_privileges_util.yaml2
-rw-r--r--lib/ansible/module_utils/csharp/Ansible.Privilege.cs443
-rw-r--r--lib/ansible/module_utils/powershell/Ansible.ModuleUtils.LinkUtil.psm15
-rw-r--r--lib/ansible/module_utils/powershell/Ansible.ModuleUtils.PrivilegeUtil.psm1436
-rw-r--r--lib/ansible/modules/windows/win_acl.ps11
-rw-r--r--lib/ansible/modules/windows/win_regedit.ps11
-rw-r--r--test/integration/targets/win_csharp_utils/library/ansible_privilege_tests.ps1324
-rw-r--r--test/integration/targets/win_csharp_utils/tasks/main.yml9
-rw-r--r--test/integration/targets/win_module_utils/library/privilege_util_test.ps178
9 files changed, 811 insertions, 488 deletions
diff --git a/changelogs/fragments/win_privileges_util.yaml b/changelogs/fragments/win_privileges_util.yaml
new file mode 100644
index 0000000000..097f04839d
--- /dev/null
+++ b/changelogs/fragments/win_privileges_util.yaml
@@ -0,0 +1,2 @@
+minor_changes:
+- Ansible.ModuleUtils.Privilege - moved C# code to it's own util called ``Ansible.Privilege`` and expanded the tests
diff --git a/lib/ansible/module_utils/csharp/Ansible.Privilege.cs b/lib/ansible/module_utils/csharp/Ansible.Privilege.cs
new file mode 100644
index 0000000000..2c0b266bf7
--- /dev/null
+++ b/lib/ansible/module_utils/csharp/Ansible.Privilege.cs
@@ -0,0 +1,443 @@
+using Microsoft.Win32.SafeHandles;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.ConstrainedExecution;
+using System.Runtime.InteropServices;
+using System.Security.Principal;
+using System.Text;
+
+namespace Ansible.Privilege
+{
+ internal class NativeHelpers
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ public struct LUID
+ {
+ public UInt32 LowPart;
+ public Int32 HighPart;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct LUID_AND_ATTRIBUTES
+ {
+ public LUID Luid;
+ public PrivilegeAttributes Attributes;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct TOKEN_PRIVILEGES
+ {
+ public UInt32 PrivilegeCount;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
+ public LUID_AND_ATTRIBUTES[] Privileges;
+ }
+ }
+
+ internal class NativeMethods
+ {
+ [DllImport("advapi32.dll", SetLastError = true)]
+ public static extern bool AdjustTokenPrivileges(
+ SafeNativeHandle TokenHandle,
+ [MarshalAs(UnmanagedType.Bool)] bool DisableAllPrivileges,
+ SafeMemoryBuffer NewState,
+ UInt32 BufferLength,
+ SafeMemoryBuffer PreviousState,
+ out UInt32 ReturnLength);
+
+ [DllImport("kernel32.dll")]
+ public static extern bool CloseHandle(
+ IntPtr hObject);
+
+ [DllImport("kernel32")]
+ public static extern SafeWaitHandle GetCurrentProcess();
+
+ [DllImport("advapi32.dll", SetLastError = true)]
+ public static extern bool GetTokenInformation(
+ SafeNativeHandle TokenHandle,
+ UInt32 TokenInformationClass,
+ SafeMemoryBuffer TokenInformation,
+ UInt32 TokenInformationLength,
+ out UInt32 ReturnLength);
+
+ [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern bool LookupPrivilegeName(
+ string lpSystemName,
+ ref NativeHelpers.LUID lpLuid,
+ StringBuilder lpName,
+ ref UInt32 cchName);
+
+ [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern bool LookupPrivilegeValue(
+ string lpSystemName,
+ string lpName,
+ out NativeHelpers.LUID lpLuid);
+
+ [DllImport("advapi32.dll", SetLastError = true)]
+ public static extern bool OpenProcessToken(
+ SafeHandle ProcessHandle,
+ TokenAccessLevels DesiredAccess,
+ out SafeNativeHandle TokenHandle);
+ }
+
+ internal class SafeMemoryBuffer : SafeHandleZeroOrMinusOneIsInvalid
+ {
+ public SafeMemoryBuffer() : base(true) { }
+ public SafeMemoryBuffer(int cb) : base(true)
+ {
+ base.SetHandle(Marshal.AllocHGlobal(cb));
+ }
+ public SafeMemoryBuffer(IntPtr handle) : base(true)
+ {
+ base.SetHandle(handle);
+ }
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ protected override bool ReleaseHandle()
+ {
+ Marshal.FreeHGlobal(handle);
+ return true;
+ }
+ }
+
+ internal class SafeNativeHandle : SafeHandleZeroOrMinusOneIsInvalid
+ {
+ public SafeNativeHandle() : base(true) { }
+ public SafeNativeHandle(IntPtr handle) : base(true) { this.handle = handle; }
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ protected override bool ReleaseHandle()
+ {
+ return NativeMethods.CloseHandle(handle);
+ }
+ }
+
+ public class Win32Exception : System.ComponentModel.Win32Exception
+ {
+ private string _msg;
+ public Win32Exception(string message) : this(Marshal.GetLastWin32Error(), message) { }
+ public Win32Exception(int errorCode, string message) : base(errorCode)
+ {
+ _msg = String.Format("{0} ({1}, Win32ErrorCode {2})", message, base.Message, errorCode);
+ }
+ public override string Message { get { return _msg; } }
+ public static explicit operator Win32Exception(string message) { return new Win32Exception(message); }
+ }
+
+ [Flags]
+ public enum PrivilegeAttributes : uint
+ {
+ Disabled = 0x00000000,
+ EnabledByDefault = 0x00000001,
+ Enabled = 0x00000002,
+ Removed = 0x00000004,
+ UsedForAccess = 0x80000000,
+ }
+
+ public class PrivilegeEnabler : IDisposable
+ {
+ private SafeHandle process;
+ private Dictionary<string, bool?> previousState;
+
+ /// <summary>
+ /// Temporarily enables the privileges specified and reverts once the class is disposed.
+ /// </summary>
+ /// <param name="strict">Whether to fail if any privilege failed to be enabled, if false then this will continue silently</param>
+ /// <param name="privileges">A list of privileges to enable</param>
+ public PrivilegeEnabler(bool strict, params string[] privileges)
+ {
+ if (privileges.Length > 0)
+ {
+ process = PrivilegeUtil.GetCurrentProcess();
+ Dictionary<string, bool?> newState = new Dictionary<string, bool?>();
+ for (int i = 0; i < privileges.Length; i++)
+ newState.Add(privileges[i], true);
+ try
+ {
+ previousState = PrivilegeUtil.SetTokenPrivileges(process, newState, strict);
+ }
+ catch (Win32Exception e)
+ {
+ throw new Win32Exception(e.NativeErrorCode, String.Format("Failed to enable privilege(s) {0}", String.Join(", ", privileges)));
+ }
+ }
+ }
+
+ public void Dispose()
+ {
+ // disables any privileges that were enabled by this class
+ if (previousState != null)
+ PrivilegeUtil.SetTokenPrivileges(process, previousState);
+ GC.SuppressFinalize(this);
+ }
+ ~PrivilegeEnabler() { this.Dispose(); }
+ }
+
+ public class PrivilegeUtil
+ {
+ private static readonly UInt32 TOKEN_PRIVILEGES = 3;
+
+ /// <summary>
+ /// Checks if the specific privilege constant is a valid privilege name
+ /// </summary>
+ /// <param name="name">The privilege constant (Se*Privilege) is valid</param>
+ /// <returns>true if valid, else false</returns>
+ public static bool CheckPrivilegeName(string name)
+ {
+ NativeHelpers.LUID luid;
+ if (!NativeMethods.LookupPrivilegeValue(null, name, out luid))
+ {
+ int errCode = Marshal.GetLastWin32Error();
+ if (errCode != 1313) // ERROR_NO_SUCH_PRIVILEGE
+ throw new Win32Exception(errCode, String.Format("LookupPrivilegeValue({0}) failed", name));
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Disables the privilege specified
+ /// </summary>
+ /// <param name="token">The process token to that contains the privilege to disable</param>
+ /// <param name="privilege">The privilege constant to disable</param>
+ /// <returns>The previous state that can be passed to SetTokenPrivileges to revert the action</returns>
+ public static Dictionary<string, bool?> DisablePrivilege(SafeHandle token, string privilege)
+ {
+ return SetTokenPrivileges(token, new Dictionary<string, bool?>() { { privilege, false } });
+ }
+
+ /// <summary>
+ /// Disables all the privileges
+ /// </summary>
+ /// <param name="token">The process token to that contains the privilege to disable</param>
+ /// <returns>The previous state that can be passed to SetTokenPrivileges to revert the action</returns>
+ public static Dictionary<string, bool?> DisableAllPrivileges(SafeHandle token)
+ {
+ return AdjustTokenPrivileges(token, null, false);
+ }
+
+ /// <summary>
+ /// Enables the privilege specified
+ /// </summary>
+ /// <param name="token">The process token to that contains the privilege to enable</param>
+ /// <param name="privilege">The privilege constant to enable</param>
+ /// <returns>The previous state that can be passed to SetTokenPrivileges to revert the action</returns>
+ public static Dictionary<string, bool?> EnablePrivilege(SafeHandle token, string privilege)
+ {
+ return SetTokenPrivileges(token, new Dictionary<string, bool?>() { { privilege, true } });
+ }
+
+ /// <summary>
+ /// Get's the status of all the privileges on the token specified
+ /// </summary>
+ /// <param name="token">The process token to get the privilege status on</param>
+ /// <returns>Dictionary where the key is the privilege constant and the value is the PrivilegeAttributes flags</returns>
+ public static Dictionary<String, PrivilegeAttributes> GetAllPrivilegeInfo(SafeHandle token)
+ {
+ SafeNativeHandle hToken = null;
+ if (!NativeMethods.OpenProcessToken(token, TokenAccessLevels.Query, out hToken))
+ throw new Win32Exception("OpenProcessToken() failed");
+
+ using (hToken)
+ {
+ UInt32 tokenLength = 0;
+ NativeMethods.GetTokenInformation(hToken, TOKEN_PRIVILEGES, new SafeMemoryBuffer(0), 0, out tokenLength);
+
+ NativeHelpers.LUID_AND_ATTRIBUTES[] privileges;
+ using (SafeMemoryBuffer privilegesPtr = new SafeMemoryBuffer((int)tokenLength))
+ {
+ if (!NativeMethods.GetTokenInformation(hToken, TOKEN_PRIVILEGES, privilegesPtr, tokenLength, out tokenLength))
+ throw new Win32Exception("GetTokenInformation() for TOKEN_PRIVILEGES failed");
+
+ NativeHelpers.TOKEN_PRIVILEGES privilegeInfo = (NativeHelpers.TOKEN_PRIVILEGES)Marshal.PtrToStructure(
+ privilegesPtr.DangerousGetHandle(), typeof(NativeHelpers.TOKEN_PRIVILEGES));
+ privileges = new NativeHelpers.LUID_AND_ATTRIBUTES[privilegeInfo.PrivilegeCount];
+ PtrToStructureArray(privileges, IntPtr.Add(privilegesPtr.DangerousGetHandle(), Marshal.SizeOf(privilegeInfo.PrivilegeCount)));
+ }
+
+ return privileges.ToDictionary(p => GetPrivilegeName(p.Luid), p => p.Attributes);
+ }
+ }
+
+ /// <summary>
+ /// Get a handle to the current process for use with the methods above
+ /// </summary>
+ /// <returns>SafeWaitHandle handle of the current process token</returns>
+ public static SafeWaitHandle GetCurrentProcess()
+ {
+ return NativeMethods.GetCurrentProcess();
+ }
+
+ /// <summary>
+ /// Removes a privilege from the token. This operation is irreversible
+ /// </summary>
+ /// <param name="token">The process token to that contains the privilege to remove</param>
+ /// <param name="privilege">The privilege constant to remove</param>
+ public static void RemovePrivilege(SafeHandle token, string privilege)
+ {
+ SetTokenPrivileges(token, new Dictionary<string, bool?>() { { privilege, null } });
+ }
+
+ /// <summary>
+ /// Do a bulk set of multiple privileges
+ /// </summary>
+ /// <param name="token">The process token to use when setting the privilege state</param>
+ /// <param name="state">A dictionary that contains the privileges to set, the key is the constant name and the value can be;
+ /// true - enable the privilege
+ /// false - disable the privilege
+ /// null - remove the privilege (this cannot be reversed)
+ /// </param>
+ /// <param name="strict">When true, will fail if one privilege failed to be set, otherwise it will silently continue</param>
+ /// <returns>The previous state that can be passed to SetTokenPrivileges to revert the action</returns>
+ public static Dictionary<string, bool?> SetTokenPrivileges(SafeHandle token, IDictionary state, bool strict = true)
+ {
+ NativeHelpers.LUID_AND_ATTRIBUTES[] privilegeAttr = new NativeHelpers.LUID_AND_ATTRIBUTES[state.Count];
+ int i = 0;
+
+ foreach (DictionaryEntry entry in state)
+ {
+ string key = (string)entry.Key;
+ NativeHelpers.LUID luid;
+ if (!NativeMethods.LookupPrivilegeValue(null, key, out luid))
+ throw new Win32Exception(String.Format("LookupPrivilegeValue({0}) failed", key));
+
+ PrivilegeAttributes attributes;
+ switch ((bool?)entry.Value)
+ {
+ case true:
+ attributes = PrivilegeAttributes.Enabled;
+ break;
+ case false:
+ attributes = PrivilegeAttributes.Disabled;
+ break;
+ default:
+ attributes = PrivilegeAttributes.Removed;
+ break;
+ }
+
+ privilegeAttr[i].Luid = luid;
+ privilegeAttr[i].Attributes = attributes;
+ i++;
+ }
+
+ return AdjustTokenPrivileges(token, privilegeAttr, strict);
+ }
+
+ private static Dictionary<string, bool?> AdjustTokenPrivileges(SafeHandle token, NativeHelpers.LUID_AND_ATTRIBUTES[] newState, bool strict)
+ {
+ bool disableAllPrivileges;
+ SafeMemoryBuffer newStatePtr;
+ NativeHelpers.LUID_AND_ATTRIBUTES[] oldStatePrivileges;
+ UInt32 returnLength;
+
+ if (newState == null)
+ {
+ disableAllPrivileges = true;
+ newStatePtr = new SafeMemoryBuffer(0);
+ }
+ else
+ {
+ disableAllPrivileges = false;
+
+ // Need to manually marshal the bytes requires for newState as the constant size
+ // of LUID_AND_ATTRIBUTES is set to 1 and can't be overridden at runtime, TOKEN_PRIVILEGES
+ // always contains at least 1 entry so we need to calculate the extra size if there are
+ // nore than 1 LUID_AND_ATTRIBUTES entry
+ int tokenPrivilegesSize = Marshal.SizeOf(typeof(NativeHelpers.TOKEN_PRIVILEGES));
+ int luidAttrSize = 0;
+ if (newState.Length > 1)
+ luidAttrSize = Marshal.SizeOf(typeof(NativeHelpers.LUID_AND_ATTRIBUTES)) * (newState.Length - 1);
+ int totalSize = tokenPrivilegesSize + luidAttrSize;
+ byte[] newStateBytes = new byte[totalSize];
+
+ // get the first entry that includes the struct details
+ NativeHelpers.TOKEN_PRIVILEGES tokenPrivileges = new NativeHelpers.TOKEN_PRIVILEGES()
+ {
+ PrivilegeCount = (UInt32)newState.Length,
+ Privileges = new NativeHelpers.LUID_AND_ATTRIBUTES[1],
+ };
+ if (newState.Length > 0)
+ tokenPrivileges.Privileges[0] = newState[0];
+ int offset = StructureToBytes(tokenPrivileges, newStateBytes, 0);
+
+ // copy the remaining LUID_AND_ATTRIBUTES (if any)
+ for (int i = 1; i < newState.Length; i++)
+ offset += StructureToBytes(newState[i], newStateBytes, offset);
+
+ // finally create the pointer to the byte array we just created
+ newStatePtr = new SafeMemoryBuffer(newStateBytes.Length);
+ Marshal.Copy(newStateBytes, 0, newStatePtr.DangerousGetHandle(), newStateBytes.Length);
+ }
+
+ using (newStatePtr)
+ {
+ SafeNativeHandle hToken;
+ if (!NativeMethods.OpenProcessToken(token, TokenAccessLevels.Query | TokenAccessLevels.AdjustPrivileges, out hToken))
+ throw new Win32Exception("OpenProcessToken() failed with Query and AdjustPrivileges");
+
+ using (hToken)
+ {
+ if (!NativeMethods.AdjustTokenPrivileges(hToken, disableAllPrivileges, newStatePtr, 0, new SafeMemoryBuffer(0), out returnLength))
+ {
+ int errCode = Marshal.GetLastWin32Error();
+ if (errCode != 122) // ERROR_INSUFFICIENT_BUFFER
+ throw new Win32Exception(errCode, "AdjustTokenPrivileges() failed to get old state size");
+ }
+
+ using (SafeMemoryBuffer oldStatePtr = new SafeMemoryBuffer((int)returnLength))
+ {
+ bool res = NativeMethods.AdjustTokenPrivileges(hToken, disableAllPrivileges, newStatePtr, returnLength, oldStatePtr, out returnLength);
+ int errCode = Marshal.GetLastWin32Error();
+
+ // even when res == true, ERROR_NOT_ALL_ASSIGNED may be set as the last error code
+ // fail if we are running with strict, otherwise ignore those privileges
+ if (!res || ((strict && errCode != 0) || (!strict && !(errCode == 0 || errCode == 0x00000514))))
+ throw new Win32Exception(errCode, "AdjustTokenPrivileges() failed");
+
+ // Marshal the oldStatePtr to the struct
+ NativeHelpers.TOKEN_PRIVILEGES oldState = (NativeHelpers.TOKEN_PRIVILEGES)Marshal.PtrToStructure(
+ oldStatePtr.DangerousGetHandle(), typeof(NativeHelpers.TOKEN_PRIVILEGES));
+ oldStatePrivileges = new NativeHelpers.LUID_AND_ATTRIBUTES[oldState.PrivilegeCount];
+ PtrToStructureArray(oldStatePrivileges, IntPtr.Add(oldStatePtr.DangerousGetHandle(), Marshal.SizeOf(oldState.PrivilegeCount)));
+ }
+ }
+ }
+
+ return oldStatePrivileges.ToDictionary(p => GetPrivilegeName(p.Luid), p => (bool?)p.Attributes.HasFlag(PrivilegeAttributes.Enabled));
+ }
+
+ private static string GetPrivilegeName(NativeHelpers.LUID luid)
+ {
+ UInt32 nameLen = 0;
+ NativeMethods.LookupPrivilegeName(null, ref luid, null, ref nameLen);
+
+ StringBuilder name = new StringBuilder((int)(nameLen + 1));
+ if (!NativeMethods.LookupPrivilegeName(null, ref luid, name, ref nameLen))
+ throw new Win32Exception("LookupPrivilegeName() failed");
+
+ return name.ToString();
+ }
+
+ private static void PtrToStructureArray<T>(T[] array, IntPtr ptr)
+ {
+ IntPtr ptrOffset = ptr;
+ for (int i = 0; i < array.Length; i++, ptrOffset = IntPtr.Add(ptrOffset, Marshal.SizeOf(typeof(T))))
+ array[i] = (T)Marshal.PtrToStructure(ptrOffset, typeof(T));
+ }
+
+ private static int StructureToBytes<T>(T structure, byte[] array, int offset)
+ {
+ int size = Marshal.SizeOf(structure);
+ using (SafeMemoryBuffer structPtr = new SafeMemoryBuffer(size))
+ {
+ Marshal.StructureToPtr(structure, structPtr.DangerousGetHandle(), false);
+ Marshal.Copy(structPtr.DangerousGetHandle(), array, offset, size);
+ }
+
+ return size;
+ }
+ }
+}
+
diff --git a/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.LinkUtil.psm1 b/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.LinkUtil.psm1
index c18adc6496..b6f4ad9955 100644
--- a/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.LinkUtil.psm1
+++ b/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.LinkUtil.psm1
@@ -1,5 +1,5 @@
- # Copyright (c) 2017 Ansible Project
- # Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
+# Copyright (c) 2017 Ansible Project
+# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
#Requires -Module Ansible.ModuleUtils.PrivilegeUtil
@@ -408,7 +408,6 @@ namespace Ansible
Add-Type -TypeDefinition $link_util
$env:TMP = $original_tmp
- Import-PrivilegeUtil
# enable the SeBackupPrivilege if it is disabled
$state = Get-AnsiblePrivilege -Name SeBackupPrivilege
if ($state -eq $false) {
diff --git a/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.PrivilegeUtil.psm1 b/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.PrivilegeUtil.psm1
index 9152d51411..03cebe75b9 100644
--- a/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.PrivilegeUtil.psm1
+++ b/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.PrivilegeUtil.psm1
@@ -1,424 +1,24 @@
# Copyright (c) 2018 Ansible Project
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
-# store in separate variables to make it easier for other module_utils to
-# share this code in their own c# code
-$ansible_privilege_util_namespaces = @(
- "Microsoft.Win32.SafeHandles",
- "System",
- "System.Collections.Generic",
- "System.Linq",
- "System.Runtime.InteropServices",
- "System.Security.Principal",
- "System.Text"
-)
-
-$ansible_privilege_util_code = @'
-namespace Ansible.PrivilegeUtil
-{
- [Flags]
- public enum PrivilegeAttributes : uint
- {
- Disabled = 0x00000000,
- EnabledByDefault = 0x00000001,
- Enabled = 0x00000002,
- Removed = 0x00000004,
- UsedForAccess = 0x80000000,
- }
-
- internal class NativeHelpers
- {
- [StructLayout(LayoutKind.Sequential)]
- internal struct LUID
- {
- public UInt32 LowPart;
- public Int32 HighPart;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- internal struct LUID_AND_ATTRIBUTES
- {
- public LUID Luid;
- public PrivilegeAttributes Attributes;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- internal struct TOKEN_PRIVILEGES
- {
- public UInt32 PrivilegeCount;
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
- public LUID_AND_ATTRIBUTES[] Privileges;
- }
- }
-
- internal class NativeMethods
- {
- [DllImport("advapi32.dll", SetLastError = true)]
- internal static extern bool AdjustTokenPrivileges(
- IntPtr TokenHandle,
- [MarshalAs(UnmanagedType.Bool)] bool DisableAllPrivileges,
- IntPtr NewState,
- UInt32 BufferLength,
- IntPtr PreviousState,
- out UInt32 ReturnLength);
-
- [DllImport("kernel32.dll")]
- internal static extern bool CloseHandle(
- IntPtr hObject);
-
- [DllImport("kernel32")]
- internal static extern SafeWaitHandle GetCurrentProcess();
-
- [DllImport("advapi32.dll", SetLastError = true)]
- internal static extern bool GetTokenInformation(
- IntPtr TokenHandle,
- UInt32 TokenInformationClass,
- IntPtr TokenInformation,
- UInt32 TokenInformationLength,
- out UInt32 ReturnLength);
-
- [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
- internal static extern bool LookupPrivilegeName(
- string lpSystemName,
- ref NativeHelpers.LUID lpLuid,
- StringBuilder lpName,
- ref UInt32 cchName);
-
- [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
- internal static extern bool LookupPrivilegeValue(
- string lpSystemName,
- string lpName,
- out NativeHelpers.LUID lpLuid);
-
- [DllImport("advapi32.dll", SetLastError = true)]
- internal static extern bool OpenProcessToken(
- SafeHandle ProcessHandle,
- TokenAccessLevels DesiredAccess,
- out IntPtr TokenHandle);
- }
-
- public class Win32Exception : System.ComponentModel.Win32Exception
- {
- private string _msg;
- public Win32Exception(string message) : this(Marshal.GetLastWin32Error(), message) { }
- public Win32Exception(int errorCode, string message) : base(errorCode)
- {
- _msg = String.Format("{0} ({1}, Win32ErrorCode {2})", message, base.Message, errorCode);
- }
- public override string Message { get { return _msg; } }
- public static explicit operator Win32Exception(string message) { return new Win32Exception(message); }
- }
-
- public class Privileges
- {
- private static readonly UInt32 TOKEN_PRIVILEGES = 3;
-
-
- public static bool CheckPrivilegeName(string name)
- {
- NativeHelpers.LUID luid;
- if (!NativeMethods.LookupPrivilegeValue(null, name, out luid))
- {
- int errCode = Marshal.GetLastWin32Error();
- if (errCode != 1313) // ERROR_NO_SUCH_PRIVILEGE
- throw new Win32Exception(errCode, String.Format("LookupPrivilegeValue({0}) failed", name));
- return false;
- }
- else
- {
- return true;
- }
- }
-
- public static Dictionary<string, bool?> DisablePrivilege(SafeHandle token, string privilege)
- {
- return SetTokenPrivileges(token, new Dictionary<string, bool?>() { { privilege, false } });
- }
-
- public static Dictionary<string, bool?> DisableAllPrivileges(SafeHandle token)
- {
- return AdjustTokenPrivileges(token, null);
- }
-
- public static Dictionary<string, bool?> EnablePrivilege(SafeHandle token, string privilege)
- {
- return SetTokenPrivileges(token, new Dictionary<string, bool?>() { { privilege, true } });
- }
-
- public static Dictionary<String, PrivilegeAttributes> GetAllPrivilegeInfo(SafeHandle token)
- {
- IntPtr hToken = IntPtr.Zero;
- if (!NativeMethods.OpenProcessToken(token, TokenAccessLevels.Query, out hToken))
- throw new Win32Exception("OpenProcessToken() failed");
-
- Dictionary<String, PrivilegeAttributes> info = new Dictionary<String, PrivilegeAttributes>();
- try
- {
- UInt32 tokenLength = 0;
- NativeMethods.GetTokenInformation(hToken, TOKEN_PRIVILEGES, IntPtr.Zero, 0, out tokenLength);
-
- NativeHelpers.LUID_AND_ATTRIBUTES[] privileges;
- IntPtr privilegesPtr = Marshal.AllocHGlobal((int)tokenLength);
- try
- {
- if (!NativeMethods.GetTokenInformation(hToken, TOKEN_PRIVILEGES, privilegesPtr, tokenLength, out tokenLength))
- throw new Win32Exception("GetTokenInformation() for TOKEN_PRIVILEGES failed");
-
- NativeHelpers.TOKEN_PRIVILEGES privilegeInfo = (NativeHelpers.TOKEN_PRIVILEGES)Marshal.PtrToStructure(privilegesPtr, typeof(NativeHelpers.TOKEN_PRIVILEGES));
- privileges = new NativeHelpers.LUID_AND_ATTRIBUTES[privilegeInfo.PrivilegeCount];
- PtrToStructureArray(privileges, IntPtr.Add(privilegesPtr, Marshal.SizeOf(privilegeInfo.PrivilegeCount)));
- }
- finally
- {
- Marshal.FreeHGlobal(privilegesPtr);
- }
-
- info = privileges.ToDictionary(p => GetPrivilegeName(p.Luid), p => p.Attributes);
- }
- finally
- {
- NativeMethods.CloseHandle(hToken);
- }
- return info;
- }
-
- public static SafeWaitHandle GetCurrentProcess()
- {
- return NativeMethods.GetCurrentProcess();
- }
-
- public static void RemovePrivilege(SafeHandle token, string privilege)
- {
- SetTokenPrivileges(token, new Dictionary<string, bool?>() { { privilege, null } });
- }
-
- public static Dictionary<string, bool?> SetTokenPrivileges(SafeHandle token, Dictionary<string, bool?> state)
- {
- NativeHelpers.LUID_AND_ATTRIBUTES[] privilegeAttr = new NativeHelpers.LUID_AND_ATTRIBUTES[state.Count];
- int i = 0;
-
- foreach (KeyValuePair<string, bool?> entry in state)
- {
- NativeHelpers.LUID luid;
- if (!NativeMethods.LookupPrivilegeValue(null, entry.Key, out luid))
- throw new Win32Exception(String.Format("LookupPrivilegeValue({0}) failed", entry.Key));
-
- PrivilegeAttributes attributes;
- switch (entry.Value)
- {
- case true:
- attributes = PrivilegeAttributes.Enabled;
- break;
- case false:
- attributes = PrivilegeAttributes.Disabled;
- break;
- default:
- attributes = PrivilegeAttributes.Removed;
- break;
- }
-
- privilegeAttr[i].Luid = luid;
- privilegeAttr[i].Attributes = attributes;
- i++;
- }
-
- return AdjustTokenPrivileges(token, privilegeAttr);
- }
-
- private static Dictionary<string, bool?> AdjustTokenPrivileges(SafeHandle token, NativeHelpers.LUID_AND_ATTRIBUTES[] newState)
- {
- bool disableAllPrivileges;
- IntPtr newStatePtr;
- NativeHelpers.LUID_AND_ATTRIBUTES[] oldStatePrivileges;
- UInt32 returnLength;
-
- if (newState == null)
- {
- disableAllPrivileges = true;
- newStatePtr = IntPtr.Zero;
- }
- else
- {
- disableAllPrivileges = false;
-
- // Need to manually marshal the bytes requires for newState as the constant size
- // of LUID_AND_ATTRIBUTES is set to 1 and can't be overridden at runtime, TOKEN_PRIVILEGES
- // always contains at least 1 entry so we need to calculate the extra size if there are
- // nore than 1 LUID_AND_ATTRIBUTES entry
- int tokenPrivilegesSize = Marshal.SizeOf(typeof(NativeHelpers.TOKEN_PRIVILEGES));
- int luidAttrSize = 0;
- if (newState.Length > 1)
- luidAttrSize = Marshal.SizeOf(typeof(NativeHelpers.LUID_AND_ATTRIBUTES)) * (newState.Length - 1);
- int totalSize = tokenPrivilegesSize + luidAttrSize;
- byte[] newStateBytes = new byte[totalSize];
-
- // get the first entry that includes the struct details
- NativeHelpers.TOKEN_PRIVILEGES tokenPrivileges = new NativeHelpers.TOKEN_PRIVILEGES()
- {
- PrivilegeCount = (UInt32)newState.Length,
- Privileges = new NativeHelpers.LUID_AND_ATTRIBUTES[1],
- };
- if (newState.Length > 0)
- tokenPrivileges.Privileges[0] = newState[0];
- int offset = StructureToBytes(tokenPrivileges, newStateBytes, 0);
-
- // copy the remaining LUID_AND_ATTRIBUTES (if any)
- for (int i = 1; i < newState.Length; i++)
- offset += StructureToBytes(newState[i], newStateBytes, offset);
-
- // finally create the pointer to the byte array we just created
- newStatePtr = Marshal.AllocHGlobal(newStateBytes.Length);
- Marshal.Copy(newStateBytes, 0, newStatePtr, newStateBytes.Length);
- }
-
- try
- {
- IntPtr hToken = IntPtr.Zero;
- if (!NativeMethods.OpenProcessToken(token, TokenAccessLevels.Query | TokenAccessLevels.AdjustPrivileges, out hToken))
- throw new Win32Exception("OpenProcessToken() failed with Query and AdjustPrivileges");
- try
- {
- IntPtr oldStatePtr = Marshal.AllocHGlobal(0);
- if (!NativeMethods.AdjustTokenPrivileges(hToken, disableAllPrivileges, newStatePtr, 0, oldStatePtr, out returnLength))
- {
- int errCode = Marshal.GetLastWin32Error();
- if (errCode != 122) // ERROR_INSUFFICIENT_BUFFER
- throw new Win32Exception(errCode, "AdjustTokenPrivileges() failed to get old state size");
- }
-
- // resize the oldStatePtr based on the length returned from Windows
- Marshal.FreeHGlobal(oldStatePtr);
- oldStatePtr = Marshal.AllocHGlobal((int)returnLength);
- try
- {
- bool res = NativeMethods.AdjustTokenPrivileges(hToken, disableAllPrivileges, newStatePtr, returnLength, oldStatePtr, out returnLength);
- int errCode = Marshal.GetLastWin32Error();
-
- // even when res == true, ERROR_NOT_ALL_ASSIGNED may be set as the last error code
- if (!res || errCode != 0)
- throw new Win32Exception(errCode, "AdjustTokenPrivileges() failed");
-
- // Marshal the oldStatePtr to the struct
- NativeHelpers.TOKEN_PRIVILEGES oldState = (NativeHelpers.TOKEN_PRIVILEGES)Marshal.PtrToStructure(oldStatePtr, typeof(NativeHelpers.TOKEN_PRIVILEGES));
- oldStatePrivileges = new NativeHelpers.LUID_AND_ATTRIBUTES[oldState.PrivilegeCount];
- PtrToStructureArray(oldStatePrivileges, IntPtr.Add(oldStatePtr, Marshal.SizeOf(oldState.PrivilegeCount)));
- }
- finally
- {
- Marshal.FreeHGlobal(oldStatePtr);
- }
- }
- finally
- {
- NativeMethods.CloseHandle(hToken);
- }
- }
- finally
- {
- if (newStatePtr != IntPtr.Zero)
- Marshal.FreeHGlobal(newStatePtr);
- }
-
- return oldStatePrivileges.ToDictionary(p => GetPrivilegeName(p.Luid), p => (bool?)p.Attributes.HasFlag(PrivilegeAttributes.Enabled));
- }
-
- private static string GetPrivilegeName(NativeHelpers.LUID luid)
- {
- UInt32 nameLen = 0;
- NativeMethods.LookupPrivilegeName(null, ref luid, null, ref nameLen);
-
- StringBuilder name = new StringBuilder((int)(nameLen + 1));
- if (!NativeMethods.LookupPrivilegeName(null, ref luid, name, ref nameLen))
- throw new Win32Exception("LookupPrivilegeName() failed");
-
- return name.ToString();
- }
-
- private static void PtrToStructureArray<T>(T[] array, IntPtr ptr)
- {
- IntPtr ptrOffset = ptr;
- for (int i = 0; i < array.Length; i++, ptrOffset = IntPtr.Add(ptrOffset, Marshal.SizeOf(typeof(T))))
- array[i] = (T)Marshal.PtrToStructure(ptrOffset, typeof(T));
- }
-
- private static int StructureToBytes<T>(T structure, byte[] array, int offset)
- {
- int size = Marshal.SizeOf(structure);
- IntPtr structPtr = Marshal.AllocHGlobal(size);
- try
- {
- Marshal.StructureToPtr(structure, structPtr, false);
- Marshal.Copy(structPtr, array, offset, size);
- }
- finally
- {
- Marshal.FreeHGlobal(structPtr);
- }
-
- return size;
- }
- }
-}
-'@
+#AnsibleRequires -CSharpUtil Ansible.Privilege
Function Import-PrivilegeUtil {
<#
.SYNOPSIS
- Compiles the C# code that can be used to manage Windows privileges from an
- Ansible module. Once this function is called, the following PowerShell
- cmdlets can be used;
-
- Get-AnsiblePrivilege
- Set-AnsiblePrivilege
-
- The above cmdlets give the ability to manage permissions on the current
- process token but the underlying .NET classes are also exposed for greater
- control. The following functions can be used by calling the .NET class
-
- [Ansible.PrivilegeUtil.Privileges]::CheckPrivilegeName($name)
- [Ansible.PrivilegeUtil.Privileges]::DisablePrivilege($process, $name)
- [Ansible.PrivilegeUtil.Privileges]::DisableAllPrivileges($process)
- [Ansible.PrivilegeUtil.Privileges]::EnablePrivilege($process, $name)
- [Ansible.PrivilegeUtil.Privileges]::GetAllPrivilegeInfo($process)
- [Ansible.PrivilegeUtil.Privileges]::RemovePrivilege($process, $name)
- [Ansible.PrivilegeUtil.Privileges]::SetTokenPrivileges($process, $new_state)
-
- Here is a brief explanation of each type of arg
- $process = The process handle to manipulate, use '[Ansible.PrivilegeUtils.Privileges]::GetCurrentProcess()' to get the current process handle
- $name = The name of the privilege, this is the constant value from https://docs.microsoft.com/en-us/windows/desktop/SecAuthZ/privilege-constants, e.g. SeAuditPrivilege
- $new_state = 'System.Collections.Generic.Dictionary`2[[System.String], [System.Nullable`1[System.Boolean]]]'
- The key is the constant name as a string, the value is a ternary boolean where
- true - will enable the privilege
- false - will disable the privilege
- null - will remove the privilege
-
- Each method that changes the privilege state will return a dictionary that
- can be used as the $new_state arg of SetTokenPrivileges to undo and revert
- back to the original state. If you remove a privilege then this is
- irreversible and won't be part of the returned dict
+ No-op, as the C# types are automatically loaded.
#>
[CmdletBinding()]
- # build the C# code to compile
- $namespace_import = ($ansible_privilege_util_namespaces | ForEach-Object { "using $_;" }) -join "`r`n"
- $platform_util = "$namespace_import`r`n`r`n$ansible_privilege_util_code"
-
- # FUTURE: find a better way to get the _ansible_remote_tmp variable
- # this is used to force csc to compile the C# code in the remote tmp
- # specified
- $original_tmp = $env:TMP
-
- $remote_tmp = $original_tmp
- $module_params = Get-Variable -Name complex_args -ErrorAction SilentlyContinue
- if ($module_params) {
- if ($module_params.Value.ContainsKey("_ansible_remote_tmp") ) {
- $remote_tmp = $module_params.Value["_ansible_remote_tmp"]
- $remote_tmp = [System.Environment]::ExpandEnvironmentVariables($remote_tmp)
+ Param()
+ $msg = "Import-PrivilegeUtil is deprecated and no longer needed, this cmdlet will be removed in a future version"
+ if ((Get-Command -Name Add-DeprecationWarning -ErrorAction SilentlyContinue) -and (Get-Variable -Name result -ErrorAction SilentlyContinue)) {
+ Add-DeprecationWarning -obj $result.Value -message $msg -version 2.12
+ } else {
+ $module = Get-Variable -Name module -ErrorAction SilentlyContinue
+ if ($null -ne $module -and $module.Value.GetType().FullName -eq "Ansible.Basic.AnsibleModule") {
+ $module.Value.Deprecate($msg, "2.12")
}
}
-
- $env:TMP = $remote_tmp
- Add-Type -TypeDefinition $platform_util
- $env:TMP = $original_tmp
}
Function Get-AnsiblePrivilege {
@@ -440,15 +40,15 @@ Function Get-AnsiblePrivilege {
[Parameter(Mandatory=$true)][String]$Name
)
- if (-not [Ansible.PrivilegeUtil.Privileges]::CheckPrivilegeName($Name)) {
+ if (-not [Ansible.Privilege.PrivilegeUtil]::CheckPrivilegeName($Name)) {
throw [System.ArgumentException] "Invalid privilege name '$Name'"
}
- $process_token = [Ansible.PrivilegeUtil.Privileges]::GetCurrentProcess()
- $privilege_info = [Ansible.PrivilegeUtil.Privileges]::GetAllPrivilegeInfo($process_token)
+ $process_token = [Ansible.Privilege.PrivilegeUtil]::GetCurrentProcess()
+ $privilege_info = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process_token)
if ($privilege_info.ContainsKey($Name)) {
$status = $privilege_info.$Name
- return $status.HasFlag([Ansible.PrivilegeUtil.PrivilegeAttributes]::Enabled)
+ return $status.HasFlag([Ansible.Privilege.PrivilegeAttributes]::Enabled)
} else {
return $null
}
@@ -487,13 +87,13 @@ Function Set-AnsiblePrivilege {
throw [System.InvalidOperationException] "Cannot $($action.ToLower()) the privilege '$Name' as it has been removed from the token"
}
- $process_token = [Ansible.PrivilegeUtil.Privileges]::GetCurrentProcess()
+ $process_token = [Ansible.Privilege.PrivilegeUtil]::GetCurrentProcess()
if ($PSCmdlet.ShouldProcess($Name, "$action the privilege $Name")) {
$new_state = New-Object -TypeName 'System.Collections.Generic.Dictionary`2[[System.String], [System.Nullable`1[System.Boolean]]]'
$new_state.Add($Name, $Value)
- [Ansible.PrivilegeUtil.Privileges]::SetTokenPrivileges($process_token, $new_state) > $null
+ [Ansible.Privilege.PrivilegeUtil]::SetTokenPrivileges($process_token, $new_state) > $null
}
}
-Export-ModuleMember -Function Import-PrivilegeUtil, Get-AnsiblePrivilege, Set-AnsiblePrivilege `
- -Variable ansible_privilege_util_namespaces, ansible_privilege_util_code \ No newline at end of file
+Export-ModuleMember -Function Import-PrivilegeUtil, Get-AnsiblePrivilege, Set-AnsiblePrivilege
+
diff --git a/lib/ansible/modules/windows/win_acl.ps1 b/lib/ansible/modules/windows/win_acl.ps1
index 75bd1393ab..1715735ce6 100644
--- a/lib/ansible/modules/windows/win_acl.ps1
+++ b/lib/ansible/modules/windows/win_acl.ps1
@@ -61,7 +61,6 @@ Function SetPrivilegeTokens() {
# This also sets us up for setting the owner as a feature.
# See the following for details of each privilege
# https://msdn.microsoft.com/en-us/library/windows/desktop/bb530716(v=vs.85).aspx
- Import-PrivilegeUtil
$privileges = @(
"SeRestorePrivilege", # Grants all write access control to any file, regardless of ACL.
"SeBackupPrivilege", # Grants all read access control to any file, regardless of ACL.
diff --git a/lib/ansible/modules/windows/win_regedit.ps1 b/lib/ansible/modules/windows/win_regedit.ps1
index 9fd0c072d5..37b7d1e09c 100644
--- a/lib/ansible/modules/windows/win_regedit.ps1
+++ b/lib/ansible/modules/windows/win_regedit.ps1
@@ -295,7 +295,6 @@ if ($hive) {
Add-Type -TypeDefinition $registry_util
$env:TMP = $original_tmp
- Import-PrivilegeUtil
try {
Set-AnsiblePrivilege -Name SeBackupPrivilege -Value $true
Set-AnsiblePrivilege -Name SeRestorePrivilege -Value $true
diff --git a/test/integration/targets/win_csharp_utils/library/ansible_privilege_tests.ps1 b/test/integration/targets/win_csharp_utils/library/ansible_privilege_tests.ps1
new file mode 100644
index 0000000000..7c76036a82
--- /dev/null
+++ b/test/integration/targets/win_csharp_utils/library/ansible_privilege_tests.ps1
@@ -0,0 +1,324 @@
+#!powershell
+
+#AnsibleRequires -CSharpUtil Ansible.Basic
+#Ansiblerequires -CSharpUtil Ansible.Privilege
+
+$module = [Ansible.Basic.AnsibleModule]::Create($args, @{})
+
+Function Assert-Equals {
+ param(
+ [Parameter(Mandatory=$true, ValueFromPipeline=$true)][AllowNull()]$Actual,
+ [Parameter(Mandatory=$true, Position=0)][AllowNull()]$Expected
+ )
+
+ $matched = $false
+ if ($Actual -is [System.Collections.ArrayList] -or $Actual -is [Array]) {
+ $Actual.Count | Assert-Equals -Expected $Expected.Count
+ for ($i = 0; $i -lt $Actual.Count; $i++) {
+ $actual_value = $Actual[$i]
+ $expected_value = $Expected[$i]
+ Assert-Equals -Actual $actual_value -Expected $expected_value
+ }
+ $matched = $true
+ } else {
+ $matched = $Actual -ceq $Expected
+ }
+
+ if (-not $matched) {
+ if ($Actual -is [PSObject]) {
+ $Actual = $Actual.ToString()
+ }
+
+ $call_stack = (Get-PSCallStack)[1]
+ $module.Result.test = $test
+ $module.Result.actual = $Actual
+ $module.Result.expected = $Expected
+ $module.Result.line = $call_stack.ScriptLineNumber
+ $module.Result.method = $call_stack.Position.Text
+ $module.FailJson("AssertionError: actual != expected")
+ }
+}
+
+Function Assert-DictionaryEquals {
+ param(
+ [Parameter(Mandatory=$true, ValueFromPipeline=$true)][AllowNull()]$Actual,
+ [Parameter(Mandatory=$true, Position=0)][AllowNull()]$Expected
+ )
+ $actual_keys = $Actual.Keys
+ $expected_keys = $Expected.Keys
+
+ $actual_keys.Count | Assert-Equals -Expected $expected_keys.Count
+ foreach ($actual_entry in $Actual.GetEnumerator()) {
+ $actual_key = $actual_entry.Key
+ ($actual_key -cin $expected_keys) | Assert-Equals -Expected $true
+ $actual_value = $actual_entry.Value
+ $expected_value = $Expected.$actual_key
+
+ if ($actual_value -is [System.Collections.IDictionary]) {
+ $actual_value | Assert-DictionaryEquals -Expected $expected_value
+ } elseif ($actual_value -is [System.Collections.ArrayList]) {
+ for ($i = 0; $i -lt $actual_value.Count; $i++) {
+ $actual_entry = $actual_value[$i]
+ $expected_entry = $expected_value[$i]
+ if ($actual_entry -is [System.Collections.IDictionary]) {
+ $actual_entry | Assert-DictionaryEquals -Expected $expected_entry
+ } else {
+ Assert-Equals -Actual $actual_entry -Expected $expected_entry
+ }
+ }
+ } else {
+ Assert-Equals -Actual $actual_value -Expected $expected_value
+ }
+ }
+ foreach ($expected_key in $expected_keys) {
+ ($expected_key -cin $actual_keys) | Assert-Equals -Expected $true
+ }
+}
+
+Function Assert-Equals {
+ param(
+ [Parameter(Mandatory=$true, ValueFromPipeline=$true)][AllowNull()]$Actual,
+ [Parameter(Mandatory=$true, Position=0)][AllowNull()]$Expected
+ )
+
+ $matched = $false
+ if ($Actual -is [System.Collections.ArrayList] -or $Actual -is [Array]) {
+ $Actual.Count | Assert-Equals -Expected $Expected.Count
+ for ($i = 0; $i -lt $Actual.Count; $i++) {
+ $actual_value = $Actual[$i]
+ $expected_value = $Expected[$i]
+ Assert-Equals -Actual $actual_value -Expected $expected_value
+ }
+ $matched = $true
+ } else {
+ $matched = $Actual -ceq $Expected
+ }
+
+ if (-not $matched) {
+ if ($Actual -is [PSObject]) {
+ $Actual = $Actual.ToString()
+ }
+
+ $call_stack = (Get-PSCallStack)[1]
+ $module.Result.test = $test
+ $module.Result.actual = $Actual
+ $module.Result.expected = $Expected
+ $module.Result.line = $call_stack.ScriptLineNumber
+ $module.Result.method = $call_stack.Position.Text
+ $module.FailJson("AssertionError: actual != expected")
+ }
+}
+
+Function Assert-DictionaryEquals {
+ param(
+ [Parameter(Mandatory=$true, ValueFromPipeline=$true)][AllowNull()]$Actual,
+ [Parameter(Mandatory=$true, Position=0)][AllowNull()]$Expected
+ )
+ $actual_keys = $Actual.Keys
+ $expected_keys = $Expected.Keys
+
+ $actual_keys.Count | Assert-Equals -Expected $expected_keys.Count
+ foreach ($actual_entry in $Actual.GetEnumerator()) {
+ $actual_key = $actual_entry.Key
+ ($actual_key -cin $expected_keys) | Assert-Equals -Expected $true
+ $actual_value = $actual_entry.Value
+ $expected_value = $Expected.$actual_key
+
+ if ($actual_value -is [System.Collections.IDictionary]) {
+ $actual_value | Assert-DictionaryEquals -Expected $expected_value
+ } elseif ($actual_value -is [System.Collections.ArrayList]) {
+ for ($i = 0; $i -lt $actual_value.Count; $i++) {
+ $actual_entry = $actual_value[$i]
+ $expected_entry = $expected_value[$i]
+ if ($actual_entry -is [System.Collections.IDictionary]) {
+ $actual_entry | Assert-DictionaryEquals -Expected $expected_entry
+ } else {
+ Assert-Equals -Actual $actual_entry -Expected $expected_entry
+ }
+ }
+ } else {
+ Assert-Equals -Actual $actual_value -Expected $expected_value
+ }
+ }
+ foreach ($expected_key in $expected_keys) {
+ ($expected_key -cin $actual_keys) | Assert-Equals -Expected $true
+ }
+}
+
+$process = [Ansible.Privilege.PrivilegeUtil]::GetCurrentProcess()
+
+$tests = @{
+ "Check valid privilege name" = {
+ $actual = [Ansible.Privilege.PrivilegeUtil]::CheckPrivilegeName("SeTcbPrivilege")
+ $actual | Assert-Equals -Expected $true
+ }
+
+ "Check invalid privilege name" = {
+ $actual = [Ansible.Privilege.PrivilegeUtil]::CheckPrivilegeName("SeFake")
+ $actual | Assert-Equals -Expected $false
+ }
+
+ "Disable a privilege" = {
+ # Ensure the privilege is enabled at the start
+ [Ansible.Privilege.PrivilegeUtil]::EnablePrivilege($process, "SeTimeZonePrivilege") > $null
+
+ $actual = [Ansible.Privilege.PrivilegeUtil]::DisablePrivilege($process, "SeTimeZonePrivilege")
+ $actual.GetType().Name | Assert-Equals -Expected 'Dictionary`2'
+ $actual.Count | Assert-Equals -Expected 1
+ $actual.SeTimeZonePrivilege | Assert-Equals -Expected $true
+
+ # Disable again
+ $actual = [Ansible.Privilege.PrivilegeUtil]::DisablePrivilege($process, "SeTimeZonePrivilege")
+ $actual.GetType().Name | Assert-Equals -Expected 'Dictionary`2'
+ $actual.Count | Assert-Equals -Expected 0
+ }
+
+ "Enable a privilege" = {
+ # Ensure the privilege is disabled at the start
+ [Ansible.Privilege.PrivilegeUtil]::DisablePrivilege($process, "SeTimeZonePrivilege") > $null
+
+ $actual = [Ansible.Privilege.PrivilegeUtil]::EnablePrivilege($process, "SeTimeZonePrivilege")
+ $actual.GetType().Name | Assert-Equals -Expected 'Dictionary`2'
+ $actual.Count | Assert-Equals -Expected 1
+ $actual.SeTimeZonePrivilege | Assert-Equals -Expected $false
+
+ # Disable again
+ $actual = [Ansible.Privilege.PrivilegeUtil]::EnablePrivilege($process, "SeTimeZonePrivilege")
+ $actual.GetType().Name | Assert-Equals -Expected 'Dictionary`2'
+ $actual.Count | Assert-Equals -Expected 0
+ }
+
+ "Disable and revert privileges" = {
+ $current_state = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
+
+ $previous_state = [Ansible.Privilege.PrivilegeUtil]::DisableAllPrivileges($process)
+ $previous_state.GetType().Name | Assert-Equals -Expected 'Dictionary`2'
+ foreach ($previous_state_entry in $previous_state.GetEnumerator()) {
+ $previous_state_entry.Value | Assert-Equals -Expected $true
+ }
+
+ # Disable again
+ $previous_state2 = [Ansible.Privilege.PrivilegeUtil]::DisableAllPrivileges($process)
+ $previous_state2.Count | Assert-Equals -Expected 0
+
+ $actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
+ foreach ($actual_entry in $actual.GetEnumerator()) {
+ $actual_entry.Value -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
+ }
+
+ [Ansible.Privilege.PrivilegeUtil]::SetTokenPrivileges($process, $previous_state) > $null
+ $actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
+ $actual | Assert-DictionaryEquals -Expected $current_state
+ }
+
+ "Remove a privilege" = {
+ [Ansible.Privilege.PrivilegeUtil]::RemovePrivilege($process, "SeUndockPrivilege") > $null
+ $actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
+ $actual.ContainsKey("SeUndockPrivilege") | Assert-Equals -Expected $false
+ }
+
+ "Test Enabler" = {
+ # Disable privilege at the start
+ $new_state = @{
+ SeTimeZonePrivilege = $false
+ SeShutdownPrivilege = $false
+ SeIncreaseWorkingSetPrivilege = $false
+ }
+ [Ansible.Privilege.PrivilegeUtil]::SetTokenPrivileges($process, $new_state) > $null
+ $check_state = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
+ $check_state.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
+ $check_state.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
+ $check_state.SeIncreaseWorkingSetPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
+
+ # Check that strict = false won't validate privileges not held but activates the ones we want
+ $enabler = New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $false, "SeTimeZonePrivilege", "SeShutdownPrivilege", "SeTcbPrivilege"
+ $actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
+ $actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
+ $actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
+ $actual.SeIncreaseWorkingSetPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
+ $actual.ContainsKey("SeTcbPrivilege") | Assert-Equals -Expected $false
+
+ # Now verify a no-op enabler will not rever back to disabled
+ $enabler2 = New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $false, "SeTimeZonePrivilege", "SeShutdownPrivilege", "SeTcbPrivilege"
+ $enabler2.Dispose()
+ $actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
+ $actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
+ $actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
+
+ # Verify that when disposing the object the privileges are reverted
+ $enabler.Dispose()
+ $actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
+ $actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
+ $actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
+ }
+
+ "Test Enabler strict" = {
+ # Disable privilege at the start
+ $new_state = @{
+ SeTimeZonePrivilege = $false
+ SeShutdownPrivilege = $false
+ SeIncreaseWorkingSetPrivilege = $false
+ }
+ [Ansible.Privilege.PrivilegeUtil]::SetTokenPrivileges($process, $new_state) > $null
+ $check_state = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
+ $check_state.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
+ $check_state.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
+ $check_state.SeIncreaseWorkingSetPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
+
+ # Check that strict = false won't validate privileges not held but activates the ones we want
+ $enabler = New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $true, "SeTimeZonePrivilege", "SeShutdownPrivilege"
+ $actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
+ $actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
+ $actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
+ $actual.SeIncreaseWorkingSetPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
+
+ # Now verify a no-op enabler will not rever back to disabled
+ $enabler2 = New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $true, "SeTimeZonePrivilege", "SeShutdownPrivilege"
+ $enabler2.Dispose()
+ $actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
+ $actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
+ $actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
+
+ # Verify that when disposing the object the privileges are reverted
+ $enabler.Dispose()
+ $actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
+ $actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
+ $actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
+ }
+
+ "Test Enabler invalid privilege" = {
+ $failed = $false
+ try {
+ New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $false, "SeTimeZonePrivilege", "SeFake"
+ } catch {
+ $failed = $true
+ $_.Exception.InnerException.Message | Assert-Equals -Expected "Failed to enable privilege(s) SeTimeZonePrivilege, SeFake (A specified privilege does not exist, Win32ErrorCode 1313)"
+ }
+ $failed | Assert-Equals -Expected $true
+ }
+
+ "Test Enabler strict failure" = {
+ # Start disabled
+ [Ansible.Privilege.PrivilegeUtil]::DisablePrivilege($process, "SeTimeZonePrivilege") > $null
+ $check_state = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
+ $check_state.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
+
+ $failed = $false
+ try {
+ New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $true, "SeTimeZonePrivilege", "SeTcbPrivilege"
+ } catch {
+ $failed = $true
+ $_.Exception.InnerException.Message | Assert-Equals -Expected "Failed to enable privilege(s) SeTimeZonePrivilege, SeTcbPrivilege (Not all privileges or groups referenced are assigned to the caller, Win32ErrorCode 1300)"
+ }
+ $failed | Assert-Equals -Expected $true
+ }
+}
+
+foreach ($test_impl in $tests.GetEnumerator()) {
+ $test = $test_impl.Key
+ &$test_impl.Value
+}
+
+$module.Result.data = "success"
+$module.ExitJson()
+
diff --git a/test/integration/targets/win_csharp_utils/tasks/main.yml b/test/integration/targets/win_csharp_utils/tasks/main.yml
index 2e5506dfc0..64c7b31675 100644
--- a/test/integration/targets/win_csharp_utils/tasks/main.yml
+++ b/test/integration/targets/win_csharp_utils/tasks/main.yml
@@ -44,3 +44,12 @@
assert:
that:
- ansible_process_tests.data == "success"
+
+- name: test Ansible.Privilege.cs
+ ansible_privilege_tests:
+ register: ansible_privilege_test
+
+- name: assert test Ansible.Privilege.cs
+ assert:
+ that:
+ - ansible_privilege_test.data == "success"
diff --git a/test/integration/targets/win_module_utils/library/privilege_util_test.ps1 b/test/integration/targets/win_module_utils/library/privilege_util_test.ps1
index 0bbfadb73f..e1ca25da81 100644
--- a/test/integration/targets/win_module_utils/library/privilege_util_test.ps1
+++ b/test/integration/targets/win_module_utils/library/privilege_util_test.ps1
@@ -1,21 +1,18 @@
#!powershell
-#Requires -Module Ansible.ModuleUtils.Legacy
+#AnsibleRequires -CSharpUtil Ansible.Basic
#Requires -Module Ansible.ModuleUtils.PrivilegeUtil
-$ErrorActionPreference = "Stop"
-
-$result = @{
- changed = $false
-}
-
-Import-PrivilegeUtil
+$module = [Ansible.Basic.AnsibleModule]::Create($args, @{})
Function Assert-Equals($actual, $expected) {
if ($actual -cne $expected) {
$call_stack = (Get-PSCallStack)[1]
- $error_msg = "AssertionError:`r`nActual: `"$actual`" != Expected: `"$expected`"`r`nLine: $($call_stack.ScriptLineNumber), Method: $($call_stack.Position.Text)"
- Fail-Json -obj $result -message $error_msg
+ $module.Result.actual = $actual
+ $module.Result.expected = $expected
+ $module.Result.line = $call_stack.ScriptLineNumber
+ $module.Result.method = $call_stack.Position.Text
+ $module.FailJson("AssertionError: actual != expected")
}
}
@@ -63,11 +60,7 @@ foreach ($raw_privilege in $raw_privilege_output) {
$split = $raw_privilege.TrimEnd() -split " "
$actual_privileges."$($split[0])" = ($split[-1] -eq "Enabled")
}
-$process = [Ansible.PrivilegeUtil.Privileges]::GetCurrentProcess()
-
-### Test variables ###
-Assert-Equals -actual ($ansible_privilege_util_namespaces -is [array]) -expected $true
-Assert-Equals -actual ($ansible_privilege_util_code -is [String]) -expected $true
+$process = [Ansible.Privilege.PrivilegeUtil]::GetCurrentProcess()
### Test PS cmdlets ###
# test ps Get-AnsiblePrivilege
@@ -81,16 +74,16 @@ foreach ($privilege in $total_privileges) {
}
# test c# GetAllPrivilegeInfo
-$actual = [Ansible.PrivilegeUtil.Privileges]::GetAllPrivilegeInfo($process)
+$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
Assert-Equals -actual $actual.GetType().Name -expected 'Dictionary`2'
Assert-Equals -actual $actual.Count -expected $actual_privileges.Count
foreach ($privilege in $total_privileges) {
if ($actual_privileges.ContainsKey($privilege)) {
$actual_value = $actual.$privilege
if ($actual_privileges.$privilege) {
- Assert-Equals -actual $actual_value.HasFlag([Ansible.PrivilegeUtil.PrivilegeAttributes]::Enabled) -expected $true
+ Assert-Equals -actual $actual_value.HasFlag([Ansible.Privilege.PrivilegeAttributes]::Enabled) -expected $true
} else {
- Assert-Equals -actual $actual_value.HasFlag([Ansible.PrivilegeUtil.PrivilegeAttributes]::Enabled) -expected $false
+ Assert-Equals -actual $actual_value.HasFlag([Ansible.Privilege.PrivilegeAttributes]::Enabled) -expected $false
}
}
}
@@ -114,51 +107,6 @@ Set-AnsiblePrivilege -Name SeUndockPrivilege -Value $false
$actual = Get-AnsiblePrivilege -Name SeUndockPrivilege
Assert-Equals -actual $actual -expected $false
-### Test C# code ###
-# test CheckPrivilegeName
-Assert-Equals -actual ([Ansible.PrivilegeUtil.Privileges]::CheckPrivilegeName($total_privileges[0])) -expected $true
-Assert-Equals -actual ([Ansible.PrivilegeUtil.Privileges]::CheckPrivilegeName("SeFake")) -expected $false
-
-# test DisablePrivilege
-# ensure we start in an enabled state
-Set-AnsiblePrivilege -Name SeTimeZonePrivilege -Value $true
-$actual = [Ansible.PrivilegeUtil.Privileges]::DisablePrivilege($process, "SeTimeZonePrivilege")
-Assert-Equals -actual $actual.GetType().Name -expected 'Dictionary`2'
-Assert-Equals -actual $actual.Count -expected 1
-Assert-Equals -actual $actual.SeTimeZonePrivilege -expected $true
-
-$actual = [Ansible.PrivilegeUtil.Privileges]::DisablePrivilege($process, "SeTimeZonePrivilege")
-Assert-Equals -actual $actual.GetType().Name -expected 'Dictionary`2'
-Assert-Equals -actual $actual.Count -expected 0
-
-# test DisableAllPrivileges
-$actual_disable_all = [Ansible.PrivilegeUtil.Privileges]::DisableAllPrivileges($process)
-Assert-Equals -actual $actual_disable_all.GetType().Name -expected 'Dictionary`2'
-
-$actual = [Ansible.PrivilegeUtil.Privileges]::DisableAllPrivileges($process)
-Assert-Equals -actual $actual.GetType().Name -expected 'Dictionary`2'
-Assert-Equals -actual $actual.Count -expected 0
-
-# test EnablePrivilege
-$actual = [Ansible.PrivilegeUtil.Privileges]::EnablePrivilege($process, "SeTimeZonePrivilege")
-Assert-Equals -actual $actual.GetType().Name -expected 'Dictionary`2'
-Assert-Equals -actual $actual.Count -expected 1
-Assert-Equals -actual $actual.SeTimeZonePrivilege -expected $false
-
-$actual = [Ansible.PrivilegeUtil.Privileges]::EnablePrivilege($process, "SeTimeZonePrivilege")
-Assert-Equals -actual $actual.GetType().Name -expected 'Dictionary`2'
-Assert-Equals -actual $actual.Count -expected 0
-
-# test SetTokenPrivileges
-$actual = [Ansible.PrivilegeUtil.Privileges]::SetTokenPrivileges($process, $actual_disable_all)
-Assert-Equals -actual $actual_disable_all.GetType().Name -expected 'Dictionary`2'
-Assert-Equals -actual $actual.ContainsKey("SeTimeZonePrivilege") -expected $false
-Assert-Equals -actual $actual.Count -expected $actual_disable_all.Count
-
-# test RemovePrivilege
-[Ansible.PrivilegeUtil.Privileges]::RemovePrivilege($process, "SeTimeZonePrivilege")
-$actual = Get-AnsiblePrivilege -Name SeTimeZonePrivilege
-Assert-Equals -actual $actual -expected $null
+$module.Result.data = "success"
+$module.ExitJson()
-$result.data = "success"
-Exit-Json -obj $result