summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Borean <jborean93@gmail.com>2018-07-31 07:48:54 +1000
committerMatt Davis <nitzmahone@users.noreply.github.com>2018-07-30 14:48:54 -0700
commit9259f31fee03e75a99a23b9fde5106b43f0cc437 (patch)
tree56c10a1eab499632f2059b0f7b3a177ecaba38f0
parentd79027b77fb30d45cd322a54f151c559b11bc1ad (diff)
downloadansible-9259f31fee03e75a99a23b9fde5106b43f0cc437.tar.gz
Add Ansible.ModuleUtils.PrivilegeUtil and converted code to use it (#43179)
* Add Ansible.ModuleUtils.PrivilegeUtil and converted code to use it * Changed namespace and class to be a better standard and fixed some typos * Changes from review * changes to avoid out of bound mem of server 2008 * changes to detect failure when setting a privileged not allowed
-rw-r--r--changelogs/fragments/win_privilege_util.yaml2
-rw-r--r--lib/ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm13
-rw-r--r--lib/ansible/module_utils/powershell/Ansible.ModuleUtils.LinkUtil.psm186
-rw-r--r--lib/ansible/module_utils/powershell/Ansible.ModuleUtils.PrivilegeUtil.psm1499
-rw-r--r--lib/ansible/modules/windows/win_acl.ps1110
-rw-r--r--lib/ansible/modules/windows/win_file.ps13
-rw-r--r--lib/ansible/modules/windows/win_find.ps13
-rw-r--r--lib/ansible/modules/windows/win_get_url.ps13
-rw-r--r--lib/ansible/modules/windows/win_regedit.ps1104
-rw-r--r--lib/ansible/modules/windows/win_region.ps13
-rw-r--r--lib/ansible/modules/windows/win_scheduled_task.ps13
-rw-r--r--lib/ansible/modules/windows/win_scheduled_task_stat.ps13
-rw-r--r--lib/ansible/modules/windows/win_user.ps13
-rw-r--r--lib/ansible/modules/windows/win_user_right.ps13
-rw-r--r--lib/ansible/modules/windows/win_whoami.ps13
-rw-r--r--lib/ansible/plugins/shell/powershell.py3
-rw-r--r--test/integration/targets/win_module_utils/library/privilege_util_test.ps1164
-rw-r--r--test/integration/targets/win_module_utils/tasks/main.yml8
18 files changed, 708 insertions, 298 deletions
diff --git a/changelogs/fragments/win_privilege_util.yaml b/changelogs/fragments/win_privilege_util.yaml
new file mode 100644
index 0000000000..6df471ea31
--- /dev/null
+++ b/changelogs/fragments/win_privilege_util.yaml
@@ -0,0 +1,2 @@
+minor_changes:
+- Added PrivilegeUtil PowerShell module util to easily control Windows Privileges in a process
diff --git a/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm1 b/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm1
index 88e0cd095d..e952a7ab21 100644
--- a/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm1
+++ b/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm1
@@ -338,7 +338,6 @@ Function Load-CommandUtils {
# FUTURE: find a better way to get the _ansible_remote_tmp variable
$original_tmp = $env:TMP
- $original_temp = $env:TEMP
$remote_tmp = $original_tmp
$module_params = Get-Variable -Name complex_args -ErrorAction SilentlyContinue
@@ -350,10 +349,8 @@ Function Load-CommandUtils {
}
$env:TMP = $remote_tmp
- $env:TEMP = $remote_tmp
Add-Type -TypeDefinition $process_util
$env:TMP = $original_tmp
- $env:TEMP = $original_temp
}
Function Get-ExecutablePath($executable, $directory) {
diff --git a/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.LinkUtil.psm1 b/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.LinkUtil.psm1
index fcef6733a4..c18adc6496 100644
--- a/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.LinkUtil.psm1
+++ b/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.LinkUtil.psm1
@@ -1,6 +1,8 @@
# 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
+
Function Load-LinkUtils() {
$link_util = @'
using Microsoft.Win32.SafeHandles;
@@ -44,21 +46,6 @@ namespace Ansible
public string[] HardTargets { get; internal set; }
}
- [StructLayout(LayoutKind.Sequential)]
- public struct LUID
- {
- public UInt32 LowPart;
- public Int32 HighPart;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- public struct TOKEN_PRIVILEGES
- {
- public UInt32 PrivilegeCount;
- public LUID Luid;
- public UInt32 Attributes;
- }
-
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct REPARSE_DATA_BUFFER
{
@@ -78,10 +65,6 @@ namespace Ansible
{
public const int MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 1024 * 16;
- private const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
- private const int TOKEN_QUERY = 0x00000008;
- private const int SE_PRIVILEGE_ENABLED = 0x00000002;
-
private const UInt32 FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
private const UInt32 FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
@@ -101,34 +84,6 @@ namespace Ansible
private const UInt32 SYMBOLIC_LINK_FLAG_FILE = 0x00000000;
private const UInt32 SYMBOLIC_LINK_FLAG_DIRECTORY = 0x00000001;
- [DllImport("kernel32.dll")]
- private static extern IntPtr GetCurrentProcess();
-
- [DllImport("kernel32.dll")]
- private static extern bool CloseHandle(
- IntPtr hObject);
-
- [DllImport("advapi32.dll")]
- private static extern bool OpenProcessToken(
- IntPtr ProcessHandle,
- UInt32 DesiredAccess,
- out IntPtr TokenHandle);
-
- [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
- private static extern bool LookupPrivilegeValue(
- string lpSystemName,
- string lpName,
- [MarshalAs(UnmanagedType.Struct)] out LUID lpLuid);
-
- [DllImport("advapi32.dll")]
- private static extern bool AdjustTokenPrivileges(
- IntPtr TokenHandle,
- [MarshalAs(UnmanagedType.Bool)] bool DisableAllPrivileges,
- ref TOKEN_PRIVILEGES NewState,
- UInt32 BufferLength,
- IntPtr PreviousState,
- IntPtr ReturnLength);
-
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern SafeFileHandle CreateFile(
string lpFileName,
@@ -206,33 +161,6 @@ namespace Ansible
string lpExistingFileName,
IntPtr lpSecurityAttributes);
- public static void EnablePrivilege(string privilege)
- {
- TOKEN_PRIVILEGES tkpPrivileges;
-
- IntPtr hToken;
- if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out hToken))
- throw new LinkUtilWin32Exception("OpenProcessToken failed");
-
- try
- {
- LUID luid;
- if (!LookupPrivilegeValue(null, privilege, out luid))
- throw new LinkUtilWin32Exception(String.Format("LookupPrivilegeValue({0}) failed", privilege));
-
- tkpPrivileges.PrivilegeCount = 1;
- tkpPrivileges.Luid = luid;
- tkpPrivileges.Attributes = SE_PRIVILEGE_ENABLED;
-
- if (!AdjustTokenPrivileges(hToken, false, ref tkpPrivileges, 0, IntPtr.Zero, IntPtr.Zero))
- throw new LinkUtilWin32Exception(String.Format("AdjustTokenPrivileges({0}) failed", privilege));
- }
- finally
- {
- CloseHandle(hToken);
- }
- }
-
public static LinkInfo GetLinkInfo(string linkPath)
{
FileAttributes attr = File.GetAttributes(linkPath);
@@ -466,7 +394,6 @@ namespace Ansible
# FUTURE: find a better way to get the _ansible_remote_tmp variable
$original_tmp = $env:TMP
- $original_temp = $env:TEMP
$remote_tmp = $original_tmp
$module_params = Get-Variable -Name complex_args -ErrorAction SilentlyContinue
@@ -478,12 +405,15 @@ namespace Ansible
}
$env:TMP = $remote_tmp
- $env:TEMP = $remote_tmp
Add-Type -TypeDefinition $link_util
$env:TMP = $original_tmp
- $env:TEMP = $original_temp
- [Ansible.LinkUtil]::EnablePrivilege("SeBackupPrivilege")
+ Import-PrivilegeUtil
+ # enable the SeBackupPrivilege if it is disabled
+ $state = Get-AnsiblePrivilege -Name SeBackupPrivilege
+ if ($state -eq $false) {
+ Set-AnsiblePrivilege -Name SeBackupPrivilege -Value $true
+ }
}
Function Get-Link($link_path) {
diff --git a/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.PrivilegeUtil.psm1 b/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.PrivilegeUtil.psm1
new file mode 100644
index 0000000000..97eb44d4bf
--- /dev/null
+++ b/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.PrivilegeUtil.psm1
@@ -0,0 +1,499 @@
+# 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, privilegesPtr.ToInt64() + 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, oldStatePtr.ToInt64() + 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, Int64 pointerAddress)
+ {
+ Int64 pointerOffset = pointerAddress;
+ for (int i = 0; i < array.Length; i++, pointerOffset += Marshal.SizeOf(typeof(T)))
+ array[i] = (T)Marshal.PtrToStructure(new IntPtr(pointerOffset), 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;
+ }
+ }
+}
+'@
+
+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
+ #>
+ [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)
+ }
+ }
+
+ $env:TMP = $remote_tmp
+ Add-Type -TypeDefinition $platform_util
+ $env:TMP = $original_tmp
+}
+
+Function Get-AnsiblePrivilege {
+ <#
+ .SYNOPSIS
+ Get the status of a privilege for the current process. This returns
+ $true - the privilege is enabled
+ $false - the privilege is disabled
+ $null - the privilege is removed from the token
+
+ If Name is not a valid privilege name, this will throw an
+ ArgumentException.
+
+ .EXAMPLE
+ Get-AnsiblePrivilege -Name SeDebugPrivilege
+ #>
+ [CmdletBinding()]
+ param(
+ [Parameter(Mandatory=$true)][String]$Name
+ )
+
+ if (-not [Ansible.PrivilegeUtil.Privileges]::CheckPrivilegeName($Name)) {
+ throw [System.ArgumentException] "Invalid privilege name '$Name'"
+ }
+
+ $process_token = [Ansible.PrivilegeUtil.Privileges]::GetCurrentProcess()
+ $privilege_info = [Ansible.PrivilegeUtil.Privileges]::GetAllPrivilegeInfo($process_token)
+ if ($privilege_info.ContainsKey($Name)) {
+ $status = $privilege_info.$Name
+ return $status.HasFlag([Ansible.PrivilegeUtil.PrivilegeAttributes]::Enabled)
+ } else {
+ return $null
+ }
+}
+
+Function Set-AnsiblePrivilege {
+ <#
+ .SYNOPSIS
+ Enables/Disables a privilege on the current process' token. If a privilege
+ has been removed from the process token, this will throw an
+ InvalidOperationException.
+
+ .EXAMPLE
+ # enable a privilege
+ Set-AnsiblePrivilege -Name SeCreateSymbolicLinkPrivilege -Value $true
+
+ # disable a privilege
+ Set-AnsiblePrivilege -Name SeCreateSymbolicLinkPrivilege -Value $false
+ #>
+ [CmdletBinding(SupportsShouldProcess)]
+ param(
+ [Parameter(Mandatory=$true)][String]$Name,
+ [Parameter(Mandatory=$true)][bool]$Value
+ )
+
+ $action = switch($Value) {
+ $true { "Enable" }
+ $false { "Disable" }
+ }
+
+ $current_state = Get-AnsiblePrivilege -Name $Name
+ if ($current_state -eq $Value) {
+ return # no change needs to occur
+ } elseif ($null -eq $current_state) {
+ # once a privilege is removed from a token we cannot do anything with it
+ throw [System.InvalidOperationException] "Cannot $($action.ToLower()) the privilege '$Name' as it has been removed from the token"
+ }
+
+ $process_token = [Ansible.PrivilegeUtil.Privileges]::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
+ }
+}
+
+Export-ModuleMember -Function Import-PrivilegeUtil, Get-AnsiblePrivilege, Set-AnsiblePrivilege `
+ -Variable ansible_privilege_util_namespaces, ansible_privilege_util_code \ No newline at end of file
diff --git a/lib/ansible/modules/windows/win_acl.ps1 b/lib/ansible/modules/windows/win_acl.ps1
index 4db0ba718b..75bd1393ab 100644
--- a/lib/ansible/modules/windows/win_acl.ps1
+++ b/lib/ansible/modules/windows/win_acl.ps1
@@ -6,6 +6,7 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#Requires -Module Ansible.ModuleUtils.Legacy
+#Requires -Module Ansible.ModuleUtils.PrivilegeUtil
#Requires -Module Ansible.ModuleUtils.SID
$ErrorActionPreference = "Stop"
@@ -43,96 +44,7 @@ function Get-UserSID {
return $userSID
}
-# Need to adjust token privs when executing Set-ACL in certain cases.
-# e.g. d:\testdir is owned by group in which current user is not a member and no perms are inherited from d:\
-# This also sets us up for setting the owner as a feature.
-$AdjustTokenPrivileges = @"
-using System;
-using System.Runtime.InteropServices;
-
-namespace Ansible {
- public class TokenManipulator {
-
- [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
- internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
- ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
-
- [DllImport("kernel32.dll", ExactSpelling = true)]
- internal static extern IntPtr GetCurrentProcess();
-
- [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
- internal static extern bool OpenProcessToken(IntPtr h, int acc,
- ref IntPtr phtok);
-
- [DllImport("advapi32.dll", SetLastError = true)]
- internal static extern bool LookupPrivilegeValue(string host, string name,
- ref long pluid);
-
- [StructLayout(LayoutKind.Sequential, Pack = 1)]
- internal struct TokPriv1Luid
- {
- public int Count;
- public long Luid;
- public int Attr;
- }
-
- internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
- internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
- internal const int TOKEN_QUERY = 0x00000008;
- internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
-
- public static bool AddPrivilege(string privilege) {
- try {
- bool retVal;
- TokPriv1Luid tp;
- IntPtr hproc = GetCurrentProcess();
- IntPtr htok = IntPtr.Zero;
- retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
- tp.Count = 1;
- tp.Luid = 0;
- tp.Attr = SE_PRIVILEGE_ENABLED;
- retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
- retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
- return retVal;
- }
- catch (Exception ex) {
- throw ex;
- }
- }
-
- public static bool RemovePrivilege(string privilege) {
- try {
- bool retVal;
- TokPriv1Luid tp;
- IntPtr hproc = GetCurrentProcess();
- IntPtr htok = IntPtr.Zero;
- retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
- tp.Count = 1;
- tp.Luid = 0;
- tp.Attr = SE_PRIVILEGE_DISABLED;
- retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
- retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
- return retVal;
- }
-
- catch (Exception ex) {
- throw ex;
- }
- }
- }
-}
-"@
-
$params = Parse-Args $args
-$_remote_tmp = Get-AnsibleParam $params "_ansible_remote_tmp" -type "path" -default $env:TMP
-
-$original_tmp = $env:TMP
-$original_temp = $env:TEMP
-$env:TMP = $_remote_tmp
-$env:TEMP = $_remote_tmp
-add-type $AdjustTokenPrivileges
-$env:TMP = $original_tmp
-$env:TEMP = $original_temp
Function SetPrivilegeTokens() {
# Set privilege tokens only if admin.
@@ -144,13 +56,23 @@ Function SetPrivilegeTokens() {
if ($myWindowsPrincipal.IsInRole($adminRole)) {
-
+ # Need to adjust token privs when executing Set-ACL in certain cases.
+ # e.g. d:\testdir is owned by group in which current user is not a member and no perms are inherited from d:\
+ # 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
-
- [void][Ansible.TokenManipulator]::AddPrivilege("SeRestorePrivilege") #Grants all write access control to any file, regardless of ACL.
- [void][Ansible.TokenManipulator]::AddPrivilege("SeBackupPrivilege") #Grants all read access control to any file, regardless of ACL.
- [void][Ansible.TokenManipulator]::AddPrivilege("SeTakeOwnershipPrivilege") #Grants ability to take owernship of an object w/out being granted discretionary access
+ 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.
+ "SeTakeOwnershipPrivilege" # Grants ability to take owernship of an object w/out being granted discretionary access
+ )
+ foreach ($privilege in $privileges) {
+ $state = Get-AnsiblePrivilege -Name $privilege
+ if ($state -eq $false) {
+ Set-AnsiblePrivilege -Name $privilege -Value $true
+ }
+ }
}
}
diff --git a/lib/ansible/modules/windows/win_file.ps1 b/lib/ansible/modules/windows/win_file.ps1
index 60cf3762e4..06f03494fa 100644
--- a/lib/ansible/modules/windows/win_file.ps1
+++ b/lib/ansible/modules/windows/win_file.ps1
@@ -52,12 +52,9 @@ namespace Ansible.Command {
}
"@
$original_tmp = $env:TMP
-$original_temp = $env:TEMP
$env:TMP = $_remote_tmp
-$env:TEMP = $_remote_tmp
Add-Type -TypeDefinition $symlink_util
$env:TMP = $original_tmp
-$env:TEMP = $original_temp
# Used to delete directories and files with logic on handling symbolic links
function Remove-File($file, $checkmode) {
diff --git a/lib/ansible/modules/windows/win_find.ps1 b/lib/ansible/modules/windows/win_find.ps1
index fc8c1dc925..f2095e09bf 100644
--- a/lib/ansible/modules/windows/win_find.ps1
+++ b/lib/ansible/modules/windows/win_find.ps1
@@ -71,12 +71,9 @@ namespace Ansible.Command {
}
"@
$original_tmp = $env:TMP
-$original_temp = $env:TEMP
$env:TMP = $_remote_tmp
-$env:TEMP = $_remote_tmp
Add-Type -TypeDefinition $symlink_util
$env:TMP = $original_tmp
-$env:TEMP = $original_temp
Function Assert-Age($info) {
$valid_match = $true
diff --git a/lib/ansible/modules/windows/win_get_url.ps1 b/lib/ansible/modules/windows/win_get_url.ps1
index c105915974..26aa1107c8 100644
--- a/lib/ansible/modules/windows/win_get_url.ps1
+++ b/lib/ansible/modules/windows/win_get_url.ps1
@@ -30,12 +30,9 @@ $webclient_util = @"
}
"@
$original_tmp = $env:TMP
-$original_temp = $env:TEMP
$env:TMP = $_remote_tmp
-$env:TEMP = $_remote_tmp
Add-Type -TypeDefinition $webclient_util
$env:TMP = $original_tmp
-$env:TEMP = $original_temp
Function CheckModified-File($url, $dest, $headers, $credentials, $timeout, $use_proxy, $proxy) {
diff --git a/lib/ansible/modules/windows/win_regedit.ps1 b/lib/ansible/modules/windows/win_regedit.ps1
index abeed87366..9fd0c072d5 100644
--- a/lib/ansible/modules/windows/win_regedit.ps1
+++ b/lib/ansible/modules/windows/win_regedit.ps1
@@ -6,6 +6,7 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#Requires -Module Ansible.ModuleUtils.Legacy
+#Requires -Module Ansible.ModuleUtils.PrivilegeUtil
$ErrorActionPreference = "Stop"
@@ -39,23 +40,8 @@ using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
-namespace Ansible
+namespace Ansible.RegEdit
{
- [StructLayout(LayoutKind.Sequential)]
- public struct LUID
- {
- public UInt32 LowPart;
- public Int32 HighPart;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- public struct TOKEN_PRIVILEGES
- {
- public UInt32 PrivilegeCount;
- public LUID Luid;
- public UInt32 Attributes;
- }
-
public enum HKEY : uint
{
LOCAL_MACHINE = 0x80000002,
@@ -74,41 +60,8 @@ namespace Ansible
public static explicit operator Win32Exception(string message) { return new Win32Exception(message); }
}
- public class RegistryUtil
+ public class Hive
{
-
- public const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
- public const int TOKEN_QUERY = 0x00000008;
- public const int SE_PRIVILEGE_ENABLED = 0x00000002;
-
- [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
- private static extern IntPtr GetCurrentProcess();
-
- [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
- private static extern bool CloseHandle(
- IntPtr hObject);
-
- [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
- private static extern bool OpenProcessToken(
- IntPtr ProcessHandle,
- UInt32 DesiredAccess,
- out IntPtr TokenHandle);
-
- [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
- private static extern bool LookupPrivilegeValue(
- string lpSystemName,
- string lpName,
- [MarshalAs(UnmanagedType.Struct)] out LUID lpLuid);
-
- [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
- private static extern bool AdjustTokenPrivileges(
- IntPtr TokenHandle,
- [MarshalAs(UnmanagedType.Bool)] bool DisableAllPrivileges,
- ref TOKEN_PRIVILEGES NewState,
- UInt32 BufferLength,
- IntPtr PreviousState,
- IntPtr ReturnLength);
-
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int RegLoadKey(
HKEY hKey,
@@ -120,41 +73,6 @@ namespace Ansible
HKEY hKey,
string lpSubKey);
- public static void EnablePrivileges()
- {
- List<String> privileges = new List<String>()
- {
- "SeRestorePrivilege",
- "SeBackupPrivilege"
- };
- foreach (string privilege in privileges)
- {
- IntPtr hToken;
- LUID luid;
- TOKEN_PRIVILEGES tkpPrivileges;
-
- if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out hToken))
- throw new Win32Exception("OpenProcessToken() failed");
-
- try
- {
- if (!LookupPrivilegeValue(null, privilege, out luid))
- throw new Win32Exception("LookupPrivilegeValue() failed");
-
- tkpPrivileges.PrivilegeCount = 1;
- tkpPrivileges.Luid = luid;
- tkpPrivileges.Attributes = SE_PRIVILEGE_ENABLED;
-
- if (!AdjustTokenPrivileges(hToken, false, ref tkpPrivileges, 0, IntPtr.Zero, IntPtr.Zero))
- throw new Win32Exception(String.Format("AdjustTokenPrivileges() failed to adjust privilege {0}", privilege));
- }
- finally
- {
- CloseHandle(hToken);
- }
- }
- }
-
public static void LoadHive(string lpSubKey, string lpFile)
{
int ret;
@@ -373,29 +291,29 @@ if ($hive) {
}
$original_tmp = $env:TMP
- $original_temp = $env:TEMP
$env:TMP = $_remote_tmp
- $env:TEMP = $_remote_tmp
Add-Type -TypeDefinition $registry_util
$env:TMP = $original_tmp
- $env:TEMP = $original_temp
+
+ Import-PrivilegeUtil
try {
- [Ansible.RegistryUtil]::EnablePrivileges()
+ Set-AnsiblePrivilege -Name SeBackupPrivilege -Value $true
+ Set-AnsiblePrivilege -Name SeRestorePrivilege -Value $true
} catch [System.ComponentModel.Win32Exception] {
- Fail-Json -obj $result -message "failed to enable SeRestorePrivilege and SeRestorePrivilege for the current process: $($_.Exception.Message)"
+ Fail-Json -obj $result -message "failed to enable SeBackupPrivilege and SeRestorePrivilege for the current process: $($_.Exception.Message)"
}
if (Test-Path -Path HKLM:\ANSIBLE) {
Add-Warning -obj $result -message "hive already loaded at HKLM:\ANSIBLE, had to unload hive for win_regedit to continue"
try {
- [Ansible.RegistryUtil]::UnloadHive("ANSIBLE")
+ [Ansible.RegEdit.Hive]::UnloadHive("ANSIBLE")
} catch [System.ComponentModel.Win32Exception] {
Fail-Json -obj $result -message "failed to unload registry hive HKLM:\ANSIBLE from $($hive): $($_.Exception.Message)"
}
}
try {
- [Ansible.RegistryUtil]::LoadHive("ANSIBLE", $hive)
+ [Ansible.RegEdit.Hive]::LoadHive("ANSIBLE", $hive)
} catch [System.ComponentModel.Win32Exception] {
Fail-Json -obj $result -message "failed to load registry hive from '$hive' to HKLM:\ANSIBLE: $($_.Exception.Message)"
}
@@ -566,7 +484,7 @@ $key_prefix[$path]
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
try {
- [Ansible.RegistryUtil]::UnloadHive("ANSIBLE")
+ [Ansible.RegEdit.Hive]::UnloadHive("ANSIBLE")
} catch [System.ComponentModel.Win32Exception] {
Fail-Json -obj $result -message "failed to unload registry hive HKLM:\ANSIBLE from $($hive): $($_.Exception.Message)"
}
diff --git a/lib/ansible/modules/windows/win_region.ps1 b/lib/ansible/modules/windows/win_region.ps1
index df2059da5d..6dc06cda64 100644
--- a/lib/ansible/modules/windows/win_region.ps1
+++ b/lib/ansible/modules/windows/win_region.ps1
@@ -88,12 +88,9 @@ Function Set-CultureLegacy($culture) {
$reg_key = 'HKCU:\Control Panel\International'
$original_tmp = $env:TMP
- $original_temp = $env:TEMP
$env:TMP = $_remote_tmp
- $env:TEMP = $_remote_tmp
Add-Type -TypeDefinition $lctype_util
$env:TMP = $original_tmp
- $env:TEMP = $original_temp
$lookup = New-Object Ansible.LocaleHelper($culture)
# hex values are from http://www.pinvoke.net/default.aspx/kernel32/GetLocaleInfoEx.html
diff --git a/lib/ansible/modules/windows/win_scheduled_task.ps1 b/lib/ansible/modules/windows/win_scheduled_task.ps1
index 8dd9323712..92e918ac02 100644
--- a/lib/ansible/modules/windows/win_scheduled_task.ps1
+++ b/lib/ansible/modules/windows/win_scheduled_task.ps1
@@ -125,12 +125,9 @@ public enum TASK_TRIGGER_TYPE2 // https://msdn.microsoft.com/en-us/library/windo
"@
$original_tmp = $env:TMP
-$original_temp = $env:TEMP
$env:TMP = $_remote_tmp
-$env:TEMP = $_remote_tmp
Add-Type -TypeDefinition $task_enums
$env:TMP = $original_tmp
-$env:TEMP = $original_temp
########################
### HELPER FUNCTIONS ###
diff --git a/lib/ansible/modules/windows/win_scheduled_task_stat.ps1 b/lib/ansible/modules/windows/win_scheduled_task_stat.ps1
index 328c15dae5..71cbe7ebfe 100644
--- a/lib/ansible/modules/windows/win_scheduled_task_stat.ps1
+++ b/lib/ansible/modules/windows/win_scheduled_task_stat.ps1
@@ -70,12 +70,9 @@ public enum TASK_TRIGGER_TYPE2
"@
$original_tmp = $env:TMP
-$original_temp = $env:TEMP
$env:TMP = $_remote_tmp
-$env:TEMP = $_remote_tmp
Add-Type -TypeDefinition $task_enums
$env:TMP = $original_tmp
-$env:TEMP = $original_temp
Function Get-PropertyValue($task_property, $com, $property) {
$raw_value = $com.$property
diff --git a/lib/ansible/modules/windows/win_user.ps1 b/lib/ansible/modules/windows/win_user.ps1
index 1c3964506a..715861e923 100644
--- a/lib/ansible/modules/windows/win_user.ps1
+++ b/lib/ansible/modules/windows/win_user.ps1
@@ -70,12 +70,9 @@ namespace Ansible
'@
$original_tmp = $env:TMP
- $original_temp = $env:TEMP
$env:TMP = $_remote_tmp
- $env:TEMP = $_remote_tmp
Add-Type -TypeDefinition $platform_util
$env:TMP = $original_tmp
- $env:TEMP = $original_temp
$handle = [IntPtr]::Zero
$logon_res = [Ansible.WinUserPInvoke]::LogonUser($Username, $null, $Password,
diff --git a/lib/ansible/modules/windows/win_user_right.ps1 b/lib/ansible/modules/windows/win_user_right.ps1
index 01d328025b..e896326e84 100644
--- a/lib/ansible/modules/windows/win_user_right.ps1
+++ b/lib/ansible/modules/windows/win_user_right.ps1
@@ -267,12 +267,9 @@ namespace Ansible
"@
$original_tmp = $env:TMP
-$original_temp = $env:TEMP
$env:TMP = $_remote_tmp
-$env:TEMP = $_remote_tmp
Add-Type -TypeDefinition $sec_helper_util
$env:TMP = $original_tmp
-$env:TEMP = $original_temp
Function Compare-UserList($existing_users, $new_users) {
$added_users = [String[]]@()
diff --git a/lib/ansible/modules/windows/win_whoami.ps1 b/lib/ansible/modules/windows/win_whoami.ps1
index 16988208a9..6c9965af7e 100644
--- a/lib/ansible/modules/windows/win_whoami.ps1
+++ b/lib/ansible/modules/windows/win_whoami.ps1
@@ -783,12 +783,9 @@ namespace Ansible
'@
$original_tmp = $env:TMP
-$original_temp = $env:TEMP
$env:TMP = $_remote_tmp
-$env:TEMP = $_remote_tmp
Add-Type -TypeDefinition $session_util
$env:TMP = $original_tmp
-$env:TEMP = $original_temp
$session_info = [Ansible.SessionUtil]::GetSessionInfo()
diff --git a/lib/ansible/plugins/shell/powershell.py b/lib/ansible/plugins/shell/powershell.py
index 77b588d8eb..4299043dca 100644
--- a/lib/ansible/plugins/shell/powershell.py
+++ b/lib/ansible/plugins/shell/powershell.py
@@ -1065,7 +1065,6 @@ Function Run($payload) {
# NB: action popping handled inside subprocess wrapper
$original_tmp = $env:TMP
- $original_temp = $env:TEMP
$remote_tmp = $payload["module_args"]["_ansible_remote_tmp"]
$remote_tmp = [System.Environment]::ExpandEnvironmentVariables($remote_tmp)
if ($null -eq $remote_tmp) {
@@ -1075,10 +1074,8 @@ Function Run($payload) {
# become process is run under a different console to the WinRM one so we
# need to set the UTF-8 codepage again
$env:TMP = $remote_tmp
- $env:TEMP = $remote_tmp
Add-Type -TypeDefinition $helper_def -Debug:$false
$env:TMP = $original_tmp
- $env:TEMP = $original_tmp
$username = $payload.become_user
$password = $payload.become_password
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
new file mode 100644
index 0000000000..0bbfadb73f
--- /dev/null
+++ b/test/integration/targets/win_module_utils/library/privilege_util_test.ps1
@@ -0,0 +1,164 @@
+#!powershell
+
+#Requires -Module Ansible.ModuleUtils.Legacy
+#Requires -Module Ansible.ModuleUtils.PrivilegeUtil
+
+$ErrorActionPreference = "Stop"
+
+$result = @{
+ changed = $false
+}
+
+Import-PrivilegeUtil
+
+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
+ }
+}
+
+# taken from https://docs.microsoft.com/en-us/windows/desktop/SecAuthZ/privilege-constants
+$total_privileges = @(
+ "SeAssignPrimaryTokenPrivilege",
+ "SeAuditPrivilege",
+ "SeBackupPrivilege",
+ "SeChangeNotifyPrivilege",
+ "SeCreateGlobalPrivilege",
+ "SeCreatePagefilePrivilege",
+ "SeCreatePermanentPrivilege",
+ "SeCreateSymbolicLinkPrivilege",
+ "SeCreateTokenPrivilege",
+ "SeDebugPrivilege",
+ "SeEnableDelegationPrivilege",
+ "SeImpersonatePrivilege",
+ "SeIncreaseBasePriorityPrivilege",
+ "SeIncreaseQuotaPrivilege",
+ "SeIncreaseWorkingSetPrivilege",
+ "SeLoadDriverPrivilege",
+ "SeLockMemoryPrivilege",
+ "SeMachineAccountPrivilege",
+ "SeManageVolumePrivilege",
+ "SeProfileSingleProcessPrivilege",
+ "SeRelabelPrivilege",
+ "SeRemoteShutdownPrivilege",
+ "SeRestorePrivilege",
+ "SeSecurityPrivilege",
+ "SeShutdownPrivilege",
+ "SeSyncAgentPrivilege",
+ "SeSystemEnvironmentPrivilege",
+ "SeSystemProfilePrivilege",
+ "SeSystemtimePrivilege",
+ "SeTakeOwnershipPrivilege",
+ "SeTcbPrivilege",
+ "SeTimeZonePrivilege",
+ "SeTrustedCredManAccessPrivilege",
+ "SeUndockPrivilege"
+)
+
+$raw_privilege_output = &whoami /priv | Where-Object { $_.StartsWith("Se") }
+$actual_privileges = @{}
+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
+
+### Test PS cmdlets ###
+# test ps Get-AnsiblePrivilege
+foreach ($privilege in $total_privileges) {
+ $expected = $null
+ if ($actual_privileges.ContainsKey($privilege)) {
+ $expected = $actual_privileges.$privilege
+ }
+ $actual = Get-AnsiblePrivilege -Name $privilege
+ Assert-Equals -actual $actual -expected $expected
+}
+
+# test c# GetAllPrivilegeInfo
+$actual = [Ansible.PrivilegeUtil.Privileges]::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
+ } else {
+ Assert-Equals -actual $actual_value.HasFlag([Ansible.PrivilegeUtil.PrivilegeAttributes]::Enabled) -expected $false
+ }
+ }
+}
+
+# test Set-AnsiblePrivilege
+Set-AnsiblePrivilege -Name SeUndockPrivilege -Value $false # ensure we start with a disabled privilege
+
+Set-AnsiblePrivilege -Name SeUndockPrivilege -Value $true -WhatIf
+$actual = Get-AnsiblePrivilege -Name SeUndockPrivilege
+Assert-Equals -actual $actual -expected $false
+
+Set-AnsiblePrivilege -Name SeUndockPrivilege -Value $true
+$actual = Get-AnsiblePrivilege -Name SeUndockPrivilege
+Assert-Equals -actual $actual -expected $true
+
+Set-AnsiblePrivilege -Name SeUndockPrivilege -Value $false -WhatIf
+$actual = Get-AnsiblePrivilege -Name SeUndockPrivilege
+Assert-Equals -actual $actual -expected $true
+
+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
+
+$result.data = "success"
+Exit-Json -obj $result
diff --git a/test/integration/targets/win_module_utils/tasks/main.yml b/test/integration/targets/win_module_utils/tasks/main.yml
index d94e85ce26..05f2dffab3 100644
--- a/test/integration/targets/win_module_utils/tasks/main.yml
+++ b/test/integration/targets/win_module_utils/tasks/main.yml
@@ -135,3 +135,11 @@
- assert:
that:
- file_util_test.data == 'success'
+
+- name: call module with PrivilegeUtil tests
+ privilege_util_test:
+ register: privilege_util_test
+
+- assert:
+ that:
+ - privilege_util_test.data == 'success'