1

我有创建模拟块的代码,以允许对远程 ini 文件进行读取访问,并对远程目录进行写入访问。当要写入的“远程”目录确实是远程计算机 UNC 路径时,系统可以正常写入,但是如果“远程”ini 文件确实是远程 UNC 路径,则 GetPrivateProfileSectionNames 返回 0。但是,如果“远程” ini 文件实际上只是一个本地 UNC 路径,此功能按预期工作。对于ini文件真正在远程计算机上的情况,有没有办法让这个功能按预期工作?

我的模拟块是使用以下一次性类完成的:

[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
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;
    }
    /// <summary>
    /// Dispose
    /// </summary>
    public void Dispose()
    {
        // Stop impersonation and revert to the process identity
        if (_impersonatedUserContext != null)
        {
            _impersonatedUserContext.Undo();
            _impersonatedUserContext.Dispose();
            _impersonatedUserContext = null;
        }
    }
}

在此类的模拟块实例中,远程 ini 文件可通过以下方式访问:

int bufLen = GetPrivateProfileSectionNames(buffer, 
                                           buffer.GetUpperBound(0),     
                                           iniFileName);
if (bufLen > 0)
{
     //process results
}

与远程计算机打交道时,如何让 GetPrivateProfileSectionNames 返回有效数据?我的用户在这台计算机或远程计算机上是否有权限?

4

1 回答 1

1

目前,我无法找到有关模拟以及它如何与 win32 dll/api 交互的信息,但是,我确实知道以下内容:

1) 如果整个进程在有权访问 ini 文件所在的远程文件夹的用户下运行,则 GetPrivateProfileSectionNames 按需要工作

2) 如果在模拟块内调用 GetPrivateProfileSectionNames,则它不能按预期工作

3)如果打开文件流,将ini文件复制到本地,则在本地ini文件上使用GetPrivateProfileSectionNames,然后GetPrivateProfileSectionNames按要求工作,允许文件流访问远程文件。

根据结果​​,我推测 win32 api 调用 GetPrivateProfileSectionNames 没有从 c# 传递模拟上下文,因此在没有访问权限的整个进程上下文下运行。我通过在本地缓存 ini 文件并跟踪它上次更改的时间来解决这个问题,以便我知道是否需要重新缓存 ini 文件,或者本地副本是否正确。

于 2013-07-10T17:23:27.060 回答