@@ -558,3 +558,179 @@ func (l *Conn) NTLMChallengeBind(ntlmBindRequest *NTLMBindRequest) (*NTLMBindRes
558
558
err = GetLDAPError (packet )
559
559
return result , err
560
560
}
561
+
562
+ // GSSAPIClient interface is used as the client-side implementation for the
563
+ // GSSAPI SASL mechanism.
564
+ // Interface inspired by GSSAPIClient from golang.org/x/crypto/ssh
565
+ type GSSAPIClient interface {
566
+ // InitSecContext initiates the establishment of a security context for
567
+ // GSS-API between the client and server.
568
+ // Initially the token parameter should be specified as nil.
569
+ // The routine may return a outputToken which should be transferred to
570
+ // the server, where the server will present it to AcceptSecContext.
571
+ // If no token need be sent, InitSecContext will indicate this by setting
572
+ // needContinue to false. To complete the context
573
+ // establishment, one or more reply tokens may be required from the server;
574
+ // if so, InitSecContext will return a needContinue which is true.
575
+ // In this case, InitSecContext should be called again when the
576
+ // reply token is received from the server, passing the reply token
577
+ // to InitSecContext via the token parameters.
578
+ // See RFC 4752 section 3.1.
579
+ InitSecContext (target string , token []byte ) (outputToken []byte , needContinue bool , err error )
580
+ // NegotiateSaslAuth performs the last step of the Sasl handshake.
581
+ // It takes a token, which, when unwrapped, describes the servers supported
582
+ // security layers (first octet) and maximum receive buffer (remaining
583
+ // three octets).
584
+ // If the received token is unacceptable an error must be returned to abort
585
+ // the handshake.
586
+ // Outputs a signed token describing the client's selected security layer
587
+ // and receive buffer size and optionally an authorization identity.
588
+ // The returned token will be sent to the server and the handshake considered
589
+ // completed successfully and the server authenticated.
590
+ // See RFC 4752 section 3.1.
591
+ NegotiateSaslAuth (token []byte , authzid string ) ([]byte , error )
592
+ // DeleteSecContext destroys any established secure context.
593
+ DeleteSecContext () error
594
+ }
595
+
596
+ // GSSAPIBindRequest represents a GSSAPI SASL mechanism bind request.
597
+ // See rfc4752 and rfc4513 section 5.2.1.2.
598
+ type GSSAPIBindRequest struct {
599
+ // Service Principal Name user for the service ticket. Eg. "ldap/<host>"
600
+ ServicePrincipalName string
601
+ // (Optional) Authorization entity
602
+ AuthZID string
603
+ // (Optional) Controls to send with the bind request
604
+ Controls []Control
605
+ }
606
+
607
+ // GSSAPIBind performs the GSSAPI SASL bind using the provided GSSAPI client.
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
+ // GSSAPIBindRequest performs the GSSAPI SASL bind using the provided GSSAPI client.
616
+ func (l * Conn ) GSSAPIBindRequest (client GSSAPIClient , req * GSSAPIBindRequest ) error {
617
+ // nolint:errcheck
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
+ // Establish secure context between client and server.
627
+ reqToken , needInit , err = client .InitSecContext (req .ServicePrincipalName , recvToken )
628
+ if err != nil {
629
+ return err
630
+ }
631
+ } else {
632
+ // Secure context is set up, perform the last step of SASL handshake.
633
+ reqToken , err = client .NegotiateSaslAuth (recvToken , req .AuthZID )
634
+ if err != nil {
635
+ return err
636
+ }
637
+ }
638
+ // Send Bind request containing the current token and extract the
639
+ // token sent by server.
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
+ // Construct LDAP Bind request with GSSAPI SASL mechanism.
656
+ envelope := ber .Encode (ber .ClassUniversal , ber .TypeConstructed , ber .TagSequence , nil , "LDAP Request" )
657
+ envelope .AppendChild (ber .NewInteger (ber .ClassUniversal , ber .TypePrimitive , ber .TagInteger , l .nextMessageID (), "MessageID" ))
658
+
659
+ request := ber .Encode (ber .ClassApplication , ber .TypeConstructed , ApplicationBindRequest , nil , "Bind Request" )
660
+ request .AppendChild (ber .NewInteger (ber .ClassUniversal , ber .TypePrimitive , ber .TagInteger , 3 , "Version" ))
661
+ request .AppendChild (ber .NewString (ber .ClassUniversal , ber .TypePrimitive , ber .TagOctetString , "" , "User Name" ))
662
+
663
+ auth := ber .Encode (ber .ClassContext , ber .TypeConstructed , 3 , "" , "authentication" )
664
+ auth .AppendChild (ber .NewString (ber .ClassUniversal , ber .TypePrimitive , ber .TagOctetString , "GSSAPI" , "SASL Mech" ))
665
+ if len (reqToken ) > 0 {
666
+ auth .AppendChild (ber .NewString (ber .ClassUniversal , ber .TypePrimitive , ber .TagOctetString , string (reqToken ), "Credentials" ))
667
+ }
668
+ request .AppendChild (auth )
669
+ envelope .AppendChild (request )
670
+ if len (reqControls ) > 0 {
671
+ envelope .AppendChild (encodeControls (reqControls ))
672
+ }
673
+
674
+ msgCtx , err := l .sendMessage (envelope )
675
+ if err != nil {
676
+ return nil , err
677
+ }
678
+ defer l .finishMessage (msgCtx )
679
+
680
+ packet , err := l .readPacket (msgCtx )
681
+ if err != nil {
682
+ return nil , err
683
+ }
684
+ l .Debug .Printf ("%d: got response %p" , msgCtx .id , packet )
685
+ if l .Debug {
686
+ if err = addLDAPDescriptions (packet ); err != nil {
687
+ return nil , err
688
+ }
689
+ ber .PrintPacket (packet )
690
+ }
691
+
692
+ // https://www.rfc-editor.org/rfc/rfc4511#section-4.1.1
693
+ // packet is an envelope
694
+ // child 0 is message id
695
+ // child 1 is protocolOp
696
+ if len (packet .Children ) != 2 {
697
+ return nil , fmt .Errorf ("bad bind response" )
698
+ }
699
+
700
+ protocolOp := packet .Children [1 ]
701
+ RESP:
702
+ switch protocolOp .Description {
703
+ case "Bind Response" : // Bind Response
704
+ // Bind Reponse is an LDAP Response (https://www.rfc-editor.org/rfc/rfc4511#section-4.1.9)
705
+ // with an additional optional serverSaslCreds string (https://www.rfc-editor.org/rfc/rfc4511#section-4.2.2)
706
+ // child 0 is resultCode
707
+ resultCode := protocolOp .Children [0 ]
708
+ if resultCode .Tag != ber .TagEnumerated {
709
+ break RESP
710
+ }
711
+ switch resultCode .Value .(int64 ) {
712
+ case 14 : // Sasl bind in progress
713
+ if len (protocolOp .Children ) < 3 {
714
+ break RESP
715
+ }
716
+ referral := protocolOp .Children [3 ]
717
+ switch referral .Description {
718
+ case "Referral" :
719
+ if referral .ClassType != ber .ClassContext || referral .Tag != ber .TagObjectDescriptor {
720
+ break RESP
721
+ }
722
+ return ioutil .ReadAll (referral .Data )
723
+ }
724
+ // Optional:
725
+ //if len(protocolOp.Children) == 4 {
726
+ // serverSaslCreds := protocolOp.Children[4]
727
+ //}
728
+ case 0 : // Success - Bind OK.
729
+ // SASL layer in effect (if any) (See https://www.rfc-editor.org/rfc/rfc4513#section-5.2.1.4)
730
+ // NOTE: SASL security layers are not supported currently.
731
+ return nil , nil
732
+ }
733
+ }
734
+
735
+ return nil , GetLDAPError (packet )
736
+ }
0 commit comments