win_become - don't dispose logon token until end (#61905)
This commit is contained in:
parent
900a51b283
commit
6e8d430872
2 changed files with 54 additions and 19 deletions
2
changelogs/fragments/win_become-shared-token.yaml
Normal file
2
changelogs/fragments/win_become-shared-token.yaml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
bugfixes:
|
||||||
|
- win_become - Do not dispose of one of the logon tokens until after the process has run
|
|
@ -282,21 +282,46 @@ namespace Ansible.Become
|
||||||
if (lpCurrentDirectory == "")
|
if (lpCurrentDirectory == "")
|
||||||
lpCurrentDirectory = null;
|
lpCurrentDirectory = null;
|
||||||
|
|
||||||
using (Process.SafeMemoryBuffer lpEnvironment = ProcessUtil.CreateEnvironmentPointer(environment))
|
// A user may have 2 tokens, 1 limited and 1 elevated. GetUserTokens will return both token to ensure
|
||||||
using (SafeNativeHandle hToken = GetUserToken(username, password, logonType))
|
// we don't close one of the pairs while the process is still running. If the process tries to retrieve
|
||||||
|
// one of the pairs and the token handle is closed then it will fail with ERROR_NO_SUCH_LOGON_SESSION.
|
||||||
|
List<SafeNativeHandle> userTokens = GetUserTokens(username, password, logonType);
|
||||||
|
try
|
||||||
{
|
{
|
||||||
StringBuilder commandLine = new StringBuilder(lpCommandLine);
|
using (Process.SafeMemoryBuffer lpEnvironment = ProcessUtil.CreateEnvironmentPointer(environment))
|
||||||
if (!NativeMethods.CreateProcessWithTokenW(hToken, logonFlags, lpApplicationName, commandLine,
|
|
||||||
creationFlags, lpEnvironment, lpCurrentDirectory, si, out pi))
|
|
||||||
{
|
{
|
||||||
throw new Process.Win32Exception("CreateProcessWithTokenW() failed");
|
bool launchSuccess = false;
|
||||||
|
StringBuilder commandLine = new StringBuilder(lpCommandLine);
|
||||||
|
foreach (SafeNativeHandle token in userTokens)
|
||||||
|
{
|
||||||
|
// GetUserTokens could return null if an elevated token could not be retrieved.
|
||||||
|
if (token == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (NativeMethods.CreateProcessWithTokenW(token, logonFlags, lpApplicationName,
|
||||||
|
commandLine, creationFlags, lpEnvironment, lpCurrentDirectory, si, out pi))
|
||||||
|
{
|
||||||
|
launchSuccess = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!launchSuccess)
|
||||||
|
throw new Process.Win32Exception("CreateProcessWithTokenW() failed");
|
||||||
}
|
}
|
||||||
|
return ProcessUtil.WaitProcess(stdoutRead, stdoutWrite, stderrRead, stderrWrite, stdinStream, stdin,
|
||||||
|
pi.hProcess);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
userTokens.Where(t => t != null).ToList().ForEach(t => t.Dispose());
|
||||||
}
|
}
|
||||||
return ProcessUtil.WaitProcess(stdoutRead, stdoutWrite, stderrRead, stderrWrite, stdinStream, stdin, pi.hProcess);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SafeNativeHandle GetUserToken(string username, string password, LogonType logonType)
|
private static List<SafeNativeHandle> GetUserTokens(string username, string password, LogonType logonType)
|
||||||
{
|
{
|
||||||
|
List<SafeNativeHandle> userTokens = new List<SafeNativeHandle>();
|
||||||
|
|
||||||
SafeNativeHandle systemToken = null;
|
SafeNativeHandle systemToken = null;
|
||||||
bool impersonated = false;
|
bool impersonated = false;
|
||||||
string becomeSid = username;
|
string becomeSid = username;
|
||||||
|
@ -322,7 +347,7 @@ namespace Ansible.Become
|
||||||
TokenUtil.ImpersonateToken(systemToken);
|
TokenUtil.ImpersonateToken(systemToken);
|
||||||
impersonated = true;
|
impersonated = true;
|
||||||
}
|
}
|
||||||
catch (Process.Win32Exception) {} // We tried, just rely on current user's permissions.
|
catch (Process.Win32Exception) { } // We tried, just rely on current user's permissions.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,7 +358,7 @@ namespace Ansible.Become
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (becomeSid == "S-1-5-18")
|
if (becomeSid == "S-1-5-18")
|
||||||
return systemToken;
|
userTokens.Add(systemToken);
|
||||||
// Cannot use String.IsEmptyOrNull() as an empty string is an account that doesn't have a pass.
|
// Cannot use String.IsEmptyOrNull() as an empty string is an account that doesn't have a pass.
|
||||||
// We only use S4U if no password was defined or it was null
|
// We only use S4U if no password was defined or it was null
|
||||||
else if (!SERVICE_SIDS.Contains(becomeSid) && password == null && logonType != LogonType.NewCredentials)
|
else if (!SERVICE_SIDS.Contains(becomeSid) && password == null && logonType != LogonType.NewCredentials)
|
||||||
|
@ -343,9 +368,16 @@ namespace Ansible.Become
|
||||||
SecurityIdentifier sid = new SecurityIdentifier(becomeSid);
|
SecurityIdentifier sid = new SecurityIdentifier(becomeSid);
|
||||||
SafeNativeHandle becomeToken = GetPrimaryTokenForUser(sid);
|
SafeNativeHandle becomeToken = GetPrimaryTokenForUser(sid);
|
||||||
if (becomeToken != null)
|
if (becomeToken != null)
|
||||||
return GetElevatedToken(becomeToken);
|
{
|
||||||
|
userTokens.Add(GetElevatedToken(becomeToken));
|
||||||
|
userTokens.Add(becomeToken);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return GetS4UTokenForUser(sid, logonType);
|
{
|
||||||
|
becomeToken = GetS4UTokenForUser(sid, logonType);
|
||||||
|
userTokens.Add(null);
|
||||||
|
userTokens.Add(becomeToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -380,9 +412,8 @@ namespace Ansible.Become
|
||||||
|
|
||||||
// Get the elevated token for a local/domain accounts only
|
// Get the elevated token for a local/domain accounts only
|
||||||
if (!SERVICE_SIDS.Contains(becomeSid))
|
if (!SERVICE_SIDS.Contains(becomeSid))
|
||||||
return GetElevatedToken(hToken);
|
userTokens.Add(GetElevatedToken(hToken));
|
||||||
else
|
userTokens.Add(hToken);
|
||||||
return hToken;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@ -390,6 +421,8 @@ namespace Ansible.Become
|
||||||
if (impersonated)
|
if (impersonated)
|
||||||
TokenUtil.RevertToSelf();
|
TokenUtil.RevertToSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return userTokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SafeNativeHandle GetPrimaryTokenForUser(SecurityIdentifier sid, List<string> requiredPrivileges = null)
|
private static SafeNativeHandle GetPrimaryTokenForUser(SecurityIdentifier sid, List<string> requiredPrivileges = null)
|
||||||
|
@ -530,9 +563,9 @@ namespace Ansible.Become
|
||||||
private static SafeNativeHandle GetElevatedToken(SafeNativeHandle hToken)
|
private static SafeNativeHandle GetElevatedToken(SafeNativeHandle hToken)
|
||||||
{
|
{
|
||||||
TokenElevationType tet = TokenUtil.GetTokenElevationType(hToken);
|
TokenElevationType tet = TokenUtil.GetTokenElevationType(hToken);
|
||||||
// We already have the best token we can get, just use it
|
// We already have the best token we can get, no linked token is really available.
|
||||||
if (tet != TokenElevationType.Limited)
|
if (tet != TokenElevationType.Limited)
|
||||||
return hToken;
|
return null;
|
||||||
|
|
||||||
SafeNativeHandle linkedToken = TokenUtil.GetTokenLinkedToken(hToken);
|
SafeNativeHandle linkedToken = TokenUtil.GetTokenLinkedToken(hToken);
|
||||||
TokenStatistics tokenStats = TokenUtil.GetTokenStatistics(linkedToken);
|
TokenStatistics tokenStats = TokenUtil.GetTokenStatistics(linkedToken);
|
||||||
|
@ -541,7 +574,7 @@ namespace Ansible.Become
|
||||||
if (tokenStats.TokenType == TokenType.Primary)
|
if (tokenStats.TokenType == TokenType.Primary)
|
||||||
return linkedToken;
|
return linkedToken;
|
||||||
else
|
else
|
||||||
return hToken;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static NativeHelpers.SECURITY_LOGON_TYPE GetTokenLogonType(SafeNativeHandle hToken)
|
private static NativeHelpers.SECURITY_LOGON_TYPE GetTokenLogonType(SafeNativeHandle hToken)
|
||||||
|
@ -600,4 +633,4 @@ namespace Ansible.Become
|
||||||
{ }
|
{ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue