You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

157 lines
5.5 KiB

  1. // This file contains the password modify extended operation as specified in rfc 3062
  2. //
  3. // https://tools.ietf.org/html/rfc3062
  4. //
  5. package ldap
  6. import (
  7. "errors"
  8. "fmt"
  9. "gopkg.in/asn1-ber.v1"
  10. )
  11. const (
  12. passwordModifyOID = "1.3.6.1.4.1.4203.1.11.1"
  13. )
  14. // PasswordModifyRequest implements the Password Modify Extended Operation as defined in https://www.ietf.org/rfc/rfc3062.txt
  15. type PasswordModifyRequest struct {
  16. // UserIdentity is an optional string representation of the user associated with the request.
  17. // This string may or may not be an LDAPDN [RFC2253].
  18. // If no UserIdentity field is present, the request acts up upon the password of the user currently associated with the LDAP session
  19. UserIdentity string
  20. // OldPassword, if present, contains the user's current password
  21. OldPassword string
  22. // NewPassword, if present, contains the desired password for this user
  23. NewPassword string
  24. }
  25. // PasswordModifyResult holds the server response to a PasswordModifyRequest
  26. type PasswordModifyResult struct {
  27. // GeneratedPassword holds a password generated by the server, if present
  28. GeneratedPassword string
  29. // Referral are the returned referral
  30. Referral string
  31. }
  32. func (r *PasswordModifyRequest) encode() (*ber.Packet, error) {
  33. request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Password Modify Extended Operation")
  34. request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, passwordModifyOID, "Extended Request Name: Password Modify OID"))
  35. extendedRequestValue := ber.Encode(ber.ClassContext, ber.TypePrimitive, 1, nil, "Extended Request Value: Password Modify Request")
  36. passwordModifyRequestValue := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Password Modify Request")
  37. if r.UserIdentity != "" {
  38. passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, r.UserIdentity, "User Identity"))
  39. }
  40. if r.OldPassword != "" {
  41. passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 1, r.OldPassword, "Old Password"))
  42. }
  43. if r.NewPassword != "" {
  44. passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 2, r.NewPassword, "New Password"))
  45. }
  46. extendedRequestValue.AppendChild(passwordModifyRequestValue)
  47. request.AppendChild(extendedRequestValue)
  48. return request, nil
  49. }
  50. // NewPasswordModifyRequest creates a new PasswordModifyRequest
  51. //
  52. // According to the RFC 3602:
  53. // userIdentity is a string representing the user associated with the request.
  54. // This string may or may not be an LDAPDN (RFC 2253).
  55. // If userIdentity is empty then the operation will act on the user associated
  56. // with the session.
  57. //
  58. // oldPassword is the current user's password, it can be empty or it can be
  59. // needed depending on the session user access rights (usually an administrator
  60. // can change a user's password without knowing the current one) and the
  61. // password policy (see pwdSafeModify password policy's attribute)
  62. //
  63. // newPassword is the desired user's password. If empty the server can return
  64. // an error or generate a new password that will be available in the
  65. // PasswordModifyResult.GeneratedPassword
  66. //
  67. func NewPasswordModifyRequest(userIdentity string, oldPassword string, newPassword string) *PasswordModifyRequest {
  68. return &PasswordModifyRequest{
  69. UserIdentity: userIdentity,
  70. OldPassword: oldPassword,
  71. NewPassword: newPassword,
  72. }
  73. }
  74. // PasswordModify performs the modification request
  75. func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error) {
  76. packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
  77. packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
  78. encodedPasswordModifyRequest, err := passwordModifyRequest.encode()
  79. if err != nil {
  80. return nil, err
  81. }
  82. packet.AppendChild(encodedPasswordModifyRequest)
  83. l.Debug.PrintPacket(packet)
  84. msgCtx, err := l.sendMessage(packet)
  85. if err != nil {
  86. return nil, err
  87. }
  88. defer l.finishMessage(msgCtx)
  89. result := &PasswordModifyResult{}
  90. l.Debug.Printf("%d: waiting for response", msgCtx.id)
  91. packetResponse, ok := <-msgCtx.responses
  92. if !ok {
  93. return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
  94. }
  95. packet, err = packetResponse.ReadPacket()
  96. l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
  97. if err != nil {
  98. return nil, err
  99. }
  100. if packet == nil {
  101. return nil, NewError(ErrorNetwork, errors.New("ldap: could not retrieve message"))
  102. }
  103. if l.Debug {
  104. if err := addLDAPDescriptions(packet); err != nil {
  105. return nil, err
  106. }
  107. ber.PrintPacket(packet)
  108. }
  109. if packet.Children[1].Tag == ApplicationExtendedResponse {
  110. err := GetLDAPError(packet)
  111. if err != nil {
  112. if IsErrorWithCode(err, LDAPResultReferral) {
  113. for _, child := range packet.Children[1].Children {
  114. if child.Tag == 3 {
  115. result.Referral = child.Children[0].Value.(string)
  116. }
  117. }
  118. }
  119. return result, err
  120. }
  121. } else {
  122. return nil, NewError(ErrorUnexpectedResponse, fmt.Errorf("unexpected Response: %d", packet.Children[1].Tag))
  123. }
  124. extendedResponse := packet.Children[1]
  125. for _, child := range extendedResponse.Children {
  126. if child.Tag == 11 {
  127. passwordModifyResponseValue := ber.DecodePacket(child.Data.Bytes())
  128. if len(passwordModifyResponseValue.Children) == 1 {
  129. if passwordModifyResponseValue.Children[0].Tag == 0 {
  130. result.GeneratedPassword = ber.DecodeString(passwordModifyResponseValue.Children[0].Data.Bytes())
  131. }
  132. }
  133. }
  134. }
  135. return result, nil
  136. }