web-dev-qa-db-ja.com

UserPrincipal.FindByIdentity(System.DirectoryServices.AccountManagement)の.NET 4.5バグ

.NET 4.5での.NET 4.0アプリケーションのテストで、FindByIdentityUserPrincipalメソッドで問題が発生しました。次のコードは、.NET 4.0ランタイムで実行すると機能しますが、.NET 4.5では失敗します。

[Test]
public void TestIsAccountLockedOut()
{
    const string activeDirectoryServer = "MyActiveDirectoryServer";
    const string activeDirectoryLogin = "MyADAccount@MyDomain";
    const string activeDirectoryPassword = "MyADAccountPassword";
    const string userAccountToTest = "TestUser@MyDomain";
    const string userPasswordToTest = "WRONGPASSWORD";

    var principalContext = new PrincipalContext(ContextType.Domain, activeDirectoryServer, activeDirectoryLogin, activeDirectoryPassword);

    var isAccountLockedOut = false;
    var isAuthenticated = principalContext.ValidateCredentials(userAccountToTest, userPasswordToTest, principalContext.Options);
    if (!isAuthenticated)
    {
        // System.DirectoryServices.AccountManagement.PrincipalOperationException : Information about the domain could not be retrieved (1355).
        using (var user = UserPrincipal.FindByIdentity(principalContext, IdentityType.UserPrincipalName, userAccountToTest))
        {
            isAccountLockedOut = (user != null) && user.IsAccountLockedOut();
        }
    }
    Assert.False(isAuthenticated);
    Assert.False(isAccountLockedOut);
}

例外スタックトレースは次のとおりです。

