首页 文章

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

提问于
浏览
1

我有一个在本地机器系统帐户下运行的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 回答

  • 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;
            }
        }
    }
    

相关问题