Windows服务在模拟时从远程共享读取文件时获取UnauthorizedAccessException

时间:2011-12-14 05:45:19

标签: c# impersonation delegation

我有一个在本地计算机系统帐户下运行的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;
    }

1 个答案:

答案 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;
        }
    }
}