mirror of https://github.com/ansible/ansible.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
458 lines
16 KiB
C#
458 lines
16 KiB
C#
using Microsoft.Win32.SafeHandles;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security.Principal;
|
|
using System.Text;
|
|
|
|
namespace Ansible.AccessToken
|
|
{
|
|
internal class NativeHelpers
|
|
{
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct LUID_AND_ATTRIBUTES
|
|
{
|
|
public Luid Luid;
|
|
public UInt32 Attributes;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct SID_AND_ATTRIBUTES
|
|
{
|
|
public IntPtr Sid;
|
|
public int Attributes;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct TOKEN_PRIVILEGES
|
|
{
|
|
public UInt32 PrivilegeCount;
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
|
|
public LUID_AND_ATTRIBUTES[] Privileges;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct TOKEN_USER
|
|
{
|
|
public SID_AND_ATTRIBUTES User;
|
|
}
|
|
|
|
public enum TokenInformationClass : uint
|
|
{
|
|
TokenUser = 1,
|
|
TokenPrivileges = 3,
|
|
TokenStatistics = 10,
|
|
TokenElevationType = 18,
|
|
TokenLinkedToken = 19,
|
|
}
|
|
}
|
|
|
|
internal class NativeMethods
|
|
{
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
|
public static extern bool CloseHandle(
|
|
IntPtr hObject);
|
|
|
|
[DllImport("advapi32.dll", SetLastError = true)]
|
|
public static extern bool DuplicateTokenEx(
|
|
SafeNativeHandle hExistingToken,
|
|
TokenAccessLevels dwDesiredAccess,
|
|
IntPtr lpTokenAttributes,
|
|
SecurityImpersonationLevel ImpersonationLevel,
|
|
TokenType TokenType,
|
|
out SafeNativeHandle phNewToken);
|
|
|
|
[DllImport("kernel32.dll")]
|
|
public static extern SafeNativeHandle GetCurrentProcess();
|
|
|
|
[DllImport("advapi32.dll", SetLastError = true)]
|
|
public static extern bool GetTokenInformation(
|
|
SafeNativeHandle TokenHandle,
|
|
NativeHelpers.TokenInformationClass TokenInformationClass,
|
|
SafeMemoryBuffer TokenInformation,
|
|
UInt32 TokenInformationLength,
|
|
out UInt32 ReturnLength);
|
|
|
|
[DllImport("advapi32.dll", SetLastError = true)]
|
|
public static extern bool ImpersonateLoggedOnUser(
|
|
SafeNativeHandle hToken);
|
|
|
|
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
public static extern bool LogonUserW(
|
|
string lpszUsername,
|
|
string lpszDomain,
|
|
string lpszPassword,
|
|
LogonType dwLogonType,
|
|
LogonProvider dwLogonProvider,
|
|
out SafeNativeHandle phToken);
|
|
|
|
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
public static extern bool LookupPrivilegeNameW(
|
|
string lpSystemName,
|
|
ref Luid lpLuid,
|
|
StringBuilder lpName,
|
|
ref UInt32 cchName);
|
|
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
|
public static extern SafeNativeHandle OpenProcess(
|
|
ProcessAccessFlags dwDesiredAccess,
|
|
bool bInheritHandle,
|
|
UInt32 dwProcessId);
|
|
|
|
[DllImport("advapi32.dll", SetLastError = true)]
|
|
public static extern bool OpenProcessToken(
|
|
SafeNativeHandle ProcessHandle,
|
|
TokenAccessLevels DesiredAccess,
|
|
out SafeNativeHandle TokenHandle);
|
|
|
|
[DllImport("advapi32.dll", SetLastError = true)]
|
|
public static extern bool RevertToSelf();
|
|
}
|
|
|
|
internal class SafeMemoryBuffer : SafeHandleZeroOrMinusOneIsInvalid
|
|
{
|
|
public SafeMemoryBuffer() : base(true) { }
|
|
public SafeMemoryBuffer(int cb) : base(true)
|
|
{
|
|
base.SetHandle(Marshal.AllocHGlobal(cb));
|
|
}
|
|
public SafeMemoryBuffer(IntPtr handle) : base(true)
|
|
{
|
|
base.SetHandle(handle);
|
|
}
|
|
|
|
protected override bool ReleaseHandle()
|
|
{
|
|
Marshal.FreeHGlobal(handle);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public enum LogonProvider
|
|
{
|
|
Default,
|
|
WinNT35,
|
|
WinNT40,
|
|
WinNT50,
|
|
}
|
|
|
|
public enum LogonType
|
|
{
|
|
Interactive = 2,
|
|
Network = 3,
|
|
Batch = 4,
|
|
Service = 5,
|
|
Unlock = 7,
|
|
NetworkCleartext = 8,
|
|
NewCredentials = 9,
|
|
}
|
|
|
|
[Flags]
|
|
public enum PrivilegeAttributes : uint
|
|
{
|
|
Disabled = 0x00000000,
|
|
EnabledByDefault = 0x00000001,
|
|
Enabled = 0x00000002,
|
|
Removed = 0x00000004,
|
|
UsedForAccess = 0x80000000,
|
|
}
|
|
|
|
[Flags]
|
|
public enum ProcessAccessFlags : uint
|
|
{
|
|
Terminate = 0x00000001,
|
|
CreateThread = 0x00000002,
|
|
VmOperation = 0x00000008,
|
|
VmRead = 0x00000010,
|
|
VmWrite = 0x00000020,
|
|
DupHandle = 0x00000040,
|
|
CreateProcess = 0x00000080,
|
|
SetQuota = 0x00000100,
|
|
SetInformation = 0x00000200,
|
|
QueryInformation = 0x00000400,
|
|
SuspendResume = 0x00000800,
|
|
QueryLimitedInformation = 0x00001000,
|
|
Delete = 0x00010000,
|
|
ReadControl = 0x00020000,
|
|
WriteDac = 0x00040000,
|
|
WriteOwner = 0x00080000,
|
|
Synchronize = 0x00100000,
|
|
}
|
|
|
|
public enum SecurityImpersonationLevel
|
|
{
|
|
Anonymous,
|
|
Identification,
|
|
Impersonation,
|
|
Delegation,
|
|
}
|
|
|
|
public enum TokenElevationType
|
|
{
|
|
Default = 1,
|
|
Full,
|
|
Limited,
|
|
}
|
|
|
|
public enum TokenType
|
|
{
|
|
Primary = 1,
|
|
Impersonation,
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct Luid
|
|
{
|
|
public UInt32 LowPart;
|
|
public Int32 HighPart;
|
|
|
|
public static explicit operator UInt64(Luid l)
|
|
{
|
|
return (UInt64)((UInt64)l.HighPart << 32) | (UInt64)l.LowPart;
|
|
}
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct TokenStatistics
|
|
{
|
|
public Luid TokenId;
|
|
public Luid AuthenticationId;
|
|
public Int64 ExpirationTime;
|
|
public TokenType TokenType;
|
|
public SecurityImpersonationLevel ImpersonationLevel;
|
|
public UInt32 DynamicCharged;
|
|
public UInt32 DynamicAvailable;
|
|
public UInt32 GroupCount;
|
|
public UInt32 PrivilegeCount;
|
|
public Luid ModifiedId;
|
|
}
|
|
|
|
public class PrivilegeInfo
|
|
{
|
|
public string Name;
|
|
public PrivilegeAttributes Attributes;
|
|
|
|
internal PrivilegeInfo(NativeHelpers.LUID_AND_ATTRIBUTES la)
|
|
{
|
|
Name = TokenUtil.GetPrivilegeName(la.Luid);
|
|
Attributes = (PrivilegeAttributes)la.Attributes;
|
|
}
|
|
}
|
|
|
|
public class SafeNativeHandle : SafeHandleZeroOrMinusOneIsInvalid
|
|
{
|
|
public SafeNativeHandle() : base(true) { }
|
|
public SafeNativeHandle(IntPtr handle) : base(true) { this.handle = handle; }
|
|
|
|
protected override bool ReleaseHandle()
|
|
{
|
|
return NativeMethods.CloseHandle(handle);
|
|
}
|
|
}
|
|
|
|
public class Win32Exception : System.ComponentModel.Win32Exception
|
|
{
|
|
private string _msg;
|
|
|
|
public Win32Exception(string message) : this(Marshal.GetLastWin32Error(), message) { }
|
|
public Win32Exception(int errorCode, string message) : base(errorCode)
|
|
{
|
|
_msg = String.Format("{0} ({1}, Win32ErrorCode {2} - 0x{2:X8})", message, base.Message, errorCode);
|
|
}
|
|
|
|
public override string Message { get { return _msg; } }
|
|
public static explicit operator Win32Exception(string message) { return new Win32Exception(message); }
|
|
}
|
|
|
|
public class TokenUtil
|
|
{
|
|
public static SafeNativeHandle DuplicateToken(SafeNativeHandle hToken, TokenAccessLevels access,
|
|
SecurityImpersonationLevel impersonationLevel, TokenType tokenType)
|
|
{
|
|
SafeNativeHandle dupToken;
|
|
if (!NativeMethods.DuplicateTokenEx(hToken, access, IntPtr.Zero, impersonationLevel, tokenType, out dupToken))
|
|
throw new Win32Exception("Failed to duplicate token");
|
|
return dupToken;
|
|
}
|
|
|
|
public static SecurityIdentifier GetTokenUser(SafeNativeHandle hToken)
|
|
{
|
|
using (SafeMemoryBuffer tokenInfo = GetTokenInformation(hToken,
|
|
NativeHelpers.TokenInformationClass.TokenUser))
|
|
{
|
|
NativeHelpers.TOKEN_USER tokenUser = (NativeHelpers.TOKEN_USER)Marshal.PtrToStructure(
|
|
tokenInfo.DangerousGetHandle(),
|
|
typeof(NativeHelpers.TOKEN_USER));
|
|
return new SecurityIdentifier(tokenUser.User.Sid);
|
|
}
|
|
}
|
|
|
|
public static List<PrivilegeInfo> GetTokenPrivileges(SafeNativeHandle hToken)
|
|
{
|
|
using (SafeMemoryBuffer tokenInfo = GetTokenInformation(hToken,
|
|
NativeHelpers.TokenInformationClass.TokenPrivileges))
|
|
{
|
|
NativeHelpers.TOKEN_PRIVILEGES tokenPrivs = (NativeHelpers.TOKEN_PRIVILEGES)Marshal.PtrToStructure(
|
|
tokenInfo.DangerousGetHandle(),
|
|
typeof(NativeHelpers.TOKEN_PRIVILEGES));
|
|
|
|
NativeHelpers.LUID_AND_ATTRIBUTES[] luidAttrs =
|
|
new NativeHelpers.LUID_AND_ATTRIBUTES[tokenPrivs.PrivilegeCount];
|
|
PtrToStructureArray(luidAttrs, IntPtr.Add(tokenInfo.DangerousGetHandle(),
|
|
Marshal.SizeOf(tokenPrivs.PrivilegeCount)));
|
|
|
|
return luidAttrs.Select(la => new PrivilegeInfo(la)).ToList();
|
|
}
|
|
}
|
|
|
|
public static TokenStatistics GetTokenStatistics(SafeNativeHandle hToken)
|
|
{
|
|
using (SafeMemoryBuffer tokenInfo = GetTokenInformation(hToken,
|
|
NativeHelpers.TokenInformationClass.TokenStatistics))
|
|
{
|
|
TokenStatistics tokenStats = (TokenStatistics)Marshal.PtrToStructure(
|
|
tokenInfo.DangerousGetHandle(),
|
|
typeof(TokenStatistics));
|
|
return tokenStats;
|
|
}
|
|
}
|
|
|
|
public static TokenElevationType GetTokenElevationType(SafeNativeHandle hToken)
|
|
{
|
|
using (SafeMemoryBuffer tokenInfo = GetTokenInformation(hToken,
|
|
NativeHelpers.TokenInformationClass.TokenElevationType))
|
|
{
|
|
return (TokenElevationType)Marshal.ReadInt32(tokenInfo.DangerousGetHandle());
|
|
}
|
|
}
|
|
|
|
public static SafeNativeHandle GetTokenLinkedToken(SafeNativeHandle hToken)
|
|
{
|
|
using (SafeMemoryBuffer tokenInfo = GetTokenInformation(hToken,
|
|
NativeHelpers.TokenInformationClass.TokenLinkedToken))
|
|
{
|
|
return new SafeNativeHandle(Marshal.ReadIntPtr(tokenInfo.DangerousGetHandle()));
|
|
}
|
|
}
|
|
|
|
public static IEnumerable<SafeNativeHandle> EnumerateUserTokens(SecurityIdentifier sid,
|
|
TokenAccessLevels access = TokenAccessLevels.Query)
|
|
{
|
|
foreach (System.Diagnostics.Process process in System.Diagnostics.Process.GetProcesses())
|
|
{
|
|
// We always need the Query access level so we can query the TokenUser
|
|
using (process)
|
|
using (SafeNativeHandle hToken = TryOpenAccessToken(process, access | TokenAccessLevels.Query))
|
|
{
|
|
if (hToken == null)
|
|
continue;
|
|
|
|
if (!sid.Equals(GetTokenUser(hToken)))
|
|
continue;
|
|
|
|
yield return hToken;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void ImpersonateToken(SafeNativeHandle hToken)
|
|
{
|
|
if (!NativeMethods.ImpersonateLoggedOnUser(hToken))
|
|
throw new Win32Exception("Failed to impersonate token");
|
|
}
|
|
|
|
public static SafeNativeHandle LogonUser(string username, string domain, string password, LogonType logonType,
|
|
LogonProvider logonProvider)
|
|
{
|
|
SafeNativeHandle hToken;
|
|
if (!NativeMethods.LogonUserW(username, domain, password, logonType, logonProvider, out hToken))
|
|
throw new Win32Exception(String.Format("Failed to logon {0}",
|
|
String.IsNullOrEmpty(domain) ? username : domain + "\\" + username));
|
|
|
|
return hToken;
|
|
}
|
|
|
|
public static SafeNativeHandle OpenProcess()
|
|
{
|
|
return NativeMethods.GetCurrentProcess();
|
|
}
|
|
|
|
public static SafeNativeHandle OpenProcess(Int32 pid, ProcessAccessFlags access, bool inherit)
|
|
{
|
|
SafeNativeHandle hProcess = NativeMethods.OpenProcess(access, inherit, (UInt32)pid);
|
|
if (hProcess.IsInvalid)
|
|
throw new Win32Exception(String.Format("Failed to open process {0} with access {1}",
|
|
pid, access.ToString()));
|
|
|
|
return hProcess;
|
|
}
|
|
|
|
public static SafeNativeHandle OpenProcessToken(SafeNativeHandle hProcess, TokenAccessLevels access)
|
|
{
|
|
SafeNativeHandle hToken;
|
|
if (!NativeMethods.OpenProcessToken(hProcess, access, out hToken))
|
|
throw new Win32Exception(String.Format("Failed to open process token with access {0}",
|
|
access.ToString()));
|
|
|
|
return hToken;
|
|
}
|
|
|
|
public static void RevertToSelf()
|
|
{
|
|
if (!NativeMethods.RevertToSelf())
|
|
throw new Win32Exception("Failed to revert thread impersonation");
|
|
}
|
|
|
|
internal static string GetPrivilegeName(Luid luid)
|
|
{
|
|
UInt32 nameLen = 0;
|
|
NativeMethods.LookupPrivilegeNameW(null, ref luid, null, ref nameLen);
|
|
|
|
StringBuilder name = new StringBuilder((int)(nameLen + 1));
|
|
if (!NativeMethods.LookupPrivilegeNameW(null, ref luid, name, ref nameLen))
|
|
throw new Win32Exception("LookupPrivilegeName() failed");
|
|
|
|
return name.ToString();
|
|
}
|
|
|
|
private static SafeMemoryBuffer GetTokenInformation(SafeNativeHandle hToken,
|
|
NativeHelpers.TokenInformationClass infoClass)
|
|
{
|
|
UInt32 tokenLength;
|
|
bool res = NativeMethods.GetTokenInformation(hToken, infoClass, new SafeMemoryBuffer(IntPtr.Zero), 0,
|
|
out tokenLength);
|
|
int errCode = Marshal.GetLastWin32Error();
|
|
if (!res && errCode != 24 && errCode != 122) // ERROR_INSUFFICIENT_BUFFER, ERROR_BAD_LENGTH
|
|
throw new Win32Exception(errCode, String.Format("GetTokenInformation({0}) failed to get buffer length",
|
|
infoClass.ToString()));
|
|
|
|
SafeMemoryBuffer tokenInfo = new SafeMemoryBuffer((int)tokenLength);
|
|
if (!NativeMethods.GetTokenInformation(hToken, infoClass, tokenInfo, tokenLength, out tokenLength))
|
|
throw new Win32Exception(String.Format("GetTokenInformation({0}) failed", infoClass.ToString()));
|
|
|
|
return tokenInfo;
|
|
}
|
|
|
|
private static void PtrToStructureArray<T>(T[] array, IntPtr ptr)
|
|
{
|
|
IntPtr ptrOffset = ptr;
|
|
for (int i = 0; i < array.Length; i++, ptrOffset = IntPtr.Add(ptrOffset, Marshal.SizeOf(typeof(T))))
|
|
array[i] = (T)Marshal.PtrToStructure(ptrOffset, typeof(T));
|
|
}
|
|
|
|
private static SafeNativeHandle TryOpenAccessToken(System.Diagnostics.Process process, TokenAccessLevels access)
|
|
{
|
|
try
|
|
{
|
|
using (SafeNativeHandle hProcess = OpenProcess(process.Id, ProcessAccessFlags.QueryInformation, false))
|
|
return OpenProcessToken(hProcess, access);
|
|
}
|
|
catch (Win32Exception)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
}
|