4

保護されたネットワーク共有間で多くのファイルを移動および作成するため、多くの偽装を行う必要があるアプリケーションがあります。メソッドがユーザー、ドメイン、パスワード、および偽装コンテキストで実行する必要があるコードを含むデリゲートを受け取る単純な静的クラスを作成しました。

私が遭遇した問題は、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ます。

注目すべき興味深い点の 1 つは、ビルド サーバーから同じ例外が発生していたことです。上記のソリューションにより、ビルドサーバーで修正されました。ベータ環境ではありません。

ここで何が欠けていますか?ありがとう。

4

3 に答える 3

3

私は同じ問題を抱えています。偽装「セッション」が適切にクリーンアップされていないことが問題のようです。ロジックを呼び出す前に問題のアセンブリを読み込むと、問題なく動作します。偽装 ID とコンテキストを元に戻して破棄した後にアセンブリを読み込もうとすると、別の FileLoadException が発生します。

別のスレッドは、トークン ハンドルを閉じるサンプル コードを示していますが、それを行ってもテスト コードに違いはありませんでした。

編集

偽装コードの前後に System.Security.Principal.WindowsIdentity.GetCurrent() を呼び出すと、何かが変更されて問題のあるアセンブリを読み込めなくなったに違いないにもかかわらず、同じ情報が返されます。

編集#2(今では100%以上のソリューションで!

Web アプリで、別の、はるかに深刻な問題を発見しました。ID を作成してハンドルを閉じるプロセスが正しくありませんでした。マルチスレッド アプリで大きな問題が発生していたため、最終的にすべてのハンドルが使い果たされたときに Web サイトが停止しました。仲間の開発者とオンライン調査 (このスレッドの最後から 2 番目の投稿からのヒントを含む) とのかなりの議論の後、トークン ハンドルが適切にクリーンアップされるようにコードを改善しました。

これにより、偽装コンテキストでのアセンブリの読み込みに関する問題も解決されたようです。どんなに頑張っても、今はエラーを再現できないようです。改善された偽装コードをここに投稿して、アプリケーションで機能するかどうかを確認してください。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 ();
    }
}
于 2009-10-07T20:17:03.117 に答える
0

何が問題なのかはわかりませんが、管理者以外のユーザー アカウントから管理者になりすましている場合、Windows の偽装はうまく機能しません。エンド ユーザーが管理者ではない可能性があります。偽装しようとすると、ファイル アクセス エラーが発生して失敗します。

于 2009-09-30T17:33:58.640 に答える
0

「問題のある」アセンブリをGACに配置できます。

または、ユーザーを偽装する直前に、アセンブリを含むディレクトリに偽装するユーザーの継承可能な読み取りと実行のアクセス許可を動的に追加します。問題のあるアセンブリの場所を管理していると思いますか?

于 2009-11-21T19:02:28.517 に答える