UserPrincipals.GetAuthorizationGroups Ocorreu um erro (1301) ao enumerar os grupos. Depois de atualizar para o Controlador de Domínio do Servidor 2012

Pesquisa:

Problema semelhante com solução alternativa, mas não solução real para problema existente

Problema semelhante apontando para a atualização do Microsoft End Point como culpado

Os links acima são os mais adequados para o meu problema, eu também tenho visto todas as questões semelhantes listadas pelo Stack Overflow ao criar este post, e apenas as questões acima mencionadas se encheckboxm no meu problema.

Fundo:

Eu tenho usado UserPrincipal.GetAuthorizationGroups para permissions para access de página específica executando o IIS 7.5 no Server 2008 R2 em um site de formulários da web C # .NET 4.0 por 2 anos e meio. Em 15 de maio de 2013, removemos um controlador de domínio primário que estava executando o Server 2008 (não o r2) e o substituímos por um controlador de domínio do Server 2012. No dia seguinte, começamos a receber a exceção listada abaixo.

Eu uso o contexto principal para autenticação de formulários. O handshake username / pass é bem-sucedido e o cookie de autenticação está definido corretamente, mas a chamada de Contexto Principal subsequente que também chama UserPrincipal.GetAuthorizationGroups falha de forma intermitente. Resolvemos alguns problemas com o BPA que apareceram no Controlador de Domínio do Server 2012, mas isso ainda precisa resolver o problema. Eu também institui um cron que é executado em dois servidores separados. Os dois servidores falharão na resolução do SID do grupo em momentos diferentes, embora estejam executando a mesma base de código. (Um ambiente de desenvolvimento e ambiente de produção).

O problema resolve-se temporariamente após a reboot do servidor web, e também no servidor de desenvolvimento ele resolverá após 12 horas de não funcionamento. O servidor de produção normalmente irá parar de funcionar corretamente até a reboot, sem se resolver sozinho.

Neste ponto, estou tentando refinar o cron que direciona controladores de domínio específicos na rede, bem como o novo controlador de domínio e usando a consulta LDAP padrão que está falhando no momento de gerar mais tempos de exceção de destino. Até agora nós descobrimos em um servidor web que não há padrão para os dias em que ele falha, mas ele irá se recuperar em aproximadamente 12 horas. Os resultados mais recentes mostram a falha de resolução do SID do Grupo entre as 8h e as 20h e depois recupera, vários dias depois irá falhar às 20h e recuperar às 8h, depois correr bem por mais 12 horas e falhar novamente. Esperamos ver se é apenas um problema específico de comunicação do servidor ou se é o conjunto inteiro de controladores de domínio.

Exceção:

 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() 

Questão:

Dadas as informações acima, alguém tem alguma idéia do motivo pelo qual o descomissionamento do Windows Server 2008 (não o r2) e a implementação de um novo DC do Server 2012 causaria falha no UserPrincipal.GetAuthorizationGroups com o erro de resolução 1301 SID? Idéias sobre a eliminação de possíveis causas também seriam apreciadas.

Aviso Legal:

Esta é a minha primeira postagem no Stack Overflow, muitas vezes pesquiso aqui, mas não participei de discussões até agora. Perdoe-me se eu deveria ter postado em outro lugar e fique à vontade para apontar passos melhores antes de postar.

ATUALIZAÇÃO 13-JUN-2013:

No dia 12 de junho abordei a possibilidade de itens não descartados causarem o problema. O prazo tem sido muito curto para determinar se o código ajustado resolveu o problema, mas continuarei atualizando enquanto trabalhamos em direção a uma resolução que talvez com alguma sorte alguém aqui possa ajudar.

Código Original

  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 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; } 

Código atualizado

  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 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; } 

Acabei de me deparar com este mesmo problema e as informações que consegui localizar podem ser úteis; como acima, vimos esse problema em que o controlador de domínio está executando o Server 2012 – primeiro com uma implantação do cliente e, em seguida, replicado em nossa própria rede.

Depois de algumas experiências, descobrimos que nosso código rodaria bem no Server 2012, mas atingimos o código de erro 1301 quando o sistema cliente estava executando o Server 2008. As principais informações sobre o que estava acontecendo foram encontradas aqui:

MS blog traduzido do alemão

O hotfix mencionado no link abaixo corrigiu o problema em nosso sistema de teste

SID S-1-18-1 e SID S-1-18-2 não podem ser mapeados

Espero que isso seja útil para alguém! Como muitos notaram este método, a chamada parece bastante frágil e provavelmente analisaremos a implementação de alguma abordagem alternativa antes de abordarmos outros problemas.

Gary

Vivenciamos esse problema quando nossa equipe de infraestrutura colocou o controlador de domínio de 2012 on-line. Também tivemos DCs pré-2012 em vigor e, por isso, enfrentamos o problema de forma intermitente. Chegamos a uma solução que eu queria compartilhar – tem duas partes.

Primeiro de tudo, instale o hotfix mencionado por Gary Hill. Isso resolverá o seguinte problema:

Ocorreu um erro (1301) ao enumerar os grupos. O SID do grupo não pôde ser resolvido.

