为什么此用户模拟代码无法授予我权限?

时间:2014-08-05 17:32:56

标签: c# impersonation

我有一个应用程序,用于将文件从一个位置镜像到另一个位置。在该程序的一个部分(可能是最重要的)中,应用程序打开源和目标的模拟上下文;假设已提供凭据。打开这些上下文后,程序会执行从一个位置到另一个位置的文件的实际镜像,然后关闭上述上下文。

它看起来像这样:

protected virtual void MirrorChanges()
{
    if (this.Source == null)
        throw new InvalidOperationException();
    else if (!this.Source.Exists)
        throw new InvalidOperationException();
    else
    {
        if( this.SourceImpersonator != null )
            if(!this.SourceImpersonator.Open())
            {
                throw new Exception("FolderMirror cannot impersonate Source user. Please review the associated credentials.");
            }

        if( this.DestinationImpersonator != null )
            if(!this.DestinationImpersonator.Open())
            {
                throw new Exception("FolderMirror cannot impersonate Destination user. Please review the associated credentials.");
            }

        this.MirrorChanges(this.Source);

        if( this.DestinationImpersonator != null )
            this.DestinationImpersonator.Close();

        if( this.SourceImpersonator != null )
            this.SourceImpersonator.Close();

        return;
    }
}

现在;你问的所有问题 - “开放”中的问题是什么?并且'关闭'这些假定的模仿者的方法'?其中大部分内容来自于其他一些关于互联网的例子,但是我们走了:

public class UserImpersonator
{
    public Boolean Open()
    {

        // Actively researching why the 'DuplicateToken' method is necessary in this method - like 
        // I said earlier, much of this is sourced from examples. I did some research and do note that 
        // there is a 'SECURITY_IMPERSONATION_LEVEL' variable in this method; but that strikes me as 
        // rather perplexing if this method's action is to simply 'duplicate a token' - but I'm off to 
        //the manual for reviewing that information.
        if (!LogonUser(this.Username, this.Domain, this.Password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50, ref this.PrimaryToken))
        {
            RaiseLastError();
            return false;
        }
        else if (!DuplicateToken(this.PrimaryToken, 2, ref this.MutatedToken))
        {
            RaiseLastError();
            return false;
        }
        else
        {
            try
            {
                this._TargetIdentity = new WindowsIdentity(this.MutatedToken);
                this._ImpersonationContext = this._TargetIdentity.Impersonate();

                return true;
            }
            catch (Exception e)
            {
                return false;
            }
        }
    }

    public void Close()
    {
        if( this._ImpersonationContext != null )
            this._ImpersonationContext.Undo();

        if( this.PrimaryToken != null )
            if (!CloseHandle(this.PrimaryToken))
                RaiseLastError();
    }

    // With some of this action, actually toward the top of the file...
    DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    private unsafe static extern int FormatMessage(int dwFlags, ref IntPtr lpSource, int dwMessageId, int dwLanguageId, ref String lpBuffer, int nSize, IntPtr* arguments);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool CloseHandle(IntPtr handle);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public extern static bool DuplicateToken(IntPtr existingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr duplicateTokenHandle);

}

现在 - 显然有些事情我可以收紧,但要找到问题的根源 - 在我提供已知有效用户名,密码和域的情况下 - 我被拒绝实际替换文件在目的地。

[编辑]我现在(正如任何理智的程序员所做的那样)(上面应用的编辑)围绕着' Impersonator.Open'通过真实的评估确保他们理清的条款。在逐步执行' Open'方法I一直在取得成功。

[编辑]提及我采取这些开放和密切的方法并将其置于“执行”中可能很有价值。将Delegate作为参数的方法。当时它似乎比目前的实施更合适;但是在写了大约一千行之后仍然得到相同的结果(其中大部分用于在执行方法的上下文中实际协商文件);我放弃了,没有回来。这是一个小问题,以防有人感兴趣。

public class UserImpersonator()
{
    public Object Execute(System.Delegate Method)
    {
        if (Method == null)
            throw new InvalidOperationException("Impersonator fixed-method already provided. Cannot implement another method on this impersonator." );
        else if (!this.Open())
            return null;
        else
        {
            try
            {
                this._Executing = true;

                Object ReturnValue = Method.DynamicInvoke();

                this.Close();

                this._Executing = false;

                return ReturnValue;
            }
            catch (Exception e)
            {
                return null;
            }
        }
    }
}

