修改用户无法 (LDAP 提供程序更改密码)

05/31/2018

本文内容

用户更改自己的密码的能力是可以授予或拒绝的权限。 若要拒绝此权限,请在安全描述符中设置两个 Ace (DACL) 用户对象的 DACL,并使用 ADS _ ACETYPE _ access _ 拒绝的 _ 对象 ace 类型。 一个 ACE 拒绝用户的权限,另一个 ACE 拒绝 Everyone 组的权限。 这两个 Ace 都是特定于对象的 deny Ace,用于指定更改密码的扩展权限的 GUID。 若要授予此权限,请使用 广告 _ ACETYPE _ 访问 _ 允许的 _ 对象 ace 类型设置相同的 ace。

下面的过程描述如何修改或添加此权限的 Ace。

修改或添加此权限的 Ace

绑定到用户对象。

从用户对象的 ntSecurityDescriptor 属性获取 IADsSecurityDescriptor对象。

枚举对象的 Ace,并搜索具有更改密码 GUID ( {AB721A53-1E2F-11D0-9819-00AA0040529B} ) 用于 IADsAccessControlEntry属性的 ace,并搜索 IADsAccessControlEntry 属性的 "Everyone" 或 "NT 核证机关 \ "。

备注

根据域中第一个域控制器的语言,"Everyone" 和 "NT 机关 \ SELF" 字符串进行了本地化。 因此,不应直接使用这些字符串。 应在运行时获取帐户名称,方法是调用 LookupAccountSid 函数,该函数的 SID 为 "Everyone" ( "s-1-1-0" ) 和 "NT 核证 \ SELF" ( "S-1-5-10" ) 众所周知的安全主体。 读取用户中所示的 GetSidAccountName、 GetSidAccountName _ Everyone 和 _ GetSidAccountName) 示例函数 无法 (LDAP 提供程序更改密码 演示如何执行此操作。

如果用户可以更改其密码,请修改找到的 _ AceType _ 访问 _ 被拒对象的访问被拒 _ 对象 的 ace 的 IADsAccessControlEntry. AceType属性。 _ _ _ _

如果找不到 "Everyone" ACE,则创建一个新的 IADsAccessControlEntry 对象,其中包含下表中显示的属性值,并使用 IADsAccessControlList. AddAce 方法将新条目添加到 ACL。

如果找不到 "NT 核证 \ 己方" ACE,则创建一个新的 IADsAccessControlEntry 对象,该对象具有下表中所示的相同属性值,但 受托者 属性包含 SID "S-1-5-10" ( "NT 核证 \ SELF" ) 的帐户名称。 将条目添加到 ACL 中,并提供 IADsAccessControlList. AddAce 方法。

若要更新对象的 ntSecurityDescriptor 属性,请在步骤2中获取的相同 IADsSecurityDescriptor上调用 IADs方法。

通过 IADs. SetInfo 方法将本地更改提交到服务器。

如果创建了任何一个 Ace,则必须重新排列 ACL 的顺序,以使 Ace 按正确的顺序排列。 为此,请调用具有对象的 LDAP ADsPath 的 GetNamedSecurityInfo 函数,然后调用具有相同 DACL 的 SetNamedSecurityInfo 函数。 添加 Ace 后,将自动进行此重新排序。

IADsAccessControlEntry 属性

广告 _ 右 _ DS _ 控制 _ 访问

广告 _如果用户可以更改其密码,则为 ACETYPE _ 访问 _ 被拒绝的 _ 对象 ; 如果用户可以更改其密码,则为 _ ACETYPE _ 访问 _ 允许的 _ 对象 。

广告 _ 标志 _ 对象 _ 类型 _ 存在

"{AB721A53-1E2F-11D0-9819-00AA0040529B}",它是以字符串形式更改密码 GUID 的。

SID "S-1-1-0" (每个人) 的帐户名称。

示例代码

下面的代码示例演示如何获取用于更改 DACL 的接口。 可以通过设置 "广告 _ 安全 _ 信息" _ DACL 选项来使用 IADsObjectOptions接口。

备注

若要使用本示例中所述的代码,你将需要是管理员。 如果您不是管理员,则需要添加更多的代码,这些代码将使用一个接口,该接口允许用户更改将客户端缓存刷新回 Active Directory 域服务的方式。

//
// Obtain an IADsObjectOptions interface from the object whose
// DACL you wish to modify.
//
long CanReadSetDACL = ADS_SECURITY_INFO_DACL;
CComPtr spObjOps;
hr = pads->QueryInterface(IID_IADsObjectOptions, (void**)&spObjOps);
if(SUCCEEDED(hr))
{
//
// Set the option mask you want to change. In this case
// we want to change the objects security information, so we'll
// use the ADS_OPTION_SECURITY_MASK. Since we want to modify the
// DACL, we'll set the variant to ADS_SEDCURITY_INFO_DACL flag.
//
CComVariant svar;
svar = CanReadSetDACL;
hr = spObjOps->SetOption(ADS_OPTION_SECURITY_MASK, svar);
}
//
// The smart pointer declared for pObjOptions can be released
// or it will be destroyed and released once the pointer goes
// out of scope.
//

下面的代码示例演示如何使用 LDAP 提供程序修改用户不能更改密码的权限。 此代码示例使用上面定义的 GetObjectACE 实用工具函数。

此示例使用 GetSidAccountName 中显示的 " _ 所有人" 和 "GetSidAccountName (" 的 _ 自 c + + 示例函数) 。

