有没有办法让 UserPrincipal.GetGroups() 和 UserPrincipal.GetAuthorizationGroups() 调用使用 LDAPS(端口 636)而不是 LDAP(端口 389)?
Is there a way to make UserPrincipal.GetGroups() and UserPrincipal.GetAuthorizationGroups() calls use LDAPS (port 636) instead of LDAP (port 389)?
我们正在为 Microsoft 的 3 月 AD 更新做准备,以仅允许使用 LDAPS 进行安全调用,在检查我们的 .Net 代码时,我发现出现了对 UserPrincipal.GetGroups() 和 UserPrincipal.GetAuthorizationGroups() 的调用使用 LDAP(端口 389)而不是 LDAPS(端口 636),即使 UserPrincipal 对象是使用在 LDAPS 上建立的 PrincipalContext 创建的,如下所示:
// Explicitly using LDAPS (port 636)
PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, "our.corpdomain.com:636", "DC=our,DC=corpdomain,DC=com", ContextOptions.Negotiate);
UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(principalContext, "someuser");
// These calls still use LDAP (port 389)
var groups = userPrincipal.GetAuthorizationGroups();
var groups2 = userPrincipal.GetGroups();
有谁知道为什么会发生这种情况,如果是这样,如何强制这些调用使用 LDAPS?如果不能强制,是否有任何解决方法?
这肯定是 .NET 代码中的错误,我会回答你的问题,但正如我在你的其他问题中提到的那样,三月更新不会 "only allow secure calls using LDAPS"。更新后,正常的 LDAP 端口 389 仍然可以使用。我没有看到任何证据表明他们曾计划禁用它。
但是如果你想确保它永远不会使用端口 389,你就不能不使用 UserPrincipal
。直接用DirectoryEntry
and/or DirectorySearcher
,反正后台用的就是UserPrincipal
。这不是 AccountManagement
命名空间中的 the first bug I've found。
我写了一篇关于finding all of a user's groups的文章,里面有一些针对不同场景的示例代码。您必须修改任何创建新 DirectoryEntry
对象并指定端口 636 的情况,如下所示:
new DirectoryEntry("LDAP://example.com:636/CN=whatever,DC=example,DC=com")
如果愿意,您实际上可以省略域名(只需 :636
而不是 example.com:636
)。
我在那篇文章中没有涉及的一个案例相当于GetAuthorizationGroups
,即读取tokenGroups
属性。这会为您提供组的 SID 列表,然后您可以查找该列表以找到组的名称。这是一个可以做到这一点的方法:
private static IEnumerable<string> GetTokenGroups(DirectoryEntry de) {
var groupsFound = 0;
//retrieve only the tokenGroups attribute from the user
de.RefreshCache(new[] {"tokenGroups"});
while (true) {
var tokenGroups = de.Properties["tokenGroups"];
foreach (byte[] groupSidByte in tokenGroups) {
groupsFound++;
var groupSid = new SecurityIdentifier(groupSidByte, 0);
var groupDe = new DirectoryEntry($"LDAP://:{de.Options.PasswordPort}/<SID={groupSid}>");
groupDe.RefreshCache(new[] {"cn"});
yield return (string) groupDe.Properties["cn"].Value;
}
//AD only gives us 1000 or 1500 at a time (depending on the server version)
//so if we've hit that, go see if there are more
if (tokenGroups.Count != 1500 && tokenGroups.Count != 1000) break;
try {
de.RefreshCache(new[] {$"memberOf;range={groupsFound}-*"});
} catch (COMException e) {
if (e.ErrorCode == unchecked((int) 0x80072020)) break; //no more results
throw;
}
}
}
这将使用您用于创建您传入的 DirectoryEntry
对象的任何端口。但是,如果您的环境中有多个域,这将中断。如果您想始终使用端口 636,那么在这种情况下事情会变得复杂。
我们正在为 Microsoft 的 3 月 AD 更新做准备,以仅允许使用 LDAPS 进行安全调用,在检查我们的 .Net 代码时,我发现出现了对 UserPrincipal.GetGroups() 和 UserPrincipal.GetAuthorizationGroups() 的调用使用 LDAP(端口 389)而不是 LDAPS(端口 636),即使 UserPrincipal 对象是使用在 LDAPS 上建立的 PrincipalContext 创建的,如下所示:
// Explicitly using LDAPS (port 636)
PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, "our.corpdomain.com:636", "DC=our,DC=corpdomain,DC=com", ContextOptions.Negotiate);
UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(principalContext, "someuser");
// These calls still use LDAP (port 389)
var groups = userPrincipal.GetAuthorizationGroups();
var groups2 = userPrincipal.GetGroups();
有谁知道为什么会发生这种情况,如果是这样,如何强制这些调用使用 LDAPS?如果不能强制,是否有任何解决方法?
这肯定是 .NET 代码中的错误,我会回答你的问题,但正如我在你的其他问题中提到的那样,三月更新不会 "only allow secure calls using LDAPS"。更新后,正常的 LDAP 端口 389 仍然可以使用。我没有看到任何证据表明他们曾计划禁用它。
但是如果你想确保它永远不会使用端口 389,你就不能不使用 UserPrincipal
。直接用DirectoryEntry
and/or DirectorySearcher
,反正后台用的就是UserPrincipal
。这不是 AccountManagement
命名空间中的 the first bug I've found。
我写了一篇关于finding all of a user's groups的文章,里面有一些针对不同场景的示例代码。您必须修改任何创建新 DirectoryEntry
对象并指定端口 636 的情况,如下所示:
new DirectoryEntry("LDAP://example.com:636/CN=whatever,DC=example,DC=com")
如果愿意,您实际上可以省略域名(只需 :636
而不是 example.com:636
)。
我在那篇文章中没有涉及的一个案例相当于GetAuthorizationGroups
,即读取tokenGroups
属性。这会为您提供组的 SID 列表,然后您可以查找该列表以找到组的名称。这是一个可以做到这一点的方法:
private static IEnumerable<string> GetTokenGroups(DirectoryEntry de) {
var groupsFound = 0;
//retrieve only the tokenGroups attribute from the user
de.RefreshCache(new[] {"tokenGroups"});
while (true) {
var tokenGroups = de.Properties["tokenGroups"];
foreach (byte[] groupSidByte in tokenGroups) {
groupsFound++;
var groupSid = new SecurityIdentifier(groupSidByte, 0);
var groupDe = new DirectoryEntry($"LDAP://:{de.Options.PasswordPort}/<SID={groupSid}>");
groupDe.RefreshCache(new[] {"cn"});
yield return (string) groupDe.Properties["cn"].Value;
}
//AD only gives us 1000 or 1500 at a time (depending on the server version)
//so if we've hit that, go see if there are more
if (tokenGroups.Count != 1500 && tokenGroups.Count != 1000) break;
try {
de.RefreshCache(new[] {$"memberOf;range={groupsFound}-*"});
} catch (COMException e) {
if (e.ErrorCode == unchecked((int) 0x80072020)) break; //no more results
throw;
}
}
}
这将使用您用于创建您传入的 DirectoryEntry
对象的任何端口。但是,如果您的环境中有多个域,这将中断。如果您想始终使用端口 636,那么在这种情况下事情会变得复杂。