web-dev-qa-db-ja.com

UserPrincipals.GetAuthorizationGroupsグループの列挙中にエラー(1301)が発生しました。 Server 2012ドメインコントローラーにアップグレードした後

研究:

回避策のある同様の問題ですが、既存の問題の実際の解決策ではありません

犯人としてのMicrosoftエンドポイントの更新を指す同様の問題

上記のリンクは私の問題に最も適しています。この投稿の作成時にStack Overflowによってリストされた同様の質問をすべて表示しましたが、上記の参照された質問のみが私の問題に当てはまります。

背景:

特定のページアクセスの権限にUserPrincipal.GetAuthorizationGroupsを使用してIIS 7.5をC#.NET 4.0 WebフォームサイトのServer 2008 R2で2年半実行しています。5月15日2013では、r2ではなくServer 2008を実行しているプラ​​イマリドメインコントローラーを削除し、Server 2012ドメインコントローラーに置き換えました。翌日、以下の例外が発生し始めました。

フォーム認証にプリンシパルコンテキストを使用します。ユーザー名/パスハンドシェイクは成功し、認証Cookieは適切に設定されますが、UserPrincipal.GetAuthorizationGroupsも呼び出す後続のプリンシパルコンテキスト呼び出しは断続的に失敗します。 Server 2012ドメインコントローラーで発生したいくつかのBPA問題を解決しましたが、これはまだ問題を解決していません。また、2つの別個のサーバーで実行するcronを導入しました。 2つのサーバーは、同じコードベースを実行していますが、グループSID解決で異なるときに失敗します。 (開発環境と本番環境)。

この問題は、Webサーバーの再起動時に一時的に解決します。また、開発サーバーでは、12時間機能しなくなった後に自動的に解決します。通常、運用サーバーは、それ自体を解決せずに再起動するまで正しく機能しなくなります。

この時点で、ネットワーク内の特定のドメインコントローラーと新しいDCを対象とし、現在より多くの対象となる例外時間を生成するのに失敗している標準LDAPクエリを使用します。 1台のWebサーバーで、障害が発生した日にはパターンがないことがわかりましたが、およそ12時間以内に回復します。最新の結果では、8時から8時の間にグループSIDの解決に失敗し、数日後に回復しますそれは午後8時に失敗し、午前8時に回復し、さらに12時間正常に動作し、再び失敗します。それが特定のサーバー通信の問題であるかどうか、またはそれがドメインコントローラーのセット全体であるかどうかを確認したいと考えています。

例外:

Exception information: 
Exception type: PrincipalOperationException 
Exception message: An error (1301) occurred while enumerating the groups.  
The group's SID could not be resolved.
at System.DirectoryServices.AccountManagement.SidList.TranslateSids(String target, IntPtr[] pSids)
at System.DirectoryServices.AccountManagement.SidList..ctor(SID_AND_ATTR[] sidAndAttr)
at System.DirectoryServices.AccountManagement.AuthZSet..ctor(Byte[] userSid, NetCred credentials, ContextOptions contextOptions, String flatUserAuthority, StoreCtx userStoreCtx, Object userCtxBase)
at System.DirectoryServices.AccountManagement.ADStoreCtx.GetGroupsMemberOfAZ(Principal p)
at System.DirectoryServices.AccountManagement.UserPrincipal.GetAuthorizationGroups()

質問:

上記の情報を前提として、Windows Server 2008(r2ではない)を廃止し、新しいサーバー2012 DCを実装すると、1301 SID解決でUserPrincipal.GetAuthorizationGroupsが失敗する原因となる理由を誰かが知っていますか?エラー?考えられる原因を排除するためのアイデアもいただければ幸いです。

免責事項:

これはStack Overflowへの私の最初の投稿です。私はここで頻繁に調査しますが、これまでは議論に参加していません。他の場所に投稿する必要があった場合はご容赦ください。投稿する前に、より良い手順を自由に指摘してください。

UPDATE 2013年6月13日:

6月12日、私は処分されなかったアイテムが問題を引き起こす可能性に対処しました。時間枠が短すぎたため、調整されたコードで問題が解決したかどうかを判断できませんでしたが、運がよければ誰かが手を貸してくれるような解決策に向けて更新を続けます。

