Open kiyakutku opened 7 years ago
I ran into the same problem but was able to put a solution together. Posting here as it might help.
Active Directory doesn't recognize the LDAP password reset extension. You'll need to encode the password yourself in "utf-16-le" and then send a ModifyRequest on the "unicodePwd" attribute to change a user password.
Borrowing heavily from this post and the link from the first answer and then cobbling together something from this post I came up with the following working code (go1.9.2), omitting error handling for brevity.
utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM)
// According to the MS docs in the links above
// The password needs to be enclosed in quotes
pwdEncoded, _ := utf16.NewEncoder().String("\"testpassword\"")
passReq := &ldap.ModifyRequest{
DN: userDN, // DN for the user we're resetting
ReplaceAttributes: []ldap.PartialAttribute{
{"unicodePwd", []string{pwdEncoded}},
},
}
l.Modify(passReq)
PS Another quirk for those AD users that might run into issues. Creating new users has been a three step process for my scripts/projects.
The top two should be known processes. Step three requires a ModifyRequest on the "userAccountControl" attribute, generally 512 for normal users in my experience.
modReq := &ldap.ModifyRequest{
DN: userDN,
ReplaceAttributes: []ldap.PartialAttribute{
{"userAccountControl", []string{"512"}},
},
}
AD not playing well with LDAP can cause some sleepless nights, I hope this will help someone out XD
My POC imports read "golang.org/x/text/encoding/unicode"
@melignus I changed the password successfully, thanks!
@melignus this works fine, i just hit one problem: It totally ignores the password history as there is currently no control type sent: https://msdn.microsoft.com/en-us/library/hh128228.aspx. Any idea how to fix that? I guess this the modifyRequest requires support for adding controls?
With the unreleased v3 (master) its able to send the additional control: This is my full solution to respect also the AD password policy: https://gist.github.com/Project0/61c13130563cf7f595e031d54fe55aab
Probably we can activate the wiki to collect such use cases?
@Project0 Thanks for the gist - I tried with it but it gives an error:
LDAP Result Code 53 "Unwilling To Perform": 0000052D: SvcErr: DSID-031A12D2, problem 5003 (WILL_NOT_PERFORM), data 0
Do you have any idea why? I'm not sure where to even begin troubleshooting this error.
@rojakcoder AFAIK this means the password did not matched the policy (what exactly is what you want) These are the errors i could find out:
LDAP Result Code 53 "Unwilling To Perform": 0000001F: SvcErr: DSID-031A12D2, problem 5003 (WILL_NOT_PERFORM) No secure connection used (tls)
LDAP Result Code 53 "Unwilling To Perform": 0000052D: SvcErr: DSID-031A129B, problem 5003 (WILL_NOT_PERFORM) PW in Password history
LDAP Result Code 53 "Unwilling To Perform": 0000052D: SvcErr: DSID-031A12D2, problem 5003 (WILL_NOT_PERFORM) Unknown (policy failed?)
I use the following lines to catch them with a more user friendly message
// catch some common ldap error messages and make them human readable
if ldap.IsErrorWithCode(err, ldap.LDAPResultUnwillingToPerform) {
errCodes := ldapErrorMatchRegex.FindStringSubmatch(err.Error())
if errCodes != nil {
switch errCodes[1] {
case "0000052D":
err = fmt.Errorf("password does not match the policy on server (password history, complexity)")
default:
}
}
}
@Project0 Seems like my problem matches the last one.
And it looks like it's really "unknown". I know for sure the password is not repeated, and that it is complex enough (vsaysVICTOR!@#123 - this is just a sample), which should not matter because I've turned off the complexity rule.
Any tips on how I should debug this next?
@rojakcoder which windows server ae you using? I remember i had some problems during testing with windows server 2016 as DC, but when testing against our production servers (2008R2 and 2012 R2) it worked as expected. You need also some write permissions at the user objects to update the password field.
@Project0 I found out why. So I ran the test to change password again in the morning (code unchanged), and found that it worked! When I then change the password and ran the test again, it failed.
This was how I discovered the minimum password age setting and it was at 1 day. (Yes, I'm completely new to AD, which is why I didn't think to modify this setting.) So I then proceeded to change the setting in AD.
Ran the test again, and it still failed! Then it occurred to me that the password was changed before the setting was updated. So I did a password change without the controls, hoping that it would take in the new setting after changing it.
Turns out I was right - ran the test for one last time and it worked! Thanks for the help!!
How do we change our own password without binding with admin account(bind with our Own account).. by using above code I am getting Insufficient Access Rights
reason: LDAP Result Code 50 "Insufficient Access Rights": 00000005: SecErr: DSID-031A1256, problem 4003 (INSUFF_ACCESS_RIGHTS), data 0
@thammuio that looks like a configuration issue on AD - google the error message - I saw some potential help suggestions. For example
Thank you @johnweldon , I will take a look at this.
But are we sure that NewPasswordModifyRequest wont work for Changing password for a user in AD? and we have to use ModifyRequest as described above?
req := ldap.NewPasswordModifyRequest(newUserDN, passwd, newPassword)
_, err = l.PasswordModify(req)
No, I'm not sure - I haven't validated that use case, and I don't have access to an AD instance to test it myself.
In General, AD will allow the users to change the password rather than modifying the object which requires admin rights. I will try some other stuff and will post here if I find anything.
My code like this:
46 utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM)
47 pwdEncoded, _ := utf16.NewEncoder().String("\"testpassword\"" )
48 passwordModify := ldap.NewModifyRequest(fmt.Sprintf(ldap_config["base_dn"], "test.qq"), nil)
49 t.Log("password is :", pwdEncoded)
50 passwordModify.Add("unicodePwd", []string{"\"testpassword\""})
51 err = l.Modify(passwordModify)
52 if err != nil {
53 t.Log(err)
54 }
And I got the error : LDAP Result Code 53 "Unwilling To Perform": 0000001F: SvcErr: DSID-031A1254, problem 5003 (WILL_NOT_PERFORM), data
I had struggled with this error so long time. Any tips? Thank you!
@triThirty , looks like your code is not using the unicode formatted password variable (line 50, should pass the utf16 password):
except that i found this, are you using tls connection? https://social.technet.microsoft.com/Forums/en-US/c8107faa-1c3b-4c67-9f8f-5601d8378d93/on-domain-controller-not-able-to-see-anything-in-logs-for-password-update-whereas-on-trying-to?forum=winservergen
@Project0 you are right, I didn't connect AD with tls. However I found this : https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/6e803168-f140-4d23-b2d3-c3a8ab5917d2 Which says:
the DC permits modification of the unicodePwd attribute over a connection that is neither SSL/TLS-encrypted nor SASL-encrypted.
So, I just turned the tls authentication off, just like this:
l, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%s", ldap_config["ip"], ldap_config["port"]), & tls.Config{InsecureSkipVerify: true,})
And the port changes from 389 to 636. Whole code is :
30 func connectLdap() (*ldap.Conn){
31
32 l, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%s", ldap_config["ip"], ldap_config["port"]), & tls.Config{InsecureSkipVerify: true,})
33 if err != nil {
34 Sugar.Error(err)
35 defer Sugar.Sync()
36 }
37 return l
38 }
And use the connect like this:
46 utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM)
47 pwdEncoded, _ := utf16.NewEncoder().String("\"testpassword\"" ) //Remember to add double quotation marks to your password string !!!!!
48 passwordModify := ldap.NewModifyRequest(fmt.Sprintf(ldap_config["base_dn"], "test.qq"), nil)
49 t.Log("password is :", pwdEncoded)
50 passwordModify.Add("unicodePwd", []string{pwdEncoded})
51 err = l.Modify(passwordModify)
52 if err != nil {
53 t.Log(err)
54 }
Hope this can help others struggling with the same issue.
@thammuio have you managed to get this to work for you? stuck on the same path.
@wirdman, check out this repo, I might have fixed it in this version - by binding with the same user who is trying to change their password.
hi, any update on this ticket? I cant use the ldap.NewModifyRequest when want to change users own password when the same user is binded.
@project0 you are right, I didn't connect AD with tls. However I found this : https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/6e803168-f140-4d23-b2d3-c3a8ab5917d2 Which says:
the DC permits modification of the unicodePwd attribute over a connection that is neither SSL/TLS-encrypted nor SASL-encrypted.
So, I just turned the tls authentication off, just like this:
l, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%s", ldap_config["ip"], ldap_config["port"]), & tls.Config{InsecureSkipVerify: true,})
And the port changes from 389 to 636. Whole code is :
30 func connectLdap() (*ldap.Conn){ 31 32 l, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%s", ldap_config["ip"], ldap_config["port"]), & tls.Config{InsecureSkipVerify: true,}) 33 if err != nil { 34 Sugar.Error(err) 35 defer Sugar.Sync() 36 } 37 return l 38 }
And use the connect like this:
46 utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) 47 pwdEncoded, _ := utf16.NewEncoder().String("\"testpassword\"" ) //Remember to add double quotation marks to your password string !!!!! 48 passwordModify := ldap.NewModifyRequest(fmt.Sprintf(ldap_config["base_dn"], "test.qq"), nil) 49 t.Log("password is :", pwdEncoded) 50 passwordModify.Add("unicodePwd", []string{pwdEncoded}) 51 err = l.Modify(passwordModify) 52 if err != nil { 53 t.Log(err) 54 }
Hope this can help others struggling with the same issue.
That worked for me when added:
passwordModify.Delete("unicodePwd", []string{currentEncodedPwd})
passwordModify.Add("unicodePwd", []string{newEncodedPwd})
err = conn.Modify(passwordModify)
if err != nil {
log.Fatalln(err)
}
You didn't turn off TLS when doing this
l, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%s", ldap_config["ip"], ldap_config["port"]), & tls.Config{InsecureSkipVerify: true,})
You only disabled the check to validate the validity of the certificate provided by the directory server. I highly recommond to not do this in production.
utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) // According to the MS docs in the links above // The password needs to be enclosed in quotes pwdEncoded, _ := utf16.NewEncoder().String("\"testpassword\"") passReq := &ldap.ModifyRequest{ DN: userDN, // DN for the user we're resetting ReplaceAttributes: []ldap.PartialAttribute{ {"unicodePwd", []string{pwdEncoded}}, }, } l.Modify(passReq)
What is the version of your goldap
According to https://github.com/go-ldap/ldap/issues/106#issuecomment-1623613469 ,
passwordModify.Delete("unicodePwd", []string{currentEncodedPwd})
passwordModify.Add("unicodePwd", []string{newEncodedPwd})
I tried to modify the password with the current password (expect it to fail if it is invalid), but I got an error like this.
{"message":"failed to modify password to the entry: failed to password modify: LDAP Result Code 16 \"No Such Attribute\": 00002085: AtrErr: DSID-03190760, 1:\n\t0: 00002085: DSID-03190760, problem 1001 (NO_ATTRIBUTE_OR_VAL), data 0, Att 9005a (unicodePwd):len 16\n\u0000"}
I confirmed that replacing unicodePwd
with a new password has succeeded. My environment is windows server 2022 standard.
passwordModify.Replace("unicodePwd", []string{newEncodedPwd})
I do not know if it's from me. Maybe it's a bug. Maybe "Basic LDAP v3 functionality" is missing coding.
When I run code from Windows 2008 Domain Controller, I get the following error. What I'm trying to do is run the modify password function
Password could not be changed: LDAP Result Code 2 "Protocol Error": 0000203D: LdapErr: DSID-0C090D9A, comment: Unknown extended request OID, data 0, v1db1 exit status 1
The ldap code I wrote with PHP v2 and v3 performs all functional operations successfully. It turns out that there is not a situation about the version from here either.
Could you tell me what the problem is ?