¿Cómo construir la funcionalidad RUNAS / NETONLY en un progtwig (C # /. NET / WinForms)?

Nuestras estaciones de trabajo no son miembros del dominio en el que se encuentra nuestro servidor SQL. (En realidad no están en un dominio, no preguntes).

Cuando usamos SSMS o cualquier otra cosa para conectarnos al SQL Server, usamos RUNAS / NETONLY con DOMAIN \ user. Luego ingresamos la contraseña y se inicia el progtwig. (RUNAS / NETONLY no le permite incluir la contraseña en el archivo por lotes).

Así que tengo una aplicación .NET WinForms que necesita una conexión SQL, y los usuarios tienen que iniciarla ejecutando un archivo por lotes que tiene la línea de comandos RUNAS / NETONLY y luego lanza el EXE.

Si el usuario inicia accidentalmente el EXE directamente, no puede conectarse a SQL Server.

Hacer clic con el botón derecho en la aplicación y usar la opción “Ejecutar como …” no funciona (presumiblemente porque la estación de trabajo no sabe realmente sobre el dominio).

Estoy buscando una forma para que la aplicación haga la funcionalidad RUNAS / NETONLY internamente antes de que comience algo significativo.

Consulte este enlace para obtener una descripción de cómo funciona RUNAS / NETONLY: http://www.eggheadcafe.com/conversation.aspx?messageid=32443204&threadid=32442982

Estoy pensando que voy a tener que usar LOGON_NETCREDENTIALS_ONLY con CreateProcessWithLogonW

Sé que este es un hilo viejo, pero fue muy útil. Tengo exactamente la misma situación que Cade Roux, ya que quería / funcionalidad de estilo netonly.

La respuesta de John Rasch funciona con una pequeña modificación.

Agregue la siguiente constante (alrededor de la línea 102 para coherencia):

 private const int LOGON32_LOGON_NEW_CREDENTIALS = 9; 

A continuación, cambie la llamada a LogonUser para usar LOGON32_LOGON_NEW_CREDENTIALS lugar de LOGON32_LOGON_INTERACTIVE .

Ese es el único cambio que tuve que hacer para que esto funcione a la perfección. ¡Gracias John y Cade!

Aquí está el código modificado completo para facilitar la copia / pegado:

 namespace Tools { #region Using directives. // ---------------------------------------------------------------------- using System; using System.Security.Principal; using System.Runtime.InteropServices; using System.ComponentModel; // ---------------------------------------------------------------------- #endregion ///////////////////////////////////////////////////////////////////////// ///  /// Impersonation of a user. Allows to execute code under another /// user context. /// Please note that the account that instantiates the Impersonator class /// needs to have the 'Act as part of operating system' privilege set. ///  ///  /// This class is based on the information in the Microsoft knowledge base /// article http://support.microsoft.com/default.aspx?scid=kb;en-us;Q306158 /// /// Encapsulate an instance into a using-directive like eg: /// /// ... /// using ( new Impersonator( "myUsername", "myDomainname", "myPassword" ) ) /// { /// ... /// [code that executes under the new context] /// ... /// } /// ... /// /// Please contact the author Uwe Keim (mailto:uwe.keim@zeta-software.de) /// for questions regarding this class. ///  public class Impersonator : IDisposable { #region Public methods. // ------------------------------------------------------------------ ///  /// Constructor. Starts the impersonation with the given credentials. /// Please note that the account that instantiates the Impersonator class /// needs to have the 'Act as part of operating system' privilege set. ///  /// The name of the user to act as. /// The domain name of the user to act as. /// The password of the user to act as. public Impersonator( string userName, string domainName, string password) { ImpersonateValidUser(userName, domainName, password); } // ------------------------------------------------------------------ #endregion #region IDisposable member. // ------------------------------------------------------------------ public void Dispose() { UndoImpersonation(); } // ------------------------------------------------------------------ #endregion #region P/Invoke. // ------------------------------------------------------------------ [DllImport("advapi32.dll", SetLastError = true)] private static extern int LogonUser( 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("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern bool RevertToSelf(); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] private static extern bool CloseHandle( IntPtr handle); private const int LOGON32_LOGON_INTERACTIVE = 2; private const int LOGON32_LOGON_NEW_CREDENTIALS = 9; private const int LOGON32_PROVIDER_DEFAULT = 0; // ------------------------------------------------------------------ #endregion #region Private member. // ------------------------------------------------------------------ ///  /// Does the actual impersonation. ///  /// The name of the user to act as. /// The domain name of the user to act as. /// The password of the user to act as. private void ImpersonateValidUser( string userName, string domain, string password) { WindowsIdentity tempWindowsIdentity = null; IntPtr token = IntPtr.Zero; IntPtr tokenDuplicate = IntPtr.Zero; try { if (RevertToSelf()) { if (LogonUser( userName, domain, password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, ref token) != 0) { if (DuplicateToken(token, 2, ref tokenDuplicate) != 0) { tempWindowsIdentity = new WindowsIdentity(tokenDuplicate); impersonationContext = tempWindowsIdentity.Impersonate(); } else { throw new Win32Exception(Marshal.GetLastWin32Error()); } } else { throw new Win32Exception(Marshal.GetLastWin32Error()); } } else { throw new Win32Exception(Marshal.GetLastWin32Error()); } } finally { if (token != IntPtr.Zero) { CloseHandle(token); } if (tokenDuplicate != IntPtr.Zero) { CloseHandle(tokenDuplicate); } } } ///  /// Reverts the impersonation. ///  private void UndoImpersonation() { if (impersonationContext != null) { impersonationContext.Undo(); } } private WindowsImpersonationContext impersonationContext = null; // ------------------------------------------------------------------ #endregion } ///////////////////////////////////////////////////////////////////////// } 

Acabo de hacer algo similar a esto usando un ImpersonationContext . Es muy intuitivo de usar y funcionó perfectamente para mí.

Para ejecutar como un usuario diferente, la syntax es:

 using ( new Impersonator( "myUsername", "myDomainname", "myPassword" ) ) { // code that executes under the new context... } 

Aquí está la clase:

 namespace Tools { #region Using directives. // ---------------------------------------------------------------------- using System; using System.Security.Principal; using System.Runtime.InteropServices; using System.ComponentModel; // ---------------------------------------------------------------------- #endregion ///////////////////////////////////////////////////////////////////////// ///  /// Impersonation of a user. Allows to execute code under another /// user context. /// Please note that the account that instantiates the Impersonator class /// needs to have the 'Act as part of operating system' privilege set. ///  ///  /// This class is based on the information in the Microsoft knowledge base /// article http://support.microsoft.com/default.aspx?scid=kb;en-us;Q306158 /// /// Encapsulate an instance into a using-directive like eg: /// /// ... /// using ( new Impersonator( "myUsername", "myDomainname", "myPassword" ) ) /// { /// ... /// [code that executes under the new context] /// ... /// } /// ... /// /// Please contact the author Uwe Keim (mailto:uwe.keim@zeta-software.de) /// for questions regarding this class. ///  public class Impersonator : IDisposable { #region Public methods. // ------------------------------------------------------------------ ///  /// Constructor. Starts the impersonation with the given credentials. /// Please note that the account that instantiates the Impersonator class /// needs to have the 'Act as part of operating system' privilege set. ///  /// The name of the user to act as. /// The domain name of the user to act as. /// The password of the user to act as. public Impersonator( string userName, string domainName, string password) { ImpersonateValidUser(userName, domainName, password); } // ------------------------------------------------------------------ #endregion #region IDisposable member. // ------------------------------------------------------------------ public void Dispose() { UndoImpersonation(); } // ------------------------------------------------------------------ #endregion #region P/Invoke. // ------------------------------------------------------------------ [DllImport("advapi32.dll", SetLastError = true)] private static extern int LogonUser( 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("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern bool RevertToSelf(); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] private static extern bool CloseHandle( IntPtr handle); private const int LOGON32_LOGON_INTERACTIVE = 2; private const int LOGON32_PROVIDER_DEFAULT = 0; // ------------------------------------------------------------------ #endregion #region Private member. // ------------------------------------------------------------------ ///  /// Does the actual impersonation. ///  /// The name of the user to act as. /// The domain name of the user to act as. /// The password of the user to act as. private void ImpersonateValidUser( string userName, string domain, string password) { WindowsIdentity tempWindowsIdentity = null; IntPtr token = IntPtr.Zero; IntPtr tokenDuplicate = IntPtr.Zero; try { if (RevertToSelf()) { if (LogonUser( 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(); } else { throw new Win32Exception(Marshal.GetLastWin32Error()); } } else { throw new Win32Exception(Marshal.GetLastWin32Error()); } } else { throw new Win32Exception(Marshal.GetLastWin32Error()); } } finally { if (token != IntPtr.Zero) { CloseHandle(token); } if (tokenDuplicate != IntPtr.Zero) { CloseHandle(tokenDuplicate); } } } ///  /// Reverts the impersonation. ///  private void UndoImpersonation() { if (impersonationContext != null) { impersonationContext.Undo(); } } private WindowsImpersonationContext impersonationContext = null; // ------------------------------------------------------------------ #endregion } ///////////////////////////////////////////////////////////////////////// } 

Reuní estos enlaces útiles:

http://www.developmentnow.com/g/36_2006_3_0_0_725350/Need-help-with-impersonation-please-.htm

http://blrchen.spaces.live.com/blog/cns!572204F8C4F8A77A!251.entry

http://geekswithblogs.net/khanna/archive/2005/02/09/22430.aspx

http://msmvps.com/blogs/martinzugec/archive/2008/06/03/use-runas-from-non-domain-computer.aspx

Resulta que tendré que usar LOGON_NETCREDENTIALS_ONLY con CreateProcessWithLogonW . Voy a ver si puedo hacer que el progtwig detecte si se ha iniciado de esa manera y, de no ser así, recostackr las credenciales del dominio y lanzarlo. De esa forma solo habrá un EXE autogestionado.

Este código es parte de una clase RunAs que usamos para iniciar un proceso externo con privilegios elevados. Al pasar null para el nombre de usuario y la contraseña aparecerá un aviso con las advertencias estándar de UAC. Al pasar un valor para el nombre de usuario y la contraseña, puede iniciar la aplicación elevada sin el aviso de UAC.

 public static Process Elevated( string process, string args, string username, string password, string workingDirectory ) { if( process == null || process.Length == 0 ) throw new ArgumentNullException( "process" ); process = Path.GetFullPath( process ); string domain = null; if( username != null ) username = GetUsername( username, out domain ); ProcessStartInfo info = new ProcessStartInfo(); info.UseShellExecute = false; info.Arguments = args; info.WorkingDirectory = workingDirectory ?? Path.GetDirectoryName( process ); info.FileName = process; info.Verb = "runas"; info.UserName = username; info.Domain = domain; info.LoadUserProfile = true; if( password != null ) { SecureString ss = new SecureString(); foreach( char c in password ) ss.AppendChar( c ); info.Password = ss; } return Process.Start( info ); } private static string GetUsername( string username, out string domain ) { SplitUserName( username, out username, out domain ); if( domain == null && username.IndexOf( '@' ) < 0 ) domain = Environment.GetEnvironmentVariable( "USERDOMAIN" ); return username; } 

Supongo que no puede simplemente agregar un usuario para la aplicación al servidor sql y luego usar la autenticación sql en lugar de la autenticación de Windows.