go-ldap / ldap

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

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 #106

Open kiyakutku opened 7 years ago

kiyakutku commented 7 years ago

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 ?

melignus commented 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.

  1. Create the user
  2. Set the password
  3. Activate the user

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

melignus commented 6 years ago

My POC imports read "golang.org/x/text/encoding/unicode"

OuSatoru commented 6 years ago

@melignus I changed the password successfully, thanks!

project0 commented 6 years ago

@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?

project0 commented 6 years ago

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?

rojakcoder commented 6 years ago

@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.

project0 commented 6 years ago

@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:
            }
        }
           }
rojakcoder commented 6 years ago

@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?

project0 commented 6 years ago

@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.

rojakcoder commented 6 years ago

@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!!

thammuio commented 6 years ago

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

johnweldon commented 6 years ago

@thammuio that looks like a configuration issue on AD - google the error message - I saw some potential help suggestions. For example

thammuio commented 6 years ago

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)
johnweldon commented 6 years ago

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.

thammuio commented 6 years ago

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.

triThirty commented 4 years ago

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!

project0 commented 4 years ago

@triThirty , looks like your code is not using the unicode formatted password variable (line 50, should pass the utf16 password):

project0 commented 4 years ago

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

triThirty commented 4 years ago

@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.

wirdman commented 4 years ago

@thammuio have you managed to get this to work for you? stuck on the same path.

thammuio commented 4 years ago

@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.

martjarvi commented 3 years ago

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.

raonisiann commented 1 year ago

@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)
    }
cpuschma commented 1 year ago

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.

polin-x commented 1 year ago

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

t2y commented 4 months ago

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})