summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mattermost/ldap/ldap.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mattermost/ldap/ldap.go')
-rw-r--r--vendor/github.com/mattermost/ldap/ldap.go345
1 files changed, 345 insertions, 0 deletions
diff --git a/vendor/github.com/mattermost/ldap/ldap.go b/vendor/github.com/mattermost/ldap/ldap.go
new file mode 100644
index 00000000..7dbc951a
--- /dev/null
+++ b/vendor/github.com/mattermost/ldap/ldap.go
@@ -0,0 +1,345 @@
+package ldap
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+
+ ber "github.com/go-asn1-ber/asn1-ber"
+)
+
+// LDAP Application Codes
+const (
+ ApplicationBindRequest = 0
+ ApplicationBindResponse = 1
+ ApplicationUnbindRequest = 2
+ ApplicationSearchRequest = 3
+ ApplicationSearchResultEntry = 4
+ ApplicationSearchResultDone = 5
+ ApplicationModifyRequest = 6
+ ApplicationModifyResponse = 7
+ ApplicationAddRequest = 8
+ ApplicationAddResponse = 9
+ ApplicationDelRequest = 10
+ ApplicationDelResponse = 11
+ ApplicationModifyDNRequest = 12
+ ApplicationModifyDNResponse = 13
+ ApplicationCompareRequest = 14
+ ApplicationCompareResponse = 15
+ ApplicationAbandonRequest = 16
+ ApplicationSearchResultReference = 19
+ ApplicationExtendedRequest = 23
+ ApplicationExtendedResponse = 24
+)
+
+// ApplicationMap contains human readable descriptions of LDAP Application Codes
+var ApplicationMap = map[uint8]string{
+ ApplicationBindRequest: "Bind Request",
+ ApplicationBindResponse: "Bind Response",
+ ApplicationUnbindRequest: "Unbind Request",
+ ApplicationSearchRequest: "Search Request",
+ ApplicationSearchResultEntry: "Search Result Entry",
+ ApplicationSearchResultDone: "Search Result Done",
+ ApplicationModifyRequest: "Modify Request",
+ ApplicationModifyResponse: "Modify Response",
+ ApplicationAddRequest: "Add Request",
+ ApplicationAddResponse: "Add Response",
+ ApplicationDelRequest: "Del Request",
+ ApplicationDelResponse: "Del Response",
+ ApplicationModifyDNRequest: "Modify DN Request",
+ ApplicationModifyDNResponse: "Modify DN Response",
+ ApplicationCompareRequest: "Compare Request",
+ ApplicationCompareResponse: "Compare Response",
+ ApplicationAbandonRequest: "Abandon Request",
+ ApplicationSearchResultReference: "Search Result Reference",
+ ApplicationExtendedRequest: "Extended Request",
+ ApplicationExtendedResponse: "Extended Response",
+}
+
+// Ldap Behera Password Policy Draft 10 (https://tools.ietf.org/html/draft-behera-ldap-password-policy-10)
+const (
+ BeheraPasswordExpired = 0
+ BeheraAccountLocked = 1
+ BeheraChangeAfterReset = 2
+ BeheraPasswordModNotAllowed = 3
+ BeheraMustSupplyOldPassword = 4
+ BeheraInsufficientPasswordQuality = 5
+ BeheraPasswordTooShort = 6
+ BeheraPasswordTooYoung = 7
+ BeheraPasswordInHistory = 8
+)
+
+// BeheraPasswordPolicyErrorMap contains human readable descriptions of Behera Password Policy error codes
+var BeheraPasswordPolicyErrorMap = map[int8]string{
+ BeheraPasswordExpired: "Password expired",
+ BeheraAccountLocked: "Account locked",
+ BeheraChangeAfterReset: "Password must be changed",
+ BeheraPasswordModNotAllowed: "Policy prevents password modification",
+ BeheraMustSupplyOldPassword: "Policy requires old password in order to change password",
+ BeheraInsufficientPasswordQuality: "Password fails quality checks",
+ BeheraPasswordTooShort: "Password is too short for policy",
+ BeheraPasswordTooYoung: "Password has been changed too recently",
+ BeheraPasswordInHistory: "New password is in list of old passwords",
+}
+
+// Adds descriptions to an LDAP Response packet for debugging
+func addLDAPDescriptions(packet *ber.Packet) (err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ err = NewError(ErrorDebugging, fmt.Errorf("ldap: cannot process packet to add descriptions: %s", r))
+ }
+ }()
+ packet.Description = "LDAP Response"
+ packet.Children[0].Description = "Message ID"
+
+ application := uint8(packet.Children[1].Tag)
+ packet.Children[1].Description = ApplicationMap[application]
+
+ switch application {
+ case ApplicationBindRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationBindResponse:
+ err = addDefaultLDAPResponseDescriptions(packet)
+ case ApplicationUnbindRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationSearchRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationSearchResultEntry:
+ packet.Children[1].Children[0].Description = "Object Name"
+ packet.Children[1].Children[1].Description = "Attributes"
+ for _, child := range packet.Children[1].Children[1].Children {
+ child.Description = "Attribute"
+ child.Children[0].Description = "Attribute Name"
+ child.Children[1].Description = "Attribute Values"
+ for _, grandchild := range child.Children[1].Children {
+ grandchild.Description = "Attribute Value"
+ }
+ }
+ if len(packet.Children) == 3 {
+ err = addControlDescriptions(packet.Children[2])
+ }
+ case ApplicationSearchResultDone:
+ err = addDefaultLDAPResponseDescriptions(packet)
+ case ApplicationModifyRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationModifyResponse:
+ case ApplicationAddRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationAddResponse:
+ case ApplicationDelRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationDelResponse:
+ case ApplicationModifyDNRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationModifyDNResponse:
+ case ApplicationCompareRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationCompareResponse:
+ case ApplicationAbandonRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationSearchResultReference:
+ case ApplicationExtendedRequest:
+ err = addRequestDescriptions(packet)
+ case ApplicationExtendedResponse:
+ }
+
+ return err
+}
+
+func addControlDescriptions(packet *ber.Packet) error {
+ packet.Description = "Controls"
+ for _, child := range packet.Children {
+ var value *ber.Packet
+ controlType := ""
+ child.Description = "Control"
+ switch len(child.Children) {
+ case 0:
+ // at least one child is required for control type
+ return fmt.Errorf("at least one child is required for control type")
+
+ case 1:
+ // just type, no criticality or value
+ controlType = child.Children[0].Value.(string)
+ child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
+
+ case 2:
+ controlType = child.Children[0].Value.(string)
+ child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
+ // Children[1] could be criticality or value (both are optional)
+ // duck-type on whether this is a boolean
+ if _, ok := child.Children[1].Value.(bool); ok {
+ child.Children[1].Description = "Criticality"
+ } else {
+ child.Children[1].Description = "Control Value"
+ value = child.Children[1]
+ }
+
+ case 3:
+ // criticality and value present
+ controlType = child.Children[0].Value.(string)
+ child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
+ child.Children[1].Description = "Criticality"
+ child.Children[2].Description = "Control Value"
+ value = child.Children[2]
+
+ default:
+ // more than 3 children is invalid
+ return fmt.Errorf("more than 3 children for control packet found")
+ }
+
+ if value == nil {
+ continue
+ }
+ switch controlType {
+ case ControlTypePaging:
+ value.Description += " (Paging)"
+ if value.Value != nil {
+ valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
+ if err != nil {
+ return fmt.Errorf("failed to decode data bytes: %s", err)
+ }
+ value.Data.Truncate(0)
+ value.Value = nil
+ valueChildren.Children[1].Value = valueChildren.Children[1].Data.Bytes()
+ value.AppendChild(valueChildren)
+ }
+ value.Children[0].Description = "Real Search Control Value"
+ value.Children[0].Children[0].Description = "Paging Size"
+ value.Children[0].Children[1].Description = "Cookie"
+
+ case ControlTypeBeheraPasswordPolicy:
+ value.Description += " (Password Policy - Behera Draft)"
+ if value.Value != nil {
+ valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
+ if err != nil {
+ return fmt.Errorf("failed to decode data bytes: %s", err)
+ }
+ value.Data.Truncate(0)
+ value.Value = nil
+ value.AppendChild(valueChildren)
+ }
+ sequence := value.Children[0]
+ for _, child := range sequence.Children {
+ if child.Tag == 0 {
+ //Warning
+ warningPacket := child.Children[0]
+ packet, err := ber.DecodePacketErr(warningPacket.Data.Bytes())
+ if err != nil {
+ return fmt.Errorf("failed to decode data bytes: %s", err)
+ }
+ val, ok := packet.Value.(int64)
+ if ok {
+ if warningPacket.Tag == 0 {
+ //timeBeforeExpiration
+ value.Description += " (TimeBeforeExpiration)"
+ warningPacket.Value = val
+ } else if warningPacket.Tag == 1 {
+ //graceAuthNsRemaining
+ value.Description += " (GraceAuthNsRemaining)"
+ warningPacket.Value = val
+ }
+ }
+ } else if child.Tag == 1 {
+ // Error
+ packet, err := ber.DecodePacketErr(child.Data.Bytes())
+ if err != nil {
+ return fmt.Errorf("failed to decode data bytes: %s", err)
+ }
+ val, ok := packet.Value.(int8)
+ if !ok {
+ val = -1
+ }
+ child.Description = "Error"
+ child.Value = val
+ }
+ }
+ }
+ }
+ return nil
+}
+
+func addRequestDescriptions(packet *ber.Packet) error {
+ packet.Description = "LDAP Request"
+ packet.Children[0].Description = "Message ID"
+ packet.Children[1].Description = ApplicationMap[uint8(packet.Children[1].Tag)]
+ if len(packet.Children) == 3 {
+ return addControlDescriptions(packet.Children[2])
+ }
+ return nil
+}
+
+func addDefaultLDAPResponseDescriptions(packet *ber.Packet) error {
+ resultCode := uint16(LDAPResultSuccess)
+ matchedDN := ""
+ description := "Success"
+ if err := GetLDAPError(packet); err != nil {
+ resultCode = err.(*Error).ResultCode
+ matchedDN = err.(*Error).MatchedDN
+ description = "Error Message"
+ }
+
+ packet.Children[1].Children[0].Description = "Result Code (" + LDAPResultCodeMap[resultCode] + ")"
+ packet.Children[1].Children[1].Description = "Matched DN (" + matchedDN + ")"
+ packet.Children[1].Children[2].Description = description
+ if len(packet.Children[1].Children) > 3 {
+ packet.Children[1].Children[3].Description = "Referral"
+ }
+ if len(packet.Children) == 3 {
+ return addControlDescriptions(packet.Children[2])
+ }
+ return nil
+}
+
+// DebugBinaryFile reads and prints packets from the given filename
+func DebugBinaryFile(fileName string) error {
+ file, err := ioutil.ReadFile(fileName)
+ if err != nil {
+ return NewError(ErrorDebugging, err)
+ }
+ ber.PrintBytes(os.Stdout, file, "")
+ packet, err := ber.DecodePacketErr(file)
+ if err != nil {
+ return fmt.Errorf("failed to decode packet: %s", err)
+ }
+ if err := addLDAPDescriptions(packet); err != nil {
+ return err
+ }
+ ber.PrintPacket(packet)
+
+ return nil
+}
+
+var hex = "0123456789abcdef"
+
+func mustEscape(c byte) bool {
+ return c > 0x7f || c == '(' || c == ')' || c == '\\' || c == '*' || c == 0
+}
+
+// EscapeFilter escapes from the provided LDAP filter string the special
+// characters in the set `()*\` and those out of the range 0 < c < 0x80,
+// as defined in RFC4515.
+func EscapeFilter(filter string) string {
+ escape := 0
+ for i := 0; i < len(filter); i++ {
+ if mustEscape(filter[i]) {
+ escape++
+ }
+ }
+ if escape == 0 {
+ return filter
+ }
+ buf := make([]byte, len(filter)+escape*2)
+ for i, j := 0, 0; i < len(filter); i++ {
+ c := filter[i]
+ if mustEscape(c) {
+ buf[j+0] = '\\'
+ buf[j+1] = hex[c>>4]
+ buf[j+2] = hex[c&0xf]
+ j += 3
+ } else {
+ buf[j] = c
+ j++
+ }
+ }
+ return string(buf)
+}