[添加]我还应该提到在我的本地系统上(还没有在部署[服务器]环境中测试),如果我提供完全无效的凭据,我仍然可以访问读取和写入。我认为这意味着我保留了已经打开的凭据 - 但如果是这样的话我的[未受过教育]假设“堆叠模仿不会起作用”。真的崩溃了。

我已经通过了不少通行证无济于事;所以,如果有人有任何建议或问题,我都会听到。

1 个答案:

答案 0 :(得分:1)

有时会出现反复试验。

纠正此问题所需的调整是LogonUser方法中的LogonType和LogonProvider变量的修改。在此特定实例中,有效(看似仅起作用)的用法是LOGON32_PROVIDER_DEFAULT和LOGON32_LOGON_INTERACTIVE常量。

我个人不知道为什么[, but would be very interested in some greater detail than that found here]。

经过一些一般性的试验和错误后,对于我的应用程序,更正后的代码如下。我希望这可以作为社区权威参考的[早期修订];因为大多数实施都是......不够彻底。

在代码转储之前,如果不注意此代码的潜在实现,那将是我的疏忽。

1. Create a new UserImpersonator using the UserImpersonator(String Username,String Password,String Domain,System.Delegate Method) constructor to define the credentials, and the method to be invoked; and then 'Execute' the UserImpersonator using either of the overloaded Execute methods that do not require a delegate. By nature, these methods refer directly to the method that was stored at construction, or set at a later time.
2. Create a new UserImpersonator using the UserImpersonator(String Username, String Password, String Domain) constructor, and perform one of a few actions:
    1. Set the UserImpersonator's Method and Execute it at a later time.
    2. Run an Execute overload that requires a delegate parameter on the UserImpersonator, and provide a delegate method for the impersonator to execute.
    3. Execute the 'UserImpersonator.Open' method to open the user context, execute your user-specific code, and then execute the 'UserImpersonator.Close' method to finalize the operation.

我们走了:

using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Security.Principal;
using System.Linq;
using System.Text;

namespace TopSekrit.Credentials
{
    public class UserImpersonator : IDisposable
    {
        #region Delegates
        // Port from generic event handler
        public delegate void SimpleEventDelegate(SimpleEventArgument Argument);
        #endregion

        #region Supporting Classes
        // Port from generic event handler
        public class SimpleEventArgument
        {
            public Object Source = null;
            public Object Message = null;

            public SimpleEventArgument(Object Source, String Message)
            {
                this.Source = Source;
                this.Message = Message;
            }
        }
        #endregion

        #region Imports
        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private unsafe static extern int FormatMessage(int dwFlags, ref IntPtr lpSource, int dwMessageId, int dwLanguageId, ref String lpBuffer, int nSize, IntPtr* arguments);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool CloseHandle(IntPtr handle);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public extern static bool DuplicateToken(IntPtr existingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr duplicateTokenHandle);
        #endregion

        #region Constants
        // Logon Types as defined in Winbase.h
        const int LOGON32_LOGON_INTERACTIVE = 2;
        const int LOGON32_LOGON_NETWORK = 3;
        const int LOGON32_LOGON_BATCH = 4;
        const int LOGON32_LOGON_SERVICE = 5;
        const int LOGON32_LOGON_UNKNOWN = 6;
        const int LOGON32_LOGON_UNLOCK = 7;
        const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
        const int LOGON32_LOGON_NEW_CREDENTIALS = 9;

        // Logon Providers as defined in Winbase.h
        const int LOGON32_PROVIDER_DEFAULT = 0;
        const int LOGON32_PROVIDER_WINNT35 = 1;
        const int LOGON32_PROVIDER_WINNT40 = 2;
        const int LOGON32_PROVIDER_WINNT50 = 3;
        #endregion

        #region Events
        public event SimpleEventDelegate OnImpersonationReset = null;

        public event SimpleEventDelegate OnImpersonationOpened = null;
        public event SimpleEventDelegate OnImpersonationClosed = null;

        public event SimpleEventDelegate OnImpersonatedExecutionStarted = null;
        public event SimpleEventDelegate OnImpersonatedExecutionFinished = null;
        #endregion

        #region Properties
        protected String _Username = String.Empty;

        public String Username
        {
            get
            {
                return this._Username;
            }

            set
            {
                if (this.IsExecuting)
                    throw new InvalidOperationException("Cannot set Username on UserImpersonator while impersonation is executing.");
                else if (this.IsOpen)
                    throw new InvalidOperationException("Cannot set Username on UserImpersonator while impersonation context is open.");
                else
                {
                    this._Username = value;

                    this.ResetImpersonation();
                }
            }
        }

