C# 如何获取 AD 用户无法从 LDAP 属性 userAccountControl 更改密码 属性?
C# How to get the AD user cannot change the password property from LDAP attribute userAccountControl?
我正在尝试使用 ASP .NET Core 5
中的库 Novell.Directory.Ldap
获取用户帐户控制属性。当我搜索用户属性时,我发现属性名称 userAccountControl
设置为某个数字。搜索解决方案后,我能够找到:
bool isUserActive = false;
bool userMustChangePassword = false;
bool passwordNeverExpires = false;
bool passwordCannotBeChanged = false;
var flags = Convert.ToInt32(attributeSet.GetAttribute("userAccountControl").StringValue);
isUserActive = !Convert.ToBoolean(flags & 0x0002); //1. checks if user is enabled
if ((flags == 66048)) //65536+512
{
passwordNeverExpires = true; //2. Password never expires property
}
long value = Convert.ToInt64(attributeSet.GetAttribute("pwdLastSet").StringValue);
if (value == 0)
{
userMustChangePassword = true; //3. User must change password at next login
}
但我无法弄清楚如何获得 User cannot change password
以及 account is locked
属性?或者如何比较 0x0040
之类的二进制值?请帮忙
编辑:
我尝试了@Gabriel Luci 在 https://www.gabescode.com/active-directory/2019/07/25/nt-security-descriptors.html 中给出的步骤并尝试了以下代码:
var act = attributeSet.GetAttribute("nTSecurityDescriptor").ByteValue;
ADsSecurityUtility secUtility = new ADsSecurityUtility();
IADsSecurityDescriptor convertAttrToSD = (IADsSecurityDescriptor)secUtility.ConvertSecurityDescriptor(act, (int)ADS_SD_FORMAT_ENUM.ADS_SD_FORMAT_RAW, (int)ADS_SD_FORMAT_ENUM.ADS_SD_FORMAT_IID);
var byteArray = (byte[])secUtility.ConvertSecurityDescriptor(
convertAttrToSD,
(int)ADS_SD_FORMAT_ENUM.ADS_SD_FORMAT_IID,
(int)ADS_SD_FORMAT_ENUM.ADS_SD_FORMAT_RAW
);
var security = new CommonSecurityDescriptor(true, true, byteArray, 0);
如果我检查 security
它显示
我不知道去哪里查看用户无法更改密码设置?
编辑 2:
根据@Gabriel Luci 更新的答案,它对我来说是这样的:
var constraints = new LdapSearchConstraints();
constraints.SetControls(new LdapControl("1.2.840.113556.1.4.801", true, new byte[] { 48, 3, 2, 1, 7 }));
var getNtSecurityByteValue=attributeSet.GetAttribute("nTSecurityDescriptor").ByteValue;
var security = new CommonSecurityDescriptor(true, true, getNtSecurityByteValue, 0);
var self = new SecurityIdentifier(WellKnownSidType.SelfSid, null);
var userChangePassword = new Guid("AB721A53-1E2F-11D0-9819-00AA0040529B");
foreach (var ace in security.DiscretionaryAcl)
{
if(ace.GetType().Name == "ObjectAce")
{
ObjectAce objAce = (ObjectAce)ace;
if (objAce.AceType == AceType.AccessDeniedObject && objAce.SecurityIdentifier == self && objAce.ObjectAceType == userChangePassword)
{
cannotChangePassword = true;
break;
}
}
}
userAccountControl
值是一个位标志,这意味着数字的二进制表示中的每一位都是“开”或“关”,具体取决于它是 1 还是 0。所以十进制值没有意义。
当您检查它是否启用时,您已经正确地检查了该值:
isUserActive = !Convert.ToBoolean(flags & 0x0002); //1. checks if user is enabled
同样,您在检查任何其他标志时也应该这样做。每个的值都列在 in the documentation.
当您检查密码是否设置为永不过期时,您是在比较十进制值,这并不总能为您提供正确答案。相反,检查位值:
passwordNeverExpires = Convert.ToBoolean(flags & 0x10000);
类似帐户被锁定:
var accountLocked = Convert.ToBoolean(flags & 0x0010);
因为用户无法更改密码设置,不幸的是,这更困难并且需要读取用户帐户的权限,我从未使用 Novell.Directory.Ldap
库完成过。但我可以尝试为您指明正确的方向。
帐户权限在 nTSecurityDescriptor
属性中。阅读有关如何从该属性获取字节数组的问题:How to read/set NT-Security-Descriptor attributes?
我写了一篇关于如何将字节数组转换成可用格式的文章:Active Directory: Handling NT Security Descriptor attributes。
然后您将寻找选中 'User cannot change password' 复选框时添加的两个权限:
- 拒绝将密码更改为 'Everyone'
- 拒绝将密码更改为 'SELF'
你可能只需要寻找 #2 就可以逃脱。
更新: 我终于亲自尝试了这个。我以前从未使用过 Novell.Directory.Ldap
库,所以这对我来说是新的。
在 的帮助下,我发现您需要为它设置一个 LDAP 控件 return nTSecurityDescriptor
属性:
var constraints = new LdapSearchConstraints();
constraints.SetControls(new LdapControl("1.2.840.113556.1.4.801", true
, new byte[] {48, 3, 2, 1, 7}));
检索对象后,您可以像这样检查权限:
var byteValue = attributeSet.GetAttribute("nTSecurityDescriptor").ByteValue;
var security = new CommonSecurityDescriptor(true, true, byteValue, 0);
var self = new SecurityIdentifier(WellKnownSidType.SelfSid, null);
var userChangePassword = new Guid("AB721A53-1E2F-11D0-9819-00AA0040529B");
var cannotChangePassword = false;
foreach (var ace in (security.DiscretionaryAcl)) {
if (ace is ObjectAce objAce && objAce.AceType == AceType.AccessDeniedObject
&& objAce.SecurityIdentifier == self && objAce.ObjectAceType == userChangePassword) {
cannotChangePassword = true;
break;
}
}
用户更改密码权限的 GUID 取自 Control Access Rights documentation。
请注意,您不需要使用 IADsSecurityDescriptor
,因此您不需要引用 Interop.ActiveDs
。这是因为我们已经将值作为字节数组给出了。
我正在尝试使用 ASP .NET Core 5
中的库 Novell.Directory.Ldap
获取用户帐户控制属性。当我搜索用户属性时,我发现属性名称 userAccountControl
设置为某个数字。搜索解决方案后,我能够找到:
bool isUserActive = false;
bool userMustChangePassword = false;
bool passwordNeverExpires = false;
bool passwordCannotBeChanged = false;
var flags = Convert.ToInt32(attributeSet.GetAttribute("userAccountControl").StringValue);
isUserActive = !Convert.ToBoolean(flags & 0x0002); //1. checks if user is enabled
if ((flags == 66048)) //65536+512
{
passwordNeverExpires = true; //2. Password never expires property
}
long value = Convert.ToInt64(attributeSet.GetAttribute("pwdLastSet").StringValue);
if (value == 0)
{
userMustChangePassword = true; //3. User must change password at next login
}
但我无法弄清楚如何获得 User cannot change password
以及 account is locked
属性?或者如何比较 0x0040
之类的二进制值?请帮忙
编辑:
我尝试了@Gabriel Luci 在 https://www.gabescode.com/active-directory/2019/07/25/nt-security-descriptors.html 中给出的步骤并尝试了以下代码:
var act = attributeSet.GetAttribute("nTSecurityDescriptor").ByteValue;
ADsSecurityUtility secUtility = new ADsSecurityUtility();
IADsSecurityDescriptor convertAttrToSD = (IADsSecurityDescriptor)secUtility.ConvertSecurityDescriptor(act, (int)ADS_SD_FORMAT_ENUM.ADS_SD_FORMAT_RAW, (int)ADS_SD_FORMAT_ENUM.ADS_SD_FORMAT_IID);
var byteArray = (byte[])secUtility.ConvertSecurityDescriptor(
convertAttrToSD,
(int)ADS_SD_FORMAT_ENUM.ADS_SD_FORMAT_IID,
(int)ADS_SD_FORMAT_ENUM.ADS_SD_FORMAT_RAW
);
var security = new CommonSecurityDescriptor(true, true, byteArray, 0);
如果我检查 security
它显示
我不知道去哪里查看用户无法更改密码设置?
编辑 2: 根据@Gabriel Luci 更新的答案,它对我来说是这样的:
var constraints = new LdapSearchConstraints();
constraints.SetControls(new LdapControl("1.2.840.113556.1.4.801", true, new byte[] { 48, 3, 2, 1, 7 }));
var getNtSecurityByteValue=attributeSet.GetAttribute("nTSecurityDescriptor").ByteValue;
var security = new CommonSecurityDescriptor(true, true, getNtSecurityByteValue, 0);
var self = new SecurityIdentifier(WellKnownSidType.SelfSid, null);
var userChangePassword = new Guid("AB721A53-1E2F-11D0-9819-00AA0040529B");
foreach (var ace in security.DiscretionaryAcl)
{
if(ace.GetType().Name == "ObjectAce")
{
ObjectAce objAce = (ObjectAce)ace;
if (objAce.AceType == AceType.AccessDeniedObject && objAce.SecurityIdentifier == self && objAce.ObjectAceType == userChangePassword)
{
cannotChangePassword = true;
break;
}
}
}
userAccountControl
值是一个位标志,这意味着数字的二进制表示中的每一位都是“开”或“关”,具体取决于它是 1 还是 0。所以十进制值没有意义。
当您检查它是否启用时,您已经正确地检查了该值:
isUserActive = !Convert.ToBoolean(flags & 0x0002); //1. checks if user is enabled
同样,您在检查任何其他标志时也应该这样做。每个的值都列在 in the documentation.
当您检查密码是否设置为永不过期时,您是在比较十进制值,这并不总能为您提供正确答案。相反,检查位值:
passwordNeverExpires = Convert.ToBoolean(flags & 0x10000);
类似帐户被锁定:
var accountLocked = Convert.ToBoolean(flags & 0x0010);
因为用户无法更改密码设置,不幸的是,这更困难并且需要读取用户帐户的权限,我从未使用 Novell.Directory.Ldap
库完成过。但我可以尝试为您指明正确的方向。
帐户权限在 nTSecurityDescriptor
属性中。阅读有关如何从该属性获取字节数组的问题:How to read/set NT-Security-Descriptor attributes?
我写了一篇关于如何将字节数组转换成可用格式的文章:Active Directory: Handling NT Security Descriptor attributes。
然后您将寻找选中 'User cannot change password' 复选框时添加的两个权限:
- 拒绝将密码更改为 'Everyone'
- 拒绝将密码更改为 'SELF'
你可能只需要寻找 #2 就可以逃脱。
更新: 我终于亲自尝试了这个。我以前从未使用过 Novell.Directory.Ldap
库,所以这对我来说是新的。
在 nTSecurityDescriptor
属性:
var constraints = new LdapSearchConstraints();
constraints.SetControls(new LdapControl("1.2.840.113556.1.4.801", true
, new byte[] {48, 3, 2, 1, 7}));
检索对象后,您可以像这样检查权限:
var byteValue = attributeSet.GetAttribute("nTSecurityDescriptor").ByteValue;
var security = new CommonSecurityDescriptor(true, true, byteValue, 0);
var self = new SecurityIdentifier(WellKnownSidType.SelfSid, null);
var userChangePassword = new Guid("AB721A53-1E2F-11D0-9819-00AA0040529B");
var cannotChangePassword = false;
foreach (var ace in (security.DiscretionaryAcl)) {
if (ace is ObjectAce objAce && objAce.AceType == AceType.AccessDeniedObject
&& objAce.SecurityIdentifier == self && objAce.ObjectAceType == userChangePassword) {
cannotChangePassword = true;
break;
}
}
用户更改密码权限的 GUID 取自 Control Access Rights documentation。
请注意,您不需要使用 IADsSecurityDescriptor
,因此您不需要引用 Interop.ActiveDs
。这是因为我们已经将值作为字节数组给出了。