Dot-Net

如何將 RUNAS /NETONLY 功能建構到(C#/.NET/WinForms)程序中?

  • April 16, 2009

我們的工作站不是我們 SQL Server 所在域的成員。(他們實際上根本不在域上 - 不要問)。

當我們使用 SSMS 或任何東西連接到 SQL Server 時,我們使用 RUNAS /NETONLY 和 DOMAIN\user。然後我們輸入密碼並啟動程序。(RUNAS /NETONLY 不允許您在批處理文件中包含密碼)。

所以我有一個需要 SQL 連接的 .NET WinForms 應用程序,使用者必須通過執行具有 RUNAS /NETONLY 命令行的批處理文件來啟動它,然後它會啟動 EXE。

如果使用者不小心直接啟動了 EXE,它就無法連接到 SQL Server。

右鍵點擊應用程序並使用“執行方式…”選項不起作用(可能是因為工作站並不真正了解域)。

我正在尋找一種方法讓應用程序在開始任何重要的事情之前在內部執行 RUNAS /NETONLY 功能。

有關 RUNAS /NETONLY 如何工作的說明,請參閱此連結:http ://www.eggheadcafe.com/conversation.aspx?messageid=32443204&threadid=32442982

我想我將不得不LOGON_NETCREDENTIALS_ONLY使用CreateProcessWithLogonW

我收集了這些有用的連結:

<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_ONLYwith CreateProcessWithLogonW。我將看看我是否可以讓程序檢測它是否以這種方式啟動,如果沒有,則收集域憑據並自行啟動。這樣,只有一個自我管理的 EXE。

我知道這是一個舊執行緒,但它非常有用。我的情況與 Cade Roux 完全相同,因為我想要 /netonly 風格的功能。

約翰·拉施 (John Rasch) 的答案只需稍作修改即可!!!

添加以下常量(為了保持一致性,在第 102 行附近):

private const int LOGON32_LOGON_NEW_CREDENTIALS = 9;

然後將呼叫更改LogonUser為 useLOGON32_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

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

   /// &lt;summary&gt;
   /// 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.
   /// &lt;/summary&gt;
   /// &lt;remarks&gt;   
   /// 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:uwe.keim@zeta-software.de)
   /// for questions regarding this class.
   /// &lt;/remarks&gt;
   public class Impersonator :
       IDisposable
   {
       #region Public methods.
       // ------------------------------------------------------------------

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

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

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

       private WindowsImpersonationContext impersonationContext = null;

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

   /////////////////////////////////////////////////////////////////////////
}

引用自:https://stackoverflow.com/questions/757857