...

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

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

     1  package ldap
     2  
     3  import (
     4  	"bytes"
     5  	hexpac "encoding/hex"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"strings"
    10  	"unicode"
    11  	"unicode/utf8"
    12  
    13  	ber "github.com/go-asn1-ber/asn1-ber"
    14  )
    15  
    16  // Filter choices
    17  const (
    18  	FilterAnd             = 0
    19  	FilterOr              = 1
    20  	FilterNot             = 2
    21  	FilterEqualityMatch   = 3
    22  	FilterSubstrings      = 4
    23  	FilterGreaterOrEqual  = 5
    24  	FilterLessOrEqual     = 6
    25  	FilterPresent         = 7
    26  	FilterApproxMatch     = 8
    27  	FilterExtensibleMatch = 9
    28  )
    29  
    30  // FilterMap contains human readable descriptions of Filter choices
    31  var FilterMap = map[uint64]string{
    32  	FilterAnd:             "And",
    33  	FilterOr:              "Or",
    34  	FilterNot:             "Not",
    35  	FilterEqualityMatch:   "Equality Match",
    36  	FilterSubstrings:      "Substrings",
    37  	FilterGreaterOrEqual:  "Greater Or Equal",
    38  	FilterLessOrEqual:     "Less Or Equal",
    39  	FilterPresent:         "Present",
    40  	FilterApproxMatch:     "Approx Match",
    41  	FilterExtensibleMatch: "Extensible Match",
    42  }
    43  
    44  // SubstringFilter options
    45  const (
    46  	FilterSubstringsInitial = 0
    47  	FilterSubstringsAny     = 1
    48  	FilterSubstringsFinal   = 2
    49  )
    50  
    51  // FilterSubstringsMap contains human readable descriptions of SubstringFilter choices
    52  var FilterSubstringsMap = map[uint64]string{
    53  	FilterSubstringsInitial: "Substrings Initial",
    54  	FilterSubstringsAny:     "Substrings Any",
    55  	FilterSubstringsFinal:   "Substrings Final",
    56  }
    57  
    58  // MatchingRuleAssertion choices
    59  const (
    60  	MatchingRuleAssertionMatchingRule = 1
    61  	MatchingRuleAssertionType         = 2
    62  	MatchingRuleAssertionMatchValue   = 3
    63  	MatchingRuleAssertionDNAttributes = 4
    64  )
    65  
    66  // MatchingRuleAssertionMap contains human readable descriptions of MatchingRuleAssertion choices
    67  var MatchingRuleAssertionMap = map[uint64]string{
    68  	MatchingRuleAssertionMatchingRule: "Matching Rule Assertion Matching Rule",
    69  	MatchingRuleAssertionType:         "Matching Rule Assertion Type",
    70  	MatchingRuleAssertionMatchValue:   "Matching Rule Assertion Match Value",
    71  	MatchingRuleAssertionDNAttributes: "Matching Rule Assertion DN Attributes",
    72  }
    73  
    74  var _SymbolAny = []byte{'*'}
    75  
    76  // CompileFilter converts a string representation of a filter into a BER-encoded packet
    77  func CompileFilter(filter string) (*ber.Packet, error) {
    78  	if len(filter) == 0 || filter[0] != '(' {
    79  		return nil, NewError(ErrorFilterCompile, errors.New("ldap: filter does not start with an '('"))
    80  	}
    81  	packet, pos, err := compileFilter(filter, 1)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	switch {
    86  	case pos > len(filter):
    87  		return nil, NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter"))
    88  	case pos < len(filter):
    89  		return nil, NewError(ErrorFilterCompile, errors.New("ldap: finished compiling filter with extra at end: "+fmt.Sprint(filter[pos:])))
    90  	}
    91  	return packet, nil
    92  }
    93  
    94  // DecompileFilter converts a packet representation of a filter into a string representation
    95  func DecompileFilter(packet *ber.Packet) (_ string, err error) {
    96  	defer func() {
    97  		if r := recover(); r != nil {
    98  			err = NewError(ErrorFilterDecompile, errors.New("ldap: error decompiling filter"))
    99  		}
   100  	}()
   101  
   102  	buf := bytes.NewBuffer(nil)
   103  	buf.WriteByte('(')
   104  	childStr := ""
   105  
   106  	switch packet.Tag {
   107  	case FilterAnd:
   108  		buf.WriteByte('&')
   109  		for _, child := range packet.Children {
   110  			childStr, err = DecompileFilter(child)
   111  			if err != nil {
   112  				return
   113  			}
   114  			buf.WriteString(childStr)
   115  		}
   116  	case FilterOr:
   117  		buf.WriteByte('|')
   118  		for _, child := range packet.Children {
   119  			childStr, err = DecompileFilter(child)
   120  			if err != nil {
   121  				return
   122  			}
   123  			buf.WriteString(childStr)
   124  		}
   125  	case FilterNot:
   126  		buf.WriteByte('!')
   127  		childStr, err = DecompileFilter(packet.Children[0])
   128  		if err != nil {
   129  			return
   130  		}
   131  		buf.WriteString(childStr)
   132  
   133  	case FilterSubstrings:
   134  		buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes()))
   135  		buf.WriteByte('=')
   136  		for i, child := range packet.Children[1].Children {
   137  			if i == 0 && child.Tag != FilterSubstringsInitial {
   138  				buf.Write(_SymbolAny)
   139  			}
   140  			buf.WriteString(EscapeFilter(ber.DecodeString(child.Data.Bytes())))
   141  			if child.Tag != FilterSubstringsFinal {
   142  				buf.Write(_SymbolAny)
   143  			}
   144  		}
   145  	case FilterEqualityMatch:
   146  		buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes()))
   147  		buf.WriteByte('=')
   148  		buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())))
   149  	case FilterGreaterOrEqual:
   150  		buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes()))
   151  		buf.WriteString(">=")
   152  		buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())))
   153  	case FilterLessOrEqual:
   154  		buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes()))
   155  		buf.WriteString("<=")
   156  		buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())))
   157  	case FilterPresent:
   158  		buf.WriteString(ber.DecodeString(packet.Data.Bytes()))
   159  		buf.WriteString("=*")
   160  	case FilterApproxMatch:
   161  		buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes()))
   162  		buf.WriteString("~=")
   163  		buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes())))
   164  	case FilterExtensibleMatch:
   165  		attr := ""
   166  		dnAttributes := false
   167  		matchingRule := ""
   168  		value := ""
   169  
   170  		for _, child := range packet.Children {
   171  			switch child.Tag {
   172  			case MatchingRuleAssertionMatchingRule:
   173  				matchingRule = ber.DecodeString(child.Data.Bytes())
   174  			case MatchingRuleAssertionType:
   175  				attr = ber.DecodeString(child.Data.Bytes())
   176  			case MatchingRuleAssertionMatchValue:
   177  				value = ber.DecodeString(child.Data.Bytes())
   178  			case MatchingRuleAssertionDNAttributes:
   179  				dnAttributes = child.Value.(bool)
   180  			}
   181  		}
   182  
   183  		if len(attr) > 0 {
   184  			buf.WriteString(attr)
   185  		}
   186  		if dnAttributes {
   187  			buf.WriteString(":dn")
   188  		}
   189  		if len(matchingRule) > 0 {
   190  			buf.WriteString(":")
   191  			buf.WriteString(matchingRule)
   192  		}
   193  		buf.WriteString(":=")
   194  		buf.WriteString(EscapeFilter(value))
   195  	}
   196  
   197  	buf.WriteByte(')')
   198  
   199  	return buf.String(), nil
   200  }
   201  
   202  func compileFilterSet(filter string, pos int, parent *ber.Packet) (int, error) {
   203  	for pos < len(filter) && filter[pos] == '(' {
   204  		child, newPos, err := compileFilter(filter, pos+1)
   205  		if err != nil {
   206  			return pos, err
   207  		}
   208  		pos = newPos
   209  		parent.AppendChild(child)
   210  	}
   211  	if pos == len(filter) {
   212  		return pos, NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter"))
   213  	}
   214  
   215  	return pos + 1, nil
   216  }
   217  
   218  func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
   219  	var (
   220  		packet *ber.Packet
   221  		err    error
   222  	)
   223  
   224  	defer func() {
   225  		if r := recover(); r != nil {
   226  			err = NewError(ErrorFilterCompile, errors.New("ldap: error compiling filter"))
   227  		}
   228  	}()
   229  	newPos := pos
   230  
   231  	currentRune, currentWidth := utf8.DecodeRuneInString(filter[newPos:])
   232  
   233  	switch currentRune {
   234  	case utf8.RuneError:
   235  		return nil, 0, NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", newPos))
   236  	case '(':
   237  		packet, newPos, err = compileFilter(filter, pos+currentWidth)
   238  		newPos++
   239  		return packet, newPos, err
   240  	case '&':
   241  		packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterAnd, nil, FilterMap[FilterAnd])
   242  		newPos, err = compileFilterSet(filter, pos+currentWidth, packet)
   243  		return packet, newPos, err
   244  	case '|':
   245  		packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterOr, nil, FilterMap[FilterOr])
   246  		newPos, err = compileFilterSet(filter, pos+currentWidth, packet)
   247  		return packet, newPos, err
   248  	case '!':
   249  		packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterNot, nil, FilterMap[FilterNot])
   250  		var child *ber.Packet
   251  		child, newPos, err = compileFilter(filter, pos+currentWidth)
   252  		packet.AppendChild(child)
   253  		return packet, newPos, err
   254  	default:
   255  		const (
   256  			stateReadingAttr                   = 0
   257  			stateReadingExtensibleMatchingRule = 1
   258  			stateReadingCondition              = 2
   259  		)
   260  
   261  		state := stateReadingAttr
   262  		attribute := bytes.NewBuffer(nil)
   263  		extensibleDNAttributes := false
   264  		extensibleMatchingRule := bytes.NewBuffer(nil)
   265  		condition := bytes.NewBuffer(nil)
   266  
   267  		for newPos < len(filter) {
   268  			remainingFilter := filter[newPos:]
   269  			currentRune, currentWidth = utf8.DecodeRuneInString(remainingFilter)
   270  			if currentRune == ')' {
   271  				break
   272  			}
   273  			if currentRune == utf8.RuneError {
   274  				return packet, newPos, NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", newPos))
   275  			}
   276  
   277  			switch state {
   278  			case stateReadingAttr:
   279  				switch {
   280  				// Extensible rule, with only DN-matching
   281  				case currentRune == ':' && strings.HasPrefix(remainingFilter, ":dn:="):
   282  					packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
   283  					extensibleDNAttributes = true
   284  					state = stateReadingCondition
   285  					newPos += 5
   286  
   287  				// Extensible rule, with DN-matching and a matching OID
   288  				case currentRune == ':' && strings.HasPrefix(remainingFilter, ":dn:"):
   289  					packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
   290  					extensibleDNAttributes = true
   291  					state = stateReadingExtensibleMatchingRule
   292  					newPos += 4
   293  
   294  				// Extensible rule, with attr only
   295  				case currentRune == ':' && strings.HasPrefix(remainingFilter, ":="):
   296  					packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
   297  					state = stateReadingCondition
   298  					newPos += 2
   299  
   300  				// Extensible rule, with no DN attribute matching
   301  				case currentRune == ':':
   302  					packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
   303  					state = stateReadingExtensibleMatchingRule
   304  					newPos++
   305  
   306  				// Equality condition
   307  				case currentRune == '=':
   308  					packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterEqualityMatch, nil, FilterMap[FilterEqualityMatch])
   309  					state = stateReadingCondition
   310  					newPos++
   311  
   312  				// Greater-than or equal
   313  				case currentRune == '>' && strings.HasPrefix(remainingFilter, ">="):
   314  					packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterGreaterOrEqual, nil, FilterMap[FilterGreaterOrEqual])
   315  					state = stateReadingCondition
   316  					newPos += 2
   317  
   318  				// Less-than or equal
   319  				case currentRune == '<' && strings.HasPrefix(remainingFilter, "<="):
   320  					packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterLessOrEqual, nil, FilterMap[FilterLessOrEqual])
   321  					state = stateReadingCondition
   322  					newPos += 2
   323  
   324  				// Approx
   325  				case currentRune == '~' && strings.HasPrefix(remainingFilter, "~="):
   326  					packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterApproxMatch, nil, FilterMap[FilterApproxMatch])
   327  					state = stateReadingCondition
   328  					newPos += 2
   329  
   330  				// Still reading the attribute name
   331  				default:
   332  					attribute.WriteRune(currentRune)
   333  					newPos += currentWidth
   334  				}
   335  
   336  			case stateReadingExtensibleMatchingRule:
   337  				switch {
   338  
   339  				// Matching rule OID is done
   340  				case currentRune == ':' && strings.HasPrefix(remainingFilter, ":="):
   341  					state = stateReadingCondition
   342  					newPos += 2
   343  
   344  				// Still reading the matching rule oid
   345  				default:
   346  					extensibleMatchingRule.WriteRune(currentRune)
   347  					newPos += currentWidth
   348  				}
   349  
   350  			case stateReadingCondition:
   351  				// append to the condition
   352  				condition.WriteRune(currentRune)
   353  				newPos += currentWidth
   354  			}
   355  		}
   356  
   357  		if newPos == len(filter) {
   358  			err = NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter"))
   359  			return packet, newPos, err
   360  		}
   361  		if packet == nil {
   362  			err = NewError(ErrorFilterCompile, errors.New("ldap: error parsing filter"))
   363  			return packet, newPos, err
   364  		}
   365  
   366  		switch {
   367  		case packet.Tag == FilterExtensibleMatch:
   368  			// MatchingRuleAssertion ::= SEQUENCE {
   369  			//         matchingRule    [1] MatchingRuleID OPTIONAL,
   370  			//         type            [2] AttributeDescription OPTIONAL,
   371  			//         matchValue      [3] AssertionValue,
   372  			//         dnAttributes    [4] BOOLEAN DEFAULT FALSE
   373  			// }
   374  
   375  			// Include the matching rule oid, if specified
   376  			if extensibleMatchingRule.Len() > 0 {
   377  				packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchingRule, extensibleMatchingRule.String(), MatchingRuleAssertionMap[MatchingRuleAssertionMatchingRule]))
   378  			}
   379  
   380  			// Include the attribute, if specified
   381  			if attribute.Len() > 0 {
   382  				packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionType, attribute.String(), MatchingRuleAssertionMap[MatchingRuleAssertionType]))
   383  			}
   384  
   385  			// Add the value (only required child)
   386  			encodedString, encodeErr := decodeEscapedSymbols(condition.Bytes())
   387  			if encodeErr != nil {
   388  				return packet, newPos, encodeErr
   389  			}
   390  			packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchValue, encodedString, MatchingRuleAssertionMap[MatchingRuleAssertionMatchValue]))
   391  
   392  			// Defaults to false, so only include in the sequence if true
   393  			if extensibleDNAttributes {
   394  				packet.AppendChild(ber.NewBoolean(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionDNAttributes, extensibleDNAttributes, MatchingRuleAssertionMap[MatchingRuleAssertionDNAttributes]))
   395  			}
   396  
   397  		case packet.Tag == FilterEqualityMatch && bytes.Equal(condition.Bytes(), _SymbolAny):
   398  			packet = ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterPresent, attribute.String(), FilterMap[FilterPresent])
   399  		case packet.Tag == FilterEqualityMatch && bytes.Contains(condition.Bytes(), _SymbolAny):
   400  			packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute.String(), "Attribute"))
   401  			packet.Tag = FilterSubstrings
   402  			packet.Description = FilterMap[uint64(packet.Tag)]
   403  			seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Substrings")
   404  			parts := bytes.Split(condition.Bytes(), _SymbolAny)
   405  			for i, part := range parts {
   406  				if len(part) == 0 {
   407  					continue
   408  				}
   409  				var tag ber.Tag
   410  				switch i {
   411  				case 0:
   412  					tag = FilterSubstringsInitial
   413  				case len(parts) - 1:
   414  					tag = FilterSubstringsFinal
   415  				default:
   416  					tag = FilterSubstringsAny
   417  				}
   418  				encodedString, encodeErr := decodeEscapedSymbols(part)
   419  				if encodeErr != nil {
   420  					return packet, newPos, encodeErr
   421  				}
   422  				seq.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, tag, encodedString, FilterSubstringsMap[uint64(tag)]))
   423  			}
   424  			packet.AppendChild(seq)
   425  		default:
   426  			encodedString, encodeErr := decodeEscapedSymbols(condition.Bytes())
   427  			if encodeErr != nil {
   428  				return packet, newPos, encodeErr
   429  			}
   430  			packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute.String(), "Attribute"))
   431  			packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, encodedString, "Condition"))
   432  		}
   433  
   434  		newPos += currentWidth
   435  		return packet, newPos, err
   436  	}
   437  }
   438  
   439  // Convert from "ABC\xx\xx\xx" form to literal bytes for transport
   440  func decodeEscapedSymbols(src []byte) (string, error) {
   441  	var (
   442  		buffer  bytes.Buffer
   443  		offset  int
   444  		reader  = bytes.NewReader(src)
   445  		byteHex []byte
   446  		byteVal []byte
   447  	)
   448  
   449  	for {
   450  		runeVal, runeSize, err := reader.ReadRune()
   451  		if err == io.EOF {
   452  			return buffer.String(), nil
   453  		} else if err != nil {
   454  			return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: failed to read filter: %v", err))
   455  		} else if runeVal == unicode.ReplacementChar {
   456  			return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", offset))
   457  		}
   458  
   459  		if runeVal == '\\' {
   460  			// http://tools.ietf.org/search/rfc4515
   461  			// \ (%x5C) is not a valid character unless it is followed by two HEX characters due to not
   462  			// being a member of UTF1SUBSET.
   463  			if byteHex == nil {
   464  				byteHex = make([]byte, 2)
   465  				byteVal = make([]byte, 1)
   466  			}
   467  
   468  			if _, err := io.ReadFull(reader, byteHex); err != nil {
   469  				if err == io.ErrUnexpectedEOF {
   470  					return "", NewError(ErrorFilterCompile, errors.New("ldap: missing characters for escape in filter"))
   471  				}
   472  				return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: invalid characters for escape in filter: %v", err))
   473  			}
   474  
   475  			if _, err := hexpac.Decode(byteVal, byteHex); err != nil {
   476  				return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: invalid characters for escape in filter: %v", err))
   477  			}
   478  
   479  			buffer.Write(byteVal)
   480  		} else {
   481  			buffer.WriteRune(runeVal)
   482  		}
   483  
   484  		offset += runeSize
   485  	}
   486  }
   487  

View as plain text