@@ -6,11 +6,11 @@ import (
6
6
enchex "encoding/hex"
7
7
"errors"
8
8
"fmt"
9
+ "github.com/Azure/go-ntlmssp"
9
10
"io/ioutil"
10
11
"math/rand"
11
12
"strings"
12
13
13
- "github.com/Azure/go-ntlmssp"
14
14
ber "github.com/go-asn1-ber/asn1-ber"
15
15
)
16
16
@@ -733,3 +733,115 @@ RESP:
733
733
734
734
return nil , GetLDAPError (packet )
735
735
}
736
+
737
+ // ServerBindStep sends the SASLBindRequest and return the result code, servercred and errors.
738
+ // If the result code is not SUCCESS(0x00) or IN_PROGRESS(0x0e), it's also considered as an error.
739
+ func (l * Conn ) ServerBindStep (clientCred []byte , dn string , methodName string , controls []Control ) (resultCode uint16 , serverCred []byte , err error ) {
740
+ packet := ber .Encode (ber .ClassUniversal , ber .TypeConstructed , ber .TagSequence , nil , "LDAP Request" )
741
+ packet .AppendChild (ber .NewInteger (ber .ClassUniversal , ber .TypePrimitive , ber .TagInteger , l .nextMessageID (), "MessageID" ))
742
+
743
+ request := ber .Encode (ber .ClassApplication , ber .TypeConstructed , ApplicationBindRequest , nil , "Bind Request" )
744
+ request .AppendChild (ber .NewInteger (ber .ClassUniversal , ber .TypePrimitive , ber .TagInteger , 3 , "Version" ))
745
+ request .AppendChild (ber .NewString (ber .ClassUniversal , ber .TypePrimitive , ber .TagOctetString , dn , "User Name" ))
746
+
747
+ auth := ber .Encode (ber .ClassContext , ber .TypeConstructed , 3 , "" , "authentication" )
748
+ auth .AppendChild (ber .NewString (ber .ClassUniversal , ber .TypePrimitive , ber .TagOctetString , methodName , "SASL Mech" ))
749
+ auth .AppendChild (ber .NewString (ber .ClassUniversal , ber .TypePrimitive , ber .TagOctetString , string (clientCred ), "Credentials" ))
750
+ request .AppendChild (auth )
751
+ packet .AppendChild (request )
752
+
753
+ if len (controls ) > 0 {
754
+ packet .AppendChild (encodeControls (controls ))
755
+ }
756
+
757
+ msgCtx , err := l .sendMessage (packet )
758
+ if err != nil {
759
+ return 0 , nil , err
760
+ }
761
+ defer l .finishMessage (msgCtx )
762
+
763
+ packetResponse , ok := <- msgCtx .responses
764
+ if ! ok {
765
+ return 0 , nil , errors .New ("ldap: response channel closed" )
766
+ }
767
+ packet , err = packetResponse .ReadPacket ()
768
+ if err != nil {
769
+ return 0 , nil , err
770
+ }
771
+
772
+ bindResponse , err := parseBindResponse (packet )
773
+ if err != nil {
774
+ return 0 , nil , err
775
+ }
776
+
777
+ // TODO: add global constants. As this package used `uint16` to represent the result code nearly everywhere,
778
+ // these constants were
779
+ const LDAPResultCodeSuccess = 0
780
+ const LDAPResultCodeSASLBindInProgress = 0x0e
781
+ if bindResponse .resultCode != LDAPResultCodeSuccess && bindResponse .resultCode != LDAPResultCodeSASLBindInProgress {
782
+ return bindResponse .resultCode , nil , & Error {
783
+ ResultCode : bindResponse .resultCode ,
784
+ MatchedDN : bindResponse .matchedDN ,
785
+ Err : fmt .Errorf ("%s" , bindResponse .errorMessage ),
786
+ Packet : packet ,
787
+ }
788
+ }
789
+ return bindResponse .resultCode , bindResponse .serverSaslCreds , nil
790
+ }
791
+
792
+ type bindResponse struct {
793
+ resultCode uint16
794
+ matchedDN string
795
+ errorMessage string
796
+ serverSaslCreds []byte
797
+ }
798
+
799
+ // parseBindResponse parses the bind response. The format of a BindResponse is like below:
800
+ //
801
+ // ```
802
+ //
803
+ // BindResponse ::= [APPLICATION 1] SEQUENCE {
804
+ // COMPONENTS OF LDAPResult,
805
+ // serverSaslCreds [7] OCTET STRING OPTIONAL }
806
+ //
807
+ // LDAPResult ::= SEQUENCE {
808
+ // resultCode ENUMERATED,
809
+ // matchedDN LDAPDN,
810
+ // errorMessage LDAPString,
811
+ // referral [3] Referral OPTIONAL }
812
+ //
813
+ // ```
814
+ //
815
+ // TODO: support `referral` field in this function
816
+ func parseBindResponse (packet * ber.Packet ) (* bindResponse , error ) {
817
+ if packet == nil {
818
+ return nil , & Error {ResultCode : ErrorUnexpectedResponse , Err : fmt .Errorf ("Empty packet" )}
819
+ }
820
+ if len (packet .Children ) < 2 {
821
+ return nil , & Error {ResultCode : ErrorNetwork , Err : fmt .Errorf ("Invalid packet format" ), Packet : packet }
822
+ }
823
+
824
+ response := packet .Children [1 ]
825
+ if response == nil {
826
+ return nil , & Error {ResultCode : ErrorUnexpectedResponse , Err : fmt .Errorf ("Empty response in packet" ), Packet : packet }
827
+ }
828
+
829
+ if ! (response .ClassType == ber .ClassApplication && response .TagType == ber .TypeConstructed && len (response .Children ) >= 3 ) {
830
+ return nil , & Error {ResultCode : ErrorUnexpectedResponse , Err : fmt .Errorf ("Empty response in packet" ), Packet : packet }
831
+ }
832
+ resp := & bindResponse {
833
+ uint16 (response .Children [0 ].Value .(int64 )),
834
+ response .Children [1 ].Value .(string ),
835
+ response .Children [2 ].Value .(string ),
836
+ nil ,
837
+ }
838
+ if len (response .Children ) < 4 {
839
+ return resp , nil
840
+ }
841
+ // then the response.Children[3] can be an referral or serverSaslCreds. It can be asserted with tag
842
+ // TODO: also add referral
843
+ if response .Children [3 ].ClassType != ber .ClassContext || response .Children [3 ].Tag != ber .TagObjectDescriptor {
844
+ resp .serverSaslCreds = response .Children [3 ].Data .Bytes ()
845
+ }
846
+ return resp , nil
847
+ }
0 commit comments