mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
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
This commit is contained in:
parent
d79027b77f
commit
9259f31fee
18 changed files with 708 additions and 298 deletions
2
changelogs/fragments/win_privilege_util.yaml
Normal file
2
changelogs/fragments/win_privilege_util.yaml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- Added PrivilegeUtil PowerShell module util to easily control Windows Privileges in a process
|
|
@ -338,7 +338,6 @@ Function Load-CommandUtils {
|
||||||
|
|
||||||
# FUTURE: find a better way to get the _ansible_remote_tmp variable
|
# FUTURE: find a better way to get the _ansible_remote_tmp variable
|
||||||
$original_tmp = $env:TMP
|
$original_tmp = $env:TMP
|
||||||
$original_temp = $env:TEMP
|
|
||||||
|
|
||||||
$remote_tmp = $original_tmp
|
$remote_tmp = $original_tmp
|
||||||
$module_params = Get-Variable -Name complex_args -ErrorAction SilentlyContinue
|
$module_params = Get-Variable -Name complex_args -ErrorAction SilentlyContinue
|
||||||
|
@ -350,10 +349,8 @@ Function Load-CommandUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
$env:TMP = $remote_tmp
|
$env:TMP = $remote_tmp
|
||||||
$env:TEMP = $remote_tmp
|
|
||||||
Add-Type -TypeDefinition $process_util
|
Add-Type -TypeDefinition $process_util
|
||||||
$env:TMP = $original_tmp
|
$env:TMP = $original_tmp
|
||||||
$env:TEMP = $original_temp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Function Get-ExecutablePath($executable, $directory) {
|
Function Get-ExecutablePath($executable, $directory) {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# Copyright (c) 2017 Ansible Project
|
# Copyright (c) 2017 Ansible Project
|
||||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||||
|
|
||||||
|
#Requires -Module Ansible.ModuleUtils.PrivilegeUtil
|
||||||
|
|
||||||
Function Load-LinkUtils() {
|
Function Load-LinkUtils() {
|
||||||
$link_util = @'
|
$link_util = @'
|
||||||
using Microsoft.Win32.SafeHandles;
|
using Microsoft.Win32.SafeHandles;
|
||||||
|
@ -44,21 +46,6 @@ namespace Ansible
|
||||||
public string[] HardTargets { get; internal set; }
|
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)]
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
public struct REPARSE_DATA_BUFFER
|
public struct REPARSE_DATA_BUFFER
|
||||||
{
|
{
|
||||||
|
@ -78,10 +65,6 @@ namespace Ansible
|
||||||
{
|
{
|
||||||
public const int MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 1024 * 16;
|
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_BACKUP_SEMANTICS = 0x02000000;
|
||||||
private const UInt32 FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
|
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_FILE = 0x00000000;
|
||||||
private const UInt32 SYMBOLIC_LINK_FLAG_DIRECTORY = 0x00000001;
|
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)]
|
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
||||||
private static extern SafeFileHandle CreateFile(
|
private static extern SafeFileHandle CreateFile(
|
||||||
string lpFileName,
|
string lpFileName,
|
||||||
|
@ -206,33 +161,6 @@ namespace Ansible
|
||||||
string lpExistingFileName,
|
string lpExistingFileName,
|
||||||
IntPtr lpSecurityAttributes);
|
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)
|
public static LinkInfo GetLinkInfo(string linkPath)
|
||||||
{
|
{
|
||||||
FileAttributes attr = File.GetAttributes(linkPath);
|
FileAttributes attr = File.GetAttributes(linkPath);
|
||||||
|
@ -466,7 +394,6 @@ namespace Ansible
|
||||||
|
|
||||||
# FUTURE: find a better way to get the _ansible_remote_tmp variable
|
# FUTURE: find a better way to get the _ansible_remote_tmp variable
|
||||||
$original_tmp = $env:TMP
|
$original_tmp = $env:TMP
|
||||||
$original_temp = $env:TEMP
|
|
||||||
|
|
||||||
$remote_tmp = $original_tmp
|
$remote_tmp = $original_tmp
|
||||||
$module_params = Get-Variable -Name complex_args -ErrorAction SilentlyContinue
|
$module_params = Get-Variable -Name complex_args -ErrorAction SilentlyContinue
|
||||||
|
@ -478,12 +405,15 @@ namespace Ansible
|
||||||
}
|
}
|
||||||
|
|
||||||
$env:TMP = $remote_tmp
|
$env:TMP = $remote_tmp
|
||||||
$env:TEMP = $remote_tmp
|
|
||||||
Add-Type -TypeDefinition $link_util
|
Add-Type -TypeDefinition $link_util
|
||||||
$env:TMP = $original_tmp
|
$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) {
|
Function Get-Link($link_path) {
|
||||||
|
|
|
@ -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
|
|
@ -6,6 +6,7 @@
|
||||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
# 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.Legacy
|
||||||
|
#Requires -Module Ansible.ModuleUtils.PrivilegeUtil
|
||||||
#Requires -Module Ansible.ModuleUtils.SID
|
#Requires -Module Ansible.ModuleUtils.SID
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
|
@ -43,96 +44,7 @@ function Get-UserSID {
|
||||||
return $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
|
$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() {
|
Function SetPrivilegeTokens() {
|
||||||
# Set privilege tokens only if admin.
|
# Set privilege tokens only if admin.
|
||||||
|
@ -144,13 +56,23 @@ Function SetPrivilegeTokens() {
|
||||||
|
|
||||||
|
|
||||||
if ($myWindowsPrincipal.IsInRole($adminRole)) {
|
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
|
# See the following for details of each privilege
|
||||||
# https://msdn.microsoft.com/en-us/library/windows/desktop/bb530716(v=vs.85).aspx
|
# https://msdn.microsoft.com/en-us/library/windows/desktop/bb530716(v=vs.85).aspx
|
||||||
|
Import-PrivilegeUtil
|
||||||
[void][Ansible.TokenManipulator]::AddPrivilege("SeRestorePrivilege") #Grants all write access control to any file, regardless of ACL.
|
$privileges = @(
|
||||||
[void][Ansible.TokenManipulator]::AddPrivilege("SeBackupPrivilege") #Grants all read access control to any file, regardless of ACL.
|
"SeRestorePrivilege", # Grants all write 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
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,12 +52,9 @@ namespace Ansible.Command {
|
||||||
}
|
}
|
||||||
"@
|
"@
|
||||||
$original_tmp = $env:TMP
|
$original_tmp = $env:TMP
|
||||||
$original_temp = $env:TEMP
|
|
||||||
$env:TMP = $_remote_tmp
|
$env:TMP = $_remote_tmp
|
||||||
$env:TEMP = $_remote_tmp
|
|
||||||
Add-Type -TypeDefinition $symlink_util
|
Add-Type -TypeDefinition $symlink_util
|
||||||
$env:TMP = $original_tmp
|
$env:TMP = $original_tmp
|
||||||
$env:TEMP = $original_temp
|
|
||||||
|
|
||||||
# Used to delete directories and files with logic on handling symbolic links
|
# Used to delete directories and files with logic on handling symbolic links
|
||||||
function Remove-File($file, $checkmode) {
|
function Remove-File($file, $checkmode) {
|
||||||
|
|
|
@ -71,12 +71,9 @@ namespace Ansible.Command {
|
||||||
}
|
}
|
||||||
"@
|
"@
|
||||||
$original_tmp = $env:TMP
|
$original_tmp = $env:TMP
|
||||||
$original_temp = $env:TEMP
|
|
||||||
$env:TMP = $_remote_tmp
|
$env:TMP = $_remote_tmp
|
||||||
$env:TEMP = $_remote_tmp
|
|
||||||
Add-Type -TypeDefinition $symlink_util
|
Add-Type -TypeDefinition $symlink_util
|
||||||
$env:TMP = $original_tmp
|
$env:TMP = $original_tmp
|
||||||
$env:TEMP = $original_temp
|
|
||||||
|
|
||||||
Function Assert-Age($info) {
|
Function Assert-Age($info) {
|
||||||
$valid_match = $true
|
$valid_match = $true
|
||||||
|
|
|
@ -30,12 +30,9 @@ $webclient_util = @"
|
||||||
}
|
}
|
||||||
"@
|
"@
|
||||||
$original_tmp = $env:TMP
|
$original_tmp = $env:TMP
|
||||||
$original_temp = $env:TEMP
|
|
||||||
$env:TMP = $_remote_tmp
|
$env:TMP = $_remote_tmp
|
||||||
$env:TEMP = $_remote_tmp
|
|
||||||
Add-Type -TypeDefinition $webclient_util
|
Add-Type -TypeDefinition $webclient_util
|
||||||
$env:TMP = $original_tmp
|
$env:TMP = $original_tmp
|
||||||
$env:TEMP = $original_temp
|
|
||||||
|
|
||||||
|
|
||||||
Function CheckModified-File($url, $dest, $headers, $credentials, $timeout, $use_proxy, $proxy) {
|
Function CheckModified-File($url, $dest, $headers, $credentials, $timeout, $use_proxy, $proxy) {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
# 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.Legacy
|
||||||
|
#Requires -Module Ansible.ModuleUtils.PrivilegeUtil
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
@ -39,23 +40,8 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices;
|
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
|
public enum HKEY : uint
|
||||||
{
|
{
|
||||||
LOCAL_MACHINE = 0x80000002,
|
LOCAL_MACHINE = 0x80000002,
|
||||||
|
@ -74,41 +60,8 @@ namespace Ansible
|
||||||
public static explicit operator Win32Exception(string message) { return new Win32Exception(message); }
|
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)]
|
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||||
private static extern int RegLoadKey(
|
private static extern int RegLoadKey(
|
||||||
HKEY hKey,
|
HKEY hKey,
|
||||||
|
@ -120,41 +73,6 @@ namespace Ansible
|
||||||
HKEY hKey,
|
HKEY hKey,
|
||||||
string lpSubKey);
|
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)
|
public static void LoadHive(string lpSubKey, string lpFile)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -373,29 +291,29 @@ if ($hive) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$original_tmp = $env:TMP
|
$original_tmp = $env:TMP
|
||||||
$original_temp = $env:TEMP
|
|
||||||
$env:TMP = $_remote_tmp
|
$env:TMP = $_remote_tmp
|
||||||
$env:TEMP = $_remote_tmp
|
|
||||||
Add-Type -TypeDefinition $registry_util
|
Add-Type -TypeDefinition $registry_util
|
||||||
$env:TMP = $original_tmp
|
$env:TMP = $original_tmp
|
||||||
$env:TEMP = $original_temp
|
|
||||||
|
Import-PrivilegeUtil
|
||||||
try {
|
try {
|
||||||
[Ansible.RegistryUtil]::EnablePrivileges()
|
Set-AnsiblePrivilege -Name SeBackupPrivilege -Value $true
|
||||||
|
Set-AnsiblePrivilege -Name SeRestorePrivilege -Value $true
|
||||||
} catch [System.ComponentModel.Win32Exception] {
|
} 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) {
|
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"
|
Add-Warning -obj $result -message "hive already loaded at HKLM:\ANSIBLE, had to unload hive for win_regedit to continue"
|
||||||
try {
|
try {
|
||||||
[Ansible.RegistryUtil]::UnloadHive("ANSIBLE")
|
[Ansible.RegEdit.Hive]::UnloadHive("ANSIBLE")
|
||||||
} catch [System.ComponentModel.Win32Exception] {
|
} catch [System.ComponentModel.Win32Exception] {
|
||||||
Fail-Json -obj $result -message "failed to unload registry hive HKLM:\ANSIBLE from $($hive): $($_.Exception.Message)"
|
Fail-Json -obj $result -message "failed to unload registry hive HKLM:\ANSIBLE from $($hive): $($_.Exception.Message)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
[Ansible.RegistryUtil]::LoadHive("ANSIBLE", $hive)
|
[Ansible.RegEdit.Hive]::LoadHive("ANSIBLE", $hive)
|
||||||
} catch [System.ComponentModel.Win32Exception] {
|
} catch [System.ComponentModel.Win32Exception] {
|
||||||
Fail-Json -obj $result -message "failed to load registry hive from '$hive' to HKLM:\ANSIBLE: $($_.Exception.Message)"
|
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]::Collect()
|
||||||
[GC]::WaitForPendingFinalizers()
|
[GC]::WaitForPendingFinalizers()
|
||||||
try {
|
try {
|
||||||
[Ansible.RegistryUtil]::UnloadHive("ANSIBLE")
|
[Ansible.RegEdit.Hive]::UnloadHive("ANSIBLE")
|
||||||
} catch [System.ComponentModel.Win32Exception] {
|
} catch [System.ComponentModel.Win32Exception] {
|
||||||
Fail-Json -obj $result -message "failed to unload registry hive HKLM:\ANSIBLE from $($hive): $($_.Exception.Message)"
|
Fail-Json -obj $result -message "failed to unload registry hive HKLM:\ANSIBLE from $($hive): $($_.Exception.Message)"
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,12 +88,9 @@ Function Set-CultureLegacy($culture) {
|
||||||
$reg_key = 'HKCU:\Control Panel\International'
|
$reg_key = 'HKCU:\Control Panel\International'
|
||||||
|
|
||||||
$original_tmp = $env:TMP
|
$original_tmp = $env:TMP
|
||||||
$original_temp = $env:TEMP
|
|
||||||
$env:TMP = $_remote_tmp
|
$env:TMP = $_remote_tmp
|
||||||
$env:TEMP = $_remote_tmp
|
|
||||||
Add-Type -TypeDefinition $lctype_util
|
Add-Type -TypeDefinition $lctype_util
|
||||||
$env:TMP = $original_tmp
|
$env:TMP = $original_tmp
|
||||||
$env:TEMP = $original_temp
|
|
||||||
|
|
||||||
$lookup = New-Object Ansible.LocaleHelper($culture)
|
$lookup = New-Object Ansible.LocaleHelper($culture)
|
||||||
# hex values are from http://www.pinvoke.net/default.aspx/kernel32/GetLocaleInfoEx.html
|
# hex values are from http://www.pinvoke.net/default.aspx/kernel32/GetLocaleInfoEx.html
|
||||||
|
|
|
@ -125,12 +125,9 @@ public enum TASK_TRIGGER_TYPE2 // https://msdn.microsoft.com/en-us/library/windo
|
||||||
"@
|
"@
|
||||||
|
|
||||||
$original_tmp = $env:TMP
|
$original_tmp = $env:TMP
|
||||||
$original_temp = $env:TEMP
|
|
||||||
$env:TMP = $_remote_tmp
|
$env:TMP = $_remote_tmp
|
||||||
$env:TEMP = $_remote_tmp
|
|
||||||
Add-Type -TypeDefinition $task_enums
|
Add-Type -TypeDefinition $task_enums
|
||||||
$env:TMP = $original_tmp
|
$env:TMP = $original_tmp
|
||||||
$env:TEMP = $original_temp
|
|
||||||
|
|
||||||
########################
|
########################
|
||||||
### HELPER FUNCTIONS ###
|
### HELPER FUNCTIONS ###
|
||||||
|
|
|
@ -70,12 +70,9 @@ public enum TASK_TRIGGER_TYPE2
|
||||||
"@
|
"@
|
||||||
|
|
||||||
$original_tmp = $env:TMP
|
$original_tmp = $env:TMP
|
||||||
$original_temp = $env:TEMP
|
|
||||||
$env:TMP = $_remote_tmp
|
$env:TMP = $_remote_tmp
|
||||||
$env:TEMP = $_remote_tmp
|
|
||||||
Add-Type -TypeDefinition $task_enums
|
Add-Type -TypeDefinition $task_enums
|
||||||
$env:TMP = $original_tmp
|
$env:TMP = $original_tmp
|
||||||
$env:TEMP = $original_temp
|
|
||||||
|
|
||||||
Function Get-PropertyValue($task_property, $com, $property) {
|
Function Get-PropertyValue($task_property, $com, $property) {
|
||||||
$raw_value = $com.$property
|
$raw_value = $com.$property
|
||||||
|
|
|
@ -70,12 +70,9 @@ namespace Ansible
|
||||||
'@
|
'@
|
||||||
|
|
||||||
$original_tmp = $env:TMP
|
$original_tmp = $env:TMP
|
||||||
$original_temp = $env:TEMP
|
|
||||||
$env:TMP = $_remote_tmp
|
$env:TMP = $_remote_tmp
|
||||||
$env:TEMP = $_remote_tmp
|
|
||||||
Add-Type -TypeDefinition $platform_util
|
Add-Type -TypeDefinition $platform_util
|
||||||
$env:TMP = $original_tmp
|
$env:TMP = $original_tmp
|
||||||
$env:TEMP = $original_temp
|
|
||||||
|
|
||||||
$handle = [IntPtr]::Zero
|
$handle = [IntPtr]::Zero
|
||||||
$logon_res = [Ansible.WinUserPInvoke]::LogonUser($Username, $null, $Password,
|
$logon_res = [Ansible.WinUserPInvoke]::LogonUser($Username, $null, $Password,
|
||||||
|
|
|
@ -267,12 +267,9 @@ namespace Ansible
|
||||||
"@
|
"@
|
||||||
|
|
||||||
$original_tmp = $env:TMP
|
$original_tmp = $env:TMP
|
||||||
$original_temp = $env:TEMP
|
|
||||||
$env:TMP = $_remote_tmp
|
$env:TMP = $_remote_tmp
|
||||||
$env:TEMP = $_remote_tmp
|
|
||||||
Add-Type -TypeDefinition $sec_helper_util
|
Add-Type -TypeDefinition $sec_helper_util
|
||||||
$env:TMP = $original_tmp
|
$env:TMP = $original_tmp
|
||||||
$env:TEMP = $original_temp
|
|
||||||
|
|
||||||
Function Compare-UserList($existing_users, $new_users) {
|
Function Compare-UserList($existing_users, $new_users) {
|
||||||
$added_users = [String[]]@()
|
$added_users = [String[]]@()
|
||||||
|
|
|
@ -783,12 +783,9 @@ namespace Ansible
|
||||||
'@
|
'@
|
||||||
|
|
||||||
$original_tmp = $env:TMP
|
$original_tmp = $env:TMP
|
||||||
$original_temp = $env:TEMP
|
|
||||||
$env:TMP = $_remote_tmp
|
$env:TMP = $_remote_tmp
|
||||||
$env:TEMP = $_remote_tmp
|
|
||||||
Add-Type -TypeDefinition $session_util
|
Add-Type -TypeDefinition $session_util
|
||||||
$env:TMP = $original_tmp
|
$env:TMP = $original_tmp
|
||||||
$env:TEMP = $original_temp
|
|
||||||
|
|
||||||
$session_info = [Ansible.SessionUtil]::GetSessionInfo()
|
$session_info = [Ansible.SessionUtil]::GetSessionInfo()
|
||||||
|
|
||||||
|
|
|
@ -1065,7 +1065,6 @@ Function Run($payload) {
|
||||||
# NB: action popping handled inside subprocess wrapper
|
# NB: action popping handled inside subprocess wrapper
|
||||||
|
|
||||||
$original_tmp = $env:TMP
|
$original_tmp = $env:TMP
|
||||||
$original_temp = $env:TEMP
|
|
||||||
$remote_tmp = $payload["module_args"]["_ansible_remote_tmp"]
|
$remote_tmp = $payload["module_args"]["_ansible_remote_tmp"]
|
||||||
$remote_tmp = [System.Environment]::ExpandEnvironmentVariables($remote_tmp)
|
$remote_tmp = [System.Environment]::ExpandEnvironmentVariables($remote_tmp)
|
||||||
if ($null -eq $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
|
# become process is run under a different console to the WinRM one so we
|
||||||
# need to set the UTF-8 codepage again
|
# need to set the UTF-8 codepage again
|
||||||
$env:TMP = $remote_tmp
|
$env:TMP = $remote_tmp
|
||||||
$env:TEMP = $remote_tmp
|
|
||||||
Add-Type -TypeDefinition $helper_def -Debug:$false
|
Add-Type -TypeDefinition $helper_def -Debug:$false
|
||||||
$env:TMP = $original_tmp
|
$env:TMP = $original_tmp
|
||||||
$env:TEMP = $original_tmp
|
|
||||||
|
|
||||||
$username = $payload.become_user
|
$username = $payload.become_user
|
||||||
$password = $payload.become_password
|
$password = $payload.become_password
|
||||||
|
|
|
@ -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
|
|
@ -135,3 +135,11 @@
|
||||||
- assert:
|
- assert:
|
||||||
that:
|
that:
|
||||||
- file_util_test.data == 'success'
|
- 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'
|
||||||
|
|
Loading…
Reference in a new issue