-
-
Save project0/61c13130563cf7f595e031d54fe55aab to your computer and use it in GitHub Desktop.
package passwordresetservice | |
import ( | |
"crypto/tls" | |
"fmt" | |
ldap "github.com/go-ldap/ldap" | |
"golang.org/x/text/encoding/unicode" | |
ber "gopkg.in/asn1-ber.v1" | |
) | |
// need at least v3 of library | |
const ( | |
ldapAttrAccountName = "sAMAccountName" | |
ldapAttrDN = "dn" | |
ldapAttrUAC = "userAccountControl" | |
ldapAttrUPN = "userPrincipalName" // [email protected] | |
ldapAttrEmail = "mail" | |
ldapAttrUnicodePw = "unicodePwd" | |
controlTypeLdapServerPolicyHints = "1.2.840.113556.1.4.2239" | |
controlTypeLdapServerPolicyHintsDeprecated = "1.2.840.113556.1.4.2066" | |
) | |
type ( | |
// ldapControlServerPolicyHints implements ldap.Control | |
ldapControlServerPolicyHints struct { | |
oid string | |
} | |
) | |
// GetControlType implements ldap.Control | |
func (c *ldapControlServerPolicyHints) GetControlType() string { | |
return c.oid | |
} | |
// Encode implements ldap.Control | |
func (c *ldapControlServerPolicyHints) Encode() *ber.Packet { | |
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control") | |
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, c.GetControlType(), "Control Type (LDAP_SERVER_POLICY_HINTS_OID)")) | |
packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, true, "Criticality")) | |
p2 := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (Policy Hints)") | |
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "PolicyHintsRequestValue") | |
seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 1, "Flags")) | |
p2.AppendChild(seq) | |
packet.AppendChild(p2) | |
return packet | |
} | |
// String implements ldap.Control | |
func (c *ldapControlServerPolicyHints) String() string { | |
return "Enforce password history policies during password set: " + c.GetControlType() | |
} | |
// ChangePassword modifies the user password of a user | |
func ChangePassword(userdn string, password string) error { | |
// requires ldaps connection | |
conn, err = ldap.DialTLS("tcp", "contoso.local:636", &tls.Config{ | |
InsecureSkipVerify: false, | |
ServerName: "contoso.local", | |
}) | |
if err != nil { | |
return err | |
} | |
defer conn.Close() | |
// PasswordModify does not work with AD | |
// https://github.com/go-ldap/ldap/issues/106 | |
// request := ldap.NewPasswordModifyRequest(username, "", password) | |
// _, err = conn.PasswordModify(request) | |
utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) | |
// The password needs to be enclosed in quotes | |
pwdEncoded, err := utf16.NewEncoder().String(fmt.Sprintf("\"%s\"", password)) | |
if err != nil { | |
return err | |
} | |
// add additional control to request if supported | |
controlTypes, err := getSupportedControl(conn) | |
if err != nil { | |
return err | |
} | |
control := []ldap.Control{} | |
for _, oid := range controlTypes { | |
if oid == controlTypeLdapServerPolicyHints || oid == controlTypeLdapServerPolicyHintsDeprecated { | |
control = append(control, &ldapControlServerPolicyHints{oid: oid}) | |
break | |
} | |
} | |
passReq := ldap.NewModifyRequest(userdn, control) | |
passReq.Replace(ldapAttrUnicodePw, []string{pwdEncoded}) | |
return onn.Modify(passReq) | |
} | |
// getSupportedControl retrieves supported extended control types | |
func getSupportedControl(conn ldap.Client) ([]string, error) { | |
req := ldap.NewSearchRequest("", ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, "(objectClass=*)", []string{"supportedControl"}, nil) | |
res, err := conn.Search(req) | |
if err != nil { | |
return nil, err | |
} | |
return res.Entries[0].GetAttributeValues("supportedControl"), nil | |
} |
I got LDAP Result Code 32 "No Such Object": 0000208D: NameErr: DSID-0310020A, problem 2001(NO_OBJECT), data 0
. I tried to use "sAMAccountName" as useDN, but did't work as well.And I' using it without tls.
utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) pwdEncoded, err := utf16.NewEncoder().String(fmt.Sprintf("\"%s\"", newPassword))I Working with
AWS Managed Microsoft AD
, using LDAP (not LDAPS), and I succeed to get list of users for example.... but I can't change user password successfully.
Update: Active Directory Require to use secure connection in order to change users passwords (Like: LDAPS, NTLM or Kerberos).
From Microsoft Docs: Change a Windows Active Directory and LDS user password through LDAP
In order to modify this attribute, the client must have a 128-bit Transport Layer Security (TLS)/Secure Socket Layer (SSL) connection to the server.
The "attribute" they are referring to is "unicodePwd"
.
LDAP Result Code 53 "Unwilling To Perform": 0000001F: SvcErr: DSID-031A12E8, problem 5003 (WILL_NOT_PERFORM), data 0
Then it is very likely you have to use ldaps. In any case i cannot really provide more information that what i have written above. Some policy on the server does not like how the request is made. It is very difficult to debug such issues if there is no exact 1:1 mapping of the error codes to a proper description.
I really suggest turning on LDAPs first, before digging more around.
https://docs.aws.amazon.com/directoryservice/latest/admin-guide/ms_ad_ldap_server_side.html
And if you run on AWS, you may also just use the AWS SDK: https://docs.aws.amazon.com/directoryservice/latest/devguide/API_ResetUserPassword.html