2009-04-16 3 views
19

Наши рабочие станции не являются членами домена, на котором работает наш SQL Server. (На самом деле они вообще не находятся в домене - не спрашивайте).Как создать функциональность RUNAS/NETONLY в программе (C# /. NET/WinForms)?

Когда мы используем SSMS или что-либо для подключения к SQL Server, мы используем RUNAS/NETONLY с DOMAIN \ user. Затем мы вводим пароль и запускаем программу. (RUNAS/NETONLY не позволяет включать пароль в пакетный файл).

У меня есть приложение .NET WinForms, которое требует подключения SQL, и пользователи должны запустить его, запустив командный файл с командной строкой RUNAS/NETONLY, а затем запускает EXE.

Если пользователь случайно запускает EXE напрямую, он не может подключиться к SQL Server.

Щелчок правой кнопкой мыши по приложению и использование опции «Выполнение как ...» не работает (предположительно, потому что рабочая станция действительно не знает о домене).

Я ищу способ, которым приложение может выполнять функции RUNAS/NETONLY внутри, прежде чем оно начнет что-либо значимое.

Пожалуйста, смотрите эту ссылку для описания того, как работает RUNAS/NETONLY: http://www.eggheadcafe.com/conversation.aspx?messageid=32443204&threadid=32442982

Я думаю, я буду иметь, чтобы использовать LOGON_NETCREDENTIALS_ONLY с CreateProcessWithLogonW

ответ

3

Я собрал эти полезные ссылки:

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

Оказывается, я собираюсь необходимо использовать LOGON_NETCREDENTIALS_ONLY с CreateProcessWithLogonW. Я посмотрю, могу ли я обнаружить программу, если она была запущена таким образом, а если нет, соберите учетные данные и запустите сам. Таким образом, будет только один самоуправляющийся EXE.

+0

Я знаю, что это действительно старо, но первые две ссылки (developmentnow.com и blrchen.spaces.live.com) деадированы. – chrnola

0

Я полагаю, вы не можете просто добавить пользователя для приложения для сервера sql, а затем использовать аутентификацию sql, а не проверку подлинности Windows?

+0

Неа. И я действительно хочу, чтобы пользователи, которые получили доступ к этой панели управления, были сами собой. Это похоже на панель управления, которая показывает ряд процессов и позволяет пользователям запускать их, видеть результаты и т. Д. –

6

Я только что-то схожу с этим, используя ImpersonationContext. Он очень интуитивно понятен и отлично работает для меня.

Для запуска в качестве другого пользователя, синтаксис:

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

Вот класс:

namespace Tools 
{ 
    #region Using directives. 
    // ---------------------------------------------------------------------- 

    using System; 
    using System.Security.Principal; 
    using System.Runtime.InteropServices; 
    using System.ComponentModel; 

    // ---------------------------------------------------------------------- 
    #endregion 

    ///////////////////////////////////////////////////////////////////////// 

    /// <summary> 
    /// 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. 
    /// </summary> 
    /// <remarks> 
    /// 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 e.g.: 
    /// 
    ///  ... 
    ///  using (new Impersonator("myUsername", "myDomainname", "myPassword")) 
    ///  { 
    ///   ... 
    ///   [code that executes under the new context] 
    ///   ... 
    ///  } 
    ///  ... 
    /// 
    /// Please contact the author Uwe Keim (mailto:[email protected]) 
    /// for questions regarding this class. 
    /// </remarks> 
    public class Impersonator : 
     IDisposable 
    { 
     #region Public methods. 
     // ------------------------------------------------------------------ 

     /// <summary> 
     /// 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. 
     /// </summary> 
     /// <param name="userName">The name of the user to act as.</param> 
     /// <param name="domainName">The domain name of the user to act as.</param> 
     /// <param name="password">The password of the user to act as.</param> 
     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. 
     // ------------------------------------------------------------------ 

     /// <summary> 
     /// Does the actual impersonation. 
     /// </summary> 
     /// <param name="userName">The name of the user to act as.</param> 
     /// <param name="domainName">The domain name of the user to act as.</param> 
     /// <param name="password">The password of the user to act as.</param> 
     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); 
       } 
      } 
     } 

     /// <summary> 
     /// Reverts the impersonation. 
     /// </summary> 
     private void UndoImpersonation() 
     { 
      if (impersonationContext != null) 
      { 
       impersonationContext.Undo(); 
      } 
     } 

     private WindowsImpersonationContext impersonationContext = null; 

     // ------------------------------------------------------------------ 
     #endregion 
    } 

    ///////////////////////////////////////////////////////////////////////// 
} 
+0

