github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/ldap.v2/ldap.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ldap
     6  
     7  import (
     8  	"errors"
     9  	"io/ioutil"
    10  	"os"
    11  
    12  	ber "gopkg.in/asn1-ber.v1"
    13  )
    14  
    15  // LDAP Application Codes
    16  const (
    17  	ApplicationBindRequest           = 0
    18  	ApplicationBindResponse          = 1
    19  	ApplicationUnbindRequest         = 2
    20  	ApplicationSearchRequest         = 3
    21  	ApplicationSearchResultEntry     = 4
    22  	ApplicationSearchResultDone      = 5
    23  	ApplicationModifyRequest         = 6
    24  	ApplicationModifyResponse        = 7
    25  	ApplicationAddRequest            = 8
    26  	ApplicationAddResponse           = 9
    27  	ApplicationDelRequest            = 10
    28  	ApplicationDelResponse           = 11
    29  	ApplicationModifyDNRequest       = 12
    30  	ApplicationModifyDNResponse      = 13
    31  	ApplicationCompareRequest        = 14
    32  	ApplicationCompareResponse       = 15
    33  	ApplicationAbandonRequest        = 16
    34  	ApplicationSearchResultReference = 19
    35  	ApplicationExtendedRequest       = 23
    36  	ApplicationExtendedResponse      = 24
    37  )
    38  
    39  // ApplicationMap contains human readable descriptions of LDAP Application Codes
    40  var ApplicationMap = map[uint8]string{
    41  	ApplicationBindRequest:           "Bind Request",
    42  	ApplicationBindResponse:          "Bind Response",
    43  	ApplicationUnbindRequest:         "Unbind Request",
    44  	ApplicationSearchRequest:         "Search Request",
    45  	ApplicationSearchResultEntry:     "Search Result Entry",
    46  	ApplicationSearchResultDone:      "Search Result Done",
    47  	ApplicationModifyRequest:         "Modify Request",
    48  	ApplicationModifyResponse:        "Modify Response",
    49  	ApplicationAddRequest:            "Add Request",
    50  	ApplicationAddResponse:           "Add Response",
    51  	ApplicationDelRequest:            "Del Request",
    52  	ApplicationDelResponse:           "Del Response",
    53  	ApplicationModifyDNRequest:       "Modify DN Request",
    54  	ApplicationModifyDNResponse:      "Modify DN Response",
    55  	ApplicationCompareRequest:        "Compare Request",
    56  	ApplicationCompareResponse:       "Compare Response",
    57  	ApplicationAbandonRequest:        "Abandon Request",
    58  	ApplicationSearchResultReference: "Search Result Reference",
    59  	ApplicationExtendedRequest:       "Extended Request",
    60  	ApplicationExtendedResponse:      "Extended Response",
    61  }
    62  
    63  // Ldap Behera Password Policy Draft 10 (https://tools.ietf.org/html/draft-behera-ldap-password-policy-10)
    64  const (
    65  	BeheraPasswordExpired             = 0
    66  	BeheraAccountLocked               = 1
    67  	BeheraChangeAfterReset            = 2
    68  	BeheraPasswordModNotAllowed       = 3
    69  	BeheraMustSupplyOldPassword       = 4
    70  	BeheraInsufficientPasswordQuality = 5
    71  	BeheraPasswordTooShort            = 6
    72  	BeheraPasswordTooYoung            = 7
    73  	BeheraPasswordInHistory           = 8
    74  )
    75  
    76  // BeheraPasswordPolicyErrorMap contains human readable descriptions of Behera Password Policy error codes
    77  var BeheraPasswordPolicyErrorMap = map[int8]string{
    78  	BeheraPasswordExpired:             "Password expired",
    79  	BeheraAccountLocked:               "Account locked",
    80  	BeheraChangeAfterReset:            "Password must be changed",
    81  	BeheraPasswordModNotAllowed:       "Policy prevents password modification",
    82  	BeheraMustSupplyOldPassword:       "Policy requires old password in order to change password",
    83  	BeheraInsufficientPasswordQuality: "Password fails quality checks",
    84  	BeheraPasswordTooShort:            "Password is too short for policy",
    85  	BeheraPasswordTooYoung:            "Password has been changed too recently",
    86  	BeheraPasswordInHistory:           "New password is in list of old passwords",
    87  }
    88  
    89  // Adds descriptions to an LDAP Response packet for debugging
    90  func addLDAPDescriptions(packet *ber.Packet) (err error) {
    91  	defer func() {
    92  		if r := recover(); r != nil {
    93  			err = NewError(ErrorDebugging, errors.New("ldap: cannot process packet to add descriptions"))
    94  		}
    95  	}()
    96  	packet.Description = "LDAP Response"
    97  	packet.Children[0].Description = "Message ID"
    98  
    99  	application := uint8(packet.Children[1].Tag)
   100  	packet.Children[1].Description = ApplicationMap[application]
   101  
   102  	switch application {
   103  	case ApplicationBindRequest:
   104  		addRequestDescriptions(packet)
   105  	case ApplicationBindResponse:
   106  		addDefaultLDAPResponseDescriptions(packet)
   107  	case ApplicationUnbindRequest:
   108  		addRequestDescriptions(packet)
   109  	case ApplicationSearchRequest:
   110  		addRequestDescriptions(packet)
   111  	case ApplicationSearchResultEntry:
   112  		packet.Children[1].Children[0].Description = "Object Name"
   113  		packet.Children[1].Children[1].Description = "Attributes"
   114  		for _, child := range packet.Children[1].Children[1].Children {
   115  			child.Description = "Attribute"
   116  			child.Children[0].Description = "Attribute Name"
   117  			child.Children[1].Description = "Attribute Values"
   118  			for _, grandchild := range child.Children[1].Children {
   119  				grandchild.Description = "Attribute Value"
   120  			}
   121  		}
   122  		if len(packet.Children) == 3 {
   123  			addControlDescriptions(packet.Children[2])
   124  		}
   125  	case ApplicationSearchResultDone:
   126  		addDefaultLDAPResponseDescriptions(packet)
   127  	case ApplicationModifyRequest:
   128  		addRequestDescriptions(packet)
   129  	case ApplicationModifyResponse:
   130  	case ApplicationAddRequest:
   131  		addRequestDescriptions(packet)
   132  	case ApplicationAddResponse:
   133  	case ApplicationDelRequest:
   134  		addRequestDescriptions(packet)
   135  	case ApplicationDelResponse:
   136  	case ApplicationModifyDNRequest:
   137  		addRequestDescriptions(packet)
   138  	case ApplicationModifyDNResponse:
   139  	case ApplicationCompareRequest:
   140  		addRequestDescriptions(packet)
   141  	case ApplicationCompareResponse:
   142  	case ApplicationAbandonRequest:
   143  		addRequestDescriptions(packet)
   144  	case ApplicationSearchResultReference:
   145  	case ApplicationExtendedRequest:
   146  		addRequestDescriptions(packet)
   147  	case ApplicationExtendedResponse:
   148  	}
   149  
   150  	return nil
   151  }
   152  
   153  func addControlDescriptions(packet *ber.Packet) {
   154  	packet.Description = "Controls"
   155  	for _, child := range packet.Children {
   156  		var value *ber.Packet
   157  		controlType := ""
   158  		child.Description = "Control"
   159  		switch len(child.Children) {
   160  		case 0:
   161  			// at least one child is required for control type
   162  			continue
   163  
   164  		case 1:
   165  			// just type, no criticality or value
   166  			controlType = child.Children[0].Value.(string)
   167  			child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
   168  
   169  		case 2:
   170  			controlType = child.Children[0].Value.(string)
   171  			child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
   172  			// Children[1] could be criticality or value (both are optional)
   173  			// duck-type on whether this is a boolean
   174  			if _, ok := child.Children[1].Value.(bool); ok {
   175  				child.Children[1].Description = "Criticality"
   176  			} else {
   177  				child.Children[1].Description = "Control Value"
   178  				value = child.Children[1]
   179  			}
   180  
   181  		case 3:
   182  			// criticality and value present
   183  			controlType = child.Children[0].Value.(string)
   184  			child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
   185  			child.Children[1].Description = "Criticality"
   186  			child.Children[2].Description = "Control Value"
   187  			value = child.Children[2]
   188  
   189  		default:
   190  			// more than 3 children is invalid
   191  			continue
   192  		}
   193  		if value == nil {
   194  			continue
   195  		}
   196  		switch controlType {
   197  		case ControlTypePaging:
   198  			value.Description += " (Paging)"
   199  			if value.Value != nil {
   200  				valueChildren := ber.DecodePacket(value.Data.Bytes())
   201  				value.Data.Truncate(0)
   202  				value.Value = nil
   203  				valueChildren.Children[1].Value = valueChildren.Children[1].Data.Bytes()
   204  				value.AppendChild(valueChildren)
   205  			}
   206  			value.Children[0].Description = "Real Search Control Value"
   207  			value.Children[0].Children[0].Description = "Paging Size"
   208  			value.Children[0].Children[1].Description = "Cookie"
   209  
   210  		case ControlTypeBeheraPasswordPolicy:
   211  			value.Description += " (Password Policy - Behera Draft)"
   212  			if value.Value != nil {
   213  				valueChildren := ber.DecodePacket(value.Data.Bytes())
   214  				value.Data.Truncate(0)
   215  				value.Value = nil
   216  				value.AppendChild(valueChildren)
   217  			}
   218  			sequence := value.Children[0]
   219  			for _, child := range sequence.Children {
   220  				if child.Tag == 0 {
   221  					//Warning
   222  					warningPacket := child.Children[0]
   223  					packet := ber.DecodePacket(warningPacket.Data.Bytes())
   224  					val, ok := packet.Value.(int64)
   225  					if ok {
   226  						if warningPacket.Tag == 0 {
   227  							//timeBeforeExpiration
   228  							value.Description += " (TimeBeforeExpiration)"
   229  							warningPacket.Value = val
   230  						} else if warningPacket.Tag == 1 {
   231  							//graceAuthNsRemaining
   232  							value.Description += " (GraceAuthNsRemaining)"
   233  							warningPacket.Value = val
   234  						}
   235  					}
   236  				} else if child.Tag == 1 {
   237  					// Error
   238  					packet := ber.DecodePacket(child.Data.Bytes())
   239  					val, ok := packet.Value.(int8)
   240  					if !ok {
   241  						val = -1
   242  					}
   243  					child.Description = "Error"
   244  					child.Value = val
   245  				}
   246  			}
   247  		}
   248  	}
   249  }
   250  
   251  func addRequestDescriptions(packet *ber.Packet) {
   252  	packet.Description = "LDAP Request"
   253  	packet.Children[0].Description = "Message ID"
   254  	packet.Children[1].Description = ApplicationMap[uint8(packet.Children[1].Tag)]
   255  	if len(packet.Children) == 3 {
   256  		addControlDescriptions(packet.Children[2])
   257  	}
   258  }
   259  
   260  func addDefaultLDAPResponseDescriptions(packet *ber.Packet) {
   261  	resultCode, _ := getLDAPResultCode(packet)
   262  	packet.Children[1].Children[0].Description = "Result Code (" + LDAPResultCodeMap[resultCode] + ")"
   263  	packet.Children[1].Children[1].Description = "Matched DN"
   264  	packet.Children[1].Children[2].Description = "Error Message"
   265  	if len(packet.Children[1].Children) > 3 {
   266  		packet.Children[1].Children[3].Description = "Referral"
   267  	}
   268  	if len(packet.Children) == 3 {
   269  		addControlDescriptions(packet.Children[2])
   270  	}
   271  }
   272  
   273  // DebugBinaryFile reads and prints packets from the given filename
   274  func DebugBinaryFile(fileName string) error {
   275  	file, err := ioutil.ReadFile(fileName)
   276  	if err != nil {
   277  		return NewError(ErrorDebugging, err)
   278  	}
   279  	ber.PrintBytes(os.Stdout, file, "")
   280  	packet := ber.DecodePacket(file)
   281  	addLDAPDescriptions(packet)
   282  	ber.PrintPacket(packet)
   283  
   284  	return nil
   285  }
   286  
   287  var hex = "0123456789abcdef"
   288  
   289  func mustEscape(c byte) bool {
   290  	return c > 0x7f || c == '(' || c == ')' || c == '\\' || c == '*' || c == 0
   291  }
   292  
   293  // EscapeFilter escapes from the provided LDAP filter string the special
   294  // characters in the set `()*\` and those out of the range 0 < c < 0x80,
   295  // as defined in RFC4515.
   296  func EscapeFilter(filter string) string {
   297  	escape := 0
   298  	for i := 0; i < len(filter); i++ {
   299  		if mustEscape(filter[i]) {
   300  			escape++
   301  		}
   302  	}
   303  	if escape == 0 {
   304  		return filter
   305  	}
   306  	buf := make([]byte, len(filter)+escape*2)
   307  	for i, j := 0, 0; i < len(filter); i++ {
   308  		c := filter[i]
   309  		if mustEscape(c) {
   310  			buf[j+0] = '\\'
   311  			buf[j+1] = hex[c>>4]
   312  			buf[j+2] = hex[c&0xf]
   313  			j += 3
   314  		} else {
   315  			buf[j] = c
   316  			j++
   317  		}
   318  	}
   319  	return string(buf)
   320  }