#include 
#define CHANGE_PASSWORD_GUID_W L"{AB721A53-1E2F-11D0-9819-00AA0040529B}"
/***************************************************************************
CreateACE()
Creates an ACE and returns the IDispatch pointer for the ACE. This
pointer can be passed directly to IADsAccessControlList::AddAce.
bstrTrustee - Contains the Trustee for the ACE.
bstrObjectType - Contains the ObjectType for the ACE.
lAccessMask - Contains the AccessMask for the ACE.
lACEType - Contains the ACEType for the ACE.
lACEFlags - Contains the ACEFlags for the ACE.
lFlags - Contains the Flags for the ACE.
***************************************************************************/
IDispatch* CreateACE(BSTR bstrTrustee,
BSTR bstrObjectType,
long lAccessMask,
long lACEType,
long lACEFlags,
long lFlags)
{
HRESULT hr;
IDispatch *pDisp = NULL;
IADsAccessControlEntry *pACE = NULL;
hr = CoCreateInstance(CLSID_AccessControlEntry,
NULL,
CLSCTX_INPROC_SERVER,
IID_IADsAccessControlEntry,
(void**)&pACE);
if(SUCCEEDED(hr))
{
hr = pACE->put_Trustee(bstrTrustee);
hr = pACE->put_ObjectType(bstrObjectType);
hr = pACE->put_AccessMask(lAccessMask);
hr = pACE->put_AceType(lACEType);
hr = pACE->put_AceFlags(lACEFlags);
hr = pACE->put_Flags(lFlags);
hr = pACE->QueryInterface(IID_IDispatch, (LPVOID*)&pDisp);
pACE->Release();
}
return pDisp;
}
/***************************************************************************
ReorderACEs()
Causes the ACEs of an object DACL to be reordered properly. The ACEs are
automatically put in the proper order when they are added to the DACL.
On older systems however, this does not occur automatically, so this
function is necessary so the deny ACEs are ordered in the list before
the allow ACEs.
pwszDN - A null-terminated Unicode string that contains the LDAP
ADsPath of the DS object to reorder the DACL for.
***************************************************************************/
HRESULT ReorderACEs(LPCWSTR pwszDN)
{
HRESULT hr = E_FAIL;
DWORD dwResult;
ACL *pdacl;
PSECURITY_DESCRIPTOR psd;
dwResult = GetNamedSecurityInfoW( (LPWSTR)pwszDN,
SE_DS_OBJECT_ALL,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
&pdacl,
NULL,
&psd);
if(ERROR_SUCCESS == dwResult)
{
dwResult = SetNamedSecurityInfoW( (LPWSTR)pwszDN,
SE_DS_OBJECT_ALL,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
pdacl,
NULL);
LocalFree(psd);
if(ERROR_SUCCESS == dwResult)
{
hr = S_OK;
}
}
return hr;
}
/***************************************************************************
SetUserCannotChangePassword()
Sets the "User Cannot Change Password" permission using the LDAP provider
to the specified setting. To do this, find the existing
ACEs and modify the AceType. If the ACE is not found, a new one of the
proper type is created and added. The ACEs should always be present, but
it is possible that the default DACL excludes them, so this situation
will be handled correctly.
pwszUserDN - A null-terminated Unicode string that contains the LDAP
ADsPath of the user object to modify.
pwszUsername - A null-terminated Unicode string that contains the user
name to use for authorization. If this is NULL, the credentials of the
current user are used.
pwszPassword - A null-terminated Unicode string that contains the
password to use for authorization. This is ignored if pwszUsername is
NULL.
fCannotChangePassword - Contains the new setting for the privilege.
Contains nonzero if the user cannot change their password or zero if
the can change their password.
***************************************************************************/
HRESULT SetUserCannotChangePassword(LPCWSTR pwszUserDN,
LPCWSTR pwszUsername,
LPCWSTR pwszPassword,
BOOL fCannotChangePassword)
{
HRESULT hr;
CComBSTR sbstrEveryone;
hr = GetSidAccountName_Everyone(&sbstrEveryone);
if(FAILED(hr))
{
return hr;
}
CComBSTR sbstrSelf;
hr = GetSidAccountName_Self(&sbstrSelf);
if(FAILED(hr))
{
return hr;
}
if(NULL == pwszUserDN)
{
return E_INVALIDARG;
}
IADs *pads;
hr = ADsOpenObject( pwszUserDN,
pwszUsername,
pwszPassword,
ADS_SECURE_AUTHENTICATION,
IID_IADs,
(LPVOID*)&pads);
if(SUCCEEDED(hr))
{
CComBSTR sbstrSecDesc = "ntSecurityDescriptor";
CComVariant svar;
hr = pads->Get(sbstrSecDesc, &svar);
if(SUCCEEDED(hr))
{
IADsSecurityDescriptor *psd;
hr = svar.pdispVal->QueryInterface(IID_IADsSecurityDescriptor, (LPVOID*)&psd);
if(SUCCEEDED(hr))
{
IDispatch *pDisp;
hr = psd->get_DiscretionaryAcl(&pDisp);
if(SUCCEEDED(hr))
{
IADsAccessControlList *pACL;
hr = pDisp->QueryInterface(IID_IADsAccessControlList, (void**)&pACL);
if(SUCCEEDED(hr))
{
BOOL fMustReorder = FALSE;
/*
Get the existing ACE for the change password permission
for Everyone. If it exists, just modify the existing
ACE. If it does not exist, create a new one and add it
to the ACL.
*/
IADsAccessControlEntry *pACEEveryone = NULL;
hr = GetObjectACE(pACL, CHANGE_PASSWORD_GUID_W, sbstrEveryone, &pACEEveryone);
if(pACEEveryone)
{
hr = pACEEveryone->put_AceType(fCannotChangePassword ?
ADS_ACETYPE_ACCESS_DENIED_OBJECT :
ADS_ACETYPE_ACCESS_ALLOWED_OBJECT);
pACEEveryone->Release();
}
else
{
IDispatch *pDispEveryone = NULL;
pDispEveryone = CreateACE(sbstrEveryone,
CComBSTR(CHANGE_PASSWORD_GUID_W),
ADS_RIGHT_DS_CONTROL_ACCESS,
fCannotChangePassword ?
ADS_ACETYPE_ACCESS_DENIED_OBJECT :
ADS_ACETYPE_ACCESS_ALLOWED_OBJECT,
0,
ADS_FLAG_OBJECT_TYPE_PRESENT);
if(pDispEveryone)
{
//add the new ACE for everyone
hr = pACL->AddAce(pDispEveryone);
pDispEveryone->Release();
fMustReorder = TRUE;
}
}
/*
Get the existing ACE for the change password permission
for Self. If it exists, just modify the existing
ACE. If it does not exist, create a new one and add it
to the ACL.
*/
IADsAccessControlEntry *pACESelf = NULL;
hr = GetObjectACE(pACL, CHANGE_PASSWORD_GUID_W, sbstrSelf, &pACESelf);
if(pACESelf)
{
hr = pACESelf->put_AceType(fCannotChangePassword ?
ADS_ACETYPE_ACCESS_DENIED_OBJECT :
ADS_ACETYPE_ACCESS_ALLOWED_OBJECT);
pACESelf->Release();
}
else
{
IDispatch *pDispSelf = NULL;
pDispSelf = CreateACE(sbstrSelf,
CComBSTR(CHANGE_PASSWORD_GUID_W),
ADS_RIGHT_DS_CONTROL_ACCESS,
fCannotChangePassword ?
ADS_ACETYPE_ACCESS_DENIED_OBJECT :
ADS_ACETYPE_ACCESS_ALLOWED_OBJECT,
0,
ADS_FLAG_OBJECT_TYPE_PRESENT);
if(pDispSelf)
{
//add the new ACE for self
hr = pACL->AddAce(pDispSelf);
pDispSelf->Release();
fMustReorder = TRUE;
}
}
//update the security descriptor property
hr = pads->Put(sbstrSecDesc, svar);
//commit the changes
hr = pads->SetInfo();
if(fMustReorder)
{
ReorderACEs(pwszUserDN);
}
pACL->Release();
}
pDisp->Release();
}
psd->Release();
}
}
}
return hr;
}