System.DirectoryServices.AccountManagement.PrincipalOperationException : Information about the domain could not be retrieved (1355).
at System.DirectoryServices.AccountManagement.Utils.GetDcName(String computerName, String domainName, String siteName, Int32 flags)   at System.DirectoryServices.AccountManagement.ADStoreCtx.LoadDomainInfo()   at 
System.DirectoryServices.AccountManagement.ADStoreCtx.get_DnsDomainName()   at System.DirectoryServices.AccountManagement.ADStoreCtx.GetAsPrincipal(Object storeObject, Object discriminant)   at 
System.DirectoryServices.AccountManagement.ADStoreCtx.FindPrincipalByIdentRefHelper(Type principalType, String urnScheme, String urnValue, DateTime referenceDate, Boolean useSidHistory)   at 
System.DirectoryServices.AccountManagement.ADStoreCtx.FindPrincipalByIdentRef(Type principalType, String urnScheme, String urnValue, DateTime referenceDate)   at 
System.DirectoryServices.AccountManagement.Principal.FindByIdentityWithTypeHelper(PrincipalContext context, Type principalType, Nullable`1 identityType, String identityValue, DateTime refDate)   at 
System.DirectoryServices.AccountManagement.Principal.FindByIdentityWithType(PrincipalContext context, Type principalType, IdentityType identityType, String identityValue)   at 
System.DirectoryServices.AccountManagement.UserPrincipal.FindByIdentity(PrincipalContext context, IdentityType identityType, String identityValue)   

他の誰かがこの問題を見て解決しましたか?そうでない場合、Active DirectoryアカウントのIsAccountLockedOutステータスを確認するより良い方法はありますか?

参考までに、すべてのテストマシンは同じサブネット内にあります。さまざまなドメイン機能モードで、Windows Server 2003、2008、および2012を実行する個別のActiveDirectoryサーバーがあります(以下を参照)。コードは.NET 4.0を実行しているマシンで機能しますが、.NET 4.5を実行しているマシンでは失敗します。

コードを実行した3つの.NETマシンは次のとおりです。
-.NET 4.0を実行するWindows 7
-.NET 4.5を実行しているWindows Vista
-.NET 4.5を実行するWindows Server 2012

私たちが試したActive Directoryサーバーは次のとおりです。
-ADドメイン機能モードがWindows 2000ネイティブに設定されたWindows 2003
-ADドメイン機能モードがWindows Server 2003に設定されたWindows 2003
-ADドメイン機能モードがWindows 2000ネイティブに設定されたWindows 2008
-ADドメイン機能モードがWindows Server 2003に設定されたWindows 2008
-ADドメイン機能モードがWindows Server 2008に設定されたWindows 2008
-ADドメイン機能モードがWindows 2012に設定されたWindows 2012

これらのActive Directoryサーバーはすべてシンプルな単一フォレストとして構成されており、クライアントマシンはドメインの一部ではありません。これらは、この動作をテストする以外の機能には使用されず、Active Directory以外は実行されていません。


編集-2012年10月9日

返信してくれた皆さんありがとう。以下は、問題を示すC#コマンドラインクライアントと、Active DirectoryとDNS構成について何も変更する必要がないことを確認した短期的な回避策です。例外はPrincipalContextのインスタンスで1回だけスローされるようです。 .NET 4.0マシン(Windows 7)と.NET 4.5マシン(Windows Vista)の出力を含めました。

using System;
using System.DirectoryServices.AccountManagement;

namespace ADBug
{
    class Program
    {
        static void Main(string[] args)
        {
            const string activeDirectoryServer = "MyActiveDirectoryServer";
            const string activeDirectoryLogin = "MyADAccount";
            const string activeDirectoryPassword = "MyADAccountPassword";
            const string validUserAccount = "[email protected]";
            const string unknownUserAccount = "[email protected]";

            var principalContext = new PrincipalContext(ContextType.Domain, activeDirectoryServer, activeDirectoryLogin, activeDirectoryPassword);

            // .NET 4.0 - First attempt with a valid account finds the user
            // .NET 4.5 - First attempt with a valid account fails with a PrincipalOperationException
            TestFindByIdentity(principalContext, validUserAccount, "Valid Account - First Attempt");
            // Second attempt with a valid account finds the user
            TestFindByIdentity(principalContext, validUserAccount, "Valid Account - Second Attempt");
            // First attempt with an unknown account does not find the user
            TestFindByIdentity(principalContext, unknownUserAccount, "Unknown Account - First Attempt");
            // Second attempt with an unknown account does not find the user (testing false positive)
            TestFindByIdentity(principalContext, unknownUserAccount, "Unknown Account - Second Attempt");
            // Subsequent attempt with a valid account still finds the user
            TestFindByIdentity(principalContext, validUserAccount, "Valid Account - Third Attempt");
        }

        private static void TestFindByIdentity(PrincipalContext principalContext, string userAccountToTest, string message)
        {
            var exceptionThrown = false;
            var userFound = false;
            try
            {
                using (var user = UserPrincipal.FindByIdentity(principalContext, IdentityType.UserPrincipalName, userAccountToTest))
                {
                    userFound = (user != null);
                }
            }
            catch (PrincipalOperationException)
            {
                exceptionThrown = true;
            }
            Console.Out.WriteLine(message + " - Exception Thrown  = {0}", exceptionThrown);
            Console.Out.WriteLine(message + " - User Found = {1}", userAccountToTest, userFound);
        }
    }
}

.NET 4.0出力

Valid Account - First Attempt - Exception Thrown  = False
Valid Account - First Attempt - User Found = True
Valid Account - Second Attempt - Exception Thrown  = False
Valid Account - Second Attempt - User Found = True
Unknown Account - First Attempt - Exception Thrown  = False
Unknown Account - First Attempt - User Found = False
Unknown Account - Second Attempt - Exception Thrown  = False
Unknown Account - Second Attempt - User Found = False
Valid Account - Third Attempt - Exception Thrown  = False
Valid Account - Third Attempt - User Found = True

.NET 4.5出力

Valid Account - First Attempt - Exception Thrown  = True
Valid Account - First Attempt - User Found = False
Valid Account - Second Attempt - Exception Thrown  = False
Valid Account - Second Attempt - User Found = True
Unknown Account - First Attempt - Exception Thrown  = False
Unknown Account - First Attempt - User Found = False
Unknown Account - Second Attempt - Exception Thrown  = False
Unknown Account - Second Attempt - User Found = False
Valid Account - Third Attempt - Exception Thrown  = False
Valid Account - Third Attempt - User Found = True
29

まったく同じ問題が発生しています(クロスドメインクエリが4.5への更新で失敗する)-既存の(4.0)コードが壊れるので、これをバグと見なします。

ただし、それを機能させるために、(現在)失敗しているクライアントの1つを見てみると、失敗しているSRVレコードに対するDNS要求の束が次の形式であることに気付きました。

_ldap._tcp.MYSERVER1.mydomain.com,INet,Srv
_ldap._tcp.dc._msdcs.mydomain.com,INet,Srv

DNSサーバー(障害が発生したクライアントが使用するDNS)を変更して、ドメイン上のDCの1つへのすべてのmydomain.comトラフィックの転送ゾーンを設定することで、問題は解決しました。

Nslookupを使用すると、以前(失敗したとき)から現在(動作中)までの動作は、これらのクエリが "存在しないドメイン"を返す前に "*サービスの場所なし(SRV)"を返すことでした。 ...で利用可能なレコード。障害点は、SRVレコードが欠落しているのではなく、ドメインが存在しないと認識されているようです。うまくいけば、MSがこの動作を元に戻しますが、それまでの間、障害のあるクライアントのDNSを制御できれば、DNSフォワードゾーンを作成できるかもしれません。

12
bkr

OP(および返信を手伝ってくれた他の人)には、まったく同じ問題があります(ありました)。私たちの開発環境では、インストールされたVS2012とアプリが実行時にログイン中に破損しました(上記で指摘したADの問題)。それで私は私のシステムを拭いて2010を使い続けましたが、その間ずっと、2012がなんとすばらしいかについての新しいブログ投稿を読むたびに涙を流していました。

スコット・ハンセルマンのおかげでこのスレッドを見つけました。開発ボックスにVMをインストールし、Windows 8開発者の90日間のプレビューとVS2012をインストールしました。アプリケーションを起動して実行すると、すぐにログインADの障害が発生しました。FindByIdentityをトライキャッチして、最初のキャッチの後で再試行するように強制しました-そしてビオラは機能します!!だから、その小さなトリックを理解した人に感謝します!!

つまり、マイナーな修正であり、ローカル開発で機能する「ハック」であり、4.5を本番環境ですぐにリリースする予定がないため、本番環境に影響を与えることはありません。

ただし、ローカルでの欠点は、ログインが2010年で実行した場合の数秒から数秒になりました:(

私が実際に状況を解決するために何を提供できるかは本当にわかりませんが、それでも大きな問題であるように思われるので、とにかく私が2セントを共有していると考えました。

5
jkat98

.netフレームワークを4.0から4.5にアップグレードした後も同じ問題がありました。

0
nefarel