托管Active Directory RSAT Powershell脚本的ASP.NET-DC挂断
作者:互联网
我正在开发一个ASP.NET(C#)应用程序,该应用程序基本上是运行Powershell脚本以执行常规管理任务的网关.其中一些脚本使用ActiveDirectory RSAT模块,我发现其中一些cmdlet在通过网关调用时将无法正确运行,并且跟踪似乎暗示与域控制器的连接已成功,但随后被关闭了由DC.
以下代码是ASP.NET Web表单,该表单具有一个输入文本以指定用户名.基本上,它执行以下操作:
>假定Web用户的身份(确认已由Powershell继承)
>创建一个Powershell运行空间和该运行空间内的管道
>调用Get-ADUser cmdlet并将用户名作为Identity参数传递
>通过将用户名读入表单上的输出元素来确认成功.
protected void LookupButton_Click( object sender, EventArgs e ) {
WindowsImpersonationContext impersonationContext = ((WindowsIdentity)User.Identity).Impersonate();
Runspace runspace;
Pipeline pipe;
try {
runspace = new_runspace();
runspace.Open();
pipe = runspace.CreatePipeline();
Command cmd = new Command("Get-ADUser");
cmd.Parameters.Add(new CommandParameter("Identity", text_username.Text));
pipe.Commands.Add(cmd);
PSObject ps_out = pipe.Invoke().First();
output.Text = ps_out.Properties["Name"].Value.ToString();
}
catch( Exception ex ) {
error.Text = ex.ToString();
}
finally {
impersonationContext.Undo();
}
}
private Runspace new_runspace( ) {
InitialSessionState init_state = InitialSessionState.CreateDefault();
init_state.ThreadOptions = PSThreadOptions.UseCurrentThread;
init_state.ImportPSModule(new[] { "ActiveDirectory" });
return RunspaceFactory.CreateRunspace(init_state);
}
有趣的部分是在catch块中暴露的错误消息中的特定措辞(强调我的意思):
System.Management.Automation.CmdletInvocationException: Unable to
contact the server. This may be because this server does not exist, it
is currently down, or it does not have the Active Directory Web
Services running. —>
Microsoft.ActiveDirectory.Management.ADServerDownException: Unable to
contact the server. This may be because this server does not exist, it
is currently down, or it does not have the Active Directory Web
Services running. —> System.ServiceModel.CommunicationException: The
socket connection was aborted. This could be caused by an error
processing your message or a receive timeout being exceeded by the
remote host, or an underlying network resource issue. Local socket
timeout was ’00:01:59.6870000′. —> System.IO.IOException: The read
operation failed, see inner exception. —>
System.ServiceModel.CommunicationException: The socket connection was
aborted. This could be caused by an error processing your message or a
receive timeout being exceeded by the remote host, or an underlying
network resource issue. Local socket timeout was ’00:01:59.6870000′.
—> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
较高级别的异常表明存在超时,但较低的异常没有指示超时(从命令返回仅花费了几秒钟).在服务器无法访问的情况下,最低级别的异常消息会说得足够多,但是这种特定的措辞使我认为此处存在某种身份验证(或其他安全性)问题.
2013年2月19日更新:
当使用描述的模拟方法here时,脚本将按预期运行.这使我认为问题可能是Windows身份验证提供的WindowsIdentity对象可能不适用于本质上针对AD进行RPC调用的脚本.不幸的是,我真的不希望放弃Windows身份验证,因为我必须在我的应用程序代码中处理用户的密码(这不是我想要的责任).
我还没有找到任何有关Windows auth到底在做什么的文件或使用它可能导致的模拟的任何文档.使用Windows身份验证时是否可以执行此操作,还是我需要要求用户提供密码?
解决方法:
原因
此问题的根本原因是,当您在IIS中使用Windows身份验证时,安全令牌仅对将Web客户端计算机向Web服务器计算机进行身份验证有效.相同的令牌对于将Web服务器计算机向任何其他计算机进行身份验证均无效,这就是我的应用程序正在尝试执行的操作:
>客户端获取安全令牌并将其发送到Web服务器.
> IIS要求DC验证令牌,并且令牌已验证.此时,Web客户端已通过Web服务器的身份验证.
> IIS根据应用程序的授权规则检查已验证的身份.
> Web应用程序使用IIS接收的令牌模拟身份,并运行脚本,该脚本随后将继承相同的安全令牌.
>该脚本尝试使用相同的令牌对远程RPC服务进行身份验证.
>域控制器将身份验证尝试识别为重播攻击(该令牌用于其他服务),并关闭连接.
称它为Kerberos的“副作用”并不完全正确,但起初对我来说并不明显,尽管事后看来很明显.我希望有人可以从此信息中受益.
解
解决方案是让应用程序生成自己的安全令牌,然后通过对LogonUser()进行API调用,将其用作Web用户身份验证其他机器上的服务.您的应用程序代码将需要访问用户的明文密码和可以通过仅在IIS中启用HTTP Basic身份验证来使其可用. Web服务器仍将强制执行相同的身份验证和授权规则,但是用户名和密码都可用于您的应用程序代码.请记住,这些凭据是明文传输到Web服务器的,因此在生产环境中使用SSL之前,您将需要SSL.
我根据here所述的过程创建了一个小的帮助程序类,该类有助于该过程.这是一个简短的演示:
登录助手:
public class IdentityHelper {
[DllImport("advapi32.dll")]
private static extern int LogonUserA( String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken );
[DllImport("advapi32.dll",
CharSet = CharSet.Auto,
SetLastError = true)]
private static extern int DuplicateToken( IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken );
[DllImport("kernel32.dll",
CharSet = CharSet.Auto)]
private static extern bool CloseHandle( IntPtr handle );
public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_PROVIDER_DEFAULT = 0;
public const int IMPERSONATION_LEVEL_IMPERSONATE = 2;
public static WindowsIdentity Logon( string username, string password, string domain = "" ) {
IntPtr token = IntPtr.Zero;
WindowsIdentity wid = null;
if( domain == "" ) {
split_username(username, ref username, ref domain);
}
if( LogonUserA(username, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0 ) {
wid = WIDFromToken(token);
}
if( token != IntPtr.Zero ) CloseHandle(token);
return wid;
}
public static WindowsIdentity WIDFromToken( IntPtr src ) {
WindowsIdentity wid = null;
IntPtr token = IntPtr.Zero;
if( DuplicateToken(src, IMPERSONATION_LEVEL_IMPERSONATE, ref token) != 0 ) {
wid = new WindowsIdentity(token);
}
if( token != IntPtr.Zero ) CloseHandle(token);
return wid;
}
private static void split_username( string username, ref string username_out, ref string domain_out ) {
string[] composite_username = username.Split(new char[] { '\\' });
if( composite_username.Length == 2 ) {
domain_out = composite_username[0];
username_out = composite_username[1];
}
}
}
Powershell助手类:
public class PSHelper {
public static Runspace new_runspace() {
InitialSessionState init_state = InitialSessionState.CreateDefault();
init_state.ThreadOptions = PSThreadOptions.UseCurrentThread;
init_state.ImportPSModule(new[] { "ActiveDirectory" });
return RunspaceFactory.CreateRunspace(init_state);
}
}
ASP.NET表单处理程序:
protected void LookupButton_Click( object sender, EventArgs e ) {
string outp = "";
WindowsIdentity wid = IdentityHelper.Logon(Request["AUTH_USER"], Request["AUTH_PASSWORD"]);
using( wid.Impersonate() ) {
Runspace runspace;
Pipeline pipe;
runspace = PSHelper.new_runspace();
runspace.Open();
pipe = runspace.CreatePipeline();
Command cmd = new Command("Get-ADUser");
cmd.Parameters.Add(new CommandParameter("Identity", text_username.Text));
pipe.Commands.Add(cmd);
outp = pipe.Invoke().First().Properties["Name"].Value.ToString();
}
output.Text = outp;
}
标签:impersonation,active-directory,powershell,asp-net,c 来源: https://codeday.me/bug/20191031/1974277.html