Nós pensamos que estávamos em casa depois de instalar esse hotfix. No entanto, depois que foi instalado, obtivemos um erro intermitente diferente. Certos grupos que estávamos interrogando tinham uma propriedade nula sAMAccountName . A propriedade real foi preenchida no Active Directory, mas foi incorretamente retornada com um valor nulo pela API. Eu presumo que este é um bug em algum lugar na API do Active Directory, mas eu não sei mais do que isso.

Felizmente, conseguimos resolver o problema alternando para usar a propriedade Name do grupo em vez da propriedade sAMAccountName . Isso funcionou para nós. Acredito que o sAMAccountName seja efetivamente preterido e exista apenas por motivos de compatibilidade com versões anteriores. Sendo esse o caso, parecia uma mudança razoável para fazer.

Eu incluo uma versão reduzida do nosso código GetRolesForUser para demonstrar a mudança no lugar.

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

Espero que ajude.

Aqui está minha solução. Parece funcionar consistentemente bem. Como o problema ocorre ao iterar sobre a coleção, eu uso uma abordagem diferente ao iterar para manipular a exceção sem bloquear a iteração real:

 private string[] GetUserRoles(string Username) { List roles = new List(); 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 groups = user.GetAuthorizationGroups(); int count = groups.Count(); for (int i = 0; i < count; i++) { IEnumerable 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(); } 

Eu experimentei o código de erro 1301 com UserPrincipal.GetAuthorizationGroups enquanto usava um novo domínio de desenvolvimento virtual que continha 2 estações de trabalho e 50 usuários / grupos (muitos dos quais são os internos). Estávamos executando o Windows Server 2012 R2 Essentials com duas estações de trabalho do Windows 8.1 Enterprise associadas ao domínio.

Consegui recursivamente obter uma lista de membros do grupo do usuário usando o seguinte código:

 class ADGroupSearch { List groupNames; public ADGroupSearch() { this.groupNames = new List(); } public List GetGroups() { return this.groupNames; } public void AddGroupName(String groupName) { this.groupNames.Add(groupName); } public List 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 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"); } } 

Eu estou em um ambiente com várias florestas e relações de confiança de domínio. Eu tenho praticamente esse mesmo código em execução em um formulário de site da Web usado para executar pesquisas de grupo de segurança de usuário nos diferentes domínios.

Eu recebo esse erro exato em um dos domínios muito grandes em que a associação ao grupo pode include mais de 50 grupos diferentes. Ele funciona bem em outras florestas de domínios.

Na minha pesquisa eu encontrei um thread que parece não relacionado, mas na verdade tem o mesmo rastreamento de pilha. É para um aplicativo remoto em execução no SBS. O thread menciona que o erro é causado por SIDS insolúveis em um grupo. Eu acredito que estes seriam o que é conhecido como “tombstoned” SIDS no diretório ativo. Veja o tópico aqui .

O encadeamento sugere que localizar os enteruses marcados para exclusão e removê-los dos grupos resolve o problema. É possível que o erro que você está recebendo seja porque os SIDS estão sendo desativados a cada 12 horas por um processo independente não relacionado? Por fim, acredito que isso seja um bug no framework e que o método não deve travar devido a SIDS marcados / não resolvidos.

Boa sorte!

Se alguém estiver interessado, esta é uma versão VB.NET do mesmo código. Poucas coisas você tem que fazer antes que este código funcione

1) Você deve referenciar o assembly System.DirectoryServices
2) Certifique-se de passar a variável “theusername” sem o domínio, portanto, se o seu domínio for “GIS” e seu nome de usuário for “Hussein”, o Windows geralmente o autenticará como GIS \ Hussein. Então você tem que enviar apenas o nome de usuário “Hussein”. Eu trabalhei o material sensível a maiúsculas e minúsculas.
3) O método GetGroupsNew recebe um nome de usuário e retorna uma lista de grupos
4) O método isMemberofnew pega um nome de usuário e um grupo e verifica se esse usuário faz parte desse grupo ou não, esse é o que me interessou.

 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 

tivemos um problema semelhante depois de atualizar o controlador de domínio para 2012. De repente, minha chamada para user.GetAuthorizationGroups () começou a falhar; Eu estava recebendo a mesma exceção que você estava (erro 1301). Então, eu mudei para user.GetGroups (). Isso funcionou por um tempo, depois começou a falhar intermitentemente em “nome de usuário ou senha inválidos”. Minha última solução parece resolvê-lo, pelo menos no momento. Em vez de chamar qualquer um desses, depois de construir o object do usuário, também construo um object de grupo, um para cada grupo que desejo ver se o usuário é um membro. ie, “user.IsMemberOf (group)”. Isso parece funcionar.

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

Eu tive a mesma exceção. Se alguém não quiser usar “LDAP”, use este código. Porque eu tenho grupos nesteds, estou usando GetMembers (true) e é um pouco mais longo que GetMembers ().

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

ou baixe a correção daqui: http://support.microsoft.com/kb/2830145

Enfrentar o mesmo problema ao enumerar grupos de autorização e os patches mencionados na resposta não se aplica ao nosso servidor da web.

Enumerar e ignorar manualmente os grupos que causam problemas está funcionando bem, no entanto:

 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; } } }