当ini文件是远程时,模拟块中运行的GetPrivateProfileSectionNames返回0

时间:2013-07-08 22:33:46

标签: c# c#-4.0 impersonation

我的代码创建了一个模拟块,允许对远程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返回有效数据?我的用户在此计算机或远程计算机上是否有权限?

1 个答案:

答案 0 :(得分:1)

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

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

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

3)如果打开文件流,并且本地复制ini文件,则在本地ini文件中使用GetPrivateProfileSectionNames,然后GetPrivateProfileSectionNames根据需要工作,并允许文件流访问远程文件。

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