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
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
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
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
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
92 func Logger(l *log.Logger) {
93 logger = l
94 }
95
96
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
169 return fmt.Errorf("at least one child is required for control type")
170
171 case 1:
172
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
180
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
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
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
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
243 value.Description += " (TimeBeforeExpiration)"
244 warningPacket.Value = val
245 } else if warningPacket.Tag == 1 {
246
247 value.Description += " (GraceAuthNsRemaining)"
248 warningPacket.Value = val
249 }
250 } else if child.Tag == 1 {
251
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
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
324
325
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
353
354
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
364 if (i == 0 || i == len(dn)-1) && r == ' ' {
365 builder.WriteRune('\\')
366 builder.WriteRune(r)
367 continue
368 }
369
370
371 if i == 0 && r == '#' {
372 builder.WriteRune('\\')
373 builder.WriteRune(r)
374 continue
375 }
376
377
378 switch r {
379 case '"', '+', ',', ';', '<', '>', '\\':
380 builder.WriteRune('\\')
381 builder.WriteRune(r)
382 case '\x00':
383 builder.WriteString("\\00")
384 default:
385 builder.WriteRune(r)
386 }
387 }
388
389 return builder.String()
390 }
391
View as plain text