go-ldap / ldap

Basic LDAP v3 functionality for the GO programming language.
Other
2.24k stars 355 forks source link

DACL: How to set PASSWD_CANT_CHANGE #348

Open jasoncmcg opened 2 years ago

jasoncmcg commented 2 years ago

TL;DR

How do I update PASSWD_CANT_CHANGE in the security descriptor discretionary access control list to set the ADS_ACETYPE_ACCESS_DENIED_OBJECT ace type in the IADsAccessControlEntry?

Note: VB.net example found at the bottom

My Best Guess

My best guess is that I can use the ldap control to do something, but I cannot find any reference for it. controls := []ldap.Control{}

Details

When activating an account, the following will activate with DONT_EXPIRE_PASSWORD and NORMAL_ACCOUNT. However, attempting to use 0x0040 for PASSWD_CANT_CHANGE has no effect.

controls := []ldap.Control{}
enableReq := ldap.NewModifyRequest(userDN, controls)
enableReq.Replace("userAccountControl", []string{fmt.Sprintf("%d", 0x10200)})

According to this page reference: Note You cannot assign this permission by directly modifying the UserAccountControl attribute. For information about how to set the permission programmatically, see the "Property flag descriptions" section.

The above redirection leads to this page, where it lists the following steps to update this option.

  1. Bind to the user object.
  2. Obtain the IADsSecurityDescriptor object from the ntSecurityDescriptor property of the user object.
  3. Obtain an IADsAccessControlList interface for the security descriptor from the IADsSecurityDescriptor.DiscretionaryAcl property.
  4. Enumerate the ACEs for the object and search for the ACEs that have the change password GUID ({AB721A53-1E2F-11D0-9819-00AA0040529B}) for the IADsAccessControlEntry.ObjectType property and "Everyone" or "NT AUTHORITY\SELF" for the IADsAccessControlEntry.Trustee property. 5.Modify the IADsAccessControlEntry.AceType property of the ACEs that were found to ADS_ACETYPE_ACCESS_DENIED_OBJECT if the user cannot change their password or ADS_ACETYPE_ACCESS_ALLOWED_OBJECT if the user can change their password.
  5. If the "Everyone" ACE is not found, create a new IADsAccessControlEntry object that contains the property values shown in the table below and add the new entry to the ACL with the IADsAccessControlList.AddAce method.
  6. If the "NT AUTHORITY\SELF" ACE is not found, create a new IADsAccessControlEntry object with the same property values shown in the table below except the Trustee property contains the account name for SID "S-1-5-10" ("NT AUTHORITY\SELF"). Add the entry to the ACL with the IADsAccessControlList.AddAce method.
  7. To update the ntSecurityDescriptor property of the object, call the IADs.Put method with the same IADsSecurityDescriptor obtained in Step 2.
  8. Commit the local changes to the server with the IADs.SetInfo method.
  9. If either of the ACEs were created, you must reorder the ACL so that the ACEs are in the correct order. To do this, call the GetNamedSecurityInfo function with the LDAP ADsPath of the object and then the SetNamedSecurityInfo function with the same DACL. This reordering will occur automatically when the ACEs are added.

More information here with examples on this setting for ADS_ACETYPE_ACCESS_DENIED_OBJECT

Set x = GetObject("LDAP://OU=Sales, DC=Fabrikam,DC=com")
Dim sd As IADsSecurityDescriptor
Set sd = x.Get("ntSecurityDescriptor")

Dim Ace2 As New AccessControlEntry
Dim Dacl As IADsAccessControlList
Set Dacl = sd.DiscretionaryAcl

Ace2.AccessMask = -1 'Full Permission (Denied)
Ace2.AceType = ADS_ACETYPE_ACCESS_DENIED
Ace2.AceFlags = ADS_ACEFLAG_INHERIT_ACE
Ace2.Trustee = "ACTIVED\Andyhar"

Dacl.AddAce Ace2

sd.DiscretionaryAcl = Dacl
x.Put "ntSecurityDescriptor", Array(sd)
x.SetInfo
jasoncmcg commented 1 year ago

Trying to come back to this one, I wanted to see what GPT-4 had to say about it. Ultimately, it tried to recommend Python... However, I will continue to stay with Go and try to solve the issue. (This all runs from a Linux machine.)

...this approach requires a good understanding of the security descriptor format and manual manipulation of the binary data. You will need a package to help you work with security descriptors.

Here's a high-level outline of the steps you should follow:

  1. Create a new security descriptor with the required settings.
  2. Create a Discretionary Access Control List (DACL) with the appropriate Access Control Entries (ACEs) for the 'password cannot change' flag.
  3. Add the created DACL to the security descriptor.
  4. Serialize the security descriptor into binary format.
  5. Include the serialized security descriptor as the ntSecurityDescriptor attribute in the AddRequest.

Please note that this approach is more complex and error-prone than using a package or library that supports the required interfaces (IADsSecurityDescriptor, IADsAccessControlList, IADsAccessControlEntry). If possible, I recommend using a package that supports these interfaces to reduce complexity and the risk of errors...

jasoncmcg commented 1 year ago

The definition: HRESULT SetUserCannotChangePassword(LPCWSTR pwszUserDN, LPCWSTR pwszUsername, LPCWSTR pwszPassword, BOOL fCannotChangePassword) on this page: https://learn.microsoft.com/en-us/windows/win32/adsi/modifying-user-cannot-change-password-ldap-provider?redirectedfrom=MSDN

I tried to convert this to a Go function, but it is apparently too complex of a process for a GPT-4 to be able to convert. The closest I could come to usable information was to use this package: https://pkg.go.dev/golang.org/x/sys/windows#pkg-overview

Meanwhile, it appears to not be possible within the constraints.

huner2 commented 11 months ago

@jasoncmcg

This may not be exactly within the scope of what you were hoping to do, but I recently came across this issue while looking to see if parsing/modifying the SDDL string for ntSecurityDescriptor was possible using this library. After determining that it wasn't part of this library, I took the time to create a library that can parse and write the binary SDDL format, so that modifying ACLs should be possible using a library such as go-ldap.

The library is here: https://github.com/huner2/go-sddlparse

jasoncmcg commented 9 months ago

@jasoncmcg

This may not be exactly within the scope of what you were hoping to do, but I recently came across this issue while looking to see if parsing/modifying the SDDL string for ntSecurityDescriptor was possible using this library. After determining that it wasn't part of this library, I took the time to create a library that can parse and write the binary SDDL format, so that modifying ACLs should be possible using a library such as go-ldap.

The library is here: https://github.com/huner2/go-sddlparse

I somehow missed this and have come back because I need to use this for something else. I appreciate it and will take a look.

stevstrong commented 1 week ago

Any progress on this?