Last active
May 3, 2025 03:38
-
-
Save project0/61c13130563cf7f595e031d54fe55aab to your computer and use it in GitHub Desktop.
Go AD password reset
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
LDAP Result Code 53 "Unwilling To Perform": 0000001F: SvcErr: DSID-031A12E8, problem 5003 (WILL_NOT_PERFORM), data 0
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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
The "attribute" they are referring to is
"unicodePwd"
.