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

发布于 2024-08-06 10:33:57 字数 1662 浏览 5 评论 0原文

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

我遇到的问题是当 CLR 尝试绑定到此上下文下引用的程序集时。抛出 FileLoadException 并显示“访问被拒绝”消息。

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

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

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

// 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 开头的行。

值得注意的一件有趣的事情是我从构建服务器得到了相同的异常。上述解决方案将其修复在构建服务器上。不在我们的测试环境中。

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

I have an application that needs to do a lot of impersonation, due to moving and creating many files across protected network shares. I've created a simple static class that has a method takes a user, domain, password and a delegate that contains code you need run under the impersonation context.

The problem I've been running into is when the CLR tries to bind to a referenced assembly under this context. A FileLoadException is thrown with an 'Access is denied' message.

I'm led to believe that this is due to the impersonated user not having sufficient privileges to the *.DLL file under the filesystem. For example, I can write benign code just before the impersonation block, which accomplishes nothing other than to load types from the problem assembly before the context switches to the impersonated user, and this works great! However I don't really consider this to be an elegant solution – am I going to need to begin worrying about what types I can use under impersonation, making sure I put random typeof() statements beforehand?

What makes this more frustrating is that I don't run into this problem on my local development machine. It's when the assemblies get shipped off to the beta environment does this problem occur. And I don't have access to read the file permissions from the beta environment to try and mimic them on my location machine.

In any case, I tried this solution:

// 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();
    }
}

No dice. I still get the 'Access is denied' exception, except now from the line starting with Assembly.Load.

One interesting thing to note is I was getting the same exception from the build server. This above solution fixed it on the build server. Not in our beta environment.

What am I missing here? Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

锦欢 2024-08-13 10:33:57

我也有同样的问题。这似乎是一个没有正确清理模拟“会话”的问题。在调用逻辑之前加载问题程序集效果很好。在撤消和处置模拟标识和上下文后尝试加载程序集会导致另一个 FileLoadException。

另一个线程显示了关闭令牌句柄的示例代码,但这样做并没有任何区别我的测试代码。

编辑

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

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

我们刚刚在我们的网络应用中发现了一个单独的、更为严重的问题。创建身份和关闭句柄的过程不正确。我们的多线程应用程序遇到了重大问题,最终导致我们的网站在所有句柄耗尽时陷入瘫痪。经过与其他开发人员的大量讨论和在线研究(包括 此线程),我改进了代码,以便正确清理令牌句柄。

这似乎也解决了我们在模拟上下文中加载程序集的问题。无论我多么努力,我现在似乎都无法重现该错误。我将在此处发布我们改进的模拟代码,以便您可以查看它是否适用于您的应用程序。注意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 ();
    }
}

I'm having the same problem. It seems to be an issue with the impersonation "session" not being cleaned up properly. Loading the problem assembly prior to calling your logic works fine. Attempting to load the assembly after undo'ing and disposing the impersonation identity and context results in another FileLoadException.

Another thread shows sample code where they close the token handles, but doing that didn't make any difference in my test code.

EDIT

Calling System.Security.Principal.WindowsIdentity.GetCurrent() before and after the impersonation code returns the same information, even though something must have changed to no longer allow the problem assembly to be loaded.

EDIT #2 (NOW WITH 100% MORE SOLUTION!)

We just discovered a separate, far more serious problem in our web app. The process of creating an identity and closing the handle was not correct. We were having major issues in our multi-threaded app, which eventually brought our web site to a grinding halt when all the handles ran out. After quite a bit of discussion with fellow developers and online research (including tips from the second-to-last post in this thread), I improved the code so that the token handles are properly cleaned up.

This seems to have also solved our problem with loading assemblies in an impersonation context. I can't seem to reproduce the error now, no matter how hard I try. I'll post our improved impersonation code here, so you can see if it works in your application. Note the lock block in GetIdentity; that is very important for a multi-threaded application.

// 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 ();
    }
}
雪化雨蝶 2024-08-13 10:33:57

我不确定问题是什么,但当从非管理员用户帐户模拟管理员时,Windows 模拟无法正常工作。您的最终用户可能是非管理员和管理员。当您尝试模拟时,它会失败并导致文件访问错误。

I am not sure what the issue is, but Windows Impersonation does not work well when one is Impersonating an Administrator from a non admin user account. It may be that your end user is an non admin & when you try to impersonate, it fails leading to file access error.

咆哮 2024-08-13 10:33:57

您可以将“有问题的”程序集放入 GAC 中。

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

You could put the 'offending' assemblies in the GAC.

Or dynamically add inheritable Read&Execute permissions for the user to-be-impersonated on the directory containing the assemblies just before impersonating the user. I'm assuming that you control the location of the offending assemblies?

迷你仙 2024-08-13 10:33:57

我遇到了一个有点类似的问题;每当我尝试加载库时,都会收到“访问被拒绝”的消息。就我而言,我必须进入 Windows 资源管理器并授予我模拟的用户对运行程序的整个文件夹的完全访问权限。(无论出于何种原因,它必须是整个文件夹,而不仅仅是我获得的特定 DLL)的例外情况)。

我不确定这是否有助于解决您的具体问题,但希望这对遇到与我相同问题的未来读者有所帮助。

I faced a somewhat similar problem; I got "Access is Denied" whenever I tried to load a library. In my case, I had go into Windows Explorer and grant the user I was impersonating full access to the entire folder my program was running in. (For whatever reason, it had to be the whole folder, not just the specific DLL I was getting the exception for).

I'm not sure if this would help with your specific problem, but hopefully this will help future readers who encounter the same problem I did.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文