元のコード

    public bool isGroupMember(string userName, ArrayList groupList)
    {
        bool valid = false;

            PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domain_server + ".domain.org:636", null, ContextOptions.Negotiate | ContextOptions.SecureSocketLayer);

            // find the user in the identity store
            UserPrincipal user =
                UserPrincipal.FindByIdentity(
                    ctx,
                    userName);

            // get the groups for the user principal and
            // store the results in a PrincipalSearchResult object
            PrincipalSearchResult<Principal> groups =
                user.GetAuthorizationGroups();

            // display the names of the groups to which the
            // user belongs
            foreach (Principal group in groups)
            {
                foreach (string groupName in groupList)
                {
                    if (group.ToString() == groupName)
                    {
                        valid = true;
                    }
                }

            }
        return valid;
    }

更新されたコード

        public bool isGroupMember(string userName, ArrayList groupList, string domain_server)
        {
        bool valid = false;

            try
            {

                using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domain_server + ".domain.org:636", null, ContextOptions.Negotiate | ContextOptions.SecureSocketLayer))
                {

                    // find the user in the identity store
                    UserPrincipal user =
                        UserPrincipal.FindByIdentity(
                            ctx,
                            userName);

                    try
                    {
                        // get the groups for the user principal and
                        // store the results in a PrincipalSearchResult object
                        using (PrincipalSearchResult<Principal> groups = user.GetAuthorizationGroups())
                        {
                            // display the names of the groups to which the
                            // user belongs

                            foreach (Principal group in groups)
                            {
                                foreach (string groupName in groupList)
                                {

                                    if (group.ToString() == groupName)
                                    {
                                        valid = true;
                                    }
                                }

                                group.Dispose();

                            }
                        }//end using-2
                    }
                    catch
                    {
                        log_gen("arbitrary info");
                        return false;
                    }
                }//end using-1
            }
            catch
            {
                log_gen("arbitrary info");
                return false;
            }

        return valid;

    }
27
Pynt

これと同じ問題が発生しました。追跡できた情報が役立つ場合があります。上記のように、ドメインコントローラーがServer 2012を実行している場合にこの問題が発生しました-最初にお客様の展開を使用し、次に独自のネットワークに複製しました。

いくつかの実験の結果、コードはServer 2012で正常に動作することがわかりましたが、クライアントシステムがServer 2008を実行しているときに1301エラーコードにヒットしました。

ドイツ語から翻訳されたMSブログ

以下のリンクで参照されている修正プログラムにより、テストシステムの問題が修正されました

SID S-1-18-1およびSID S-1-18-2はマップできません

これが誰かに役立つことを願っています!多くの人がこのメソッド呼び出しはかなり壊れやすいように見えるので、他の問題にぶつかる前に別のアプローチを実装することを検討するでしょう。

ゲイリー

18
Gary Hill

これが私の解決策です。それは一貫してうまく機能するようです。コレクションを反復するときに問題が発生するため、実際の反復をブロックせずに例外を処理するために、反復するときに別のアプローチを使用します。

private string[] GetUserRoles(string Username)
{    
    List<string> roles = new List<string>();
    try
    {
        string domain = Username.Contains("\\") ? Username.Substring(0, Username.IndexOf("\\")) : string.Empty;
        string username = Username.Contains("\\") ? Username.Substring(Username.LastIndexOf("\\") + 1) : Username;
        if (!string.IsNullOrEmpty(domain) && !string.IsNullOrEmpty(username))
        {
            PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, domain);
            UserPrincipal user = UserPrincipal.FindByIdentity(principalContext, username);
            if (user != null)
            {
                PrincipalSearchResult<Principal> groups = user.GetAuthorizationGroups();
                int count = groups.Count();
                for (int i = 0; i < count; i++)
                {
                    IEnumerable<Principal> principalCollection = groups.Skip(i).Take(1);
                    Principal principal = null;
                    try
                    {
                        principal = principalCollection.FirstOrDefault();
                    }
                    catch (Exception e)
                    {
                        //Error handling...
                        //Known exception - sometimes AD can't query a particular group, requires server hotfix?
                        //http://support.Microsoft.com/kb/2830145
                    }

                    if (principal!=null && principal is GroupPrincipal)
                    {
                        GroupPrincipal groupPrincipal = (GroupPrincipal)principal;
                        if (groupPrincipal != null && !string.IsNullOrEmpty(groupPrincipal.Name))
                        {
                            roles.Add(groupPrincipal.Name.Trim());
                        }
                    }
                }
            }
        }
    }
    catch (Exception e)
    {
        //Error handling...
    }
    return roles.ToArray();
}
5
AlishahNovin

