|
|
|||||||
back in the day, I created an UDF that beautifully took care of "user cannot change password" flag in my AD. http://www.kixtart.org/forums/ubbthreads.php?ubb=showflat&Number=207611 well, not any longer. I tried it as it is, as per MS documentation here: https://msdn.microsoft.com/en-us/library/aa746398%28v=vs.85%29.aspx and with removeAce() like here: http://activexperts.com/network-monitor/windowsmanagement/scripts/activedirectory/user/passwords/ but still no go, it just keeps failing. Anyone have any working code or insight what I need to change to make it fly? First year I ran the code, I ran it as domain admin. But last year I could swear it worked fine as account operator. This year, no matter what I do, I keep ending with error 9 on setinfo line. I do not get it. After fighting with it for days, I would welcome any help I can get to get it working again. Today after clicking manually on few hundred accounts to untick that option, I can tell you my wrist hurts. |
||||||||
|
|
|||||||
Is this what you are referring to... I see some similarities I guess Code: Const ADS_ACETYPE_ACCESS_DENIED_OBJECT = &H6 Const ADS_ACEFLAG_OBJECT_TYPE_PRESENT = &H1 Const CHANGE_PASSWORD_GUID = "{ab721a53-1e2f-11d0-9819-00aa0040529b}" Const ADS_RIGHT_DS_CONTROL_ACCESS = &H100 Set objUser = GetObject _ ("LDAP://cn=myerken,ou=management,dc=fabrikam,dc=com") Set objSD = objUser.Get("ntSecurityDescriptor") Set objDACL = objSD.DiscretionaryAcl arrTrustees = array("nt authority\self", "EVERYONE") For Each strTrustee in arrTrustees Set objACE = CreateObject("AccessControlEntry") objACE.Trustee = strTrustee objACE.AceFlags = 0 objACE.AceType = ADS_ACETYPE_ACCESS_DENIED_OBJECT objACE.Flags = ADS_ACEFLAG_OBJECT_TYPE_PRESENT objACE.ObjectType = CHANGE_PASSWORD_GUID objACE.AccessMask = ADS_RIGHT_DS_CONTROL_ACCESS objDACL.AddAce objACE Next objSD.DiscretionaryAcl = objDACL objUser.Put "nTSecurityDescriptor", objSD objUser. SetInfo |
||||||||
|
|
|||||||
Nope. I guess I wasn't too clear. My code is a translation of Microsoft code and that msdn link shows the original. |
||||||||
|
|
|||||||
It might be that you have more Aces then the 2 you are specifying (previous admins who don't know what they're doing etc.) If so, keep in mind that you have to reorder the Aces before you put the DACL back. Deny aces go first, then just LIFO (Last in first out). |
||||||||
|
|
|||||||
By the way, your function works correctly on a 2008 R2 functional domain. |
||||||||
|
|
|||||||
I wonder... I added some 2012r2 DCs this summer.... |
||||||||
|
|
|||||||
The second link you provided has the code I pasted above, and purports to do what you want. |
||||||||
|
|
|||||||
2012 R2 doesn't matter, your domain functional level matters. This is what changes the AD Schema. |
||||||||
|
|
|||||||
Allen, second link? maybe, but my second link has removeace which is different from addace. maybe removing requires you to add another for allow, but ... I guess I need to try that. Arend, my func is still on 2003 as the last 2003 DCs got removed this summer, so that side can't be what changed. edit, ok, my second link is actually the UDF code. now I am confused. |
||||||||
|
|
|||||||
Second link after your function... come on man get with the program |
||||||||
|
|
|||||||
second link after the function, so the last link? ok, the first link has the UDF. the second link has the c++ and VB versions of my code and the third, second after the UDF, or the third link, has the removeAce() version. I have never tried addAce() but I know ntfsperms() UDF didn't work in my domain either and I still have to resort to icacls. |
||||||||
|
|
|||||||
3rd link in your post... Quote: Prevent Users From Changing Their Passwords Enables the User Cannot Change Password option, which prevents the user from changing his or her password. Code: Const ADS_ACETYPE_ACCESS_DENIED_OBJECT = &H6 Const ADS_ACEFLAG_OBJECT_TYPE_PRESENT = &H1 Const CHANGE_PASSWORD_GUID = "{ab721a53-1e2f-11d0-9819-00aa0040529b}" Const ADS_RIGHT_DS_CONTROL_ACCESS = &H100 Set objUser = GetObject _ ("LDAP://cn=myerken,ou=management,dc=fabrikam,dc=com") Set objSD = objUser.Get("ntSecurityDescriptor") Set objDACL = objSD.DiscretionaryAcl arrTrustees = array("nt authority\self", "EVERYONE") For Each strTrustee in arrTrustees Set objACE = CreateObject("AccessControlEntry") objACE.Trustee = strTrustee objACE.AceFlags = 0 objACE.AceType = ADS_ACETYPE_ACCESS_DENIED_OBJECT objACE.Flags = ADS_ACEFLAG_OBJECT_TYPE_PRESENT objACE.ObjectType = CHANGE_PASSWORD_GUID objACE.AccessMask = ADS_RIGHT_DS_CONTROL_ACCESS objDACL.AddAce objACE Next objSD.DiscretionaryAcl = objDACL objUser.Put "nTSecurityDescriptor", objSD objUser. SetInfo |
||||||||
|
|
|||||||
in the MSDN article it does say this: Quote: Note To use the code documented in this example, you will need to be an Administrator. If you are not an Administrator, then you will need to add more code that will use an interface that will allow a user to change the way the client-side cache is flushed back to the Active Directory Domain Service. and I just wonder, does that mean I have to be an administrator on the local computer or in domain? if on local computer, I do have UAC enabled, so that could be it. if on domain, well, that's just stupid. |
||||||||
|
|
|||||||
ok, that code is completely different as it requires adding a new ACE and supposedly that is not needed. and it shouldn't since all these accounts already have an ACE. I just get an error when I try to modify them. |
||||||||
|
|
|||||||
Maybe one of these will help? http://www.rlmueller.net/Programs/CannotChgPW.txt http://www.wisesoft.co.uk/scripts/vbscript_enable-disable_user_cannot_change_password.aspx http://orangescripts.blogspot.com/2007/10/vbscript-user-cannot-change-password.html |
||||||||
|
|
|||||||
the second is just a modified version of the firsts older version. And the last one is perfect copy of removeAce version from my last link. I have not tried the ace reordering part yet, so I guess I can give that a try. I also checked on MSDN and all specs I found claim this stuff to work on server 2003 or newer. and last thing, not a single user this far has had these ACEs missing. So adding ACEs shouldn't be needed. |
||||||||
|
|
|||||||
If NTFSPerms doesn't work then either you have a UAC problem, or a problem with the ADsSecurityUtility object. I verified this function to work from XP all the way up to 2012 R2. |
||||||||
|
|
|||||||
Well, it would have to be domain wide since it didn't work on some 150 computers I had it some time ago. |
||||||||
|
|
|||||||
From the links from Allen this is in the VB code. Quote: Based on Microsoft KB articles 301287 and 269159. ' Requires that ADsSecurity.dll be registered on client. How to set the "User Cannot Change Password" option by using a program How to use Visual Basic and ADsSecurity.dll to properly order ACEs in an ACL For reference if page is later unavailable on the Web. Code: ' CannotChgPW.vbs ' VBScript program to configure a user so they cannot change their ' password. ' ' ---------------------------------------------------------------------- ' Copyright (c) 2002-2010 Richard L. Mueller ' Hilltop Lab web site - http://www.rlmueller.net ' Version 1.0 - November 10, 2002 ' Version 1.1 - February 19, 2003 - Standardize Hungarian notation. ' Version 1.2 - March 29, 2003 - Reorder ACE's in DACL. ' Version 1.3 - April 7, 2003 - Use function to reorder ACE's. ' Version 1.4 - January 25, 2004 - Modify error trapping. ' Version 1.5 - November 6, 2010 - No need to set objects to Nothing. ' The Distinguished Name of the user object is passed to the program as ' a parameter. ' Based on Microsoft KB articles 301287 and 269159. ' Requires that ADsSecurity.dll be registered on client. ' ' You have a royalty-free right to use, modify, reproduce, and ' distribute this script file in any way you find useful, provided that ' you agree that the copyright owner above has no warranty, obligations, ' or liability for such use. Option Explicit Const CHANGE_PASSWORD_GUID = "{AB721A53-1E2F-11D0-9819-00AA0040529B}" Const ADS_RIGHT_DS_CONTROL_ACCESS = &H100 Const ADS_ACETYPE_ACCESS_ALLOWED = &H0 Const ADS_ACETYPE_ACCESS_DENIED = &H1 Const ADS_ACETYPE_ACCESS_ALLOWED_OBJECT = &H5 Const ADS_ACETYPE_ACCESS_DENIED_OBJECT = &H6 Const ADS_ACEFLAG_INHERITED_ACE = &H10 Const ADS_ACEFLAG_OBJECT_TYPE_PRESENT = &H1 Dim objACESelf, objACEEveryone, objSecDescriptor, objDACL, objUser Dim strDN, objACE, blnSelf, blnEveryone, blnModified ' Check for required argument. If (Wscript.Arguments.Count < 1) Then Wscript.Echo "Required argument <Distinguished Name> missing. " _ & "For example:" & vbCrLf _ & "cscript CannotChgPW.vbs cn=TestUser,ou=Sales,dc=MyDomain,dc=com" Wscript.Quit(0) End If ' Bind to the user object with the LDAP provider. strDN = Wscript.Arguments(0) On Error Resume Next Set objUser = GetObject("LDAP://" & strDN) If (Err.Number <> 0) Then On Error GoTo 0 Wscript.Echo "User not found" & vbCrLf & strDN Wscript.Quit(1) End If On Error GoTo 0 ' Bind to the user security objects. Set objSecDescriptor = objUser.Get("ntSecurityDescriptor") Set objDACL = objSecDescriptor.discretionaryAcl ' Search for ACE's for Change Password and modify. blnSelf = False blnEveryone = False blnModified = False For Each objACE In objDACL If (UCase(objACE.objectType) = UCase(CHANGE_PASSWORD_GUID)) Then If (UCase(objACE.Trustee) = "NT AUTHORITY\SELF") Then If (objACE.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT) Then objACE.AceType = ADS_ACETYPE_ACCESS_DENIED_OBJECT blnModified = True End If blnSelf = True End If If (UCase(objACE.Trustee) = "EVERYONE") Then If (objACE.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT) Then objACE.AceType = ADS_ACETYPE_ACCESS_DENIED_OBJECT blnModified = True End If blnEveryone = True End If End If Next ' If ACE's found and modified, save changes and exit. If (blnSelf = True) And (blnEveryone = True) Then If (blnModified = False) Then Wscript.Echo "User already cannot change their password" Wscript.Quit Else objSecDescriptor.discretionaryACL = Reorder(objDACL) objUser.Put "ntSecurityDescriptor", objSecDescriptor objUser.SetInfo Wscript.Echo "User modified so they cannot change their password" Wscript.Quit End If End If ' If ACE's not found, add to DACL. If (blnSelf = False) Then ' Create the ACE for Self. Set objACESelf = CreateObject("AccessControlEntry") objACESelf.Trustee = "NT AUTHORITY\SELF" objACESelf.AceFlags = 0 objACESelf.AceType = ADS_ACETYPE_ACCESS_DENIED_OBJECT objACESelf.Flags = ADS_ACEFLAG_OBJECT_TYPE_PRESENT objACESelf.objectType = CHANGE_PASSWORD_GUID objACESelf.AccessMask = ADS_RIGHT_DS_CONTROL_ACCESS objDACL.AddAce objACESelf End If If (blnEveryone = False) Then ' Create the ACE for Everyone. Set objACEEveryone = CreateObject("AccessControlEntry") objACEEveryone.Trustee = "Everyone" objACEEveryone.AceFlags = 0 objACEEveryone.AceType = ADS_ACETYPE_ACCESS_DENIED_OBJECT objACEEveryone.Flags = ADS_ACEFLAG_OBJECT_TYPE_PRESENT objACEEveryone.objectType = CHANGE_PASSWORD_GUID objACEEveryone.AccessMask = ADS_RIGHT_DS_CONTROL_ACCESS objDACL.AddAce objACEEveryone End If objSecDescriptor.discretionaryACL = Reorder(objDACL) ' Update the user object. objUser.Put "ntSecurityDescriptor", objSecDescriptor objUser.SetInfo Wscript.Echo "User denied permission to change their password" Function Reorder(ByVal objDACL) ' Reorder ACE's in DACL. Dim objNewDACL, objInheritedDACL, objAllowDACL, objDenyDACL Dim objAllowObjectDACL, objDenyObjectDACL, objACE Set objNewDACL = CreateObject("AccessControlList") Set objInheritedDACL = CreateObject("AccessControlList") Set objAllowDACL = CreateObject("AccessControlList") Set objDenyDACL = CreateObject("AccessControlList") Set objAllowObjectDACL = CreateObject("AccessControlList") Set objDenyObjectDACL = CreateObject("AccessControlList") For Each objACE In objDACL If ((objACE.AceFlags And ADS_ACEFLAG_INHERITED_ACE) = _ ADS_ACEFLAG_INHERITED_ACE) Then objInheritedDACL.AddAce objACE Else Select Case objACE.AceType Case ADS_ACETYPE_ACCESS_ALLOWED objAllowDACL.AddAce objACE Case ADS_ACETYPE_ACCESS_DENIED objDenyDACL.AddAce objACE Case ADS_ACETYPE_ACCESS_ALLOWED_OBJECT objAllowObjectDACL.AddAce objACE Case ADS_ACETYPE_ACCESS_DENIED_OBJECT objDenyObjectDACL.AddAce objACE Case Else blnACL = False End Select End If Next For Each objACE In objDenyDACL objNewDACL.AddAce objACE Next For Each objACE In objDenyObjectDACL objNewDACL.AddAce objACE Next For Each objACE In objAllowDACL objNewDACL.AddAce objACE Next For Each objACE In objAllowObjectDACL objNewDACL.AddAce objACE Next For Each objACE In objInheritedDACL objNewDACL.AddAce objACE Next objNewDACL.ACLRevision = objDACL.ACLRevision Set Reorder = objNewDACL End Function Integer8 Attributes Quote: Many attributes in Active Directory have a data type (syntax) called Integer8. These 64-bit numbers (8 bytes) often represent time in 100-nanosecond intervals. If the Integer8 attribute is a date, the value represents the number of 100-nanosecond intervals since 12:00 AM January 1, 1601. Any leap seconds are ignored. In .NET Framework (and PowerShell) these 100-nanosecond intervals are called ticks, equal to one ten-millionth of a second. There are 10,000 ticks per millisecond. In addition, .NET Framework and PowerShell DateTime values represent dates as the number of ticks since 12:00 AM January 1, 0001. ADSI automatically employs the IADsLargeInteger interface to deal with these 64-bit numbers. This interface has two property methods, HighPart and LowPart, which break the number up into two 32-bit numbers. The HighPart and LowPart property methods return values between -2^31 and 2^31 - 1. The standard method of handling these attributes is demonstrated by this VBScript program to retrieve the domain lockoutDuration value in minutes. |