下面的代码示例演示如何使用 LDAP 提供程序修改用户不能更改密码的权限。

备注

下面的示例仅适用于主要语言为英语的域,因为根据 \ 域中的第一个域控制器的语言,"Everyone" 和 "NT 机关 SELF" 字符串将进行本地化。 无法在 Visual Basic 获取已知安全主体的帐户名称,而无需调用 LookupAccountSid 函数。 如果使用 Visual Basic,则建议使用 WinNT 提供程序修改 "用户无法更改密码" 权限,如 " 修改用户无法更改密码 (WinNT 提供程序)中所示。

'******************************************************************************

'
' SetUserCannotChangePassword
'
' Sets the "User Cannot Change Password" permission using the LDAP provider
' to the specified setting. This is accomplished by finding the existing
' ACEs and modifying the AceType. The ACEs should always be present, but
' it is possible that the default DACL excludes them. This function will not
' work correctly if both ACEs are not present.
'
' strUserDN - A string that contains the LDAP ADsPath of the user object to
' modify.
'
' strUsername - A string that contains the user name to use for
' authorization. If this is an empty string, the credentials of the current
' user are used.
'
' strPassword - A string that contains the password to use for authorization.
' This is ignored if strUsername is empty.
'
' fCannotChangePassword - Contains the new setting for the privilege.
' Contains True if the user cannot change their password or False if
' the can change their password.
'
'******************************************************************************
Sub SetUserCannotChangePassword(strUserDN As String, strUsername As String, strPassword As String, fUserCannotChangePassword As Boolean)
Dim oUser As IADs
Dim oSecDesc As IADsSecurityDescriptor
Dim oACL As IADsAccessControlList
Dim oACE As IADsAccessControlEntry
fEveryone = False
fSelf = False
If "" <> strUsername Then
Dim dso As IADsOpenDSObject
' Bind to the group with the specified user name and password.
Set dso = GetObject("LDAP:")
Set oUser = dso.OpenDSObject(strUserDN, strUsername, strPassword, 1)
Else
' Bind to the group with the current credentials.
Set oUser = GetObject(strUserDN)
End If
Set oSecDesc = oUser.Get("ntSecurityDescriptor")
Set oACL = oSecDesc.DiscretionaryAcl
' Modify the existing entries.
For Each oACE In oACL
If UCase(oACE.ObjectType) = UCase(CHANGE_PASSWORD_GUID) Then
If oACE.Trustee = "Everyone" Then
' Modify the ace type of the entry.
If fUserCannotChangePassword Then
oACE.AceType = ADS_ACETYPE_ACCESS_DENIED_OBJECT
Else
oACE.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
End If
End If
If oACE.Trustee = "NT AUTHORITY\SELF" Then
' Modify the ace type of the entry.
If fUserCannotChangePassword Then
oACE.AceType = ADS_ACETYPE_ACCESS_DENIED_OBJECT
Else
oACE.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
End If
End If
End If
Next
' Update the ntSecurityDescriptor property.
oUser.Put "ntSecurityDescriptor", oSecDesc
' Commit the changes to the server.
oUser.SetInfo
End Sub