@@ -6,11 +6,11 @@ import (
66 enchex "encoding/hex"
77 "errors"
88 "fmt"
9+ "github.com/Azure/go-ntlmssp"
910 "io/ioutil"
1011 "math/rand"
1112 "strings"
1213
13- "github.com/Azure/go-ntlmssp"
1414 ber "github.com/go-asn1-ber/asn1-ber"
1515)
1616
@@ -733,3 +733,115 @@ RESP:
733733
734734 return nil , GetLDAPError (packet )
735735}
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