...

Source file src/github.com/go-ldap/ldap/v3/ldap.go

Documentation: github.com/go-ldap/ldap/v3

     1  package ldap
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"log"
     7  	"os"
     8  	"strings"
     9  
    10  	ber "github.com/go-asn1-ber/asn1-ber"
    11  )
    12  
    13  // LDAP Application Codes
    14  const (
    15  	ApplicationBindRequest           = 0
    16  	ApplicationBindResponse          = 1
    17  	ApplicationUnbindRequest         = 2
    18  	ApplicationSearchRequest         = 3
    19  	ApplicationSearchResultEntry     = 4
    20  	ApplicationSearchResultDone      = 5
    21  	ApplicationModifyRequest         = 6
    22  	ApplicationModifyResponse        = 7
    23  	ApplicationAddRequest            = 8
    24  	ApplicationAddResponse           = 9
    25  	ApplicationDelRequest            = 10
    26  	ApplicationDelResponse           = 11
    27  	ApplicationModifyDNRequest       = 12
    28  	ApplicationModifyDNResponse      = 13
    29  	ApplicationCompareRequest        = 14
    30  	ApplicationCompareResponse       = 15
    31  	ApplicationAbandonRequest        = 16
    32  	ApplicationSearchResultReference = 19
    33  	ApplicationExtendedRequest       = 23
    34  	ApplicationExtendedResponse      = 24
    35  	ApplicationIntermediateResponse  = 25
    36  )
    37  
    38  // ApplicationMap contains human readable descriptions of LDAP Application Codes
    39  var ApplicationMap = map[uint8]string{
    40  	ApplicationBindRequest:           "Bind Request",
    41  	ApplicationBindResponse:          "Bind Response",
    42  	ApplicationUnbindRequest:         "Unbind Request",
    43  	ApplicationSearchRequest:         "Search Request",
    44  	ApplicationSearchResultEntry:     "Search Result Entry",
    45  	ApplicationSearchResultDone:      "Search Result Done",
    46  	ApplicationModifyRequest:         "Modify Request",
    47  	ApplicationModifyResponse:        "Modify Response",
    48  	ApplicationAddRequest:            "Add Request",
    49  	ApplicationAddResponse:           "Add Response",
    50  	ApplicationDelRequest:            "Del Request",
    51  	ApplicationDelResponse:           "Del Response",
    52  	ApplicationModifyDNRequest:       "Modify DN Request",
    53  	ApplicationModifyDNResponse:      "Modify DN Response",
    54  	ApplicationCompareRequest:        "Compare Request",
    55  	ApplicationCompareResponse:       "Compare Response",
    56  	ApplicationAbandonRequest:        "Abandon Request",
    57  	ApplicationSearchResultReference: "Search Result Reference",
    58  	ApplicationExtendedRequest:       "Extended Request",
    59  	ApplicationExtendedResponse:      "Extended Response",
    60  	ApplicationIntermediateResponse:  "Intermediate 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  var logger = log.New(os.Stderr, "", log.LstdFlags)
    90  
    91  // Logger allows clients to override the default logger
    92  func Logger(l *log.Logger) {
    93  	logger = l
    94  }
    95  
    96  // Adds descriptions to an LDAP Response packet for debugging
    97  func addLDAPDescriptions(packet *ber.Packet) (err error) {
    98  	defer func() {
    99  		if r := recover(); r != nil {
   100  			err = NewError(ErrorDebugging, fmt.Errorf("ldap: cannot process packet to add descriptions: %s", r))
   101  		}
   102  	}()
   103  	packet.Description = "LDAP Response"
   104  	packet.Children[0].Description = "Message ID"
   105  
   106  	application := uint8(packet.Children[1].Tag)
   107  	packet.Children[1].Description = ApplicationMap[application]
   108  
   109  	switch application {
   110  	case ApplicationBindRequest:
   111  		err = addRequestDescriptions(packet)
   112  	case ApplicationBindResponse:
   113  		err = addDefaultLDAPResponseDescriptions(packet)
   114  	case ApplicationUnbindRequest:
   115  		err = addRequestDescriptions(packet)
   116  	case ApplicationSearchRequest:
   117  		err = addRequestDescriptions(packet)
   118  	case ApplicationSearchResultEntry:
   119  		packet.Children[1].Children[0].Description = "Object Name"
   120  		packet.Children[1].Children[1].Description = "Attributes"
   121  		for _, child := range packet.Children[1].Children[1].Children {
   122  			child.Description = "Attribute"
   123  			child.Children[0].Description = "Attribute Name"
   124  			child.Children[1].Description = "Attribute Values"
   125  			for _, grandchild := range child.Children[1].Children {
   126  				grandchild.Description = "Attribute Value"
   127  			}
   128  		}
   129  		if len(packet.Children) == 3 {
   130  			err = addControlDescriptions(packet.Children[2])
   131  		}
   132  	case ApplicationSearchResultDone:
   133  		err = addDefaultLDAPResponseDescriptions(packet)
   134  	case ApplicationModifyRequest:
   135  		err = addRequestDescriptions(packet)
   136  	case ApplicationModifyResponse:
   137  	case ApplicationAddRequest:
   138  		err = addRequestDescriptions(packet)
   139  	case ApplicationAddResponse:
   140  	case ApplicationDelRequest:
   141  		err = addRequestDescriptions(packet)
   142  	case ApplicationDelResponse:
   143  	case ApplicationModifyDNRequest:
   144  		err = addRequestDescriptions(packet)
   145  	case ApplicationModifyDNResponse:
   146  	case ApplicationCompareRequest:
   147  		err = addRequestDescriptions(packet)
   148  	case ApplicationCompareResponse:
   149  	case ApplicationAbandonRequest:
   150  		err = addRequestDescriptions(packet)
   151  	case ApplicationSearchResultReference:
   152  	case ApplicationExtendedRequest:
   153  		err = addRequestDescriptions(packet)
   154  	case ApplicationExtendedResponse:
   155  	}
   156  
   157  	return err
   158  }
   159  
   160  func addControlDescriptions(packet *ber.Packet) error {
   161  	packet.Description = "Controls"
   162  	for _, child := range packet.Children {
   163  		var value *ber.Packet
   164  		controlType := ""
   165  		child.Description = "Control"
   166  		switch len(child.Children) {
   167  		case 0:
   168  			// at least one child is required for control type
   169  			return fmt.Errorf("at least one child is required for control type")
   170  
   171  		case 1:
   172  			// just type, no criticality or value
   173  			controlType = child.Children[0].Value.(string)
   174  			child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
   175  
   176  		case 2:
   177  			controlType = child.Children[0].Value.(string)
   178  			child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
   179  			// Children[1] could be criticality or value (both are optional)
   180  			// duck-type on whether this is a boolean
   181  			if _, ok := child.Children[1].Value.(bool); ok {
   182  				child.Children[1].Description = "Criticality"
   183  			} else {
   184  				child.Children[1].Description = "Control Value"
   185  				value = child.Children[1]
   186  			}
   187  
   188  		case 3:
   189  			// criticality and value present
   190  			controlType = child.Children[0].Value.(string)
   191  			child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
   192  			child.Children[1].Description = "Criticality"
   193  			child.Children[2].Description = "Control Value"
   194  			value = child.Children[2]
   195  
   196  		default:
   197  			// more than 3 children is invalid
   198  			return fmt.Errorf("more than 3 children for control packet found")
   199  		}
   200  
   201  		if value == nil {
   202  			continue
   203  		}
   204  		switch controlType {
   205  		case ControlTypePaging:
   206  			value.Description += " (Paging)"
   207  			if value.Value != nil {
   208  				valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
   209  				if err != nil {
   210  					return fmt.Errorf("failed to decode data bytes: %s", err)
   211  				}
   212  				value.Data.Truncate(0)
   213  				value.Value = nil
   214  				valueChildren.Children[1].Value = valueChildren.Children[1].Data.Bytes()
   215  				value.AppendChild(valueChildren)
   216  			}
   217  			value.Children[0].Description = "Real Search Control Value"
   218  			value.Children[0].Children[0].Description = "Paging Size"
   219  			value.Children[0].Children[1].Description = "Cookie"
   220  
   221  		case ControlTypeBeheraPasswordPolicy:
   222  			value.Description += " (Password Policy - Behera Draft)"
   223  			if value.Value != nil {
   224  				valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
   225  				if err != nil {
   226  					return fmt.Errorf("failed to decode data bytes: %s", err)
   227  				}
   228  				value.Data.Truncate(0)
   229  				value.Value = nil
   230  				value.AppendChild(valueChildren)
   231  			}
   232  			sequence := value.Children[0]
   233  			for _, child := range sequence.Children {
   234  				if child.Tag == 0 {
   235  					// Warning
   236  					warningPacket := child.Children[0]
   237  					val, err := ber.ParseInt64(warningPacket.Data.Bytes())
   238  					if err != nil {
   239  						return fmt.Errorf("failed to decode data bytes: %s", err)
   240  					}
   241  					if warningPacket.Tag == 0 {
   242  						// timeBeforeExpiration
   243  						value.Description += " (TimeBeforeExpiration)"
   244  						warningPacket.Value = val
   245  					} else if warningPacket.Tag == 1 {
   246  						// graceAuthNsRemaining
   247  						value.Description += " (GraceAuthNsRemaining)"
   248  						warningPacket.Value = val
   249  					}
   250  				} else if child.Tag == 1 {
   251  					// Error
   252  					bs := child.Data.Bytes()
   253  					if len(bs) != 1 || bs[0] > 8 {
   254  						return fmt.Errorf("failed to decode data bytes: %s", "invalid PasswordPolicyResponse enum value")
   255  					}
   256  					val := int8(bs[0])
   257  					child.Description = "Error"
   258  					child.Value = val
   259  				}
   260  			}
   261  		}
   262  	}
   263  	return nil
   264  }
   265  
   266  func addRequestDescriptions(packet *ber.Packet) error {
   267  	packet.Description = "LDAP Request"
   268  	packet.Children[0].Description = "Message ID"
   269  	packet.Children[1].Description = ApplicationMap[uint8(packet.Children[1].Tag)]
   270  	if len(packet.Children) == 3 {
   271  		return addControlDescriptions(packet.Children[2])
   272  	}
   273  	return nil
   274  }
   275  
   276  func addDefaultLDAPResponseDescriptions(packet *ber.Packet) error {
   277  	resultCode := uint16(LDAPResultSuccess)
   278  	matchedDN := ""
   279  	description := "Success"
   280  	if err := GetLDAPError(packet); err != nil {
   281  		resultCode = err.(*Error).ResultCode
   282  		matchedDN = err.(*Error).MatchedDN
   283  		description = "Error Message"
   284  	}
   285  
   286  	packet.Children[1].Children[0].Description = "Result Code (" + LDAPResultCodeMap[resultCode] + ")"
   287  	packet.Children[1].Children[1].Description = "Matched DN (" + matchedDN + ")"
   288  	packet.Children[1].Children[2].Description = description
   289  	if len(packet.Children[1].Children) > 3 {
   290  		packet.Children[1].Children[3].Description = "Referral"
   291  	}
   292  	if len(packet.Children) == 3 {
   293  		return addControlDescriptions(packet.Children[2])
   294  	}
   295  	return nil
   296  }
   297  
   298  // DebugBinaryFile reads and prints packets from the given filename
   299  func DebugBinaryFile(fileName string) error {
   300  	file, err := ioutil.ReadFile(fileName)
   301  	if err != nil {
   302  		return NewError(ErrorDebugging, err)
   303  	}
   304  	ber.PrintBytes(os.Stdout, file, "")
   305  	packet, err := ber.DecodePacketErr(file)
   306  	if err != nil {
   307  		return fmt.Errorf("failed to decode packet: %s", err)
   308  	}
   309  	if err := addLDAPDescriptions(packet); err != nil {
   310  		return err
   311  	}
   312  	ber.PrintPacket(packet)
   313  
   314  	return nil
   315  }
   316  
   317  var hex = "0123456789abcdef"
   318  
   319  func mustEscape(c byte) bool {
   320  	return c > 0x7f || c == '(' || c == ')' || c == '\\' || c == '*' || c == 0
   321  }
   322  
   323  // EscapeFilter escapes from the provided LDAP filter string the special
   324  // characters in the set `()*\` and those out of the range 0 < c < 0x80,
   325  // as defined in RFC4515.
   326  func EscapeFilter(filter string) string {
   327  	escape := 0
   328  	for i := 0; i < len(filter); i++ {
   329  		if mustEscape(filter[i]) {
   330  			escape++
   331  		}
   332  	}
   333  	if escape == 0 {
   334  		return filter
   335  	}
   336  	buf := make([]byte, len(filter)+escape*2)
   337  	for i, j := 0, 0; i < len(filter); i++ {
   338  		c := filter[i]
   339  		if mustEscape(c) {
   340  			buf[j+0] = '\\'
   341  			buf[j+1] = hex[c>>4]
   342  			buf[j+2] = hex[c&0xf]
   343  			j += 3
   344  		} else {
   345  			buf[j] = c
   346  			j++
   347  		}
   348  	}
   349  	return string(buf)
   350  }
   351  
   352  // EscapeDN escapes distinguished names as described in RFC4514. Characters in the
   353  // set `"+,;<>\` are escaped by prepending a backslash, which is also done for trailing
   354  // spaces or a leading `#`. Null bytes are replaced with `\00`.
   355  func EscapeDN(dn string) string {
   356  	if dn == "" {
   357  		return ""
   358  	}
   359  
   360  	builder := strings.Builder{}
   361  
   362  	for i, r := range dn {
   363  		// Escape leading and trailing spaces
   364  		if (i == 0 || i == len(dn)-1) && r == ' ' {
   365  			builder.WriteRune('\\')
   366  			builder.WriteRune(r)
   367  			continue
   368  		}
   369  
   370  		// Escape leading '#'
   371  		if i == 0 && r == '#' {
   372  			builder.WriteRune('\\')
   373  			builder.WriteRune(r)
   374  			continue
   375  		}
   376  
   377  		// Escape characters as defined in RFC4514
   378  		switch r {
   379  		case '"', '+', ',', ';', '<', '>', '\\':
   380  			builder.WriteRune('\\')
   381  			builder.WriteRune(r)
   382  		case '\x00': // Null byte may not be escaped by a leading backslash
   383  			builder.WriteString("\\00")
   384  		default:
   385  			builder.WriteRune(r)
   386  		}
   387  	}
   388  
   389  	return builder.String()
   390  }
   391  

View as plain text