1 using Microsoft.Win32.SafeHandles; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Runtime.ConstrainedExecution; 6 using System.Runtime.InteropServices; 7 using System.Security.Principal; 8 using System.Text; 9 10 namespace Ansible.AccessToken 11 { 12 internal class NativeHelpers 13 { 14 [StructLayout(LayoutKind.Sequential)] 15 public struct LUID_AND_ATTRIBUTES 16 { 17 public Luid Luid; 18 public UInt32 Attributes; 19 } 20 21 [StructLayout(LayoutKind.Sequential)] 22 public struct SID_AND_ATTRIBUTES 23 { 24 public IntPtr Sid; 25 public int Attributes; 26 } 27 28 [StructLayout(LayoutKind.Sequential)] 29 public struct TOKEN_PRIVILEGES 30 { 31 public UInt32 PrivilegeCount; 32 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] 33 public LUID_AND_ATTRIBUTES[] Privileges; 34 } 35 36 [StructLayout(LayoutKind.Sequential)] 37 public struct TOKEN_USER 38 { 39 public SID_AND_ATTRIBUTES User; 40 } 41 42 public enum TokenInformationClass : uint 43 { 44 TokenUser = 1, 45 TokenPrivileges = 3, 46 TokenStatistics = 10, write_to_file_descriptor(fd, obj)47 TokenElevationType = 18, 48 TokenLinkedToken = 19, 49 } 50 } 51 52 internal class NativeMethods 53 { 54 [DllImport("kernel32.dll", SetLastError = true)] 55 public static extern bool CloseHandle( 56 IntPtr hObject); 57 58 [DllImport("advapi32.dll", SetLastError = true)] 59 public static extern bool DuplicateTokenEx( 60 SafeNativeHandle hExistingToken, 61 TokenAccessLevels dwDesiredAccess, 62 IntPtr lpTokenAttributes, 63 SecurityImpersonationLevel ImpersonationLevel, 64 TokenType TokenType, 65 out SafeNativeHandle phNewToken); 66 67 [DllImport("kernel32.dll")] 68 public static extern SafeNativeHandle GetCurrentProcess(); 69 70 [DllImport("advapi32.dll", SetLastError = true)] 71 public static extern bool GetTokenInformation( 72 SafeNativeHandle TokenHandle, 73 NativeHelpers.TokenInformationClass TokenInformationClass, 74 SafeMemoryBuffer TokenInformation, 75 UInt32 TokenInformationLength, 76 out UInt32 ReturnLength); 77 78 [DllImport("advapi32.dll", SetLastError = true)] 79 public static extern bool ImpersonateLoggedOnUser( 80 SafeNativeHandle hToken); 81 82 [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 83 public static extern bool LogonUserW( 84 string lpszUsername, 85 string lpszDomain, 86 string lpszPassword, 87 LogonType dwLogonType, 88 LogonProvider dwLogonProvider, 89 out SafeNativeHandle phToken); 90 91 [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 92 public static extern bool LookupPrivilegeNameW( 93 string lpSystemName, 94 ref Luid lpLuid, 95 StringBuilder lpName, 96 ref UInt32 cchName); 97 98 [DllImport("kernel32.dll", SetLastError = true)] 99 public static extern SafeNativeHandle OpenProcess( 100 ProcessAccessFlags dwDesiredAccess, 101 bool bInheritHandle, 102 UInt32 dwProcessId); 103 104 [DllImport("advapi32.dll", SetLastError = true)] 105 public static extern bool OpenProcessToken( 106 SafeNativeHandle ProcessHandle, 107 TokenAccessLevels DesiredAccess, 108 out SafeNativeHandle TokenHandle); 109 110 [DllImport("advapi32.dll", SetLastError = true)] 111 public static extern bool RevertToSelf(); 112 } 113 114 internal class SafeMemoryBuffer : SafeHandleZeroOrMinusOneIsInvalid 115 { 116 public SafeMemoryBuffer() : base(true) { } 117 public SafeMemoryBuffer(int cb) : base(true) 118 { 119 base.SetHandle(Marshal.AllocHGlobal(cb)); 120 } 121 public SafeMemoryBuffer(IntPtr handle) : base(true) 122 { 123 base.SetHandle(handle); 124 } 125 126 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 127 protected override bool ReleaseHandle() 128 { 129 Marshal.FreeHGlobal(handle); 130 return true; 131 } 132 } 133 134 public enum LogonProvider 135 { 136 Default, 137 WinNT35, 138 WinNT40, 139 WinNT50, 140 } 141 142 public enum LogonType 143 { 144 Interactive = 2, 145 Network = 3, 146 Batch = 4, 147 Service = 5, 148 Unlock = 7, 149 NetworkCleartext = 8, 150 NewCredentials = 9, 151 } 152 153 [Flags] 154 public enum PrivilegeAttributes : uint 155 { 156 Disabled = 0x00000000, 157 EnabledByDefault = 0x00000001, 158 Enabled = 0x00000002, 159 Removed = 0x00000004, 160 UsedForAccess = 0x80000000, 161 } 162 163 [Flags] 164 public enum ProcessAccessFlags : uint 165 { 166 Terminate = 0x00000001, 167 CreateThread = 0x00000002, 168 VmOperation = 0x00000008, 169 VmRead = 0x00000010, 170 VmWrite = 0x00000020, 171 DupHandle = 0x00000040, 172 CreateProcess = 0x00000080, 173 SetQuota = 0x00000100, 174 SetInformation = 0x00000200, 175 QueryInformation = 0x00000400, 176 SuspendResume = 0x00000800, 177 QueryLimitedInformation = 0x00001000, 178 Delete = 0x00010000, 179 ReadControl = 0x00020000, 180 WriteDac = 0x00040000, 181 WriteOwner = 0x00080000, 182 Synchronize = 0x00100000, 183 } 184 185 public enum SecurityImpersonationLevel 186 { 187 Anonymous, 188 Identification, 189 Impersonation, 190 Delegation, 191 } 192 193 public enum TokenElevationType 194 { 195 Default = 1, 196 Full, 197 Limited, 198 } 199 200 public enum TokenType 201 { 202 Primary = 1, 203 Impersonation, 204 } 205 206 [StructLayout(LayoutKind.Sequential)] 207 public struct Luid 208 { 209 public UInt32 LowPart; 210 public Int32 HighPart; 211 212 public static explicit operator UInt64(Luid l) 213 { 214 return (UInt64)((UInt64)l.HighPart << 32) | (UInt64)l.LowPart; 215 } 216 } 217 218 [StructLayout(LayoutKind.Sequential)] 219 public struct TokenStatistics 220 { 221 public Luid TokenId; 222 public Luid AuthenticationId; 223 public Int64 ExpirationTime; 224 public TokenType TokenType; 225 public SecurityImpersonationLevel ImpersonationLevel; 226 public UInt32 DynamicCharged; 227 public UInt32 DynamicAvailable; 228 public UInt32 GroupCount; 229 public UInt32 PrivilegeCount; 230 public Luid ModifiedId; 231 } 232 233 public class PrivilegeInfo 234 { 235 public string Name; 236 public PrivilegeAttributes Attributes; 237 238 internal PrivilegeInfo(NativeHelpers.LUID_AND_ATTRIBUTES la) 239 { 240 Name = TokenUtil.GetPrivilegeName(la.Luid); 241 Attributes = (PrivilegeAttributes)la.Attributes; 242 } 243 } 244 245 public class SafeNativeHandle : SafeHandleZeroOrMinusOneIsInvalid 246 { 247 public SafeNativeHandle() : base(true) { } 248 public SafeNativeHandle(IntPtr handle) : base(true) { this.handle = handle; } 249 250 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 251 protected override bool ReleaseHandle() 252 { 253 return NativeMethods.CloseHandle(handle); 254 } 255 } 256 257 public class Win32Exception : System.ComponentModel.Win32Exception 258 { 259 private string _msg; 260 261 public Win32Exception(string message) : this(Marshal.GetLastWin32Error(), message) { } 262 public Win32Exception(int errorCode, string message) : base(errorCode) 263 { 264 _msg = String.Format("{0} ({1}, Win32ErrorCode {2} - 0x{2:X8})", message, base.Message, errorCode); 265 } 266 267 public override string Message { get { return _msg; } } 268 public static explicit operator Win32Exception(string message) { return new Win32Exception(message); } 269 } 270 271 public class TokenUtil 272 { 273 public static SafeNativeHandle DuplicateToken(SafeNativeHandle hToken, TokenAccessLevels access, 274 SecurityImpersonationLevel impersonationLevel, TokenType tokenType) 275 { 276 SafeNativeHandle dupToken; 277 if (!NativeMethods.DuplicateTokenEx(hToken, access, IntPtr.Zero, impersonationLevel, tokenType, out dupToken)) 278 throw new Win32Exception("Failed to duplicate token"); 279 return dupToken; 280 } 281 282 public static SecurityIdentifier GetTokenUser(SafeNativeHandle hToken) 283 { 284 using (SafeMemoryBuffer tokenInfo = GetTokenInformation(hToken, 285 NativeHelpers.TokenInformationClass.TokenUser)) 286 { 287 NativeHelpers.TOKEN_USER tokenUser = (NativeHelpers.TOKEN_USER)Marshal.PtrToStructure( 288 tokenInfo.DangerousGetHandle(), 289 typeof(NativeHelpers.TOKEN_USER)); 290 return new SecurityIdentifier(tokenUser.User.Sid); 291 } 292 } 293 294 public static List<PrivilegeInfo> GetTokenPrivileges(SafeNativeHandle hToken) 295 { 296 using (SafeMemoryBuffer tokenInfo = GetTokenInformation(hToken, 297 NativeHelpers.TokenInformationClass.TokenPrivileges)) 298 { 299 NativeHelpers.TOKEN_PRIVILEGES tokenPrivs = (NativeHelpers.TOKEN_PRIVILEGES)Marshal.PtrToStructure( 300 tokenInfo.DangerousGetHandle(), 301 typeof(NativeHelpers.TOKEN_PRIVILEGES)); 302 303 NativeHelpers.LUID_AND_ATTRIBUTES[] luidAttrs = 304 new NativeHelpers.LUID_AND_ATTRIBUTES[tokenPrivs.PrivilegeCount]; 305 PtrToStructureArray(luidAttrs, IntPtr.Add(tokenInfo.DangerousGetHandle(), 306 Marshal.SizeOf(tokenPrivs.PrivilegeCount))); 307 308 return luidAttrs.Select(la => new PrivilegeInfo(la)).ToList(); 309 } 310 } 311 312 public static TokenStatistics GetTokenStatistics(SafeNativeHandle hToken) 313 { 314 using (SafeMemoryBuffer tokenInfo = GetTokenInformation(hToken, 315 NativeHelpers.TokenInformationClass.TokenStatistics)) 316 { 317 TokenStatistics tokenStats = (TokenStatistics)Marshal.PtrToStructure( 318 tokenInfo.DangerousGetHandle(), 319 typeof(TokenStatistics)); 320 return tokenStats; 321 } 322 } 323 324 public static TokenElevationType GetTokenElevationType(SafeNativeHandle hToken) 325 { 326 using (SafeMemoryBuffer tokenInfo = GetTokenInformation(hToken, 327 NativeHelpers.TokenInformationClass.TokenElevationType)) 328 { 329 return (TokenElevationType)Marshal.ReadInt32(tokenInfo.DangerousGetHandle()); 330 } 331 } 332 333 public static SafeNativeHandle GetTokenLinkedToken(SafeNativeHandle hToken) 334 { 335 using (SafeMemoryBuffer tokenInfo = GetTokenInformation(hToken, 336 NativeHelpers.TokenInformationClass.TokenLinkedToken)) 337 { 338 return new SafeNativeHandle(Marshal.ReadIntPtr(tokenInfo.DangerousGetHandle())); 339 } 340 } 341 342 public static IEnumerable<SafeNativeHandle> EnumerateUserTokens(SecurityIdentifier sid, 343 TokenAccessLevels access = TokenAccessLevels.Query) 344 { 345 foreach (System.Diagnostics.Process process in System.Diagnostics.Process.GetProcesses()) 346 { 347 // We always need the Query access level so we can query the TokenUser 348 using (process) 349 using (SafeNativeHandle hToken = TryOpenAccessToken(process, access | TokenAccessLevels.Query)) 350 { 351 if (hToken == null) 352 continue; 353 354 if (!sid.Equals(GetTokenUser(hToken))) 355 continue; 356 357 yield return hToken; 358 } 359 } 360 } 361 362 public static void ImpersonateToken(SafeNativeHandle hToken) 363 { 364 if (!NativeMethods.ImpersonateLoggedOnUser(hToken)) 365 throw new Win32Exception("Failed to impersonate token"); 366 } 367 368 public static SafeNativeHandle LogonUser(string username, string domain, string password, LogonType logonType, 369 LogonProvider logonProvider) 370 { 371 SafeNativeHandle hToken; 372 if (!NativeMethods.LogonUserW(username, domain, password, logonType, logonProvider, out hToken)) 373 throw new Win32Exception(String.Format("Failed to logon {0}", 374 String.IsNullOrEmpty(domain) ? username : domain + "\\" + username)); 375 376 return hToken; 377 } 378 379 public static SafeNativeHandle OpenProcess() 380 { 381 return NativeMethods.GetCurrentProcess(); 382 } 383 384 public static SafeNativeHandle OpenProcess(Int32 pid, ProcessAccessFlags access, bool inherit) 385 { 386 SafeNativeHandle hProcess = NativeMethods.OpenProcess(access, inherit, (UInt32)pid); 387 if (hProcess.IsInvalid) 388 throw new Win32Exception(String.Format("Failed to open process {0} with access {1}", 389 pid, access.ToString())); 390 391 return hProcess; 392 } 393 394 public static SafeNativeHandle OpenProcessToken(SafeNativeHandle hProcess, TokenAccessLevels access) 395 { 396 SafeNativeHandle hToken; 397 if (!NativeMethods.OpenProcessToken(hProcess, access, out hToken)) 398 throw new Win32Exception(String.Format("Failed to open proces token with access {0}", 399 access.ToString())); 400 401 return hToken; 402 } 403 404 public static void RevertToSelf() 405 { 406 if (!NativeMethods.RevertToSelf()) 407 throw new Win32Exception("Failed to revert thread impersonation"); 408 } 409 410 internal static string GetPrivilegeName(Luid luid) 411 { 412 UInt32 nameLen = 0; 413 NativeMethods.LookupPrivilegeNameW(null, ref luid, null, ref nameLen); 414 415 StringBuilder name = new StringBuilder((int)(nameLen + 1)); 416 if (!NativeMethods.LookupPrivilegeNameW(null, ref luid, name, ref nameLen)) 417 throw new Win32Exception("LookupPrivilegeName() failed"); 418 419 return name.ToString(); 420 } 421 422 private static SafeMemoryBuffer GetTokenInformation(SafeNativeHandle hToken, 423 NativeHelpers.TokenInformationClass infoClass) 424 { 425 UInt32 tokenLength; 426 bool res = NativeMethods.GetTokenInformation(hToken, infoClass, new SafeMemoryBuffer(IntPtr.Zero), 0, 427 out tokenLength); 428 int errCode = Marshal.GetLastWin32Error(); 429 if (!res && errCode != 24 && errCode != 122) // ERROR_INSUFFICIENT_BUFFER, ERROR_BAD_LENGTH 430 throw new Win32Exception(errCode, String.Format("GetTokenInformation({0}) failed to get buffer length", 431 infoClass.ToString())); 432 433 SafeMemoryBuffer tokenInfo = new SafeMemoryBuffer((int)tokenLength); 434 if (!NativeMethods.GetTokenInformation(hToken, infoClass, tokenInfo, tokenLength, out tokenLength)) 435 throw new Win32Exception(String.Format("GetTokenInformation({0}) failed", infoClass.ToString())); 436 437 return tokenInfo; 438 } 439 440 private static void PtrToStructureArray<T>(T[] array, IntPtr ptr) 441 { 442 IntPtr ptrOffset = ptr; 443 for (int i = 0; i < array.Length; i++, ptrOffset = IntPtr.Add(ptrOffset, Marshal.SizeOf(typeof(T)))) 444 array[i] = (T)Marshal.PtrToStructure(ptrOffset, typeof(T)); 445 } 446 447 private static SafeNativeHandle TryOpenAccessToken(System.Diagnostics.Process process, TokenAccessLevels access) 448 { 449 try 450 { 451 using (SafeNativeHandle hProcess = OpenProcess(process.Id, ProcessAccessFlags.QueryInformation, false)) 452 return OpenProcessToken(hProcess, access); 453 } 454 catch (Win32Exception) 455 { 456 return null; 457 } 458 } 459 } 460 } 461