Dentre os meus últimos desafios surgiu-me a necessidade de executar um comando PowerShell em um servidor via uma página ASP.Net, mas com a premissa de executar este comando dentro de um contexto seguro, isto é baseado em permissões de usuário distintas daquelas que estão rodando o Application Pool do IIS.
Desta forma, de acordo com as credenciais do usuário logado na aplicação, seria permitido ou não a execução dos comandos via PowerShell. Entretanto, como princípio de segurança, a execução deste comando seria feita sobre um contexto diferente do usuário do Application Pool.
Para tanto, me baseei em dois posts: o primeiro de Antoine Habert (MVP de PowerShell) que ensina como executar comandos PowerShell via páginas ASP.Net (http://devinfra-us.blogspot.com/2011/02/using-powershell-20-from-aspnet-part-1.html) e um segundo post do Microsoft Support que demonstra como implementar Impersonation programaticamente em aplicações ASP.Net (http://support.microsoft.com/kb/306158).
Para a execução deste exemplo será preciso fazer download e instalação do Windows PowerShell 2.0 Software Development Kit (http://http://www.microsoft.com/download/en/details.aspx?id=2560).
O download do exemplo na integra pode ser feito por aqui: http://code.msdn.microsoft.com/Utilizao-de-Impersonate-4dde6f1d.
Para execução do Impersonation foi necessário fazer referência aos namespaces System.Runtime.InteropServices e System.Security.Principal, além de utilizar o seguinte bloco de código:
public const int LOGON32_LOGON_INTERACTIVE = 2; public const int LOGON32_PROVIDER_DEFAULT = 0; WindowsImpersonationContext impersonationContext; [DllImport("advapi32.dll")] public 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)] public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern bool RevertToSelf(); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern bool CloseHandle(IntPtr handle); private bool ImpersonateValidUser(String userName, String domain, String password) { WindowsIdentity tempWindowsIdentity; IntPtr token = IntPtr.Zero; IntPtr tokenDuplicate = IntPtr.Zero; if (RevertToSelf()) { if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0) { if (DuplicateToken(token, 2, ref tokenDuplicate) != 0) { tempWindowsIdentity = new WindowsIdentity(tokenDuplicate); impersonationContext = tempWindowsIdentity.Impersonate(); if (impersonationContext != null) { CloseHandle(token); CloseHandle(tokenDuplicate); return true; } } } } if (token != IntPtr.Zero) CloseHandle(token); if (tokenDuplicate != IntPtr.Zero) CloseHandle(tokenDuplicate); return false; } private void UndoImpersonation() { impersonationContext.Undo(); }
E para execução do comando via PowerShell foi necessário fazer referência ao namespace System.Management.Automation e o seguinte código (com a inclusão do Impersonation):
bool userNeedsToBeImpersonated = false; bool impersonateOccurswithSuccess = false; #region [ Verificar se é preciso fazer impersonate ] userNeedsToBeImpersonated = Convert.ToBoolean(this.rdbImpersonate.SelectedValue); #endregion #region [ Executa impersonate se necessário ] if (userNeedsToBeImpersonated) { try { UserImpersonate user = UserImpersonate.LoadCredentials(); impersonateOccurswithSuccess = ImpersonateValidUser(user.Login, user.Domain, user.Password); } catch (Exception ex) { FormatException(ex); return; } } #endregion #region [ Executa powershell ] try { ResultBox.Text = string.Empty; var shell = PowerShell.Create(); shell.Commands.AddScript(PowerShellCodeBox.Text); var results = shell.Invoke(); if (results.Count > 0) { var builder = new StringBuilder(); foreach (var psObject in results) { builder.Append(psObject.BaseObject.ToString() + "\r\n"); } ResultBox.Text = Server.HtmlEncode(builder.ToString()); } } catch (Exception ex) { FormatException(ex); } #endregion #region [ Volta a execução ao seu usuário normal ] if (userNeedsToBeImpersonated && impersonateOccurswithSuccess) { UndoImpersonation(); } #endregion
Achei interessante o contexto da solução, e também por serem necessários dois recursos que são bastante utilizados em aplicações web mais avançadas, por isso fiz este post.
Obrigado e até o próximo!
Por
Fernando Henrique Inocêncio Borba Ferreira