minor become/runas cleanup (#32564)

* removed/blobified unused PInvoke stuff
* added try/finally around impersonation to ensure RevertToSelf is called in all cases
* added a few explanatory comments
pull/32604/head
Matt Davis 7 years ago committed by GitHub
parent 16e98c8c5b
commit 8ecc7bc4a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -204,13 +204,8 @@ namespace Ansible
public IntPtr lpReserved; public IntPtr lpReserved;
public IntPtr lpDesktop; public IntPtr lpDesktop;
public IntPtr lpTitle; public IntPtr lpTitle;
public Int32 dwX; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)]
public Int32 dwY; public byte[] _data1;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags; public Int32 dwFlags;
public Int16 wShowWindow; public Int16 wShowWindow;
public Int16 cbReserved2; public Int16 cbReserved2;
@ -257,17 +252,6 @@ namespace Ansible
public SID_AND_ATTRIBUTES User; public SID_AND_ATTRIBUTES User;
} }
[StructLayout(LayoutKind.Sequential)]
public struct IO_COUNTERS
{
public UInt64 ReadOperationCount;
public UInt64 WriteOperationCount;
public UInt64 OtherOperationCount;
public UInt64 ReadTransferCount;
public UInt64 WriteTransferCount;
public UInt64 OtherTransferCount;
}
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct JOBOBJECT_BASIC_LIMIT_INFORMATION public struct JOBOBJECT_BASIC_LIMIT_INFORMATION
{ {
@ -283,14 +267,13 @@ namespace Ansible
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION public class JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{ {
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation = new JOBOBJECT_BASIC_LIMIT_INFORMATION();
public IO_COUNTERS IoInfo; [MarshalAs(UnmanagedType.ByValArray, SizeConst=48)]
public UIntPtr ProcessMemoryLimit; public byte[] IO_COUNTERS_BLOB;
public UIntPtr JobMemoryLimit; [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
public UIntPtr PeakProcessMemoryUsed; public UIntPtr[] LIMIT_BLOB;
public UIntPtr PeakJobMemoryUsed;
} }
[Flags] [Flags]
@ -305,8 +288,6 @@ namespace Ansible
CREATE_BREAKAWAY_FROM_JOB = 0x01000000, CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
CREATE_DEFAULT_ERROR_MODE = 0x04000000, CREATE_DEFAULT_ERROR_MODE = 0x04000000,
CREATE_NEW_CONSOLE = 0x00000010, CREATE_NEW_CONSOLE = 0x00000010,
CREATE_NEW_PROCESS_GROUP = 0x00000200,
CREATE_SEPARATE_WOW_VDM = 0x00000800,
CREATE_SUSPENDED = 0x00000004, CREATE_SUSPENDED = 0x00000004,
CREATE_UNICODE_ENVIRONMENT = 0x00000400, CREATE_UNICODE_ENVIRONMENT = 0x00000400,
EXTENDED_STARTUPINFO_PRESENT = 0x00080000 EXTENDED_STARTUPINFO_PRESENT = 0x00080000
@ -339,9 +320,6 @@ namespace Ansible
public enum LogonProvider public enum LogonProvider
{ {
LOGON32_PROVIDER_DEFAULT = 0, LOGON32_PROVIDER_DEFAULT = 0,
LOGON32_PROVIDER_WINNT35 = 1,
LOGON32_PROVIDER_WINNT40 = 2,
LOGON32_PROVIDER_WINNT50 = 3
} }
public enum TokenInformationClass public enum TokenInformationClass
@ -363,28 +341,12 @@ namespace Ansible
[Flags] [Flags]
public enum ProcessAccessFlags : uint public enum ProcessAccessFlags : uint
{ {
PROCESS_ALL_ACCESS = 0x001F0FFF,
PROCESS_TERMINATE = 0x00000001,
PROCESS_CREATE_THREAD = 0x00000002,
PROCESS_VM_OPERATION = 0x00000008,
PROCESS_VM_READ = 0x00000010,
PROCESS_VM_WRITE = 0x00000020,
PROCESS_DUP_HANDLE = 0x00000040,
PROCESS_CREATE_PROCESS = 0x000000080,
PROCESS_SET_QUOTA = 0x00000100,
PROCESS_SET_INFORMATION = 0x00000200,
PROCESS_QUERY_INFORMATION = 0x00000400, PROCESS_QUERY_INFORMATION = 0x00000400,
PROCESS_SUSPEND_RESUME = 0x00000800,
PROCESS_QUERY_LIMITED_INFORMATION = 0x00001000,
SYNCHRONIZE = 0x00100000
} }
public enum SECURITY_IMPERSONATION_LEVEL public enum SECURITY_IMPERSONATION_LEVEL
{ {
SecurityAnoynmous,
SecurityIdentification,
SecurityImpersonation, SecurityImpersonation,
SecurityDelegation
} }
public enum TOKEN_TYPE public enum TOKEN_TYPE
@ -395,13 +357,7 @@ namespace Ansible
enum JobObjectInfoType enum JobObjectInfoType
{ {
AssociateCompletionPortInformation = 7,
BasicLimitInformation = 2,
BasicUIRestrictions = 4,
EndOfJobTimeInformation = 6,
ExtendedLimitInformation = 9, ExtendedLimitInformation = 9,
SecurityLimitInformation = 5,
GroupInformation = 11
} }
[Flags] [Flags]
@ -455,8 +411,8 @@ namespace Ansible
private static extern bool SetInformationJobObject( private static extern bool SetInformationJobObject(
IntPtr hJob, IntPtr hJob,
JobObjectInfoType JobObjectInfoClass, JobObjectInfoType JobObjectInfoClass,
IntPtr lpJobObjectInfo, JOBOBJECT_EXTENDED_LIMIT_INFORMATION lpJobObjectInfo,
UInt32 cbJobObjectInfoLength); int cbJobObjectInfoLength);
[DllImport("kernel32.dll", SetLastError = true)] [DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AssignProcessToJobObject( private static extern bool AssignProcessToJobObject(
@ -475,17 +431,11 @@ namespace Ansible
if (handle == IntPtr.Zero) if (handle == IntPtr.Zero)
throw new Win32Exception("CreateJobObject() failed"); throw new Win32Exception("CreateJobObject() failed");
JOBOBJECT_BASIC_LIMIT_INFORMATION jobInfo = new JOBOBJECT_BASIC_LIMIT_INFORMATION();
jobInfo.LimitFlags = LimitFlags.JOB_OBJECT_LIMIT_BREAKAWAY_OK | LimitFlags.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedJobInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION(); JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedJobInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION();
extendedJobInfo.BasicLimitInformation = jobInfo; // on OSs that support nested jobs, one of the jobs must allow breakaway for async to work properly under WinRM
extendedJobInfo.BasicLimitInformation.LimitFlags = LimitFlags.JOB_OBJECT_LIMIT_BREAKAWAY_OK | LimitFlags.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
IntPtr pExtendedJobInfo = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(extendedJobInfo, pExtendedJobInfo, false);
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, pExtendedJobInfo, (UInt32)length)) if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedJobInfo, Marshal.SizeOf(extendedJobInfo)))
throw new Win32Exception("SetInformationJobObject() failed"); throw new Win32Exception("SetInformationJobObject() failed");
} }
@ -628,8 +578,6 @@ namespace Ansible
{ {
SecurityIdentifier account = GetBecomeSid(username); SecurityIdentifier account = GetBecomeSid(username);
CreationFlags startup_flags = CreationFlags.CREATE_UNICODE_ENVIRONMENT | CreationFlags.CREATE_BREAKAWAY_FROM_JOB | CreationFlags.CREATE_SUSPENDED;
STARTUPINFOEX si = new STARTUPINFOEX(); STARTUPINFOEX si = new STARTUPINFOEX();
si.startupInfo.dwFlags = (int)StartupInfoFlags.USESTDHANDLES; si.startupInfo.dwFlags = (int)StartupInfoFlags.USESTDHANDLES;
@ -665,6 +613,9 @@ namespace Ansible
// Create the environment block if set // Create the environment block if set
IntPtr lpEnvironment = IntPtr.Zero; IntPtr lpEnvironment = IntPtr.Zero;
// To support async + become, we have to do some job magic later, which requires both breakaway and starting suspended
CreationFlags startup_flags = CreationFlags.CREATE_UNICODE_ENVIRONMENT | CreationFlags.CREATE_BREAKAWAY_FROM_JOB | CreationFlags.CREATE_SUSPENDED;
PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
// Get the user tokens to try running processes with // Get the user tokens to try running processes with
@ -761,6 +712,9 @@ namespace Ansible
GrantAccessToWindowStationAndDesktop(account); GrantAccessToWindowStationAndDesktop(account);
string account_sid = account.ToString(); string account_sid = account.ToString();
bool impersonated = false; bool impersonated = false;
try
{
IntPtr hSystemTokenDup = IntPtr.Zero; IntPtr hSystemTokenDup = IntPtr.Zero;
// Try to get SYSTEM token handle so we can impersonate to get full admin token // Try to get SYSTEM token handle so we can impersonate to get full admin token
@ -792,12 +746,16 @@ namespace Ansible
else if (service_sids.Contains(account_sid)) else if (service_sids.Contains(account_sid))
throw new Win32Exception("Failed to impersonate as SYSTEM account"); throw new Win32Exception("Failed to impersonate as SYSTEM account");
} }
// If SYSTEM impersonation failed but we're trying to become a regular user, just proceed;
// might get a limited token in UAC-enabled cases, but better than nothing...
} }
LogonType logonType; LogonType logonType;
string domain = null; string domain = null;
if (service_sids.Contains(account_sid)) if (service_sids.Contains(account_sid))
{ {
// We're using a well-known service account, do a service logon instead of interactive
logonType = LogonType.LOGON32_LOGON_SERVICE; logonType = LogonType.LOGON32_LOGON_SERVICE;
domain = "NT AUTHORITY"; domain = "NT AUTHORITY";
password = null; password = null;
@ -848,10 +806,15 @@ namespace Ansible
IntPtr hTokenElevated = GetElevatedToken(hToken); IntPtr hTokenElevated = GetElevatedToken(hToken);
tokens.Add(hTokenElevated); tokens.Add(hTokenElevated);
} }
tokens.Add(hToken);
// add the original token as a fallback
tokens.Add(hToken);
}
finally
{
if (impersonated) if (impersonated)
RevertToSelf(); RevertToSelf();
}
return tokens; return tokens;
} }

Loading…
Cancel
Save