Как создать функциональность RUNAS / NETONLY в программу (C#/.NET/WinForms)?

наши рабочие станции не являются членами домена, в котором находится наш SQL Server. (На самом деле они вообще не находятся в домене - не спрашивайте).

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

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

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

щелкните правой кнопкой мыши на приложении и используйте "Run As..."опция не работает (предположительно, потому что рабочая станция действительно не знает о домене).

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

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

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

5 ответов


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

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

добавьте следующую константу (вокруг строки 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:uwe.keim@zeta-software.de)
    /// 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
    }

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

Я просто сделал что-то похожее на это, используя 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:uwe.keim@zeta-software.de)
    /// 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
    }

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

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

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.запись

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.


этот код является частью класса 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;
}

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