首页 文章

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

提问于
浏览
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返回有效数据?我的用户在此计算机或远程计算机上是否有权限?

1 回答

  • 1

    在这个时间点,我无法找到有关模拟以及它如何与win32 dlls / apis交互的信息,但是,我知道以下内容:

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

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

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

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

相关问题