WindowsImpersonationContext下的程序集绑定。如何防止FileLoadException?

时间:2009-09-30 17:25:58

标签: c# .net assemblies clr assemblybinding

由于在受保护的网络共享中移动和创建了许多文件,我有一个需要进行大量模拟的应用程序。我创建了一个简单的静态类,它有一个方法,它接受用户,域,密码和包含你需要在模拟上下文下运行的代码的委托。

我遇到的问题是CLR在此上下文中尝试绑定到引用的程序集。抛出FileLoadException并显示“Access is denied”消息。

我认为这是因为模拟用户对文件系统下的* .DLL文件没有足够的权限。例如,我可以在模拟块之前编写良性代码,除了在上下文切换到模拟用户之前从问题程序集加载类型之外什么都不做,这非常有用!但是我并不认为这是一个优雅的解决方案 - 我是否需要开始担心我可以在模仿下使用哪些类型,确保我事先添加随机typeof()语句?

更令人沮丧的是,我没有在本地开发机器上遇到这个问题。当程序集运送到beta环境时,会出现此问题。而且我无权从beta环境中读取文件权限,以尝试在我的位置计算机上模仿它们。

无论如何,我尝试了这个解决方案:

// Defined above:
// System.Security.Principal.WindowsIdentity identity;
// System.Security.Principal.WindowsImpersonationContext context;

context = identity.Impersonate();

int tries = 0;
while ( true )
{
    try
    {
        contextAction();
    }
    catch ( FileLoadException ex )
    {
        if ( tries > MAX_TRIES )
        {
            // don't allow an infinite loop
            throw;
        }
        if ( String.IsNullOrEmpty( ex.FileName ) )
        {
            // if this is null/empty, we can't really recover
            throw;
        }
        context.Undo(); // return to current logon
        try
        {
            var assemblyName = new AssemblyName( ex.FileName );
            Assembly.Load( assemblyName );
            tries++;
            continue;
        }
        finally
        {
            context = identity.Impersonate(); // re-impersonate
        }
    }   
    finally
    {
        // return to your current windows logon
        context.Undo();
    }
}

没有骰子。我仍然得到“访问被拒绝”例外,现在除了从Assembly.Load开始的行。

有一点值得注意的是我从构建服务器获得了相同的异常。以上解决方案将其修复在构建服务器上。不在我们的测试版环境中。

我在这里缺少什么?感谢。

3 个答案:

答案 0 :(得分:3)

我遇到了同样的问题。似乎是模拟“会话”没有被正确清理的问题。在调用逻辑之前加载问题组件工作正常。在撤消并处理模拟身份和上下文后尝试加载程序集会导致另一个FileLoadException。

Another thread显示了关闭令牌句柄的示例代码,但这样做对我的测试代码没有任何影响。

修改

在模拟代码之前和之后调用System.Security.Principal.WindowsIdentity.GetCurrent()会返回相同的信息,即使某些内容必须已更改为不再允许加载问题程序集。

编辑#2(现在提供100%更多解决方案!

我们在网络应用中发现了一个单独的,更严重的问题。创建标识和关闭句柄的过程不正确。我们的多线程应用程序遇到了重大问题,当所有句柄耗尽时,最终使我们的网站陷入停顿。经过与开发人员和在线研究的讨论(包括来自this thread的倒数第二篇文章的提示),我改进了代码,以便正确清理令牌句柄。

这似乎也解决了我们在模拟上下文中加载程序集的问题。无论我怎么努力,我现在似乎无法重现错误。我将在此处发布我们改进的模拟代码,以便您可以查看它是否适用于您的应用程序。注意GetIdentity中的锁定块;这对于多线程应用程序非常重要。

// LogonType = 8        // LOGON32_LOGON_NETWORK_CLEARTEXT
// LogonProvider = 0    // LOGON32_PROVIDER_DEFAULT

[DllImport ( "advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true )]
private static extern bool LogonUser( string userName, string domain,
    string password, int logonType, int logonProvider, ref IntPtr accessToken );

[DllImport ( "kernel32.dll", SetLastError = true )]
private static extern bool CloseHandle( IntPtr handle );

private static readonly object Locker = new Object ();

private static WindowsIdentity GetIdentity( string username, string domain, string password )
{
    lock ( Locker )
    {
        IntPtr token = IntPtr.Zero;
        if ( LogonUser ( username, domain, password,
            (int) LogonType, (int) LogonProvider, ref token ) )
        {
            // using the token to create an instance of WindowsIdentity class
            var identity = new WindowsIdentity ( token );
            CloseHandle ( token ); // the WindowsIdentity object duplicates this token internally
            return identity;
        }

        throw new SecurityException ( string.Format (
            "Invalid username/password (domain: '{0}', username: '{1}')",
            domain, username ) );
    }
}

public static T ExecuteAction<T>( string username, string domain, string password,
    Func<T> contextAction )
{
    var identity = GetIdentity ( username, domain, password );
    var context = identity.Impersonate ();
    try
    {

        return contextAction ();
    }
    finally
    {
        context.Undo ();
        context.Dispose ();
    }
}

答案 1 :(得分:0)

我不确定问题是什么,但是当一个人从非管理员用户帐户模拟管理员时,Windows模拟效果不佳。可能是您的最终用户是非管理员和当您尝试模拟时,它会导致文件访问错误。

答案 2 :(得分:0)

您可以将'违规'程序集放在GAC中。

或者在模拟用户之前为包含程序集的目录中的用户动态添加可继承的读取和执行权限。我假设您控制了违规装配的位置?