Bump github.com/go-ldap/ldap/v3 from 3.4.5 to 3.4.6

Bumps [github.com/go-ldap/ldap/v3](https://github.com/go-ldap/ldap) from 3.4.5 to 3.4.6.
- [Release notes](https://github.com/go-ldap/ldap/releases)
- [Commits](https://github.com/go-ldap/ldap/compare/v3.4.5...v3.4.6)

---
updated-dependencies:
- dependency-name: github.com/go-ldap/ldap/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2023-11-22 10:51:19 +00:00
committed by Ralf Haferkamp
parent 56073ba09a
commit 7dfb0706b0
15 changed files with 853 additions and 114 deletions

4
go.mod
View File

@@ -23,7 +23,7 @@ require (
github.com/go-chi/chi/v5 v5.0.10
github.com/go-chi/cors v1.2.1
github.com/go-chi/render v1.0.3
github.com/go-ldap/ldap/v3 v3.4.5
github.com/go-ldap/ldap/v3 v3.4.6
github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3
github.com/go-micro/plugins/v4/client/grpc v1.2.1
github.com/go-micro/plugins/v4/logger/zerolog v1.2.0
@@ -184,7 +184,7 @@ require (
github.com/gdexlab/go-render v1.0.1 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-acme/lego/v4 v4.4.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.3.1 // indirect
github.com/go-git/go-git/v5 v5.4.2 // indirect

12
go.sum
View File

@@ -1121,8 +1121,8 @@ github.com/go-acme/lego/v4 v4.4.0 h1:uHhU5LpOYQOdp3aDU+XY2bajseu8fuExphTL1Ss6/Fc
github.com/go-acme/lego/v4 v4.4.0/go.mod h1:l3+tFUFZb590dWcqhWZegynUthtaHJbG2fevUpoOOE0=
github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-asn1-ber/asn1-ber v1.4.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
@@ -1161,8 +1161,8 @@ github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBj
github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U=
github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk=
github.com/go-ldap/ldap/v3 v3.1.7/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=
github.com/go-ldap/ldap/v3 v3.4.5 h1:ekEKmaDrpvR2yf5Nc/DClsGG9lAmdDixe44mLzlW5r8=
github.com/go-ldap/ldap/v3 v3.4.5/go.mod h1:bMGIq3AGbytbaMwf8wdv5Phdxz0FWHTIYMSzyrYgnQs=
github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A=
github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc=
github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3 h1:sfz1YppV05y4sYaW7kXZtrocU/+vimnIWt4cxAYh7+o=
github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3/go.mod h1:ZXFhGda43Z2TVbfGZefXyMJzsDHhCh0go3bZUcwTx7o=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
@@ -1377,6 +1377,7 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
@@ -2146,6 +2147,7 @@ golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -2495,6 +2497,7 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -2516,6 +2519,7 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

View File

@@ -170,12 +170,10 @@ func PrintPacket(p *Packet) {
printPacket(os.Stdout, p, 0, false)
}
func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) {
indentStr := ""
for len(indentStr) != indent {
indentStr += " "
}
// Return a string describing packet content. This is not recursive,
// If the packet is a sequence, use `printPacket()`, or browse
// sequence yourself.
func DescribePacket(p *Packet) string {
classStr := ClassMap[p.ClassType]
@@ -194,7 +192,17 @@ func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) {
description = p.Description + ": "
}
_, _ = fmt.Fprintf(out, "%s%s(%s, %s, %s) Len=%d %q\n", indentStr, description, classStr, tagTypeStr, tagStr, p.Data.Len(), value)
return fmt.Sprintf("%s(%s, %s, %s) Len=%d %q", description, classStr, tagTypeStr, tagStr, p.Data.Len(), value)
}
func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) {
indentStr := ""
for len(indentStr) != indent {
indentStr += " "
}
_, _ = fmt.Fprintf(out, "%s%s\n", indentStr, DescribePacket(p))
if printBytes {
PrintBytes(out, p.Bytes(), indentStr)
@@ -317,7 +325,7 @@ func readPacket(reader io.Reader) (*Packet, int, error) {
// Read the next packet
child, r, err := readPacket(reader)
if err != nil {
return nil, read, err
return nil, read, unexpectedEOF(err)
}
contentRead += r
read += r
@@ -348,10 +356,7 @@ func readPacket(reader io.Reader) (*Packet, int, error) {
if length > 0 {
_, err := io.ReadFull(reader, content)
if err != nil {
if err == io.EOF {
return nil, read, io.ErrUnexpectedEOF
}
return nil, read, err
return nil, read, unexpectedEOF(err)
}
read += length
}

View File

@@ -37,7 +37,7 @@ func readIdentifier(reader io.Reader) (Identifier, int, error) {
if Debug {
fmt.Printf("error reading high-tag-number tag byte %d: %v\n", tagBytes, err)
}
return Identifier{}, read, err
return Identifier{}, read, unexpectedEOF(err)
}
tagBytes++
read++

View File

@@ -13,7 +13,7 @@ func readLength(reader io.Reader) (length int, read int, err error) {
if Debug {
fmt.Printf("error reading length byte: %v\n", err)
}
return 0, 0, err
return 0, 0, unexpectedEOF(err)
}
read++
@@ -47,7 +47,7 @@ func readLength(reader io.Reader) (length int, read int, err error) {
if Debug {
fmt.Printf("error reading long-form length byte %d: %v\n", i, err)
}
return 0, read, err
return 0, read, unexpectedEOF(err)
}
read++

View File

@@ -89,12 +89,18 @@ func parseBinaryFloat(v []byte) (float64, error) {
case 0x02:
expLen = 3
case 0x03:
if len(v) < 2 {
return 0.0, errors.New("invalid data")
}
expLen = int(v[0])
if expLen > 8 {
return 0.0, errors.New("too big value of exponent")
}
v = v[1:]
}
if expLen > len(v) {
return 0.0, errors.New("too big value of exponent")
}
buf, v = v[:expLen], v[expLen:]
exponent, err := ParseInt64(buf)
if err != nil {

View File

@@ -6,14 +6,18 @@ func readByte(reader io.Reader) (byte, error) {
bytes := make([]byte, 1)
_, err := io.ReadFull(reader, bytes)
if err != nil {
if err == io.EOF {
return 0, io.ErrUnexpectedEOF
}
return 0, err
}
return bytes[0], nil
}
func unexpectedEOF(err error) error {
if err == io.EOF {
return io.ErrUnexpectedEOF
}
return err
}
func isEOCPacket(p *Packet) bool {
return p != nil &&
p.Tag == TagEOC &&

View File

@@ -1,6 +1,7 @@
package ldap
import (
"context"
"crypto/tls"
"time"
)
@@ -32,6 +33,9 @@ type Client interface {
PasswordModify(*PasswordModifyRequest) (*PasswordModifyResult, error)
Search(*SearchRequest) (*SearchResult, error)
SearchAsync(ctx context.Context, searchRequest *SearchRequest, bufferSize int) Response
SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error)
DirSync(searchRequest *SearchRequest, flags, maxAttrCount int64, cookie []byte) (*SearchResult, error)
DirSyncAsync(ctx context.Context, searchRequest *SearchRequest, bufferSize int, flags, maxAttrCount int64, cookie []byte) Response
Syncrepl(ctx context.Context, searchRequest *SearchRequest, bufferSize int, mode ControlSyncRequestMode, cookie []byte, reloadHint bool) Response
}

View File

@@ -288,9 +288,9 @@ func (l *Conn) Close() (err error) {
l.chanMessage <- &messagePacket{Op: MessageQuit}
timeoutCtx := context.Background()
if l.requestTimeout > 0 {
if l.getTimeout() > 0 {
var cancelFunc context.CancelFunc
timeoutCtx, cancelFunc = context.WithTimeout(timeoutCtx, time.Duration(l.requestTimeout))
timeoutCtx, cancelFunc = context.WithTimeout(timeoutCtx, time.Duration(l.getTimeout()))
defer cancelFunc()
}
select {
@@ -316,6 +316,10 @@ func (l *Conn) SetTimeout(timeout time.Duration) {
atomic.StoreInt64(&l.requestTimeout, int64(timeout))
}
func (l *Conn) getTimeout() int64 {
return atomic.LoadInt64(&l.requestTimeout)
}
// Returns the next available messageID
func (l *Conn) nextMessageID() int64 {
if messageID, ok := <-l.chanMessageID; ok {
@@ -325,8 +329,10 @@ func (l *Conn) nextMessageID() int64 {
}
// GetLastError returns the last recorded error from goroutines like processMessages and reader.
// // Only the last recorded error will be returned.
// Only the last recorded error will be returned.
func (l *Conn) GetLastError() error {
l.messageMutex.Lock()
defer l.messageMutex.Unlock()
return l.err
}
@@ -484,7 +490,7 @@ func (l *Conn) processMessages() {
// If we are closing due to an error, inform anyone who
// is waiting about the error.
if l.IsClosing() && l.closeErr.Load() != nil {
msgCtx.sendResponse(&PacketResponse{Error: l.closeErr.Load().(error)}, time.Duration(l.requestTimeout))
msgCtx.sendResponse(&PacketResponse{Error: l.closeErr.Load().(error)}, time.Duration(l.getTimeout()))
}
l.Debug.Printf("Closing channel for MessageID %d", messageID)
close(msgCtx.responses)
@@ -512,7 +518,7 @@ func (l *Conn) processMessages() {
_, err := l.conn.Write(buf)
if err != nil {
l.Debug.Printf("Error Sending Message: %s", err.Error())
message.Context.sendResponse(&PacketResponse{Error: fmt.Errorf("unable to send request: %s", err)}, time.Duration(l.requestTimeout))
message.Context.sendResponse(&PacketResponse{Error: fmt.Errorf("unable to send request: %s", err)}, time.Duration(l.getTimeout()))
close(message.Context.responses)
break
}
@@ -522,9 +528,10 @@ func (l *Conn) processMessages() {
l.messageContexts[message.MessageID] = message.Context
// Add timeout if defined
if l.requestTimeout > 0 {
requestTimeout := l.getTimeout()
if requestTimeout > 0 {
go func() {
timer := time.NewTimer(time.Duration(l.requestTimeout))
timer := time.NewTimer(time.Duration(requestTimeout))
defer func() {
if err := recover(); err != nil {
l.err = fmt.Errorf("ldap: recovered panic in RequestTimeout: %v", err)
@@ -547,7 +554,7 @@ func (l *Conn) processMessages() {
case MessageResponse:
l.Debug.Printf("Receiving message %d", message.MessageID)
if msgCtx, ok := l.messageContexts[message.MessageID]; ok {
msgCtx.sendResponse(&PacketResponse{message.Packet, nil}, time.Duration(l.requestTimeout))
msgCtx.sendResponse(&PacketResponse{message.Packet, nil}, time.Duration(l.getTimeout()))
} else {
l.err = fmt.Errorf("ldap: received unexpected message %d, %v", message.MessageID, l.IsClosing())
l.Debug.PrintPacket(message.Packet)
@@ -557,7 +564,7 @@ func (l *Conn) processMessages() {
// All reads will return immediately
if msgCtx, ok := l.messageContexts[message.MessageID]; ok {
l.Debug.Printf("Receiving message timeout for %d", message.MessageID)
msgCtx.sendResponse(&PacketResponse{message.Packet, NewError(ErrorNetwork, errors.New("ldap: connection timed out"))}, time.Duration(l.requestTimeout))
msgCtx.sendResponse(&PacketResponse{message.Packet, NewError(ErrorNetwork, errors.New("ldap: connection timed out"))}, time.Duration(l.getTimeout()))
delete(l.messageContexts, message.MessageID)
close(msgCtx.responses)
}

View File

@@ -5,6 +5,7 @@ import (
"strconv"
ber "github.com/go-asn1-ber/asn1-ber"
"github.com/google/uuid"
)
const (
@@ -36,6 +37,15 @@ const (
ControlTypeMicrosoftServerLinkTTL = "1.2.840.113556.1.4.2309"
// ControlTypeDirSync - Active Directory DirSync - https://msdn.microsoft.com/en-us/library/aa366978(v=vs.85).aspx
ControlTypeDirSync = "1.2.840.113556.1.4.841"
// ControlTypeSyncRequest - https://www.ietf.org/rfc/rfc4533.txt
ControlTypeSyncRequest = "1.3.6.1.4.1.4203.1.9.1.1"
// ControlTypeSyncState - https://www.ietf.org/rfc/rfc4533.txt
ControlTypeSyncState = "1.3.6.1.4.1.4203.1.9.1.2"
// ControlTypeSyncDone - https://www.ietf.org/rfc/rfc4533.txt
ControlTypeSyncDone = "1.3.6.1.4.1.4203.1.9.1.3"
// ControlTypeSyncInfo - https://www.ietf.org/rfc/rfc4533.txt
ControlTypeSyncInfo = "1.3.6.1.4.1.4203.1.9.1.4"
)
// Flags for DirSync control
@@ -58,6 +68,10 @@ var ControlTypeMap = map[string]string{
ControlTypeServerSideSorting: "Server Side Sorting Request - LDAP Control Extension for Server Side Sorting of Search Results (RFC2891)",
ControlTypeServerSideSortingResult: "Server Side Sorting Results - LDAP Control Extension for Server Side Sorting of Search Results (RFC2891)",
ControlTypeDirSync: "DirSync",
ControlTypeSyncRequest: "Sync Request",
ControlTypeSyncState: "Sync State",
ControlTypeSyncDone: "Sync Done",
ControlTypeSyncInfo: "Sync Info",
}
// Control defines an interface controls provide to encode and describe themselves
@@ -390,7 +404,13 @@ func DecodeControl(packet *ber.Packet) (Control, error) {
case 2:
packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
ControlType = packet.Children[0].Value.(string)
if packet.Children[0].Value != nil {
ControlType = packet.Children[0].Value.(string)
} else if packet.Children[0].Data != nil {
ControlType = packet.Children[0].Data.String()
} else {
return nil, fmt.Errorf("not found where to get the control type")
}
// Children[1] could be criticality or value (both are optional)
// duck-type on whether this is a boolean
@@ -514,29 +534,28 @@ func DecodeControl(packet *ber.Packet) (Control, error) {
return NewControlServerSideSortingResult(value)
case ControlTypeDirSync:
value.Description += " (DirSync)"
c := new(ControlDirSync)
if value.Value != nil {
valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
if err != nil {
return nil, err
}
value.Data.Truncate(0)
value.Value = nil
value.AppendChild(valueChildren)
return NewResponseControlDirSync(value)
case ControlTypeSyncState:
value.Description += " (Sync State)"
valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
if err != nil {
return nil, fmt.Errorf("failed to decode data bytes: %s", err)
}
value = value.Children[0]
if len(value.Children) != 3 { // also on initial creation, Cookie is an empty string
return nil, fmt.Errorf("invalid number of children in dirSync control")
return NewControlSyncState(valueChildren)
case ControlTypeSyncDone:
value.Description += " (Sync Done)"
valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
if err != nil {
return nil, fmt.Errorf("failed to decode data bytes: %s", err)
}
value.Description = "DirSync Control Value"
value.Children[0].Description = "Flags"
value.Children[1].Description = "MaxAttrCnt"
value.Children[2].Description = "Cookie"
c.Flags = value.Children[0].Value.(int64)
c.MaxAttrCnt = value.Children[1].Value.(int64)
c.Cookie = value.Children[2].Data.Bytes()
value.Children[2].Value = c.Cookie
return c, nil
return NewControlSyncDone(valueChildren)
case ControlTypeSyncInfo:
value.Description += " (Sync Info)"
valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
if err != nil {
return nil, fmt.Errorf("failed to decode data bytes: %s", err)
}
return NewControlSyncInfo(valueChildren)
default:
c := new(ControlString)
c.ControlType = ControlType
@@ -610,20 +629,59 @@ func encodeControls(controls []Control) *ber.Packet {
// ControlDirSync implements the control described in https://msdn.microsoft.com/en-us/library/aa366978(v=vs.85).aspx
type ControlDirSync struct {
Flags int64
MaxAttrCnt int64
Cookie []byte
Criticality bool
Flags int64
MaxAttrCount int64
Cookie []byte
}
// NewControlDirSync returns a dir sync control
// @deprecated Use NewRequestControlDirSync instead
func NewControlDirSync(flags int64, maxAttrCount int64, cookie []byte) *ControlDirSync {
return NewRequestControlDirSync(flags, maxAttrCount, cookie)
}
// NewRequestControlDirSync returns a dir sync control
func NewRequestControlDirSync(
flags int64, maxAttrCount int64, cookie []byte,
) *ControlDirSync {
return &ControlDirSync{
Flags: flags,
MaxAttrCnt: maxAttrCount,
Cookie: cookie,
Criticality: true,
Flags: flags,
MaxAttrCount: maxAttrCount,
Cookie: cookie,
}
}
// NewResponseControlDirSync returns a dir sync control
func NewResponseControlDirSync(value *ber.Packet) (*ControlDirSync, error) {
if value.Value != nil {
valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
if err != nil {
return nil, fmt.Errorf("failed to decode data bytes: %s", err)
}
value.Data.Truncate(0)
value.Value = nil
value.AppendChild(valueChildren)
}
child := value.Children[0]
if len(child.Children) != 3 { // also on initial creation, Cookie is an empty string
return nil, fmt.Errorf("invalid number of children in dirSync control")
}
child.Description = "DirSync Control Value"
child.Children[0].Description = "Flags"
child.Children[1].Description = "MaxAttrCount"
child.Children[2].Description = "Cookie"
cookie := child.Children[2].Data.Bytes()
child.Children[2].Value = cookie
return &ControlDirSync{
Criticality: true,
Flags: child.Children[0].Value.(int64),
MaxAttrCount: child.Children[1].Value.(int64),
Cookie: cookie,
}, nil
}
// GetControlType returns the OID
func (c *ControlDirSync) GetControlType() string {
return ControlTypeDirSync
@@ -631,28 +689,33 @@ func (c *ControlDirSync) GetControlType() string {
// String returns a human-readable description
func (c *ControlDirSync) String() string {
return fmt.Sprintf("ControlType: %s (%q), Criticality: true, ControlValue: Flags: %d, MaxAttrCnt: %d", ControlTypeMap[ControlTypeDirSync], ControlTypeDirSync, c.Flags, c.MaxAttrCnt)
return fmt.Sprintf(
"ControlType: %s (%q) Criticality: %t ControlValue: Flags: %d MaxAttrCount: %d",
ControlTypeMap[ControlTypeDirSync],
ControlTypeDirSync,
c.Criticality,
c.Flags,
c.MaxAttrCount,
)
}
// Encode returns the ber packet representation
func (c *ControlDirSync) Encode() *ber.Packet {
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeDirSync, "Control Type ("+ControlTypeMap[ControlTypeDirSync]+")"))
packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, true, "Criticality")) // must be true always
val := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (DirSync)")
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "DirSync Control Value")
seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.Flags), "Flags"))
seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.MaxAttrCnt), "MaxAttrCount"))
cookie := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "Cookie")
if len(c.Cookie) != 0 {
cookie.Value = c.Cookie
cookie.Data.Write(c.Cookie)
}
seq.AppendChild(cookie)
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeDirSync, "Control Type ("+ControlTypeMap[ControlTypeDirSync]+")"))
packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality")) // must be true always
val := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (DirSync)")
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "DirSync Control Value")
seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.Flags), "Flags"))
seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.MaxAttrCount), "MaxAttrCount"))
seq.AppendChild(cookie)
val.AppendChild(seq)
packet.AppendChild(val)
@@ -850,3 +913,382 @@ func (c *ControlServerSideSortingResult) String() string {
c.Result,
)
}
// Mode for ControlTypeSyncRequest
type ControlSyncRequestMode int64
const (
SyncRequestModeRefreshOnly ControlSyncRequestMode = 1
SyncRequestModeRefreshAndPersist ControlSyncRequestMode = 3
)
// ControlSyncRequest implements the Sync Request Control described in https://www.ietf.org/rfc/rfc4533.txt
type ControlSyncRequest struct {
Criticality bool
Mode ControlSyncRequestMode
Cookie []byte
ReloadHint bool
}
func NewControlSyncRequest(
mode ControlSyncRequestMode, cookie []byte, reloadHint bool,
) *ControlSyncRequest {
return &ControlSyncRequest{
Criticality: true,
Mode: mode,
Cookie: cookie,
ReloadHint: reloadHint,
}
}
// GetControlType returns the OID
func (c *ControlSyncRequest) GetControlType() string {
return ControlTypeSyncRequest
}
// Encode encodes the control
func (c *ControlSyncRequest) Encode() *ber.Packet {
_mode := int64(c.Mode)
mode := ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, _mode, "Mode")
var cookie *ber.Packet
if len(c.Cookie) > 0 {
cookie = ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Cookie")
cookie.Value = c.Cookie
cookie.Data.Write(c.Cookie)
}
reloadHint := ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.ReloadHint, "Reload Hint")
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeSyncRequest, "Control Type ("+ControlTypeMap[ControlTypeSyncRequest]+")"))
packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))
val := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (Sync Request)")
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Sync Request Value")
seq.AppendChild(mode)
if cookie != nil {
seq.AppendChild(cookie)
}
seq.AppendChild(reloadHint)
val.AppendChild(seq)
packet.AppendChild(val)
return packet
}
// String returns a human-readable description
func (c *ControlSyncRequest) String() string {
return fmt.Sprintf(
"Control Type: %s (%q) Criticality: %t Mode: %d Cookie: %s ReloadHint: %t",
ControlTypeMap[ControlTypeSyncRequest],
ControlTypeSyncRequest,
c.Criticality,
c.Mode,
string(c.Cookie),
c.ReloadHint,
)
}
// State for ControlSyncState
type ControlSyncStateState int64
const (
SyncStatePresent ControlSyncStateState = 0
SyncStateAdd ControlSyncStateState = 1
SyncStateModify ControlSyncStateState = 2
SyncStateDelete ControlSyncStateState = 3
)
// ControlSyncState implements the Sync State Control described in https://www.ietf.org/rfc/rfc4533.txt
type ControlSyncState struct {
Criticality bool
State ControlSyncStateState
EntryUUID uuid.UUID
Cookie []byte
}
func NewControlSyncState(pkt *ber.Packet) (*ControlSyncState, error) {
var (
state ControlSyncStateState
entryUUID uuid.UUID
cookie []byte
err error
)
switch len(pkt.Children) {
case 0, 1:
return nil, fmt.Errorf("at least two children are required: %d", len(pkt.Children))
case 2:
state = ControlSyncStateState(pkt.Children[0].Value.(int64))
entryUUID, err = uuid.FromBytes(pkt.Children[1].ByteValue)
if err != nil {
return nil, fmt.Errorf("failed to decode uuid: %w", err)
}
case 3:
state = ControlSyncStateState(pkt.Children[0].Value.(int64))
entryUUID, err = uuid.FromBytes(pkt.Children[1].ByteValue)
if err != nil {
return nil, fmt.Errorf("failed to decode uuid: %w", err)
}
cookie = pkt.Children[2].ByteValue
}
return &ControlSyncState{
Criticality: false,
State: state,
EntryUUID: entryUUID,
Cookie: cookie,
}, nil
}
// GetControlType returns the OID
func (c *ControlSyncState) GetControlType() string {
return ControlTypeSyncState
}
// Encode encodes the control
func (c *ControlSyncState) Encode() *ber.Packet {
return nil
}
// String returns a human-readable description
func (c *ControlSyncState) String() string {
return fmt.Sprintf(
"Control Type: %s (%q) Criticality: %t State: %d EntryUUID: %s Cookie: %s",
ControlTypeMap[ControlTypeSyncState],
ControlTypeSyncState,
c.Criticality,
c.State,
c.EntryUUID.String(),
string(c.Cookie),
)
}
// ControlSyncDone implements the Sync Done Control described in https://www.ietf.org/rfc/rfc4533.txt
type ControlSyncDone struct {
Criticality bool
Cookie []byte
RefreshDeletes bool
}
func NewControlSyncDone(pkt *ber.Packet) (*ControlSyncDone, error) {
var (
cookie []byte
refreshDeletes bool
)
switch len(pkt.Children) {
case 0:
// have nothing to do
case 1:
cookie = pkt.Children[0].ByteValue
case 2:
cookie = pkt.Children[0].ByteValue
refreshDeletes = pkt.Children[1].Value.(bool)
}
return &ControlSyncDone{
Criticality: false,
Cookie: cookie,
RefreshDeletes: refreshDeletes,
}, nil
}
// GetControlType returns the OID
func (c *ControlSyncDone) GetControlType() string {
return ControlTypeSyncDone
}
// Encode encodes the control
func (c *ControlSyncDone) Encode() *ber.Packet {
return nil
}
// String returns a human-readable description
func (c *ControlSyncDone) String() string {
return fmt.Sprintf(
"Control Type: %s (%q) Criticality: %t Cookie: %s RefreshDeletes: %t",
ControlTypeMap[ControlTypeSyncDone],
ControlTypeSyncDone,
c.Criticality,
string(c.Cookie),
c.RefreshDeletes,
)
}
// Tag For ControlSyncInfo
type ControlSyncInfoValue uint64
const (
SyncInfoNewcookie ControlSyncInfoValue = 0
SyncInfoRefreshDelete ControlSyncInfoValue = 1
SyncInfoRefreshPresent ControlSyncInfoValue = 2
SyncInfoSyncIdSet ControlSyncInfoValue = 3
)
// ControlSyncInfoNewCookie implements a part of syncInfoValue described in https://www.ietf.org/rfc/rfc4533.txt
type ControlSyncInfoNewCookie struct {
Cookie []byte
}
// String returns a human-readable description
func (c *ControlSyncInfoNewCookie) String() string {
return fmt.Sprintf(
"NewCookie[Cookie: %s]",
string(c.Cookie),
)
}
// ControlSyncInfoRefreshDelete implements a part of syncInfoValue described in https://www.ietf.org/rfc/rfc4533.txt
type ControlSyncInfoRefreshDelete struct {
Cookie []byte
RefreshDone bool
}
// String returns a human-readable description
func (c *ControlSyncInfoRefreshDelete) String() string {
return fmt.Sprintf(
"RefreshDelete[Cookie: %s RefreshDone: %t]",
string(c.Cookie),
c.RefreshDone,
)
}
// ControlSyncInfoRefreshPresent implements a part of syncInfoValue described in https://www.ietf.org/rfc/rfc4533.txt
type ControlSyncInfoRefreshPresent struct {
Cookie []byte
RefreshDone bool
}
// String returns a human-readable description
func (c *ControlSyncInfoRefreshPresent) String() string {
return fmt.Sprintf(
"RefreshPresent[Cookie: %s RefreshDone: %t]",
string(c.Cookie),
c.RefreshDone,
)
}
// ControlSyncInfoSyncIdSet implements a part of syncInfoValue described in https://www.ietf.org/rfc/rfc4533.txt
type ControlSyncInfoSyncIdSet struct {
Cookie []byte
RefreshDeletes bool
SyncUUIDs []uuid.UUID
}
// String returns a human-readable description
func (c *ControlSyncInfoSyncIdSet) String() string {
return fmt.Sprintf(
"SyncIdSet[Cookie: %s RefreshDeletes: %t SyncUUIDs: %v]",
string(c.Cookie),
c.RefreshDeletes,
c.SyncUUIDs,
)
}
// ControlSyncInfo implements the Sync Info Control described in https://www.ietf.org/rfc/rfc4533.txt
type ControlSyncInfo struct {
Criticality bool
Value ControlSyncInfoValue
NewCookie *ControlSyncInfoNewCookie
RefreshDelete *ControlSyncInfoRefreshDelete
RefreshPresent *ControlSyncInfoRefreshPresent
SyncIdSet *ControlSyncInfoSyncIdSet
}
func NewControlSyncInfo(pkt *ber.Packet) (*ControlSyncInfo, error) {
var (
cookie []byte
refreshDone = true
refreshDeletes bool
syncUUIDs []uuid.UUID
)
c := &ControlSyncInfo{Criticality: false}
switch ControlSyncInfoValue(pkt.Identifier.Tag) {
case SyncInfoNewcookie:
c.Value = SyncInfoNewcookie
c.NewCookie = &ControlSyncInfoNewCookie{
Cookie: pkt.ByteValue,
}
case SyncInfoRefreshDelete:
c.Value = SyncInfoRefreshDelete
switch len(pkt.Children) {
case 0:
// have nothing to do
case 1:
cookie = pkt.Children[0].ByteValue
case 2:
cookie = pkt.Children[0].ByteValue
refreshDone = pkt.Children[1].Value.(bool)
}
c.RefreshDelete = &ControlSyncInfoRefreshDelete{
Cookie: cookie,
RefreshDone: refreshDone,
}
case SyncInfoRefreshPresent:
c.Value = SyncInfoRefreshPresent
switch len(pkt.Children) {
case 0:
// have nothing to do
case 1:
cookie = pkt.Children[0].ByteValue
case 2:
cookie = pkt.Children[0].ByteValue
refreshDone = pkt.Children[1].Value.(bool)
}
c.RefreshPresent = &ControlSyncInfoRefreshPresent{
Cookie: cookie,
RefreshDone: refreshDone,
}
case SyncInfoSyncIdSet:
c.Value = SyncInfoSyncIdSet
switch len(pkt.Children) {
case 0:
// have nothing to do
case 1:
cookie = pkt.Children[0].ByteValue
case 2:
cookie = pkt.Children[0].ByteValue
refreshDeletes = pkt.Children[1].Value.(bool)
case 3:
cookie = pkt.Children[0].ByteValue
refreshDeletes = pkt.Children[1].Value.(bool)
syncUUIDs = make([]uuid.UUID, 0, len(pkt.Children[2].Children))
for _, child := range pkt.Children[2].Children {
u, err := uuid.FromBytes(child.ByteValue)
if err != nil {
return nil, fmt.Errorf("failed to decode uuid: %w", err)
}
syncUUIDs = append(syncUUIDs, u)
}
}
c.SyncIdSet = &ControlSyncInfoSyncIdSet{
Cookie: cookie,
RefreshDeletes: refreshDeletes,
SyncUUIDs: syncUUIDs,
}
default:
return nil, fmt.Errorf("unknown sync info value: %d", pkt.Identifier.Tag)
}
return c, nil
}
// GetControlType returns the OID
func (c *ControlSyncInfo) GetControlType() string {
return ControlTypeSyncInfo
}
// Encode encodes the control
func (c *ControlSyncInfo) Encode() *ber.Packet {
return nil
}
// String returns a human-readable description
func (c *ControlSyncInfo) String() string {
return fmt.Sprintf(
"Control Type: %s (%q) Criticality: %t Value: %d %s %s %s %s",
ControlTypeMap[ControlTypeSyncInfo],
ControlTypeSyncInfo,
c.Criticality,
c.Value,
c.NewCookie,
c.RefreshDelete,
c.RefreshPresent,
c.SyncIdSet,
)
}

View File

@@ -192,6 +192,8 @@ func (e *Error) Error() string {
return fmt.Sprintf("LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[e.ResultCode], e.Err.Error())
}
func (e *Error) Unwrap() error { return e.Err }
// GetLDAPError creates an Error out of a BER packet representing a LDAPResult
// The return is an error object. It can be casted to a Error structure.
// This function returns nil if resultCode in the LDAPResult sequence is success(0).
@@ -206,15 +208,21 @@ func GetLDAPError(packet *ber.Packet) error {
return &Error{ResultCode: ErrorUnexpectedResponse, Err: fmt.Errorf("Empty response in packet"), Packet: packet}
}
if response.ClassType == ber.ClassApplication && response.TagType == ber.TypeConstructed && len(response.Children) >= 3 {
resultCode := uint16(response.Children[0].Value.(int64))
if resultCode == 0 { // No error
return nil
}
return &Error{
ResultCode: resultCode,
MatchedDN: response.Children[1].Value.(string),
Err: fmt.Errorf("%s", response.Children[2].Value.(string)),
Packet: packet,
if ber.Type(response.Children[0].Tag) == ber.Type(ber.TagInteger) || ber.Type(response.Children[0].Tag) == ber.Type(ber.TagEnumerated) {
resultCode := uint16(response.Children[0].Value.(int64))
if resultCode == 0 { // No error
return nil
}
if ber.Type(response.Children[1].Tag) == ber.Type(ber.TagOctetString) &&
ber.Type(response.Children[2].Tag) == ber.Type(ber.TagOctetString) {
return &Error{
ResultCode: resultCode,
MatchedDN: response.Children[1].Value.(string),
Err: fmt.Errorf("%s", response.Children[2].Value.(string)),
Packet: packet,
}
}
}
}
}

View File

@@ -32,6 +32,7 @@ const (
ApplicationSearchResultReference = 19
ApplicationExtendedRequest = 23
ApplicationExtendedResponse = 24
ApplicationIntermediateResponse = 25
)
// ApplicationMap contains human readable descriptions of LDAP Application Codes
@@ -56,6 +57,7 @@ var ApplicationMap = map[uint8]string{
ApplicationSearchResultReference: "Search Result Reference",
ApplicationExtendedRequest: "Extended Request",
ApplicationExtendedResponse: "Extended Response",
ApplicationIntermediateResponse: "Intermediate Response",
}
// Ldap Behera Password Policy Draft 10 (https://tools.ietf.org/html/draft-behera-ldap-password-policy-10)

207
vendor/github.com/go-ldap/ldap/v3/response.go generated vendored Normal file
View File

@@ -0,0 +1,207 @@
package ldap
import (
"context"
"errors"
"fmt"
ber "github.com/go-asn1-ber/asn1-ber"
)
// Response defines an interface to get data from an LDAP server
type Response interface {
Entry() *Entry
Referral() string
Controls() []Control
Err() error
Next() bool
}
type searchResponse struct {
conn *Conn
ch chan *SearchSingleResult
entry *Entry
referral string
controls []Control
err error
}
// Entry returns an entry from the given search request
func (r *searchResponse) Entry() *Entry {
return r.entry
}
// Referral returns a referral from the given search request
func (r *searchResponse) Referral() string {
return r.referral
}
// Controls returns controls from the given search request
func (r *searchResponse) Controls() []Control {
return r.controls
}
// Err returns an error when the given search request was failed
func (r *searchResponse) Err() error {
return r.err
}
// Next returns whether next data exist or not
func (r *searchResponse) Next() bool {
res, ok := <-r.ch
if !ok {
return false
}
if res == nil {
return false
}
r.err = res.Error
if r.err != nil {
return false
}
r.entry = res.Entry
r.referral = res.Referral
r.controls = res.Controls
return true
}
func (r *searchResponse) start(ctx context.Context, searchRequest *SearchRequest) {
go func() {
defer func() {
close(r.ch)
if err := recover(); err != nil {
r.conn.err = fmt.Errorf("ldap: recovered panic in searchResponse: %v", err)
}
}()
if r.conn.IsClosing() {
return
}
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, r.conn.nextMessageID(), "MessageID"))
// encode search request
err := searchRequest.appendTo(packet)
if err != nil {
r.ch <- &SearchSingleResult{Error: err}
return
}
r.conn.Debug.PrintPacket(packet)
msgCtx, err := r.conn.sendMessage(packet)
if err != nil {
r.ch <- &SearchSingleResult{Error: err}
return
}
defer r.conn.finishMessage(msgCtx)
foundSearchSingleResultDone := false
for !foundSearchSingleResultDone {
select {
case <-ctx.Done():
r.conn.Debug.Printf("%d: %s", msgCtx.id, ctx.Err().Error())
return
default:
r.conn.Debug.Printf("%d: waiting for response", msgCtx.id)
packetResponse, ok := <-msgCtx.responses
if !ok {
err := NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
r.ch <- &SearchSingleResult{Error: err}
return
}
packet, err = packetResponse.ReadPacket()
r.conn.Debug.Printf("%d: got response %p", msgCtx.id, packet)
if err != nil {
r.ch <- &SearchSingleResult{Error: err}
return
}
if r.conn.Debug {
if err := addLDAPDescriptions(packet); err != nil {
r.ch <- &SearchSingleResult{Error: err}
return
}
ber.PrintPacket(packet)
}
switch packet.Children[1].Tag {
case ApplicationSearchResultEntry:
result := &SearchSingleResult{
Entry: &Entry{
DN: packet.Children[1].Children[0].Value.(string),
Attributes: unpackAttributes(packet.Children[1].Children[1].Children),
},
}
if len(packet.Children) != 3 {
r.ch <- result
continue
}
decoded, err := DecodeControl(packet.Children[2].Children[0])
if err != nil {
werr := fmt.Errorf("failed to decode search result entry: %w", err)
result.Error = werr
r.ch <- result
return
}
result.Controls = append(result.Controls, decoded)
r.ch <- result
case ApplicationSearchResultDone:
if err := GetLDAPError(packet); err != nil {
r.ch <- &SearchSingleResult{Error: err}
return
}
if len(packet.Children) == 3 {
result := &SearchSingleResult{}
for _, child := range packet.Children[2].Children {
decodedChild, err := DecodeControl(child)
if err != nil {
werr := fmt.Errorf("failed to decode child control: %w", err)
r.ch <- &SearchSingleResult{Error: werr}
return
}
result.Controls = append(result.Controls, decodedChild)
}
r.ch <- result
}
foundSearchSingleResultDone = true
case ApplicationSearchResultReference:
ref := packet.Children[1].Children[0].Value.(string)
r.ch <- &SearchSingleResult{Referral: ref}
case ApplicationIntermediateResponse:
decoded, err := DecodeControl(packet.Children[1])
if err != nil {
werr := fmt.Errorf("failed to decode intermediate response: %w", err)
r.ch <- &SearchSingleResult{Error: werr}
return
}
result := &SearchSingleResult{}
result.Controls = append(result.Controls, decoded)
r.ch <- result
default:
err := fmt.Errorf("unknown tag: %d", packet.Children[1].Tag)
r.ch <- &SearchSingleResult{Error: err}
return
}
}
}
r.conn.Debug.Printf("%d: returning", msgCtx.id)
}()
}
func newSearchResponse(conn *Conn, bufferSize int) *searchResponse {
var ch chan *SearchSingleResult
if bufferSize > 0 {
ch = make(chan *SearchSingleResult, bufferSize)
} else {
ch = make(chan *SearchSingleResult)
}
return &searchResponse{
conn: conn,
ch: ch,
}
}

View File

@@ -1,6 +1,7 @@
package ldap
import (
"context"
"errors"
"fmt"
"reflect"
@@ -377,6 +378,28 @@ func (s *SearchResult) appendTo(r *SearchResult) {
r.Controls = append(r.Controls, s.Controls...)
}
// SearchSingleResult holds the server's single entry response to a search request
type SearchSingleResult struct {
// Entry is the returned entry
Entry *Entry
// Referral is the returned referral
Referral string
// Controls are the returned controls
Controls []Control
// Error is set when the search request was failed
Error error
}
// Print outputs a human-readable description
func (s *SearchSingleResult) Print() {
s.Entry.Print()
}
// PrettyPrint outputs a human-readable description with indenting
func (s *SearchSingleResult) PrettyPrint(indent int) {
s.Entry.PrettyPrint(indent)
}
// SearchRequest represents a search request to send to the server
type SearchRequest struct {
BaseDN string
@@ -561,6 +584,32 @@ func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) {
}
}
// SearchAsync performs a search request and returns all search results asynchronously.
// This means you get all results until an error happens (or the search successfully finished),
// e.g. for size / time limited requests all are recieved until the limit is reached.
// To stop the search, call cancel function of the context.
func (l *Conn) SearchAsync(
ctx context.Context, searchRequest *SearchRequest, bufferSize int) Response {
r := newSearchResponse(l, bufferSize)
r.start(ctx, searchRequest)
return r
}
// Syncrepl is a short name for LDAP Sync Replication engine that works on the
// consumer-side. This can perform a persistent search and returns an entry
// when the entry is updated on the server side.
// To stop the search, call cancel function of the context.
func (l *Conn) Syncrepl(
ctx context.Context, searchRequest *SearchRequest, bufferSize int,
mode ControlSyncRequestMode, cookie []byte, reloadHint bool,
) Response {
control := NewControlSyncRequest(mode, cookie, reloadHint)
searchRequest.Controls = append(searchRequest.Controls, control)
r := newSearchResponse(l, bufferSize)
r.start(ctx, searchRequest)
return r
}
// unpackAttributes will extract all given LDAP attributes and it's values
// from the ber.Packet
func unpackAttributes(children []*ber.Packet) []*EntryAttribute {
@@ -586,55 +635,56 @@ func unpackAttributes(children []*ber.Packet) []*EntryAttribute {
}
// DirSync does a Search with dirSync Control.
func (l *Conn) DirSync(searchRequest *SearchRequest, flags int64, maxAttrCount int64, cookie []byte) (*SearchResult, error) {
var dirSyncControl *ControlDirSync
func (l *Conn) DirSync(
searchRequest *SearchRequest, flags int64, maxAttrCount int64, cookie []byte,
) (*SearchResult, error) {
control := FindControl(searchRequest.Controls, ControlTypeDirSync)
if control == nil {
dirSyncControl = NewControlDirSync(flags, maxAttrCount, cookie)
searchRequest.Controls = append(searchRequest.Controls, dirSyncControl)
c := NewRequestControlDirSync(flags, maxAttrCount, cookie)
searchRequest.Controls = append(searchRequest.Controls, c)
} else {
castControl, ok := control.(*ControlDirSync)
if !ok {
return nil, fmt.Errorf("Expected DirSync control to be of type *ControlDirSync, got %v", control)
c := control.(*ControlDirSync)
if c.Flags != flags {
return nil, fmt.Errorf("flags given in search request (%d) conflicts with flags given in search call (%d)", c.Flags, flags)
}
if castControl.Flags != flags {
return nil, fmt.Errorf("flags given in search request (%d) conflicts with flags given in search call (%d)", castControl.Flags, flags)
if c.MaxAttrCount != maxAttrCount {
return nil, fmt.Errorf("MaxAttrCnt given in search request (%d) conflicts with maxAttrCount given in search call (%d)", c.MaxAttrCount, maxAttrCount)
}
if castControl.MaxAttrCnt != maxAttrCount {
return nil, fmt.Errorf("MaxAttrCnt given in search request (%d) conflicts with maxAttrCount given in search call (%d)", castControl.MaxAttrCnt, maxAttrCount)
}
dirSyncControl = castControl
}
searchResult := new(SearchResult)
result, err := l.Search(searchRequest)
searchResult, err := l.Search(searchRequest)
l.Debug.Printf("Looking for result...")
if err != nil {
return searchResult, err
return nil, err
}
if result == nil {
return searchResult, NewError(ErrorNetwork, errors.New("ldap: packet not received"))
if searchResult == nil {
return nil, NewError(ErrorNetwork, errors.New("ldap: packet not received"))
}
searchResult.Entries = append(searchResult.Entries, result.Entries...)
searchResult.Referrals = append(searchResult.Referrals, result.Referrals...)
searchResult.Controls = append(searchResult.Controls, result.Controls...)
l.Debug.Printf("Looking for DirSync Control...")
dirSyncResult := FindControl(result.Controls, ControlTypeDirSync)
if dirSyncResult == nil {
dirSyncControl = nil
resultControl := FindControl(searchResult.Controls, ControlTypeDirSync)
if resultControl == nil {
l.Debug.Printf("Could not find dirSyncControl control. Breaking...")
return searchResult, nil
}
cookie = dirSyncResult.(*ControlDirSync).Cookie
cookie = resultControl.(*ControlDirSync).Cookie
if len(cookie) == 0 {
dirSyncControl = nil
l.Debug.Printf("Could not find cookie. Breaking...")
return searchResult, nil
}
dirSyncControl.SetCookie(cookie)
return searchResult, nil
}
// DirSyncDirSyncAsync performs a search request and returns all search results
// asynchronously. This is efficient when the server returns lots of entries.
func (l *Conn) DirSyncAsync(
ctx context.Context, searchRequest *SearchRequest, bufferSize int,
flags, maxAttrCount int64, cookie []byte,
) Response {
control := NewRequestControlDirSync(flags, maxAttrCount, cookie)
searchRequest.Controls = append(searchRequest.Controls, control)
r := newSearchResponse(l, bufferSize)
r.start(ctx, searchRequest)
return r
}

4
vendor/modules.txt vendored
View File

@@ -800,7 +800,7 @@ github.com/ghodss/yaml
## explicit; go 1.15
github.com/go-acme/lego/v4/acme
github.com/go-acme/lego/v4/challenge
# github.com/go-asn1-ber/asn1-ber v1.5.4
# github.com/go-asn1-ber/asn1-ber v1.5.5
## explicit; go 1.13
github.com/go-asn1-ber/asn1-ber
# github.com/go-chi/chi/v5 v5.0.10
@@ -880,7 +880,7 @@ github.com/go-jose/go-jose/v3/json
## explicit; go 1.17
github.com/go-kit/log
github.com/go-kit/log/level
# github.com/go-ldap/ldap/v3 v3.4.5
# github.com/go-ldap/ldap/v3 v3.4.6
## explicit; go 1.14
github.com/go-ldap/ldap/v3
# github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3