我有一个在本地计算机系统帐户下运行的Windows服务。在此服务中,它尝试读取远程共享文件夹上可用的远程.ini文件。尝试读取此文件的代码使用LogonUser进行模拟(下面是代码的简化版本,以了解它正在做什么)。模拟成功开始模拟配置的用户,但是当它尝试读取远程网络共享上找到的远程ini文件时,会抛出UnauthorizedAccessException。即使配置的用户对远程计算机具有读/写权限,也会发生这种情况。当我修改代码以删除所有模拟,而是以配置的用户身份运行Windows服务时,对远程.ini文件的所有访问都是成功的。我宁愿使用模拟来访问此文件,而不是像用户那样运行服务。任何人都可以在示例中看到模拟代码的错误吗?我需要在我的Vista 64位盒上做些什么来启用它吗?我的IT同事需要启用特定的活动目录权限吗?
private WindowsImpersonationContext _impersonatedUser;
private IntPtr _token;
// Declare signatures for Win32 LogonUser and CloseHandle APIs
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(
string principal,
string authority,
string password,
LogonSessionType logonType,
LogonProvider logonProvider,
out IntPtr token);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);
enum LogonSessionType : uint
{
Interactive = 2,
Network,
Batch,
Service,
NetworkCleartext = 8,
NewCredentials
}
enum LogonProvider : uint
{
Default = 0, // default for platform (use this!)
WinNT35, // sends smoke signals to authority
WinNT40, // uses NTLM
WinNT50 // negotiates Kerb or NTLM
}
....
var result = LogonUser(exchangeUserId, exchangeDomain,
password,
LogonSessionType.Network,
LogonProvider.Default,
out _token);
var id = new WindowsIdentity(_token);
_impersonatedUser = id.Impersonate();
try
{
//Validate access to the file on the remote computer/share
File.GetAccessControl(remoteFileAvailableInSharedFolder);
}
catch (UnauthorizedAccessException unauthorized)
{
...
}
....
// Stop impersonation and revert to the process identity
if (_impersonatedUser != null)
{
_impersonatedUser.Undo();
_impersonatedUser = null;
}
if (_token != IntPtr.Zero)
{
CloseHandle(_token);
_token = IntPtr.Zero;
}
答案 0 :(得分:1)
进行进一步的研究我发现了由模拟代码引起的核心问题。我不得不添加api DuplicateToken的使用,并添加了api RevertToSelf的用法。在进行这些更改(请参阅下面的类)时,模拟工作以及在我的域用户下运行整个服务时代码执行的操作。希望代码可以帮助其他人解决同样的问题。
public class Impersonation : IDisposable
{
private WindowsImpersonationContext _impersonatedUserContext;
// Declare signatures for Win32 LogonUser and CloseHandle APIs
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(
string principal,
string authority,
string password,
LogonSessionType logonType,
LogonProvider logonProvider,
out IntPtr token);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool RevertToSelf();
// ReSharper disable UnusedMember.Local
enum LogonSessionType : uint
{
Interactive = 2,
Network,
Batch,
Service,
NetworkCleartext = 8,
NewCredentials
}
// ReSharper disable InconsistentNaming
enum LogonProvider : uint
{
Default = 0, // default for platform (use this!)
WinNT35, // sends smoke signals to authority
WinNT40, // uses NTLM
WinNT50 // negotiates Kerb or NTLM
}
// ReSharper restore InconsistentNaming
// ReSharper restore UnusedMember.Local
/// <summary>
/// Class to allow running a segment of code under a given user login context
/// </summary>
/// <param name="user">domain\user</param>
/// <param name="password">user's domain password</param>
public Impersonation(string user, string password)
{
var token = ValidateParametersAndGetFirstLoginToken(user, password);
var duplicateToken = IntPtr.Zero;
try
{
if (DuplicateToken(token, 2, ref duplicateToken) == 0)
{
throw new Exception("DuplicateToken call to reset permissions for this token failed");
}
var identityForLoggedOnUser = new WindowsIdentity(duplicateToken);
_impersonatedUserContext = identityForLoggedOnUser.Impersonate();
if (_impersonatedUserContext == null)
{
throw new Exception("WindowsIdentity.Impersonate() failed");
}
}
finally
{
if (token != IntPtr.Zero)
CloseHandle(token);
if (duplicateToken != IntPtr.Zero)
CloseHandle(duplicateToken);
}
}
private static IntPtr ValidateParametersAndGetFirstLoginToken(string user, string password)
{
if (string.IsNullOrEmpty(user))
{
throw new ConfigurationErrorsException("No user passed into impersonation class");
}
var userHaves = user.Split('\\');
if (userHaves.Length != 2)
{
throw new ConfigurationErrorsException("User must be formatted as follows: domain\\user");
}
if (!RevertToSelf())
{
throw new Exception("RevertToSelf call to remove any prior impersonations failed");
}
IntPtr token;
var result = LogonUser(userHaves[1], userHaves[0],
password,
LogonSessionType.Interactive,
LogonProvider.Default,
out token);
if (!result)
{
throw new ConfigurationErrorsException("Logon for user " + user + " failed.");
}
return token;
}
public void Dispose()
{
// Stop impersonation and revert to the process identity
if (_impersonatedUserContext != null)
{
_impersonatedUserContext.Undo();
_impersonatedUserContext = null;
}
}
}