        protected String _Password = String.Empty;

        public String Password
        {
            get
            {
                return this._Password;
            }

            set
            {
                if (this.IsExecuting)
                    throw new InvalidOperationException("Cannot set Password on UserImpersonator while impersonation is executing.");
                else if (this.IsOpen)
                    throw new InvalidOperationException("Cannot set Password on UserImpersonator while impersonation context is open.");
                else
                {
                    this._Password = value;

                    this.ResetImpersonation();
                }
            }
        }

        protected String _Domain = String.Empty;

        public String Domain
        {
            get
            {
                return this._Domain;
            }

            set
            {
                if (this.IsExecuting)
                    throw new InvalidOperationException("Cannot set Domain on UserImpersonator while impersonation is executing.");
                else if (this.IsOpen)
                    throw new InvalidOperationException("Cannot set Domain on UserImpersonator while impersonation context is open.");
                else
                {
                    this._Domain = value;

                    this.ResetImpersonation();
                }
            }
        }

        protected System.Delegate _Method = null;

        public System.Delegate Method
        {
            get
            {
                return this._Method;
            }

            set
            {
                this._Method = value;
            }
        }                                                               

        protected IntPtr PrimaryToken = IntPtr.Zero;

        protected IntPtr MutatedToken = IntPtr.Zero;

        protected WindowsIdentity _TargetIdentity = null;

        public WindowsIdentity TargetIdentity
        {
            get
            {
                return this._TargetIdentity;
            }
        }

        protected WindowsImpersonationContext _ImpersonationContext = null;

        public WindowsImpersonationContext ImpersonationContext
        {
            get
            {
                return this._ImpersonationContext;
            }
        }

        protected Boolean _IsExecuting = false;

        public Boolean IsExecuting
        {
            get
            {
                return this._IsExecuting;
            }
        }

        public Boolean IsOpen
        {
            get
            {
                if (this.PrimaryToken != null)
                    return true;
                else if (this.MutatedToken != null)
                    return true;
                else if (this.TargetIdentity != null)
                    return true;
                else if (this.ImpersonationContext != null)
                    return true;
                else
                    return false;
            }
        }

        protected int _LogonType = LOGON32_LOGON_INTERACTIVE;

        public int LogonType
        {
            get
            {
                return this._LogonType;
            }

            set
            {
                if (this.IsExecuting)
                    throw new InvalidOperationException("Cannot set LogonType on UserImpersonator while impersonation is executing.");
                else if (this.IsOpen)
                    throw new InvalidOperationException("Cannot set LogonType on UserImpersonator while impersonation context is open.");
                else
                {
                    this._LogonType = value;

                    this.ResetImpersonation();
                }
            }
        }

        protected int _LogonProvider = LOGON32_PROVIDER_DEFAULT;

        public int LogonProvider
        {
            get
            {
                return this._LogonProvider;
            }

            set
            {
                if (this.IsExecuting)
                    throw new InvalidOperationException("Cannot set LogonProvider on UserImpersonator while impersonation is executing.");
                else if (this.IsOpen)
                    throw new InvalidOperationException("Cannot set LogonProvider on UserImpersonator while impersonation context is open.");
                else
                {
                    this._LogonProvider = value;

                    this.ResetImpersonation();
                }
            }
        }
        #endregion

        #region Constructors
        public UserImpersonator(String Username,String Password,String Domain,System.Delegate Method,int LogonType,int LogonProvider)
        {
            if (String.IsNullOrEmpty(Username))
                throw new ArgumentNullException();
            else
            {
                this._Username = Username;
                this._Password = Password;
                this._Domain = Domain;

                this._Method = Method;

                this._LogonType = LogonType;
                this._LogonProvider = LogonProvider;

                return;
            }
        }

        public UserImpersonator(String Username, String Password, String Domain, System.Delegate Method, int LogonType)
            : this(Username, Password, Domain, Method, LogonType, LOGON32_PROVIDER_DEFAULT)
        {

        }

        public UserImpersonator(String Username, String Password, String Domain, System.Delegate Method)
            : this(Username,Password,Domain,Method,LOGON32_LOGON_INTERACTIVE)
        {

        }

        public UserImpersonator(String Username, String Password, String Domain)
            :this( Username, Password, Domain, null)
        {
        }

        public UserImpersonator(String Username, String Password)
            : this(Username, Password,String.Empty)
        {

        }

        public UserImpersonator(String Username)
            : this(Username, String.Empty)
        {

        }
        #endregion

