2011-12-14 69 views
1

我有一個Windows服務在本地計算機系統帳戶下運行。在此服務中,它會嘗試讀取遠程共享文件夾上可用的遠程.ini文件。嘗試讀取此文件的代碼使用LogonUser進行模擬(下面簡化了代碼的版本,以瞭解它在做什麼)。模擬成功啓動模擬配置的用戶,但是它立即嘗試讀取遠程網絡共享上找到的遠程ini文件,則會引發UnauthorizedAccessException。即使配置的用戶在遠程計算機上具有讀/寫權限,也會發生這種情況。當我修改代碼以刪除所有模擬,而是以配置的用戶身份運行Windows服務時,對遠程.ini文件的所有訪問均成功。我寧願使用模擬來訪問這個文件,而不是像用戶那樣運行服務。任何人都可以在示例中看到模擬代碼的錯誤?我需要在Vista 64位盒上做些什麼來啓用它?我的IT同事需要啓用特定的活動目錄權限嗎?windows服務獲取UnauthorizedAccessException在從遠程共享讀取文件時冒充

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

如果它作爲用戶帳戶運行,它是否可以從文件中模擬另一個帳戶? – bryanmac 2011-12-14 05:50:43

回答

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; 
     } 
    } 
} 
相關問題