我有一个应用程序,用于将文件从一个位置镜像到另一个位置。在该程序的一个部分(可能是最重要的)中,应用程序打开源和目标的模拟上下文;假设已提供凭据。打开这些上下文后,程序会执行从一个位置到另一个位置的文件的实际镜像,然后关闭上述上下文。
它看起来像这样:
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;
}
}
}
}
[添加]我还应该提到在我的本地系统上(还没有在部署[服务器]环境中测试),如果我提供完全无效的凭据,我仍然可以访问读取和写入。我认为这意味着我保留了已经打开的凭据 - 但如果是这样的话我的[未受过教育]假设“堆叠模仿不会起作用”。真的崩溃了。
我已经通过了不少通行证无济于事;所以,如果有人有任何建议或问题,我都会听到。
答案 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用户模仿的更彻底的实现之一 - 我希望你们都能在其中找到一些用处。欢迎提出问题或意见;我相信这仍有改进的余地。