Printout Header
RSS Feed

AD Permissions :
(De)Activate the option "User cannot change password"


When you develop scripts in Active Directory environments, sometimes you need to change the permissions of AD objects, even if at first glance it does not look like that. An example of this is to set the option "User can not change password" in the account properties of an Active Directory user account:

AD Users and Computers: Benutzer kann Kennwort nicht ändern

This is not a property which is set to TRUE or FALSE. The check mark visible here is managed in a very classical manner through AD permissions:


Set option "User cannot change password" in your own script

On the command line interface, you could simply perform this command with the system utility DSACLS.EXE on the domain controller (depending on whether you want to activate the option or not):

dsacls "LDAP path of user account" /D "EVERYONE":CA;"Change Password"       <- denies the permission to change password


dsacls "LDAP path of user account" /G "SELF":CA;"Change Password"           <- grants the permission to change password
dsacls "LDAP path of user account" /G "EVERYONE":CA;"Change Password"

The LDAP path of a user looks - for example - like this: cn=Philipp,ou=Dev,dc=ldapexplorer,dc=com.


Attention: You can set the option "User cannot change password" once, but with DSACLS, the existing default grant permission is NOT deleted. In this case, two opposite entries in the ACL of the user exist (of which the "Deny" wins):

DSACLS: Benutzer kann Kennwort nicht ändern


So you cannot replace single ACL entries with DSACLS, you can only replace the entire access list. But this is not desirable for our case.
Man kann mit DSACLS eben nicht einzelne ACL-Einträge ersetzen, sondern nur die gesamte Rechte-Liste, was für unseren Fall jedoch nicht erwünscht ist. So to actually turn the option on and off, we need a separate script with the following approach:

  1. Reads the list of relevant user rights object. Takes all entries EXCEPT those in which "Self" and "EVERYONE" are granted or denied the "Change password" permission.
  2. Builts an appropriate new ACL from the entries which were found.
  3. Adds to this ACL an entry with the "Self" and "EVERYONE" permission to change the password (granted or denied as required).
  4. Writes the new ACL back to the directory.

There is an SelfADSI tuorial article which explains in detailed the internal structure of ACLs and how they can be manipulated: AD Security Descriptors.

Therefor we show "only" the pure script here that can set the option "User cannot change password" for a specific user:

'you have to configure an object and a trustee from you own environment! Set user = GetObject("LDAP://CN=user01,OU=accounts,DC=ldapexplorer,DC=com") '__________________________________________________________________ constants we need Const ADS_REVISION_DS = 4 Const ADS_ACETYPE_ACCESS_DENIED_OBJECT = 6 Const ADS_RIGHT_DS_CONTROL_ACCESS = &H100 Const ADS_FLAG_OBJECT_TYPE_PRESENT = 1 Const GUID_RIGHT_CHANGEPASSWORD = "{AB721A53-1E2F-11D0-9819-00AA0040529B}" Const WKSID_SELF_SDDL = "S-1-5-10" Const WKSID_SELF = "NT AUTHORITY\SELF" Const WKSID_EVERYONE_SDDL = "S-1-1-0" Const WKSID_EVERYONE = "EVERYONE" '__________________________________________________________________ read ACL Set userACL = user.get("nTSecurityDescriptor") Set userDACL = userACL.DiscretionaryAcl WScript.Echo user.distinguishedname & vbCrLf '__________________________________________________________________ built new ACL Set newDACL = CreateObject("AccessControlList") newDACL.AclRevision = ADS_REVISION_DS newDACL.AceCount = 0 Set userAce = CreateObject("AccessControlEntry") For Each userAce In userDACL 'keep all former ACEs with the following properties ' 1) not grant/deny "User cannot set Passwort" permissions ' 2) are not inherited If Not ((UCase(CStr(userAce.ObjectType)) = GUID_RIGHT_CHANGEPASSWORD) And _ ((UCase(CStr(userAce.Trustee)) = WKSID_SELF) Or _ (UCase(CStr(userAce.Trustee)) = WKSID_EVERYONE))) Then If ((userAce.AceFlags And ADS_ACEFLAG_INHERITED_ACE) = 0) Then newDAcl.AddAce userAce End If End If Next '_______________________________ create new ACE entry : Deny Set Passwort for EveryOne Set newAce = CreateObject("AccessControlEntry") newAce.Trustee = WKSID_EVERYONE_SDDL newAce.AceType = ADS_ACETYPE_ACCESS_DENIED_OBJECT newAce.AceFlags = 0 newAce.AccessMask = ADS_RIGHT_DS_CONTROL_ACCESS newACE.Flags = ADS_FLAG_OBJECT_TYPE_PRESENT newACE.ObjectType = GUID_RIGHT_CHANGEPASSWORD newDACL.AddAce newAce '_______________________________ write back new ACL to directory userACL.DiscretionaryAcl = newDACL user.Put "ntSecurityDescriptor", userACL user.SetInfo