インフラストラクチャチームが2012ドメインコントローラーをオンラインにしたときに、この問題が発生しました。また、2012年以前のDCが設置されていたため、断続的に問題が発生しました。私は私が共有したかった修正を思いつきました-それは2つの部分を持っています。

まず最初に、Gary Hillが述べた hotfix をインストールします。これにより、次の問題が解決されます。

グループの列挙中にエラー(1301)が発生しました。グループのSIDを解決できませんでした。

私たちは、この修正プログラムをインストールした後、私たちは家から解放されたと思いました。ただし、インストール後に別の断続的なエラーが発生しました。私たちが調査していた特定のグループには、null sAMAccountNameプロパティがありました。実際のプロパティはActive Directoryに入力されましたが、APIによってnull値が誤って返されていました。これはActive Directory APIのどこかのバグだと思いますが、それ以上は知りません。

幸い、Nameプロパティの代わりにグループsAMAccountNameプロパティを使用するように切り替えることで、この問題を回避することができました。これでうまくいきました。 sAMAccountNameは事実上非推奨であり、下位互換性のためにのみ存在すると私は信じています。その場合、それは妥当な変更であるように見えました。

GetRolesForUserコードの簡略版を同封して、変更箇所を示します。

using (var context = new PrincipalContext(ContextType.Domain, _domainName))
{
    try
    {
        var p = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, username);
        if (p == null) throw new NullReferenceException(string.Format("UserPrincipal.FindByIdentity returned null for user: {0}, this can indicate a problem with one or more of the AD controllers", username));

        var groups = p.GetAuthorizationGroups();
        var domain = username.Substring(0, username.IndexOf(@"\", StringComparison.InvariantCultureIgnoreCase)).ToLower();

        foreach (GroupPrincipal group in groups)
        {
            if (!string.IsNullOrEmpty(group.Name))
            {
                var domainGroup = domain + @"\" + group.Name.ToLower();

                if (_groupsToUse.Any(x => x.Equals(domainGroup, StringComparison.InvariantCultureIgnoreCase)))
                {
                    // Go through each application role defined and check if the AD domain group is part of it
                    foreach (string role in roleKeys)
                    {
                        string[] roleMembers = new [] { "role1", "role2" };

                        foreach (string member in roleMembers)
                        {
                            // Check if the domain group is part of the role
                            if (member.ToLower().Contains(domainGroup))
                            {
                                // Cache the Application Role (NOT the AD role)
                                results.Add(role);
                            }
                        }
                    }
                }
            }

            group.Dispose();
        }
    }
    catch (Exception ex)
    {
        throw new ProviderException("Unable to query Active Directory.", ex);
    }
}

お役に立てば幸いです。

4
John Reilly

2つのワークステーションと50のユーザー/グループ(その多くは組み込みのもの)を含む新しい仮想開発ドメインを使用しているときに、UserPrincipal.GetAuthorizationGroupsでエラーコード1301が発生しました。ドメインに参加した2つのWindows 8.1 EnterpriseワークステーションでWindows Server 2012 R2 Essentialsを実行していました。

次のコードを使用して、ユーザーのグループメンバーシップのリストを再帰的に取得できました。

class ADGroupSearch
{
    List<String> groupNames;

    public ADGroupSearch()
    {
        this.groupNames = new List<String>();
    }

    public List<String> GetGroups()
    {
        return this.groupNames;
    }

    public void AddGroupName(String groupName)
    {
        this.groupNames.Add(groupName);
    }

    public List<String> GetListOfGroupsRecursively(String samAcctName)
    {
        PrincipalContext ctx = new PrincipalContext(ContextType.Domain, System.Environment.UserDomainName);
        Principal principal = Principal.FindByIdentity(ctx, IdentityType.SamAccountName, samAcctName);
        if (principal == null)
        {
            return GetGroups();
        }
        else
        {
            PrincipalSearchResult<Principal> searchResults = principal.GetGroups();

            if (searchResults != null)
            {
                foreach (GroupPrincipal sr in searchResults)
                {
                    if (!this.groupNames.Contains(sr.Name))
                    {
                        AddGroupName(sr.Name);
                    }
                    Principal p = Principal.FindByIdentity(ctx, IdentityType.SamAccountName, sr.SamAccountName);

                    try
                    {
                        GetMembersForGroup(p);
                    }
                    catch (Exception ex)
                    {
                        //ignore errors and continue
                    }
                }

            }
            return GetGroups();
        }

    }



    private void GetMembersForGroup(Principal group)
    {
        if (group != null && typeof(GroupPrincipal) == group.GetType())
        {
            GetListOfGroupsRecursively(group.SamAccountName);
        } 
    }

    private bool IsGroup(Principal principal)
    {
        return principal.StructuralObjectClass.ToLower().Equals("group");
    }
}
2
TS12

複数のドメインフォレストと信頼がある環境にいます。私は、さまざまなドメインにわたってユーザーセキュリティグループの検索を実行するために使用されるWebサイトフォームで実行しているこのまったく同じコードをほぼ持っています。

グループメンバーシップに50以上の異なるグループを含めることができる非常に大きなドメインの1つで、この正確なエラーが発生します。他のドメインフォレストでは正常に動作します。

私の調査で、無関係に見えても実際には同じスタックトレースを持つスレッドを見つけました。これは、SBSで実行されているリモートアプリケーション用です。スレッドは、エラーがグループ内の解決できないSIDSによって引き起こされることを述べています。これらは、Active Directoryで「墓石化された」SIDSと呼ばれるものになると思います。 ここのスレッドを参照

スレッドは、墓碑化されたエンターテイを見つけてグループから削除することで問題が解決することを示唆しています。 SIDSが個別の無関係なプロセスによって12時間ごとに廃棄されていることが原因で、エラーが発生している可能性はありますか?結局のところ、これはフレームワークのバグであり、廃棄された/解決できないSIDSが原因でメソッドがクラッシュしないはずです。

幸運を!

1
Bri

誰かが興味を持っている場合、これは同じコードのVB.NETバージョンです。このコードが機能する前に行う必要があるいくつかのこと

1)アセンブリSystem.DirectoryServicesを参照する必要があります
2)ドメインなしで「theusername」変数を渡すようにしてください。ドメインが「GIS」で、ユーザー名が「Hussein」の場合、Windowsは通常、GIS\Husseinとして認証します。したがって、純粋にユーザー名「フセイン」を送信する必要があります。大文字と小文字を区別するものを解決しました。
3)メソッドGetGroupsNewはユーザー名を受け取り、グループのリストを返します
4)メソッドisMemberofnewはユーザー名とグループを取得し、このユーザーがそのグループの一部であるかどうかを確認します。これは私が興味を持っていたものです。

