Служба Windows получает UnauthorizedAccessException при чтении файла с удаленного общего ресурса при олицетворении - PullRequest
1 голос
/ 14 декабря 2011

У меня есть служба Windows, которая работает под системной учетной записью локального компьютера.Внутри этой службы он пытается прочитать удаленный INI-файл, который находится в удаленной общей папке.Код, пытающийся прочитать этот файл, обернут в олицетворение с использованием LogonUser (ниже приведена упрощенная версия кода, чтобы понять, что он делает).Олицетворение успешно начинает олицетворение настроенного пользователя, однако в тот момент, когда он пытается прочитать удаленный INI-файл, найденный на удаленном сетевом ресурсе, выдается исключение UnauthorizedAccessException.Это происходит, даже если настроенный пользователь имеет права на чтение / запись на удаленном компьютере.Когда я изменяю код, чтобы удалить все олицетворения, и вместо этого запускаю службу Windows в качестве настроенного пользователя, весь доступ к удаленному INI-файлу успешен.Я бы предпочел использовать олицетворение, чтобы получить доступ к этому файлу, а не взламывать, например, запускать службу от имени пользователя.Кто-нибудь может увидеть ошибки с кодом олицетворения в примере?Есть ли что-то, что мне нужно сделать на моем 64-битном компьютере Vista, чтобы включить это?Существуют ли определенные разрешения для активных каталогов, которые необходимо включить моим ИТ-сотрудникам?

    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 голос
/ 14 декабря 2011

Проводя дальнейшие исследования, я обнаружил, что основная проблема вызвана кодом олицетворения. Мне пришлось добавить использование 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;
        }
    }
}
...