        #region Impersonated Execution
        public virtual Object Execute()
        {
            if (this.IsExecuting)
                throw new InvalidOperationException("UserImpersonator cannot Execute while another execution is already in progress.");
            else if (this.Method == null)
                throw new InvalidOperationException("UserImpersonator cannot Execute without a supplied, or stored Method to invoke.");
            else if (!this.Open())
                throw new InvalidOperationException("Could not open security context.");
            else
            {
                try
                {
                    this._IsExecuting = true;

                    Object ReturnValue = this.Method.DynamicInvoke();

                    this.Close();

                    this._IsExecuting = false;

                    return ReturnValue;
                }
                catch (Exception e)
                {
                    return null;
                }
            }
        }

        public virtual Object Execute(params object[] Arguments)
        {
            if (this.IsExecuting)
                throw new InvalidOperationException("UserImpersonator cannot Execute while another execution is already in progress.");
            else if (this.Method == null)
                throw new InvalidOperationException("UserImpersonator cannot Execute without a supplied, or stored Method to invoke.");
            else if (!this.Open())
                throw new InvalidOperationException("Could not open security context.");
            else
            {
                try
                {
                    this._IsExecuting = true;

                    Object ReturnValue = this.Method.DynamicInvoke(Arguments);

                    this.Close();

                    this._IsExecuting = false;

                    return ReturnValue;
                }
                catch (Exception e)
                {
                    return null;
                }
            }
        }

        public virtual Object Execute(System.Delegate Method)
        {
            if (this.IsExecuting)
                throw new InvalidOperationException("UserImpersonator cannot Execute while another execution is already in progress.");
            else if (Method == null)
                throw new InvalidOperationException("UserImpersonator cannot Execute without a supplied, or stored Method to invoke.");
            else if (!this.Open())
                throw new InvalidOperationException("Could not open security context.");
            else
            {
                try
                {
                    this._IsExecuting = true;

                    Object ReturnValue = Method.DynamicInvoke();

                    this.Close();

                    this._IsExecuting = false;

                    return ReturnValue;
                }
                catch (Exception e)
                {
                    return null;
                }
            }
        }

        public virtual Object Execute(System.Delegate Method, params object[] Arguments)
        {
            if (this.IsExecuting)
                throw new InvalidOperationException("UserImpersonator cannot Execute while another execution is already in progress.");
            else if (Method == null)
                throw new InvalidOperationException("UserImpersonator cannot Execute without a supplied, or stored Method to invoke.");
            else if (!this.Open())
                throw new InvalidOperationException("Could not open security context.");
            else
            {
                try
                {
                    this._IsExecuting = true;

                    Object ReturnValue = Method.DynamicInvoke(Arguments);

                    this.Close();

                    this._IsExecuting = false;

                    return ReturnValue;
                }
                catch (Exception e)
                {
                    return null;
                }
            }
        }
        #endregion

        #region Impersonation / Depersonation
        public virtual Boolean Open()
        {
            if (this.IsOpen)
            {
                if( this.IsExecuting )
                    throw new InvalidOperationException("UserImpersonator cannot Open user context while a user context is already open and executing.");
                else
                {
                    this.Close();

                    return this.Open();
                }
            }
            else if (!LogonUser(this.Username, this.Domain, this.Password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref this.PrimaryToken))
                throw this.GetLastException();
            else if (!DuplicateToken(this.PrimaryToken, 2, ref this.MutatedToken))
                throw this.GetLastException();
            else
            {
                try
                {
                    this._TargetIdentity = new WindowsIdentity(this.MutatedToken);
                }
                catch (Exception e)
                {
                    throw new Exception("UserImpersonator could not Open user context. An exception was encountered while creating the WindowsIdentity.\r\n" + e.Message + "\r\n" + e.StackTrace);
                }
                finally
                {
                    try
                    {
                        this._ImpersonationContext = this._TargetIdentity.Impersonate();
                    }
                    catch (Exception e)
                    {
                        throw new Exception("UserImpersonator could not Open user context. An exception was encountered while creating the WindowsImpersonationContext.\r\n" + e.Message + "\r\n" + e.StackTrace);
                    }
                    finally
                    {
                        this.FireImpersonationOpened();
                    }
                }
                return true;
            }
        }

