从一开始:
我当前的项目要求我针对 Active Directory 组验证用户。问题是,计算机可能并不总是连接到域,但用户可能仍需要运行该工具。
此时,您必须接受攻击者可以绕过任何强制执行的安全性,因为它完全是在客户端上强制执行的。不完全是解决方案的一部分,但请记住它。
我了解在未连接时无法查询 Active Directory,而是尝试查询机器 SAM (MSAM)。
安全帐户管理器仅存储本地帐户(MACHINENAME\Administrator 等)。它没有域用户凭据。您正在考虑LSA 缓存,它会记住最后 N 个域登录的凭据(其中 N 是由组策略配置的从 0 到 50 的数字)和最后 N 个 SID 到名称的映射(默认为 128,可通过注册表配置)。安全帐户管理器仅将域帐户存储在域控制器上。
当我与网络断开连接时,我无法确定当前用户。这是我正在使用的: PrincipalContext principalctx = new PrincipalContext(ContextType.Machine); UserPrincipal uprincipal = new UserPrincipal(principalctx);
另外,有人可以描述三个 ContextType 选项 ApplicationDirectory、Domain、Machine。恐怕我不完全理解每个选项,因此可能会错误地使用它。
如上所述,信息不会被缓存,但 ContextType 枚举可以描述如下:
来自 MSDN:
- 域:域存储。这表示 AD DS 存储。(正如它所说,这是针对域帐户的,如 Active Directory 的 LDAP 目录访问 - 这需要网络访问)
- ApplicationDirectory:应用程序目录存储。这代表 AD LDS 存储。(AD 轻量级目录服务(以前称为 ADAM)是 Active Directory 的较小版本,旨在存储单个应用程序的凭据。它与本次讨论无关,但也使用 LDAP。)
- 机器:电脑商店。这代表 SAM 存储。(仅枚举本地帐户)
从这一点来说,我如何询问当前谁登录到本地机器。
您始终可以通过调用来检查已登录的用户WindowsIdentity.GetCurrent()
。如果登录发生在脱机时,这将返回登录用户的 SID 和组 SID,可能已缓存。
连接到域后,我可以使用 UserPrincipal.Current 进行查询。如果我没有连接到域,它将失败说“无法联系服务器”。注意:使用上面的代码这个方法是不可用的,我可以放弃 PrincipalContext 直接查询当前用户。识别出当前用户后,我可以查询 GetGroups() 并确定他们是否在所需的组之一中。
要确定组成员身份,请检查您想要的组的 SID 是否在返回的标识中WindowsIdentity.GetCurrent
。如果您没有在访问控制系统中使用 SID,您可以通过调用SecurityIdentifier.Translate将名称转换为 SID 。您需要在线翻译它,然后将其缓存以供离线使用。它可以以字符串或二进制形式存储,因此两者都非常适合注册表。
// while we are online, translate the Group to SID
// Obviously, administrators would be a bad example as it is a well known SID...
var admins = new NTAccount("Administrators");
var adminSid = (SecurityIdentifier)admins.Translate(typeof(SecurityIdentifier));
// store the sid as a byte array on disk somewhere
byte[] adminSIDbytes = new byte[adminSid.BinaryLength];
adminSid.GetBinaryForm(adminSIDbytes, 0);
// at time of check, retrieve the sid and check membership
var sidToCheck = new SecurityIdentifier(adminSIDbytes, 0);
if (!wi.Groups.Contains(sidToCheck))
throw new UnauthorizedAccessException("User is not a member of required group");