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
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
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
45 const (
46 FilterSubstringsInitial = 0
47 FilterSubstringsAny = 1
48 FilterSubstringsFinal = 2
49 )
50
51
52 var FilterSubstringsMap = map[uint64]string{
53 FilterSubstringsInitial: "Substrings Initial",
54 FilterSubstringsAny: "Substrings Any",
55 FilterSubstringsFinal: "Substrings Final",
56 }
57
58
59 const (
60 MatchingRuleAssertionMatchingRule = 1
61 MatchingRuleAssertionType = 2
62 MatchingRuleAssertionMatchValue = 3
63 MatchingRuleAssertionDNAttributes = 4
64 )
65
66
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
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
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
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
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
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
301 case currentRune == ':':
302 packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
303 state = stateReadingExtensibleMatchingRule
304 newPos++
305
306
307 case currentRune == '=':
308 packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterEqualityMatch, nil, FilterMap[FilterEqualityMatch])
309 state = stateReadingCondition
310 newPos++
311
312
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
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
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
331 default:
332 attribute.WriteRune(currentRune)
333 newPos += currentWidth
334 }
335
336 case stateReadingExtensibleMatchingRule:
337 switch {
338
339
340 case currentRune == ':' && strings.HasPrefix(remainingFilter, ":="):
341 state = stateReadingCondition
342 newPos += 2
343
344
345 default:
346 extensibleMatchingRule.WriteRune(currentRune)
347 newPos += currentWidth
348 }
349
350 case stateReadingCondition:
351
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
369
370
371
372
373
374
375
376 if extensibleMatchingRule.Len() > 0 {
377 packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchingRule, extensibleMatchingRule.String(), MatchingRuleAssertionMap[MatchingRuleAssertionMatchingRule]))
378 }
379
380
381 if attribute.Len() > 0 {
382 packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionType, attribute.String(), MatchingRuleAssertionMap[MatchingRuleAssertionType]))
383 }
384
385
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
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
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
461
462
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