        public virtual void Close()
        {
            if (this.IsExecuting)
                throw new InvalidOperationException("UserImpersonator cannot Close impersonation context while in execution.");
            else
            {
                try
                {
                    if (this._TargetIdentity != null)
                    {
                        this._TargetIdentity.Dispose();
                        this._TargetIdentity = null;
                    }
                }
                catch (Exception e)
                {
                    throw new Exception("Exception encountered while disposing TargetIdentity on UserImpersonator.\r\n" + e.Message + "\r\n" + e.StackTrace);
                }
                finally
                {
                    try
                    {
                        if (this._ImpersonationContext != null)
                        {
                            this._ImpersonationContext.Undo();
                            this._ImpersonationContext.Dispose();
                            this._ImpersonationContext = null;
                        }
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Exception encountered while undoing or disposing ImpersonationContext on UserImpersonator.\r\n" + e.Message + "\r\n" + e.StackTrace);
                    }
                    finally
                    {
                        try
                        {
                            if (this.MutatedToken != null)
                                if (!CloseHandle(MutatedToken))
                                    this.GetLastException();
                        }
                        catch (Exception e)
                        {
                            throw new Exception("Exception encountered while closing MutatedToken on UserImpersonator.\r\n" + e.Message + "\r\n" + e.StackTrace);
                        }
                        finally
                        {
                            try
                            {
                                if (this.PrimaryToken != null)
                                    if (!CloseHandle(this.PrimaryToken))
                                        this.GetLastException();
                            }
                            catch (Exception e)
                            {
                                throw new Exception("Exception encountered while closing PrimaryToken on UserImpersonator.\r\n" + e.Message + "\r\n" + e.StackTrace);
                            }
                            finally
                            {
                                this.FireImpersonationClosed();
                            }
                        }
                    }
                }
                return;
            }
        }

        protected virtual void ResetImpersonation()
        {
            if (this.IsExecuting)
                throw new InvalidOperationException("UserImpersonator cannot ResetImpersonation while impersonation is already executing.");
            else if (this.IsOpen)
            {
                this.Close();

                this.ResetImpersonation();

                return;
            }
            else
            {
                this.Open();

                this.FireImpersonationReset();

                return;
            }
        }
        #endregion

        #region Error Handling
        private Exception GetLastException()
        {
            return new ApplicationException(this.GetErrorMessage(Marshal.GetLastWin32Error()));
        }

        public unsafe string GetErrorMessage(int ErrorCode)
        {
            int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
            int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
            int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;

            int messageSize = 255;
            string lpMsgBuf = "";
            int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;

            IntPtr ptrlpSource = IntPtr.Zero;
            IntPtr ptrArguments = IntPtr.Zero;

            int retVal = FormatMessage(dwFlags, ref ptrlpSource, ErrorCode, 0, ref lpMsgBuf, messageSize, &ptrArguments);

            if (retVal == 0)
                return string.Format("Failed to format message for error code '{0}'.", ErrorCode);

            return lpMsgBuf;
        }
        #endregion

        #region Disposability
        public virtual void Dispose()
        {
            this.Close();

            this._Username = null;
            this._Password = null;
            this._Domain = null;
            this._Method = null;

            return;
        }
        #endregion

        #region Event Firing
        protected virtual void FireImpersonationReset()
        {
            if (this.OnImpersonationReset != null)
                this.OnImpersonationReset(new Events.SimpleArgument(this, "Impersonation context has been reset."));
            return;
        }

        protected virtual void FireImpersonationOpened()
        {
            if (this.OnImpersonationOpened != null)
                this.OnImpersonationOpened(new Events.SimpleArgument(this, "Impersonation context has been opened."));
            return;
        }

        protected virtual void FireImpersonationClosed()
        {
            if (this.OnImpersonationClosed != null)
                this.OnImpersonationClosed(new Events.SimpleArgument(this, "Impersonation context has been closed."));
            return;

        }

        protected virtual void FireImpersonationExecutionStarted()
        {
            if (this.OnImpersonatedExecutionStarted != null )
                this.OnImpersonatedExecutionStarted(new Events.SimpleArgument(this, "Impersonated execution has started."));
            return;
        }

        protected virtual void FireImpersonationExecutionFinished()
        {
            if (this.OnImpersonatedExecutionFinished != null)
                this.OnImpersonatedExecutionFinished(new Events.SimpleArgument(this, "Impersonated execution has finished."));
            return;
        }
        #endregion
    }
}

[编辑]:2014年12月14日更新 - 添加了LogonType和LogonProvider构造函数。第一次可用时,打开方法会覆盖。

我的感觉是,这是Windows用户模仿的更彻底的实现之一 - 我希望你们都能在其中找到一些用处。欢迎提出问题或意见;我相信这仍有改进的余地。