Attention: In environments with other OS languages than english the variables WKSID_SELF and WKSID_EVERYONE have to be adjusted accordingly. In german DC environments for example, you have to use "NT-AUTORITÄT\SELBST" and "JEDER" here!

Here comes the version of the script, which in reversion to previously deactivates the option "User cannot change password":

'you have to configure an object and a trustee from you own environment! Set user = GetObject("LDAP://CN=user01,OU=accounts,DC=ldapexplorer,DC=com") '__________________________________________________________________ constants we need Const ADS_REVISION_DS = 4 Const ADS_ACETYPE_ACCESS_ALLOWED_OBJECT = 5 Const ADS_RIGHT_DS_CONTROL_ACCESS = &H100 Const ADS_FLAG_OBJECT_TYPE_PRESENT = 1 Const GUID_RIGHT_CHANGEPASSWORD = "{AB721A53-1E2F-11D0-9819-00AA0040529B}" Const WKSID_SELF_SDDL = "S-1-5-10" Const WKSID_SELF = "NT AUTHORITY\SELF" Const WKSID_EVERYONE_SDDL = "S-1-1-0" Const WKSID_EVERYONE = "EVERYONE" '__________________________________________________________________ read ACL Set userACL = user.get("nTSecurityDescriptor") Set userDACL = userACL.DiscretionaryAcl WScript.Echo user.distinguishedname & vbCrLf '__________________________________________________________________ built new ACL Set newDACL = CreateObject("AccessControlList") newDACL.AclRevision = ADS_REVISION_DS newDACL.AceCount = 0 Set userAce = CreateObject("AccessControlEntry") For Each userAce In userDACL 'keep all former ACEs with the following properties ' 1) not grant/deny "User cannot set Passwort" permissions ' 2) are not inherited If Not ((UCase(CStr(userAce.ObjectType)) = GUID_RIGHT_CHANGEPASSWORD) And _ ((UCase(CStr(userAce.Trustee)) = WKSID_SELF) Or _ (UCase(CStr(userAce.Trustee)) = WKSID_EVERYONE))) Then If ((userAce.AceFlags And ADS_ACEFLAG_INHERITED_ACE) = 0) Then newDAcl.AddAce userAce End If End If Next '_______________________________ create new ACE entry : Grant Set Passwort for EveryOne Set newAce = CreateObject("AccessControlEntry") newAce.Trustee = WKSID_EVERYONE_SDDL newAce.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT newAce.AceFlags = 0 newAce.AccessMask = ADS_RIGHT_DS_CONTROL_ACCESS newACE.Flags = ADS_FLAG_OBJECT_TYPE_PRESENT newACE.ObjectType = GUID_RIGHT_CHANGEPASSWORD newDACL.AddAce newAce '_______________________________ create new ACE entry : Grant Set Passwort for Self Set newAce = CreateObject("AccessControlEntry") newAce.Trustee = WKSID_SELF_SDDL newAce.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT newAce.AceFlags = 0 newAce.AccessMask = ADS_RIGHT_DS_CONTROL_ACCESS newACE.Flags = ADS_FLAG_OBJECT_TYPE_PRESENT newACE.ObjectType = GUID_RIGHT_CHANGEPASSWORD newDACL.AddAce newAce '_______________________________ write back new ACL to directory userACL.DiscretionaryAcl = newDACL user.Put "ntSecurityDescriptor", userACL user.SetInfo

Attention: In environments with other OS languages than english the variables WKSID_SELF and WKSID_EVERYONE have to be adjusted accordingly. In german DC environments for example, you have to use "NT-AUTORITÄT\SELBST" and "JEDER" here!