Private Function getGroupsNew(theusername As String) As List(Of String)
    Dim lstGroups As New List(Of String)
    Try

        Dim allDomains = Forest.GetCurrentForest().Domains.Cast(Of Domain)()

        Dim allSearcher = allDomains.[Select](Function(domain)
                                                  Dim searcher As New DirectorySearcher(New DirectoryEntry("LDAP://" + domain.Name))

                                                  searcher.Filter = [String].Format("(&(&(objectCategory=person)(objectClass=user)(userPrincipalName=*{0}*)))", theusername)

                                                  Return searcher

                                              End Function)

        Dim directoryEntriesFound = allSearcher.SelectMany(Function(searcher) searcher.FindAll().Cast(Of SearchResult)().[Select](Function(result) result.GetDirectoryEntry()))

        Dim memberOf = directoryEntriesFound.[Select](Function(entry)
                                                          Using entry
                                                              Return New With { _
                                                               Key .Name = entry.Name, _
                                                               Key .GroupName = DirectCast(entry.Properties("MemberOf").Value, Object()).[Select](Function(obj) obj.ToString()) _
                                                              }
                                                          End Using

                                                      End Function)



        For Each user As Object In memberOf
            For Each groupName As Object In user.GroupName
                lstGroups.Add(groupName)
            Next
        Next

        Return lstGroups

    Catch ex As Exception
        Throw
    End Try
End Function

Private Function isMemberofGroupNew(theusername As String, thegroupname As String) As Boolean

    Try

        Dim lstGroups As List(Of String) = getGroupsNew(theusername)

        For Each sGroup In lstGroups
            If sGroup.ToLower.Contains(thegroupname.ToLower) Then Return True
        Next

        Return False


    Catch ex As Exception
        Throw
    End Try

End Function
1
Hussein Nasser

VPN経由で接続してgroups=UserPrincipal.GetGroups()を使用すると、グループを反復するときに例外が発生するという問題がありました。

誰かがユーザーのすべてのグループを読みたい場合は、次の可能性があります(GetGroups()を使用するよりも高速です)。

