1 using Microsoft.Win32.SafeHandles; 2 using System; 3 using System.Collections; 4 using System.Collections.Generic; 5 using System.IO; 6 using System.Linq; 7 using System.Runtime.ConstrainedExecution; 8 using System.Runtime.InteropServices; 9 using System.Security.AccessControl; 10 using System.Security.Principal; 11 using System.Text; 12 using Ansible.AccessToken; 13 using Ansible.Process; 14 15 namespace Ansible.Become 16 { 17 internal class NativeHelpers 18 { 19 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 20 public struct KERB_S4U_LOGON 21 { 22 public UInt32 MessageType; 23 public UInt32 Flags; 24 public LSA_UNICODE_STRING ClientUpn; 25 public LSA_UNICODE_STRING ClientRealm; 26 } 27 28 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 29 public struct LSA_STRING 30 { 31 public UInt16 Length; 32 public UInt16 MaximumLength; 33 [MarshalAs(UnmanagedType.LPStr)] public string Buffer; 34 ExitHandler(int rc)35 public static implicit operator string(LSA_STRING s) 36 { 37 return s.Buffer; 38 } 39 40 public static implicit operator LSA_STRING(string s) 41 { 42 if (s == null) 43 s = ""; 44 45 LSA_STRING lsaStr = new LSA_STRING 46 { 47 Buffer = s, 48 Length = (UInt16)s.Length, 49 MaximumLength = (UInt16)(s.Length + 1), 50 }; 51 return lsaStr; 52 } 53 } 54 55 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 56 public struct LSA_UNICODE_STRING 57 { 58 public UInt16 Length; 59 public UInt16 MaximumLength; 60 public IntPtr Buffer; 61 } 62 63 [StructLayout(LayoutKind.Sequential)] 64 public struct SECURITY_LOGON_SESSION_DATA 65 { 66 public UInt32 Size; 67 public Luid LogonId; 68 public LSA_UNICODE_STRING UserName; 69 public LSA_UNICODE_STRING LogonDomain; 70 public LSA_UNICODE_STRING AuthenticationPackage; 71 public SECURITY_LOGON_TYPE LogonType; 72 } 73 74 [StructLayout(LayoutKind.Sequential)] 75 public struct TOKEN_SOURCE 76 { 77 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public char[] SourceName; 78 public Luid SourceIdentifier; 79 } 80 81 public enum SECURITY_LOGON_TYPE 82 { 83 System = 0, // Used only by the Sytem account 84 Interactive = 2, 85 Network, 86 Batch, 87 Service, 88 Proxy, 89 Unlock, 90 NetworkCleartext, 91 NewCredentials, 92 RemoteInteractive, 93 CachedInteractive, 94 CachedRemoteInteractive, 95 CachedUnlock 96 } 97 } 98 99 internal class NativeMethods 100 { 101 [DllImport("advapi32.dll", SetLastError = true)] 102 public static extern bool AllocateLocallyUniqueId( 103 out Luid Luid); 104 105 [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 106 public static extern bool CreateProcessWithTokenW( 107 SafeNativeHandle hToken, 108 LogonFlags dwLogonFlags, 109 [MarshalAs(UnmanagedType.LPWStr)] string lpApplicationName, 110 StringBuilder lpCommandLine, 111 Process.NativeHelpers.ProcessCreationFlags dwCreationFlags, 112 Process.SafeMemoryBuffer lpEnvironment, 113 [MarshalAs(UnmanagedType.LPWStr)] string lpCurrentDirectory, 114 Process.NativeHelpers.STARTUPINFOEX lpStartupInfo, 115 out Process.NativeHelpers.PROCESS_INFORMATION lpProcessInformation); 116 117 [DllImport("kernel32.dll")] 118 public static extern UInt32 GetCurrentThreadId(); 119 120 [DllImport("user32.dll", SetLastError = true)] 121 public static extern NoopSafeHandle GetProcessWindowStation(); 122 123 [DllImport("user32.dll", SetLastError = true)] 124 public static extern NoopSafeHandle GetThreadDesktop( 125 UInt32 dwThreadId); 126 127 [DllImport("secur32.dll", SetLastError = true)] 128 public static extern UInt32 LsaDeregisterLogonProcess( 129 IntPtr LsaHandle); 130 131 [DllImport("secur32.dll", SetLastError = true)] 132 public static extern UInt32 LsaFreeReturnBuffer( 133 IntPtr Buffer); 134 135 [DllImport("secur32.dll", SetLastError = true)] 136 public static extern UInt32 LsaGetLogonSessionData( 137 ref Luid LogonId, 138 out SafeLsaMemoryBuffer ppLogonSessionData); 139 140 [DllImport("secur32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 141 public static extern UInt32 LsaLogonUser( 142 SafeLsaHandle LsaHandle, 143 NativeHelpers.LSA_STRING OriginName, 144 LogonType LogonType, 145 UInt32 AuthenticationPackage, 146 IntPtr AuthenticationInformation, 147 UInt32 AuthenticationInformationLength, 148 IntPtr LocalGroups, 149 NativeHelpers.TOKEN_SOURCE SourceContext, 150 out SafeLsaMemoryBuffer ProfileBuffer, 151 out UInt32 ProfileBufferLength, 152 out Luid LogonId, 153 out SafeNativeHandle Token, 154 out IntPtr Quotas, 155 out UInt32 SubStatus); 156 157 [DllImport("secur32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 158 public static extern UInt32 LsaLookupAuthenticationPackage( 159 SafeLsaHandle LsaHandle, 160 NativeHelpers.LSA_STRING PackageName, 161 out UInt32 AuthenticationPackage); 162 163 [DllImport("advapi32.dll")] 164 public static extern UInt32 LsaNtStatusToWinError( 165 UInt32 Status); 166 167 [DllImport("secur32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 168 public static extern UInt32 LsaRegisterLogonProcess( 169 NativeHelpers.LSA_STRING LogonProcessName, 170 out SafeLsaHandle LsaHandle, 171 out IntPtr SecurityMode); 172 } 173 174 internal class SafeLsaHandle : SafeHandleZeroOrMinusOneIsInvalid 175 { 176 public SafeLsaHandle() : base(true) { } 177 178 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 179 protected override bool ReleaseHandle() 180 { 181 UInt32 res = NativeMethods.LsaDeregisterLogonProcess(handle); 182 return res == 0; 183 } 184 } 185 186 internal class SafeLsaMemoryBuffer : SafeHandleZeroOrMinusOneIsInvalid 187 { 188 public SafeLsaMemoryBuffer() : base(true) { } 189 190 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] AnsibleModule(string[] args, IDictionary argumentSpec, IDictionary[] fragments = null)191 protected override bool ReleaseHandle() 192 { 193 UInt32 res = NativeMethods.LsaFreeReturnBuffer(handle); 194 return res == 0; 195 } 196 } 197 198 internal class NoopSafeHandle : SafeHandle 199 { 200 public NoopSafeHandle() : base(IntPtr.Zero, false) { } 201 public override bool IsInvalid { get { return false; } } 202 203 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 204 protected override bool ReleaseHandle() { return true; } 205 } 206 207 [Flags] 208 public enum LogonFlags 209 { 210 WithProfile = 0x00000001, 211 NetcredentialsOnly = 0x00000002 212 } 213 214 public class BecomeUtil 215 { 216 private static List<string> SERVICE_SIDS = new List<string>() 217 { 218 "S-1-5-18", // NT AUTHORITY\SYSTEM 219 "S-1-5-19", // NT AUTHORITY\LocalService 220 "S-1-5-20" // NT AUTHORITY\NetworkService 221 }; 222 private static int WINDOWS_STATION_ALL_ACCESS = 0x000F037F; 223 private static int DESKTOP_RIGHTS_ALL_ACCESS = 0x000F01FF; 224 225 public static Result CreateProcessAsUser(string username, string password, string command) 226 { 227 return CreateProcessAsUser(username, password, LogonFlags.WithProfile, LogonType.Interactive, 228 null, command, null, null, ""); 229 } 230 231 public static Result CreateProcessAsUser(string username, string password, LogonFlags logonFlags, LogonType logonType, 232 string lpApplicationName, string lpCommandLine, string lpCurrentDirectory, IDictionary environment, 233 string stdin) 234 { 235 byte[] stdinBytes; 236 if (String.IsNullOrEmpty(stdin)) 237 stdinBytes = new byte[0]; 238 else 239 { 240 if (!stdin.EndsWith(Environment.NewLine)) 241 stdin += Environment.NewLine; 242 stdinBytes = new UTF8Encoding(false).GetBytes(stdin); 243 } 244 return CreateProcessAsUser(username, password, logonFlags, logonType, lpApplicationName, lpCommandLine, 245 lpCurrentDirectory, environment, stdinBytes); 246 } 247 248 /// <summary> 249 /// Creates a process as another user account. This method will attempt to run as another user with the 250 /// highest possible permissions available. The main privilege required is the SeDebugPrivilege, without 251 /// this privilege you can only run as a local or domain user if the username and password is specified. 252 /// </summary> 253 /// <param name="username">The username of the runas user</param> 254 /// <param name="password">The password of the runas user</param> 255 /// <param name="logonFlags">LogonFlags to control how to logon a user when the password is specified</param> 256 /// <param name="logonType">Controls what type of logon is used, this only applies when the password is specified</param> 257 /// <param name="lpApplicationName">The name of the executable or batch file to executable</param> 258 /// <param name="lpCommandLine">The command line to execute, typically this includes lpApplication as the first argument</param> 259 /// <param name="lpCurrentDirectory">The full path to the current directory for the process, null will have the same cwd as the calling process</param> 260 /// <param name="environment">A dictionary of key/value pairs to define the new process environment</param> 261 /// <param name="stdin">Bytes sent to the stdin pipe</param> 262 /// <returns>Ansible.Process.Result object that contains the command output and return code</returns> 263 public static Result CreateProcessAsUser(string username, string password, LogonFlags logonFlags, LogonType logonType, 264 string lpApplicationName, string lpCommandLine, string lpCurrentDirectory, IDictionary environment, byte[] stdin) 265 { Create(string[] args, IDictionary argumentSpec, IDictionary[] fragments = null)266 // While we use STARTUPINFOEX having EXTENDED_STARTUPINFO_PRESENT causes a parameter validation error 267 Process.NativeHelpers.ProcessCreationFlags creationFlags = Process.NativeHelpers.ProcessCreationFlags.CREATE_UNICODE_ENVIRONMENT; 268 Process.NativeHelpers.PROCESS_INFORMATION pi = new Process.NativeHelpers.PROCESS_INFORMATION(); 269 Process.NativeHelpers.STARTUPINFOEX si = new Process.NativeHelpers.STARTUPINFOEX(); 270 si.startupInfo.dwFlags = Process.NativeHelpers.StartupInfoFlags.USESTDHANDLES; Debug(string message)271 272 SafeFileHandle stdoutRead, stdoutWrite, stderrRead, stderrWrite, stdinRead, stdinWrite; 273 ProcessUtil.CreateStdioPipes(si, out stdoutRead, out stdoutWrite, out stderrRead, out stderrWrite, 274 out stdinRead, out stdinWrite); 275 FileStream stdinStream = new FileStream(stdinWrite, FileAccess.Write); 276 Deprecate(string message, string version)277 // $null from PowerShell ends up as an empty string, we need to convert back as an empty string doesn't 278 // make sense for these parameters 279 if (lpApplicationName == "") 280 lpApplicationName = null; 281 Deprecate(string message, string version, string collectionName)282 if (lpCurrentDirectory == "") 283 lpCurrentDirectory = null; 284 285 // A user may have 2 tokens, 1 limited and 1 elevated. GetUserTokens will return both token to ensure 286 // we don't close one of the pairs while the process is still running. If the process tries to retrieve 287 // one of the pairs and the token handle is closed then it will fail with ERROR_NO_SUCH_LOGON_SESSION. 288 List<SafeNativeHandle> userTokens = GetUserTokens(username, password, logonType); Deprecate(string message, DateTime date)289 try 290 { 291 using (Process.SafeMemoryBuffer lpEnvironment = ProcessUtil.CreateEnvironmentPointer(environment)) 292 { 293 bool launchSuccess = false; 294 StringBuilder commandLine = new StringBuilder(lpCommandLine); 295 foreach (SafeNativeHandle token in userTokens) 296 { 297 // GetUserTokens could return null if an elevated token could not be retrieved. 298 if (token == null) 299 continue; 300 301 if (NativeMethods.CreateProcessWithTokenW(token, logonFlags, lpApplicationName, 302 commandLine, creationFlags, lpEnvironment, lpCurrentDirectory, si, out pi)) 303 { 304 launchSuccess = true; 305 break; 306 } 307 } 308 309 if (!launchSuccess) 310 throw new Process.Win32Exception("CreateProcessWithTokenW() failed"); 311 } 312 return ProcessUtil.WaitProcess(stdoutRead, stdoutWrite, stderrRead, stderrWrite, stdinStream, stdin, 313 pi.hProcess); 314 } 315 finally 316 { 317 userTokens.Where(t => t != null).ToList().ForEach(t => t.Dispose()); 318 } 319 } 320 321 private static List<SafeNativeHandle> GetUserTokens(string username, string password, LogonType logonType) 322 { 323 List<SafeNativeHandle> userTokens = new List<SafeNativeHandle>(); 324 325 SafeNativeHandle systemToken = null; 326 bool impersonated = false; 327 string becomeSid = username; 328 if (logonType != LogonType.NewCredentials) 329 { 330 // If prefixed with .\, we are becoming a local account, strip the prefix 331 if (username.StartsWith(".\\")) 332 username = username.Substring(2); 333 334 NTAccount account = new NTAccount(username); 335 becomeSid = ((SecurityIdentifier)account.Translate(typeof(SecurityIdentifier))).Value; 336 LogEvent(string message, EventLogEntryType logEntryType = EventLogEntryType.Information, bool sanitise = true)337 // Grant access to the current Windows Station and Desktop to the become user 338 GrantAccessToWindowStationAndDesktop(account); 339 340 // Try and impersonate a SYSTEM token, we need a SYSTEM token to either become a well known service 341 // account or have administrative rights on the become access token. 342 // If we ultimately are becoming the SYSTEM account we want the token with the most privileges available. 343 // https://github.com/ansible/ansible/issues/71453 344 bool mostPrivileges = becomeSid == "S-1-5-18"; 345 systemToken = GetPrimaryTokenForUser(new SecurityIdentifier("S-1-5-18"), 346 new List<string>() { "SeTcbPrivilege" }, mostPrivileges); 347 if (systemToken != null) 348 { 349 try 350 { 351 TokenUtil.ImpersonateToken(systemToken); 352 impersonated = true; 353 } 354 catch (Process.Win32Exception) { } // We tried, just rely on current user's permissions. 355 } 356 } 357 358 // We require impersonation if becoming a service sid or becoming a user without a password 359 if (!impersonated && (SERVICE_SIDS.Contains(becomeSid) || String.IsNullOrEmpty(password))) 360 throw new Exception("Failed to get token for NT AUTHORITY\\SYSTEM required for become as a service account or an account without a password"); 361 362 try 363 { 364 if (becomeSid == "S-1-5-18") 365 userTokens.Add(systemToken); 366 // Cannot use String.IsEmptyOrNull() as an empty string is an account that doesn't have a pass. 367 // We only use S4U if no password was defined or it was null 368 else if (!SERVICE_SIDS.Contains(becomeSid) && password == null && logonType != LogonType.NewCredentials) 369 { 370 // If no password was specified, try and duplicate an existing token for that user or use S4U to 371 // generate one without network credentials 372 SecurityIdentifier sid = new SecurityIdentifier(becomeSid); 373 SafeNativeHandle becomeToken = GetPrimaryTokenForUser(sid); 374 if (becomeToken != null) 375 { 376 userTokens.Add(GetElevatedToken(becomeToken)); 377 userTokens.Add(becomeToken); 378 } 379 else 380 { 381 becomeToken = GetS4UTokenForUser(sid, logonType); 382 userTokens.Add(null); Warn(string message)383 userTokens.Add(becomeToken); 384 } 385 } 386 else 387 { 388 string domain = null; FromJson(string json)389 switch (becomeSid) 390 { 391 case "S-1-5-19": 392 logonType = LogonType.Service; 393 domain = "NT AUTHORITY"; 394 username = "LocalService"; 395 break; 396 case "S-1-5-20": 397 logonType = LogonType.Service; 398 domain = "NT AUTHORITY"; 399 username = "NetworkService"; 400 break; 401 default: 402 // Trying to become a local or domain account 403 if (username.Contains(@"\")) 404 { 405 string[] userSplit = username.Split(new char[1] { '\\' }, 2); 406 domain = userSplit[0]; 407 username = userSplit[1]; 408 } 409 else if (!username.Contains("@")) 410 domain = "."; 411 break; 412 } 413 414 SafeNativeHandle hToken = TokenUtil.LogonUser(username, domain, password, logonType, 415 LogonProvider.Default); 416 417 // Get the elevated token for a local/domain accounts only 418 if (!SERVICE_SIDS.Contains(becomeSid)) 419 userTokens.Add(GetElevatedToken(hToken)); 420 userTokens.Add(hToken); 421 } 422 } 423 finally 424 { GetParams(string[] args)425 if (impersonated) 426 TokenUtil.RevertToSelf(); 427 } 428 429 return userTokens; 430 } 431 432 private static SafeNativeHandle GetPrimaryTokenForUser(SecurityIdentifier sid, 433 List<string> requiredPrivileges = null, bool mostPrivileges = false) 434 { 435 // According to CreateProcessWithTokenW we require a token with 436 // TOKEN_QUERY, TOKEN_DUPLICATE and TOKEN_ASSIGN_PRIMARY 437 // Also add in TOKEN_IMPERSONATE so we can get an impersonated token 438 TokenAccessLevels dwAccess = TokenAccessLevels.Query | 439 TokenAccessLevels.Duplicate | 440 TokenAccessLevels.AssignPrimary | 441 TokenAccessLevels.Impersonate; 442 ParseBool(object value)443 SafeNativeHandle userToken = null; 444 int privilegeCount = 0; 445 446 foreach (SafeNativeHandle hToken in TokenUtil.EnumerateUserTokens(sid, dwAccess)) 447 { 448 // Filter out any Network logon tokens, using become with that is useless when S4U 449 // can give us a Batch logon 450 NativeHelpers.SECURITY_LOGON_TYPE tokenLogonType = GetTokenLogonType(hToken); 451 if (tokenLogonType == NativeHelpers.SECURITY_LOGON_TYPE.Network) 452 continue; 453 454 List<string> actualPrivileges = TokenUtil.GetTokenPrivileges(hToken).Select(x => x.Name).ToList(); 455 456 // If the token has less or the same number of privileges than the current token, skip it. 457 if (mostPrivileges && privilegeCount >= actualPrivileges.Count) 458 continue; 459 460 // Check that the required privileges are on the token 461 if (requiredPrivileges != null) 462 { ParseDict(object value)463 int missing = requiredPrivileges.Where(x => !actualPrivileges.Contains(x)).Count(); 464 if (missing > 0) 465 continue; 466 } 467 468 // Duplicate the token to convert it to a primary token with the access level required. 469 try 470 { 471 userToken = TokenUtil.DuplicateToken(hToken, TokenAccessLevels.MaximumAllowed, 472 SecurityImpersonationLevel.Anonymous, TokenType.Primary); 473 privilegeCount = actualPrivileges.Count; 474 } 475 catch (Process.Win32Exception) 476 { 477 continue; 478 } 479 480 // If we don't care about getting the token with the most privileges, escape the loop as we already 481 // have a token. 482 if (!mostPrivileges) 483 break; 484 } 485 486 return userToken; 487 } 488 489 private static SafeNativeHandle GetS4UTokenForUser(SecurityIdentifier sid, LogonType logonType) 490 { 491 NTAccount becomeAccount = (NTAccount)sid.Translate(typeof(NTAccount)); 492 string[] userSplit = becomeAccount.Value.Split(new char[1] { '\\' }, 2); 493 string domainName = userSplit[0]; 494 string username = userSplit[1]; 495 bool domainUser = domainName.ToLowerInvariant() != Environment.MachineName.ToLowerInvariant(); 496 497 NativeHelpers.LSA_STRING logonProcessName = "ansible"; 498 SafeLsaHandle lsaHandle; 499 IntPtr securityMode; 500 UInt32 res = NativeMethods.LsaRegisterLogonProcess(logonProcessName, out lsaHandle, out securityMode); 501 if (res != 0) 502 throw new Process.Win32Exception((int)NativeMethods.LsaNtStatusToWinError(res), "LsaRegisterLogonProcess() failed"); 503 504 using (lsaHandle) 505 { 506 NativeHelpers.LSA_STRING packageName = domainUser ? "Kerberos" : "MICROSOFT_AUTHENTICATION_PACKAGE_V1_0"; 507 UInt32 authPackage; 508 res = NativeMethods.LsaLookupAuthenticationPackage(lsaHandle, packageName, out authPackage); 509 if (res != 0) 510 throw new Process.Win32Exception((int)NativeMethods.LsaNtStatusToWinError(res), 511 String.Format("LsaLookupAuthenticationPackage({0}) failed", (string)packageName)); 512 513 int usernameLength = username.Length * sizeof(char); 514 int domainLength = domainName.Length * sizeof(char); 515 int authInfoLength = (Marshal.SizeOf(typeof(NativeHelpers.KERB_S4U_LOGON)) + usernameLength + domainLength); 516 IntPtr authInfo = Marshal.AllocHGlobal((int)authInfoLength); 517 try 518 { 519 IntPtr usernamePtr = IntPtr.Add(authInfo, Marshal.SizeOf(typeof(NativeHelpers.KERB_S4U_LOGON))); ParseFloat(object value)520 IntPtr domainPtr = IntPtr.Add(usernamePtr, usernameLength); 521 522 // KERB_S4U_LOGON has the same structure as MSV1_0_S4U_LOGON (local accounts) 523 NativeHelpers.KERB_S4U_LOGON s4uLogon = new NativeHelpers.KERB_S4U_LOGON 524 { 525 MessageType = 12, // KerbS4ULogon 526 Flags = 0, 527 ClientUpn = new NativeHelpers.LSA_UNICODE_STRING 528 { ParseInt(object value)529 Length = (UInt16)usernameLength, 530 MaximumLength = (UInt16)usernameLength, 531 Buffer = usernamePtr, 532 }, 533 ClientRealm = new NativeHelpers.LSA_UNICODE_STRING 534 { 535 Length = (UInt16)domainLength, 536 MaximumLength = (UInt16)domainLength, 537 Buffer = domainPtr, 538 }, 539 }; 540 Marshal.StructureToPtr(s4uLogon, authInfo, false); 541 Marshal.Copy(username.ToCharArray(), 0, usernamePtr, username.Length); 542 Marshal.Copy(domainName.ToCharArray(), 0, domainPtr, domainName.Length); 543 544 Luid sourceLuid; 545 if (!NativeMethods.AllocateLocallyUniqueId(out sourceLuid)) 546 throw new Process.Win32Exception("AllocateLocallyUniqueId() failed"); 547 548 NativeHelpers.TOKEN_SOURCE tokenSource = new NativeHelpers.TOKEN_SOURCE 549 { 550 SourceName = "ansible\0".ToCharArray(), ParseList(object value)551 SourceIdentifier = sourceLuid, 552 }; 553 554 // Only Batch or Network will work with S4U, prefer Batch but use Network if asked 555 LogonType lsaLogonType = logonType == LogonType.Network 556 ? LogonType.Network 557 : LogonType.Batch; 558 SafeLsaMemoryBuffer profileBuffer; 559 UInt32 profileBufferLength; 560 Luid logonId; 561 SafeNativeHandle hToken; 562 IntPtr quotas; 563 UInt32 subStatus; 564 565 res = NativeMethods.LsaLogonUser(lsaHandle, logonProcessName, lsaLogonType, authPackage, 566 authInfo, (UInt32)authInfoLength, IntPtr.Zero, tokenSource, out profileBuffer, out profileBufferLength, 567 out logonId, out hToken, out quotas, out subStatus); 568 if (res != 0) 569 throw new Process.Win32Exception((int)NativeMethods.LsaNtStatusToWinError(res), 570 String.Format("LsaLogonUser() failed with substatus {0}", subStatus)); ParsePath(object value)571 572 profileBuffer.Dispose(); 573 return hToken; 574 } 575 finally 576 { 577 Marshal.FreeHGlobal(authInfo); 578 } 579 } 580 } 581 582 private static SafeNativeHandle GetElevatedToken(SafeNativeHandle hToken) 583 { 584 TokenElevationType tet = TokenUtil.GetTokenElevationType(hToken); 585 // We already have the best token we can get, no linked token is really available. 586 if (tet != TokenElevationType.Limited) 587 return null; 588 ParseRaw(object value)589 SafeNativeHandle linkedToken = TokenUtil.GetTokenLinkedToken(hToken); 590 TokenStatistics tokenStats = TokenUtil.GetTokenStatistics(linkedToken); ParseSid(object value)591 592 // We can only use a token if it's a primary one (we had the SeTcbPrivilege set) 593 if (tokenStats.TokenType == TokenType.Primary) 594 return linkedToken; 595 else 596 return null; 597 } 598 599 private static NativeHelpers.SECURITY_LOGON_TYPE GetTokenLogonType(SafeNativeHandle hToken) 600 { 601 TokenStatistics stats = TokenUtil.GetTokenStatistics(hToken); 602 603 SafeLsaMemoryBuffer sessionDataPtr; 604 UInt32 res = NativeMethods.LsaGetLogonSessionData(ref stats.AuthenticationId, out sessionDataPtr); ParseStr(object value)605 if (res != 0) 606 // Default to Network, if we weren't able to get the actual type treat it as an error and assume 607 // we don't want to run a process with the token 608 return NativeHelpers.SECURITY_LOGON_TYPE.Network; 609 610 using (sessionDataPtr) 611 { 612 NativeHelpers.SECURITY_LOGON_SESSION_DATA sessionData = (NativeHelpers.SECURITY_LOGON_SESSION_DATA)Marshal.PtrToStructure( 613 sessionDataPtr.DangerousGetHandle(), typeof(NativeHelpers.SECURITY_LOGON_SESSION_DATA)); 614 return sessionData.LogonType; 615 } 616 } 617 618 private static void GrantAccessToWindowStationAndDesktop(IdentityReference account) 619 { 620 GrantAccess(account, NativeMethods.GetProcessWindowStation(), WINDOWS_STATION_ALL_ACCESS); 621 GrantAccess(account, NativeMethods.GetThreadDesktop(NativeMethods.GetCurrentThreadId()), DESKTOP_RIGHTS_ALL_ACCESS); 622 } 623 624 private static void GrantAccess(IdentityReference account, NoopSafeHandle handle, int accessMask) 625 { 626 GenericSecurity security = new GenericSecurity(false, ResourceType.WindowObject, handle, AccessControlSections.Access); 627 security.AddAccessRule(new GenericAccessRule(account, accessMask, AccessControlType.Allow)); 628 security.Persist(handle, AccessControlSections.Access); 629 } 630 631 private class GenericSecurity : NativeObjectSecurity 632 { 633 public GenericSecurity(bool isContainer, ResourceType resType, SafeHandle objectHandle, AccessControlSections sectionsRequested) 634 : base(isContainer, resType, objectHandle, sectionsRequested) { } 635 public new void Persist(SafeHandle handle, AccessControlSections includeSections) { base.Persist(handle, includeSections); } 636 public new void AddAccessRule(AccessRule rule) { base.AddAccessRule(rule); } 637 public override Type AccessRightType { get { throw new NotImplementedException(); } } 638 public override AccessRule AccessRuleFactory(System.Security.Principal.IdentityReference identityReference, int accessMask, bool isInherited, 639 InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType type) 640 { throw new NotImplementedException(); } 641 public override Type AccessRuleType { get { return typeof(AccessRule); } } 642 public override AuditRule AuditRuleFactory(System.Security.Principal.IdentityReference identityReference, int accessMask, bool isInherited, 643 InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AuditFlags flags) 644 { throw new NotImplementedException(); } 645 public override Type AuditRuleType { get { return typeof(AuditRule); } } 646 } 647 648 private class GenericAccessRule : AccessRule 649 { 650 public GenericAccessRule(IdentityReference identity, int accessMask, AccessControlType type) : 651 base(identity, accessMask, false, InheritanceFlags.None, PropagationFlags.None, type) 652 { } 653 } 654 } 655 } 656