@ -364,7 +364,13 @@ Function Run($payload) {
[ Ansible . Shell . ProcessUtil ] : : GrantAccessToWindowStationAndDesktop ( $ username )
[ Ansible . Shell . ProcessUtil ] : : GrantAccessToWindowStationAndDesktop ( $ username )
Try {
$ proc . Start ( ) | Out - Null # will always return $true for non shell-exec cases
$ proc . Start ( ) | Out - Null # will always return $true for non shell-exec cases
}
Catch {
Write - Output $ _ . Exception . InnerException
return
}
$ payload_string = $ payload | ConvertTo - Json - Depth 99 - Compress
$ payload_string = $ payload | ConvertTo - Json - Depth 99 - Compress
@ -494,7 +500,7 @@ Function Run($payload) {
IntPtr lpEnvironment ,
IntPtr lpEnvironment ,
[ MarshalAs ( UnmanagedType . LPTStr ) ]
[ MarshalAs ( UnmanagedType . LPTStr ) ]
string lpCurrentDirectory ,
string lpCurrentDirectory ,
STARTUPINFO lpStartupInfo ,
STARTUPINFO EX lpStartupInfo ,
out PROCESS_INFORMATION lpProcessInformation ) ;
out PROCESS_INFORMATION lpProcessInformation ) ;
[ DllImport ( " kernel32.dll " , SetLastError = true , CharSet = CharSet . Unicode ) ]
[ DllImport ( " kernel32.dll " , SetLastError = true , CharSet = CharSet . Unicode ) ]
@ -516,6 +522,18 @@ Function Run($payload) {
[ DllImport ( " kernel32.dll " , SetLastError = true ) ]
[ DllImport ( " kernel32.dll " , SetLastError = true ) ]
public static extern bool SetHandleInformation ( IntPtr hObject , HandleFlags dwMask , int dwFlags ) ;
public static extern bool SetHandleInformation ( IntPtr hObject , HandleFlags dwMask , int dwFlags ) ;
[ DllImport ( " kernel32.dll " , SetLastError = true ) ]
public static extern bool InitializeProcThreadAttributeList ( IntPtr lpAttributeList , int dwAttributeCount , int dwFlags , ref int lpSize ) ;
[ DllImport ( " kernel32.dll " , SetLastError = true ) ]
public static extern bool UpdateProcThreadAttribute (
IntPtr lpAttributeList ,
uint dwFlags ,
IntPtr Attribute ,
IntPtr lpValue ,
IntPtr cbSize ,
IntPtr lpPreviousValue ,
IntPtr lpReturnSize ) ;
public static string SearchPath ( string findThis )
public static string SearchPath ( string findThis )
{
{
@ -617,6 +635,17 @@ Function Run($payload) {
}
}
}
}
[ StructLayout ( LayoutKind . Sequential ) ]
public class STARTUPINFOEX {
public STARTUPINFO startupInfo ;
public IntPtr lpAttributeList ;
public STARTUPINFOEX ( ) {
startupInfo = new STARTUPINFO ( ) ;
startupInfo . cb = Marshal . SizeOf ( this ) ;
}
}
[ StructLayout ( LayoutKind . Sequential ) ]
[ StructLayout ( LayoutKind . Sequential ) ]
public struct PROCESS_INFORMATION
public struct PROCESS_INFORMATION
{
{
@ -669,22 +698,24 @@ Function Run($payload) {
# FUTURE: create under new job to ensure all children die on exit?
# FUTURE: create under new job to ensure all children die on exit?
# FUTURE: move these flags into C# enum
# FUTURE: move these flags into C# enum ?
# start process suspended + breakaway so we can record the watchdog pid without worrying about a completion race
# start process suspended + breakaway so we can record the watchdog pid without worrying about a completion race
Set - Variable CREATE_BREAKAWAY_FROM_JOB - Value ( [ uint32 ] 0x01000000 ) - Option Constant
Set - Variable CREATE_BREAKAWAY_FROM_JOB - Value ( [ uint32 ] 0x01000000 ) - Option Constant
Set - Variable CREATE_SUSPENDED - Value ( [ uint32 ] 0x00000004 ) - Option Constant
Set - Variable CREATE_SUSPENDED - Value ( [ uint32 ] 0x00000004 ) - Option Constant
Set - Variable CREATE_UNICODE_ENVIRONMENT - Value ( [ uint32 ] 0x000000400 ) - Option Constant
Set - Variable CREATE_UNICODE_ENVIRONMENT - Value ( [ uint32 ] 0x000000400 ) - Option Constant
Set - Variable CREATE_NEW_CONSOLE - Value ( [ uint32 ] 0x00000010 ) - Option Constant
Set - Variable CREATE_NEW_CONSOLE - Value ( [ uint32 ] 0x00000010 ) - Option Constant
Set - Variable EXTENDED_STARTUPINFO_PRESENT - Value ( [ uint32 ] 0x00080000 ) - Option Constant
$ pstartup_flags = $ CREATE_BREAKAWAY_FROM_JOB - bor $ CREATE_UNICODE_ENVIRONMENT - bor $ CREATE_NEW_CONSOLE - bor $ CREATE_SUSPENDED
$ pstartup_flags = $ CREATE_BREAKAWAY_FROM_JOB - bor $ CREATE_UNICODE_ENVIRONMENT - bor $ CREATE_NEW_CONSOLE `
- bor $ CREATE_SUSPENDED - bor $ EXTENDED_STARTUPINFO_PRESENT
# execute the dynamic watchdog as a breakway process , which will in turn exec the module
# execute the dynamic watchdog as a breakway process to free us from the WinRM job , which will in turn exec the module
$ si = New - Object Ansible . Async . STARTUPINFO
$ si = New - Object Ansible . Async . STARTUPINFO EX
# setup stdin redirection, we'll leave stdout/stderr as normal
# setup stdin redirection, we'll leave stdout/stderr as normal
$ si . dwFlags = [ Ansible . Async . StartupInfoFlags ] : : USESTDHANDLES
$ si . startupInfo. dwFlags = [ Ansible . Async . StartupInfoFlags ] : : USESTDHANDLES
$ si . hStdOutput = [ Ansible . Async . NativeProcessUtil ] : : GetStdHandle ( [ Ansible . Async . StandardHandleValues ] : : STD_OUTPUT_HANDLE )
$ si . startupInfo. hStdOutput = [ Ansible . Async . NativeProcessUtil ] : : GetStdHandle ( [ Ansible . Async . StandardHandleValues ] : : STD_OUTPUT_HANDLE )
$ si . hStdError = [ Ansible . Async . NativeProcessUtil ] : : GetStdHandle ( [ Ansible . Async . StandardHandleValues ] : : STD_ERROR_HANDLE )
$ si . startupInfo. hStdError = [ Ansible . Async . NativeProcessUtil ] : : GetStdHandle ( [ Ansible . Async . StandardHandleValues ] : : STD_ERROR_HANDLE )
$ stdin_read = $ stdin_write = 0
$ stdin_read = $ stdin_write = 0
@ -697,7 +728,35 @@ Function Run($payload) {
If ( - not [ Ansible . Async . NativeProcessUtil ] : : SetHandleInformation ( $ stdin_write , [ Ansible . Async . HandleFlags ] : : INHERIT , 0 ) ) {
If ( - not [ Ansible . Async . NativeProcessUtil ] : : SetHandleInformation ( $ stdin_write , [ Ansible . Async . HandleFlags ] : : INHERIT , 0 ) ) {
throw " Stdin handle setup failed, Win32Error: $([System.Runtime.InteropServices.Marshal]::GetLastWin32Error()) "
throw " Stdin handle setup failed, Win32Error: $([System.Runtime.InteropServices.Marshal]::GetLastWin32Error()) "
}
}
$ si . hStdInput = $ stdin_read
$ si . startupInfo . hStdInput = $ stdin_read
# create an attribute list with our explicit handle inheritance list to pass to CreateProcess
[ int ] $ buf_sz = 0
# determine the buffer size necessary for our attribute list
If ( - not [ Ansible . Async . NativeProcessUtil ] : : InitializeProcThreadAttributeList ( [ IntPtr ] : : Zero , 1 , 0 , [ ref ] $ buf_sz ) ) {
$ last_err = [ System . Runtime . InteropServices . Marshal ] : : GetLastWin32Error ( )
If ( $ last_err - ne 122 ) { # ERROR_INSUFFICIENT_BUFFER
throw " Attribute list size query failed, Win32Error: $last_err "
}
}
$ si . lpAttributeList = [ System . Runtime . InteropServices . Marshal ] : : AllocHGlobal ( $ buf_sz )
# initialize the attribute list
If ( - not [ Ansible . Async . NativeProcessUtil ] : : InitializeProcThreadAttributeList ( $ si . lpAttributeList , 1 , 0 , [ ref ] $ buf_sz ) ) {
throw " Attribute list init failed, Win32Error: $([System.Runtime.InteropServices.Marshal]::GetLastWin32Error()) "
}
$ handles_to_inherit = [ IntPtr [ ] ] @ ( $ stdin_read )
$ pinned_handles = [ System . Runtime . InteropServices . GCHandle ] : : Alloc ( $ handles_to_inherit , [ System . Runtime . InteropServices . GCHandleType ] : : Pinned )
# update the attribute list with the handles we want to inherit
If ( - not [ Ansible . Async . NativeProcessUtil ] : : UpdateProcThreadAttribute ( $ si . lpAttributeList , 0 , 0x20002 < # PROC_THREAD_ATTRIBUTE_HANDLE_LIST #>, `
$ pinned_handles . AddrOfPinnedObject ( ) , [ System . Runtime . InteropServices . Marshal ] : : SizeOf ( [ type ] [ IntPtr ] ) * $ handles_to_inherit . Length , `
[ System . IntPtr ] : : Zero , [ System . IntPtr ] : : Zero ) ) {
throw " Attribute list update failed, Win32Error: $([System.Runtime.InteropServices.Marshal]::GetLastWin32Error()) "
}
# need to use a preamble-free version of UTF8Encoding
# need to use a preamble-free version of UTF8Encoding
$ utf8_encoding = New - Object System . Text . UTF8Encoding @ ( $ false )
$ utf8_encoding = New - Object System . Text . UTF8Encoding @ ( $ false )
@ -894,12 +953,8 @@ class ShellModule(object):
# TODO: implement module transfer
# TODO: implement module transfer
# TODO: implement #Requires -Modules parser/locator
# TODO: implement #Requires -Modules parser/locator
# TODO: add raw failure + errcode preservation (all success right now)
# TODO: add KEEP_REMOTE_FILES support + debug wrapper dump
# TODO: add KEEP_REMOTE_FILES support + debug wrapper dump
# TODO: add become support
# TODO: add binary module support
# TODO: add binary module support
# TODO: figure out non-pipelined path (or force pipelining)
def assert_safe_env_key ( self , key ) :
def assert_safe_env_key ( self , key ) :
if not self . safe_envkey . match ( key ) :
if not self . safe_envkey . match ( key ) :