private IList<string> GetUserGroupsLDAP(string samAccountName)
{
    var groupList = new List<string>();
    var domainConnection = new DirectoryEntry("LDAP://" + serverName, serverUser, serverUserPassword); // probably you don't need username and password

    var samSearcher = new DirectorySearcher();
    samSearcher.SearchRoot = domainConnection;
    samSearcher.Filter = "(samAccountName=" + samAccountName + ")";

    var samResult = samSearcher.FindOne();
    if (samResult != null)
    {
        var theUser = samResult.GetDirectoryEntry();
        theUser.RefreshCache(new string[] { "tokenGroups" });

        var sidSearcher = new DirectorySearcher();
        sidSearcher.SearchRoot = domainConnection;
        sidSearcher.PropertiesToLoad.Add("name");
        sidSearcher.Filter = CreateFilter(theUser);

        foreach (SearchResult result in sidSearcher.FindAll())
        {
            groupList.Add((string)result.Properties["name"][0]);
        }
    }
    return groupList;
}

private string CreateFilter(DirectoryEntry theUser)
{
    string filter = "(|";
    foreach (byte[] resultBytes in theUser.Properties["tokenGroups"])
    {
        var SID = new SecurityIdentifier(resultBytes, 0);
        filter += "(objectSid=" + SID.Value + ")";
    }
    filter += ")";
    return filter;
}
0
Apfelkuacha

ドメインコントローラーを2012にアップグレードした後、同様の問題が発生しました。突然、user.GetAuthorizationGroups()への呼び出しが失敗し始めました。あなたと同じ例外が発生しました(エラー1301)。そこで、user.GetGroups()に変更しました。それはしばらくの間機能しましたが、その後、「不正なユーザー名またはパスワード」で断続的に失敗し始めました。私の最新の回避策は、少なくとも現時点ではそれを修正しているようです。これらのいずれかを呼び出す代わりに、ユーザーオブジェクトを作成した後、ユーザーがメンバーであるかどうかを確認するグループごとに1つずつ、グループオブジェクトも作成します。つまり、「user.IsMemberOf(group)」です。それはうまくいくようです。

try
{
using (HostingEnvironment.Impersonate())
{
    using (var principalContext = new PrincipalContext(ContextType.Domain, "MYDOMAIN"))
    {
        using (var user = UserPrincipal.FindByIdentity(principalContext, userName))
        {
            if (user == null)
            {
                Log.Debug("UserPrincipal.FindByIdentity failed for userName = " + userName + ", thus not authorized!");
                isAuthorized = false;
            }

            if (isAuthorized)
            {
                firstName = user.GivenName;
                lastName = user.Surname;

                // so this code started failing:

                // var groups = user.GetGroups();
                // adGroups.AddRange(from @group in groups where 
                // @group.Name.ToUpper().Contains("MYSEARCHSTRING") select @group.Name);

                // so the following workaround, which calls, instead, 
                // "user.IsMemberOf(group)", 
                // appears to work (for now at least).  Will monitor for issues.

                // test membership in SuperUsers
                const string superUsersGroupName = "MyApp-SuperUsers";
                using (var superUsers = GroupPrincipal.FindByIdentity(principalContext, superUsersGroupName))
                {
                    if (superUsers != null && user.IsMemberOf(superUsers))
                        // add to the list of groups this user is a member of
                        // then do something with it later
                        adGroups.Add(superUsersGroupName);                                        
                }
0
David Barrows

同じ例外がありました。誰かが「LDAP」を使いたくない場合は、このコードを使用してください。ネストされたグループがあったため、GetMembers(true)を使用していて、GetMembers()よりも少し時間が長くなっています。

https://stackoverflow.com/a/27548271/1857271

またはここから修正をダウンロードします: http://support.Microsoft.com/kb/2830145

0
Nazar Iaremii

承認グループと回答に記載されているパッチを列挙する同じ問題に直面しても、Webサーバーには適用されませんでした。

ただし、問題の原因となっているグループを手動で列挙して無視することはうまくいきます。

private static bool UserIsMember(string usr, string grp)
{
    usr = usr.ToLower();
    grp = grp.ToLower();

    using (var pc = new PrincipalContext(ContextType.Domain, "DOMAIN_NAME"))
    {
        using (var user = UserPrincipal.FindByIdentity(pc, usr))
        {
            var isMember = false;
            var authGroups = user?.GetAuthorizationGroups().GetEnumerator();

            while (authGroups?.MoveNext() ?? false)
            {
                try
                {

                    isMember = authGroups.Current.Name.ToLower().Contains(grp);
                    if (isMember) break;
                }
                catch
                {
                    // ignored
                }
            }

            authGroups?.Dispose();
            return isMember;
        }
    }
}
0
davidmdem