Skip to content

Commit a1003a3

Browse files
committed
add function to support bind step by step
Signed-off-by: Yang Keao <[email protected]>
1 parent 83b8f31 commit a1003a3

File tree

1 file changed

+113
-1
lines changed

1 file changed

+113
-1
lines changed

v3/bind.go

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)