Я использовал аналогичный код в пользовательском установщике - однако право «действовать как часть операционной системы» очень мощное, и я получил много откликов от системных администраторов в предоставлении этого средним пользователям. YMMV, конечно, –

+0

Ну, этот код хорош только для регистрации на локальном компьютере - для учетных данных удаленного доступа (в основном для просто подключения SQL) вы должны использовать эквивалент флага/NETONLY, я пытаюсь прибить его прямо сейчас. –

+1

Эта ссылка иллюстрирует разницу: http://www.eggheadcafe.com/conversation.aspx?messageid=32443204&threadid=32442982 - Мне может понадобиться создать новый процесс. –

2

Этот код является частью класса RunAs, который мы используем, чтобы запустить внешний процесс с приподнятыми привилегиями. Передача null для имени пользователя & пароль будет запрашивать стандартные предупреждения UAC. При передаче значения для имени пользователя и пароля вы можете фактически запустить приложение без приглашения 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; 
} 
+0

ProcessStartInfo (и, следовательно, метод Process.Start) не имеет эквивалентных настроек для RUNAS/NETONLY, где сеть учетные данные используются только для сетевого подключения, а не для разрешения локального потока/процесса. –

+0

Bummer ... вам, возможно, придется прибегнуть к PInvoke и CreateProcess. –

10

Я знаю, что это старая нить, но это было очень полезно. У меня такая же ситуация, как у Cade Roux, так как я хотел/не только стиль.

Ответ Джона Раша работает с одной небольшой модификацией !!!

Добавьте следующую константу (около линии 102 для консистенции):

private const int LOGON32_LOGON_NEW_CREDENTIALS = 9; 

Затем измените вызов LogonUser использовать LOGON32_LOGON_NEW_CREDENTIALS вместо LOGON32_LOGON_INTERACTIVE.

Это только изменение, которое я должен был сделать, чтобы это отлично работало !!! Спасибо, Джон и Кейд!

Вот измененный код в полном объеме для удобства копирования/вставки:

namespace Tools 
{ 
    #region Using directives. 
    // ---------------------------------------------------------------------- 

    using System; 
    using System.Security.Principal; 
    using System.Runtime.InteropServices; 
    using System.ComponentModel; 

    // ---------------------------------------------------------------------- 
    #endregion 

    ///////////////////////////////////////////////////////////////////////// 

    /// <summary> 
    /// 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. 
    /// </summary> 
    /// <remarks> 
    /// 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 e.g.: 
    /// 
    ///  ... 
    ///  using (new Impersonator("myUsername", "myDomainname", "myPassword")) 
    ///  { 
    ///   ... 
    ///   [code that executes under the new context] 
    ///   ... 
    ///  } 
    ///  ... 
    /// 
    /// Please contact the author Uwe Keim (mailto:[email protected]) 
    /// for questions regarding this class. 
    /// </remarks> 
    public class Impersonator : 
     IDisposable 
    { 
     #region Public methods. 
     // ------------------------------------------------------------------ 

     /// <summary> 
     /// 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. 
     /// </summary> 
     /// <param name="userName">The name of the user to act as.</param> 
     /// <param name="domainName">The domain name of the user to act as.</param> 
     /// <param name="password">The password of the user to act as.</param> 
     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. 
     // ------------------------------------------------------------------ 

     /// <summary> 
     /// Does the actual impersonation. 
     /// </summary> 
     /// <param name="userName">The name of the user to act as.</param> 
     /// <param name="domainName">The domain name of the user to act as.</param> 
     /// <param name="password">The password of the user to act as.</param> 
     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); 
       } 
      } 
     } 

     /// <summary> 
     /// Reverts the impersonation. 
     /// </summary> 
     private void UndoImpersonation() 
     { 
      if (impersonationContext != null) 
      { 
       impersonationContext.Undo(); 
      } 
     } 

     private WindowsImpersonationContext impersonationContext = null; 

     // ------------------------------------------------------------------ 
     #endregion 
    } 

    ///////////////////////////////////////////////////////////////////////// 
} 
+1

Я знаю, что это несколько лет спустя, но спасибо !!!!!!!!! Это помогло мне тонну. –