1 package ldap
2
3 import (
4 "bytes"
5 "crypto/md5"
6 enchex "encoding/hex"
7 "errors"
8 "fmt"
9 "io/ioutil"
10 "math/rand"
11 "strings"
12
13 "github.com/Azure/go-ntlmssp"
14 ber "github.com/go-asn1-ber/asn1-ber"
15 )
16
17
18 type SimpleBindRequest struct {
19
20 Username string
21
22 Password string
23
24 Controls []Control
25
26
27 AllowEmptyPassword bool
28 }
29
30
31 type SimpleBindResult struct {
32 Controls []Control
33 }
34
35
36 func NewSimpleBindRequest(username string, password string, controls []Control) *SimpleBindRequest {
37 return &SimpleBindRequest{
38 Username: username,
39 Password: password,
40 Controls: controls,
41 AllowEmptyPassword: false,
42 }
43 }
44
45 func (req *SimpleBindRequest) appendTo(envelope *ber.Packet) error {
46 pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
47 pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
48 pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.Username, "User Name"))
49 pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, req.Password, "Password"))
50
51 envelope.AppendChild(pkt)
52 if len(req.Controls) > 0 {
53 envelope.AppendChild(encodeControls(req.Controls))
54 }
55
56 return nil
57 }
58
59
60 func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) {
61 if simpleBindRequest.Password == "" && !simpleBindRequest.AllowEmptyPassword {
62 return nil, NewError(ErrorEmptyPassword, errors.New("ldap: empty password not allowed by the client"))
63 }
64
65 msgCtx, err := l.doRequest(simpleBindRequest)
66 if err != nil {
67 return nil, err
68 }
69 defer l.finishMessage(msgCtx)
70
71 packet, err := l.readPacket(msgCtx)
72 if err != nil {
73 return nil, err
74 }
75
76 result := &SimpleBindResult{
77 Controls: make([]Control, 0),
78 }
79
80 if len(packet.Children) == 3 {
81 for _, child := range packet.Children[2].Children {
82 decodedChild, decodeErr := DecodeControl(child)
83 if decodeErr != nil {
84 return nil, fmt.Errorf("failed to decode child control: %s", decodeErr)
85 }
86 result.Controls = append(result.Controls, decodedChild)
87 }
88 }
89
90 err = GetLDAPError(packet)
91 return result, err
92 }
93
94
95
96
97
98 func (l *Conn) Bind(username, password string) error {
99 req := &SimpleBindRequest{
100 Username: username,
101 Password: password,
102 AllowEmptyPassword: false,
103 }
104 _, err := l.SimpleBind(req)
105 return err
106 }
107
108
109
110
111
112
113
114
115 func (l *Conn) UnauthenticatedBind(username string) error {
116 req := &SimpleBindRequest{
117 Username: username,
118 Password: "",
119 AllowEmptyPassword: true,
120 }
121 _, err := l.SimpleBind(req)
122 return err
123 }
124
125
126 type DigestMD5BindRequest struct {
127 Host string
128
129 Username string
130
131 Password string
132
133 Controls []Control
134 }
135
136 func (req *DigestMD5BindRequest) appendTo(envelope *ber.Packet) error {
137 request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
138 request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
139 request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name"))
140
141 auth := ber.Encode(ber.ClassContext, ber.TypeConstructed, 3, "", "authentication")
142 auth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "DIGEST-MD5", "SASL Mech"))
143 request.AppendChild(auth)
144 envelope.AppendChild(request)
145 if len(req.Controls) > 0 {
146 envelope.AppendChild(encodeControls(req.Controls))
147 }
148 return nil
149 }
150
151
152 type DigestMD5BindResult struct {
153 Controls []Control
154 }
155
156
157 func (l *Conn) MD5Bind(host, username, password string) error {
158 req := &DigestMD5BindRequest{
159 Host: host,
160 Username: username,
161 Password: password,
162 }
163 _, err := l.DigestMD5Bind(req)
164 return err
165 }
166
167
168 func (l *Conn) DigestMD5Bind(digestMD5BindRequest *DigestMD5BindRequest) (*DigestMD5BindResult, error) {
169 if digestMD5BindRequest.Password == "" {
170 return nil, NewError(ErrorEmptyPassword, errors.New("ldap: empty password not allowed by the client"))
171 }
172
173 msgCtx, err := l.doRequest(digestMD5BindRequest)
174 if err != nil {
175 return nil, err
176 }
177 defer l.finishMessage(msgCtx)
178
179 packet, err := l.readPacket(msgCtx)
180 if err != nil {
181 return nil, err
182 }
183 l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
184 if l.Debug {
185 if err = addLDAPDescriptions(packet); err != nil {
186 return nil, err
187 }
188 ber.PrintPacket(packet)
189 }
190
191 result := &DigestMD5BindResult{
192 Controls: make([]Control, 0),
193 }
194 var params map[string]string
195 if len(packet.Children) == 2 {
196 if len(packet.Children[1].Children) == 4 {
197 child := packet.Children[1].Children[0]
198 if child.Tag != ber.TagEnumerated {
199 return result, GetLDAPError(packet)
200 }
201 if child.Value.(int64) != 14 {
202 return result, GetLDAPError(packet)
203 }
204 child = packet.Children[1].Children[3]
205 if child.Tag != ber.TagObjectDescriptor {
206 return result, GetLDAPError(packet)
207 }
208 if child.Data == nil {
209 return result, GetLDAPError(packet)
210 }
211 data, _ := ioutil.ReadAll(child.Data)
212 params, err = parseParams(string(data))
213 if err != nil {
214 return result, fmt.Errorf("parsing digest-challenge: %s", err)
215 }
216 }
217 }
218
219 if params != nil {
220 resp := computeResponse(
221 params,
222 "ldap/"+strings.ToLower(digestMD5BindRequest.Host),
223 digestMD5BindRequest.Username,
224 digestMD5BindRequest.Password,
225 )
226 packet = ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
227 packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
228
229 request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
230 request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
231 request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name"))
232
233 auth := ber.Encode(ber.ClassContext, ber.TypeConstructed, 3, "", "authentication")
234 auth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "DIGEST-MD5", "SASL Mech"))
235 auth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, resp, "Credentials"))
236 request.AppendChild(auth)
237 packet.AppendChild(request)
238 msgCtx, err = l.sendMessage(packet)
239 if err != nil {
240 return nil, fmt.Errorf("send message: %s", err)
241 }
242 defer l.finishMessage(msgCtx)
243 packetResponse, ok := <-msgCtx.responses
244 if !ok {
245 return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
246 }
247 packet, err = packetResponse.ReadPacket()
248 l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
249 if err != nil {
250 return nil, fmt.Errorf("read packet: %s", err)
251 }
252 }
253
254 err = GetLDAPError(packet)
255 return result, err
256 }
257
258 func parseParams(str string) (map[string]string, error) {
259 m := make(map[string]string)
260 var key, value string
261 var state int
262 for i := 0; i <= len(str); i++ {
263 switch state {
264 case 0:
265 if i == len(str) {
266 return nil, fmt.Errorf("syntax error on %d", i)
267 }
268 if str[i] != '=' {
269 key += string(str[i])
270 continue
271 }
272 state = 1
273 case 1:
274 if i == len(str) {
275 m[key] = value
276 break
277 }
278 switch str[i] {
279 case ',':
280 m[key] = value
281 state = 0
282 key = ""
283 value = ""
284 case '"':
285 if value != "" {
286 return nil, fmt.Errorf("syntax error on %d", i)
287 }
288 state = 2
289 default:
290 value += string(str[i])
291 }
292 case 2:
293 if i == len(str) {
294 return nil, fmt.Errorf("syntax error on %d", i)
295 }
296 if str[i] != '"' {
297 value += string(str[i])
298 } else {
299 state = 1
300 }
301 }
302 }
303 return m, nil
304 }
305
306 func computeResponse(params map[string]string, uri, username, password string) string {
307 nc := "00000001"
308 qop := "auth"
309 cnonce := enchex.EncodeToString(randomBytes(16))
310 x := username + ":" + params["realm"] + ":" + password
311 y := md5Hash([]byte(x))
312
313 a1 := bytes.NewBuffer(y)
314 a1.WriteString(":" + params["nonce"] + ":" + cnonce)
315 if len(params["authzid"]) > 0 {
316 a1.WriteString(":" + params["authzid"])
317 }
318 a2 := bytes.NewBuffer([]byte("AUTHENTICATE"))
319 a2.WriteString(":" + uri)
320 ha1 := enchex.EncodeToString(md5Hash(a1.Bytes()))
321 ha2 := enchex.EncodeToString(md5Hash(a2.Bytes()))
322
323 kd := ha1
324 kd += ":" + params["nonce"]
325 kd += ":" + nc
326 kd += ":" + cnonce
327 kd += ":" + qop
328 kd += ":" + ha2
329 resp := enchex.EncodeToString(md5Hash([]byte(kd)))
330 return fmt.Sprintf(
331 `username="%s",realm="%s",nonce="%s",cnonce="%s",nc=00000001,qop=%s,digest-uri="%s",response=%s`,
332 username,
333 params["realm"],
334 params["nonce"],
335 cnonce,
336 qop,
337 uri,
338 resp,
339 )
340 }
341
342 func md5Hash(b []byte) []byte {
343 hasher := md5.New()
344 hasher.Write(b)
345 return hasher.Sum(nil)
346 }
347
348 func randomBytes(len int) []byte {
349 b := make([]byte, len)
350 for i := 0; i < len; i++ {
351 b[i] = byte(rand.Intn(256))
352 }
353 return b
354 }
355
356 var externalBindRequest = requestFunc(func(envelope *ber.Packet) error {
357 pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
358 pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
359 pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name"))
360
361 saslAuth := ber.Encode(ber.ClassContext, ber.TypeConstructed, 3, "", "authentication")
362 saslAuth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "EXTERNAL", "SASL Mech"))
363 saslAuth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "SASL Cred"))
364
365 pkt.AppendChild(saslAuth)
366
367 envelope.AppendChild(pkt)
368
369 return nil
370 })
371
372
373
374
375
376
377 func (l *Conn) ExternalBind() error {
378 msgCtx, err := l.doRequest(externalBindRequest)
379 if err != nil {
380 return err
381 }
382 defer l.finishMessage(msgCtx)
383
384 packet, err := l.readPacket(msgCtx)
385 if err != nil {
386 return err
387 }
388
389 return GetLDAPError(packet)
390 }
391
392
393
394
395 type NTLMBindRequest struct {
396
397 Domain string
398
399 Username string
400
401 Password string
402
403
404 AllowEmptyPassword bool
405
406 Hash string
407
408 Controls []Control
409 }
410
411 func (req *NTLMBindRequest) appendTo(envelope *ber.Packet) error {
412 request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
413 request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
414 request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name"))
415
416
417 negMessage, err := ntlmssp.NewNegotiateMessage(req.Domain, "")
418 if err != nil {
419 return fmt.Errorf("err creating negmessage: %s", err)
420 }
421
422
423 auth := ber.Encode(ber.ClassContext, ber.TypePrimitive, ber.TagEnumerated, negMessage, "authentication")
424 request.AppendChild(auth)
425 envelope.AppendChild(request)
426 if len(req.Controls) > 0 {
427 envelope.AppendChild(encodeControls(req.Controls))
428 }
429 return nil
430 }
431
432
433 type NTLMBindResult struct {
434 Controls []Control
435 }
436
437
438 func (l *Conn) NTLMBind(domain, username, password string) error {
439 req := &NTLMBindRequest{
440 Domain: domain,
441 Username: username,
442 Password: password,
443 }
444 _, err := l.NTLMChallengeBind(req)
445 return err
446 }
447
448
449
450
451
452
453 func (l *Conn) NTLMUnauthenticatedBind(domain, username string) error {
454 req := &NTLMBindRequest{
455 Domain: domain,
456 Username: username,
457 Password: "",
458 AllowEmptyPassword: true,
459 }
460 _, err := l.NTLMChallengeBind(req)
461 return err
462 }
463
464
465 func (l *Conn) NTLMBindWithHash(domain, username, hash string) error {
466 req := &NTLMBindRequest{
467 Domain: domain,
468 Username: username,
469 Hash: hash,
470 }
471 _, err := l.NTLMChallengeBind(req)
472 return err
473 }
474
475
476 func (l *Conn) NTLMChallengeBind(ntlmBindRequest *NTLMBindRequest) (*NTLMBindResult, error) {
477 if !ntlmBindRequest.AllowEmptyPassword && ntlmBindRequest.Password == "" && ntlmBindRequest.Hash == "" {
478 return nil, NewError(ErrorEmptyPassword, errors.New("ldap: empty password not allowed by the client"))
479 }
480
481 msgCtx, err := l.doRequest(ntlmBindRequest)
482 if err != nil {
483 return nil, err
484 }
485 defer l.finishMessage(msgCtx)
486 packet, err := l.readPacket(msgCtx)
487 if err != nil {
488 return nil, err
489 }
490 l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
491 if l.Debug {
492 if err = addLDAPDescriptions(packet); err != nil {
493 return nil, err
494 }
495 ber.PrintPacket(packet)
496 }
497 result := &NTLMBindResult{
498 Controls: make([]Control, 0),
499 }
500 var ntlmsspChallenge []byte
501
502
503 if len(packet.Children) == 2 {
504 if len(packet.Children[1].Children) == 3 {
505 child := packet.Children[1].Children[1]
506 ntlmsspChallenge = child.ByteValue
507
508 if len(ntlmsspChallenge) < 7 || !bytes.Equal(ntlmsspChallenge[:7], []byte("NTLMSSP")) {
509 return result, GetLDAPError(packet)
510 }
511 l.Debug.Printf("%d: found ntlmssp challenge", msgCtx.id)
512 }
513 }
514 if ntlmsspChallenge != nil {
515 var err error
516 var responseMessage []byte
517
518 if ntlmBindRequest.Hash != "" {
519 responseMessage, err = ntlmssp.ProcessChallengeWithHash(ntlmsspChallenge, ntlmBindRequest.Username, ntlmBindRequest.Hash)
520 } else if ntlmBindRequest.Password != "" || ntlmBindRequest.AllowEmptyPassword {
521 _, _, domainNeeded := ntlmssp.GetDomain(ntlmBindRequest.Username)
522 responseMessage, err = ntlmssp.ProcessChallenge(ntlmsspChallenge, ntlmBindRequest.Username, ntlmBindRequest.Password, domainNeeded)
523 } else {
524 err = fmt.Errorf("need a password or hash to generate reply")
525 }
526 if err != nil {
527 return result, fmt.Errorf("parsing ntlm-challenge: %s", err)
528 }
529 packet = ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
530 packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
531
532 request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
533 request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
534 request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name"))
535
536
537 auth := ber.Encode(ber.ClassContext, ber.TypePrimitive, ber.TagEmbeddedPDV, responseMessage, "authentication")
538
539 request.AppendChild(auth)
540 packet.AppendChild(request)
541 msgCtx, err = l.sendMessage(packet)
542 if err != nil {
543 return nil, fmt.Errorf("send message: %s", err)
544 }
545 defer l.finishMessage(msgCtx)
546 packetResponse, ok := <-msgCtx.responses
547 if !ok {
548 return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
549 }
550 packet, err = packetResponse.ReadPacket()
551 l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
552 if err != nil {
553 return nil, fmt.Errorf("read packet: %s", err)
554 }
555
556 }
557
558 err = GetLDAPError(packet)
559 return result, err
560 }
561
562
563
564
565 type GSSAPIClient interface {
566
567
568
569
570
571
572
573
574
575
576
577
578
579 InitSecContext(target string, token []byte) (outputToken []byte, needContinue bool, err error)
580
581
582
583
584
585
586
587
588
589
590
591 NegotiateSaslAuth(token []byte, authzid string) ([]byte, error)
592
593 DeleteSecContext() error
594 }
595
596
597
598 type GSSAPIBindRequest struct {
599
600 ServicePrincipalName string
601
602 AuthZID string
603
604 Controls []Control
605 }
606
607
608 func (l *Conn) GSSAPIBind(client GSSAPIClient, servicePrincipal, authzid string) error {
609 return l.GSSAPIBindRequest(client, &GSSAPIBindRequest{
610 ServicePrincipalName: servicePrincipal,
611 AuthZID: authzid,
612 })
613 }
614
615
616 func (l *Conn) GSSAPIBindRequest(client GSSAPIClient, req *GSSAPIBindRequest) error {
617
618 defer client.DeleteSecContext()
619
620 var err error
621 var reqToken []byte
622 var recvToken []byte
623 needInit := true
624 for {
625 if needInit {
626
627 reqToken, needInit, err = client.InitSecContext(req.ServicePrincipalName, recvToken)
628 if err != nil {
629 return err
630 }
631 } else {
632
633 reqToken, err = client.NegotiateSaslAuth(recvToken, req.AuthZID)
634 if err != nil {
635 return err
636 }
637 }
638
639
640 recvToken, err = l.saslBindTokenExchange(req.Controls, reqToken)
641 if err != nil {
642 return err
643 }
644
645 if !needInit && len(recvToken) == 0 {
646 break
647 }
648 }
649
650 return nil
651 }
652
653 func (l *Conn) saslBindTokenExchange(reqControls []Control, reqToken []byte) ([]byte, error) {
654
655 envelope := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
656 envelope.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
657
658 request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
659 request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
660 request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name"))
661
662 auth := ber.Encode(ber.ClassContext, ber.TypeConstructed, 3, "", "authentication")
663 auth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "GSSAPI", "SASL Mech"))
664 if len(reqToken) > 0 {
665 auth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, string(reqToken), "Credentials"))
666 }
667 request.AppendChild(auth)
668 envelope.AppendChild(request)
669 if len(reqControls) > 0 {
670 envelope.AppendChild(encodeControls(reqControls))
671 }
672
673 msgCtx, err := l.sendMessage(envelope)
674 if err != nil {
675 return nil, err
676 }
677 defer l.finishMessage(msgCtx)
678
679 packet, err := l.readPacket(msgCtx)
680 if err != nil {
681 return nil, err
682 }
683 l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
684 if l.Debug {
685 if err = addLDAPDescriptions(packet); err != nil {
686 return nil, err
687 }
688 ber.PrintPacket(packet)
689 }
690
691
692
693
694
695 if len(packet.Children) != 2 {
696 return nil, fmt.Errorf("bad bind response")
697 }
698
699 protocolOp := packet.Children[1]
700 RESP:
701 switch protocolOp.Description {
702 case "Bind Response":
703
704
705
706 resultCode := protocolOp.Children[0]
707 if resultCode.Tag != ber.TagEnumerated {
708 break RESP
709 }
710 switch resultCode.Value.(int64) {
711 case 14:
712 if len(protocolOp.Children) < 3 {
713 break RESP
714 }
715 referral := protocolOp.Children[3]
716 switch referral.Description {
717 case "Referral":
718 if referral.ClassType != ber.ClassContext || referral.Tag != ber.TagObjectDescriptor {
719 break RESP
720 }
721 return ioutil.ReadAll(referral.Data)
722 }
723
724
725
726
727 case 0:
728
729
730 return nil, nil
731 }
732 }
733
734 return nil, GetLDAPError(packet)
735 }
736
View as plain text