From 9fd2332baa558eb90d33d1da0b51da71448036a3 Mon Sep 17 00:00:00 2001 From: Christopher Puschmann Date: Tue, 23 Apr 2024 20:28:05 +0200 Subject: [PATCH 1/8] feat: add extended requests --- client.go | 1 + extend.go | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++ v3/client.go | 1 + v3/extend.go | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+) create mode 100644 extend.go create mode 100644 v3/extend.go diff --git a/client.go b/client.go index ed96e840..8d103dd6 100644 --- a/client.go +++ b/client.go @@ -28,6 +28,7 @@ type Client interface { Modify(*ModifyRequest) error ModifyDN(*ModifyDNRequest) error ModifyWithResult(*ModifyRequest) (*ModifyResult, error) + Extended(*ExtendedRequest) (*ExtendResponse, error) Compare(dn, attribute, value string) (bool, error) PasswordModify(*PasswordModifyRequest) (*PasswordModifyResult, error) diff --git a/extend.go b/extend.go new file mode 100644 index 00000000..c95de140 --- /dev/null +++ b/extend.go @@ -0,0 +1,73 @@ +package ldap + +import ( + "fmt" + ber "github.com/go-asn1-ber/asn1-ber" +) + +// ExtendedRequest TODO +// See: https://www.rfc-editor.org/rfc/rfc4511#section-4.12 +type ExtendedRequest struct { + Name string + Value string +} + +func NewExtendedRequest(name, value string) *ExtendedRequest { + return &ExtendedRequest{ + Name: name, + Value: value, + } +} + +func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { + // ExtendedRequest ::= [APPLICATION 23] SEQUENCE { + // requestName [0] LDAPOID, + // requestValue [1] OCTET STRING OPTIONAL } + // + // Despite the RFC documentation stating otherwise, the requestName field needs to be + // of class application and type EOC, otherwise the directory server will terminate + // the connection right away (tested against OpenLDAP, Active Directory). + pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Extend Request") + pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, ber.TagEOC, er.Name, "Extension Name")) + if er.Value != "" { + pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, er.Value, "Extension Value")) + } + envelope.AppendChild(pkt) + return nil +} + +type ExtendResponse struct { + Name string + Value string +} + +func (l *Conn) Extended(er *ExtendedRequest) (*ExtendResponse, error) { + msgCtx, err := l.doRequest(er) + if err != nil { + return nil, err + } + defer l.finishMessage(msgCtx) + + // ExtendedResponse ::= [APPLICATION 24] SEQUENCE { + // COMPONENTS OF LDAPResult, + // responseName [10] LDAPOID OPTIONAL, + // responseValue [11] OCTET STRING OPTIONAL } + packet, err := l.readPacket(msgCtx) + if err != nil { + return nil, err + } + if len(packet.Children) < 2 || len(packet.Children[1].Children) < 4 { + return nil, fmt.Errorf( + "ldap: malformed extended response: expected 4 children, got: %d", + len(packet.Children), + ) + } + + response := new(ExtendResponse) + response.Name = packet.Children[1].Children[3].Data.String() + if len(packet.Children) == 4 { + response.Value = packet.Children[1].Children[4].Data.String() + } + + return response, nil +} diff --git a/v3/client.go b/v3/client.go index ed96e840..8d103dd6 100644 --- a/v3/client.go +++ b/v3/client.go @@ -28,6 +28,7 @@ type Client interface { Modify(*ModifyRequest) error ModifyDN(*ModifyDNRequest) error ModifyWithResult(*ModifyRequest) (*ModifyResult, error) + Extended(*ExtendedRequest) (*ExtendResponse, error) Compare(dn, attribute, value string) (bool, error) PasswordModify(*PasswordModifyRequest) (*PasswordModifyResult, error) diff --git a/v3/extend.go b/v3/extend.go new file mode 100644 index 00000000..c95de140 --- /dev/null +++ b/v3/extend.go @@ -0,0 +1,73 @@ +package ldap + +import ( + "fmt" + ber "github.com/go-asn1-ber/asn1-ber" +) + +// ExtendedRequest TODO +// See: https://www.rfc-editor.org/rfc/rfc4511#section-4.12 +type ExtendedRequest struct { + Name string + Value string +} + +func NewExtendedRequest(name, value string) *ExtendedRequest { + return &ExtendedRequest{ + Name: name, + Value: value, + } +} + +func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { + // ExtendedRequest ::= [APPLICATION 23] SEQUENCE { + // requestName [0] LDAPOID, + // requestValue [1] OCTET STRING OPTIONAL } + // + // Despite the RFC documentation stating otherwise, the requestName field needs to be + // of class application and type EOC, otherwise the directory server will terminate + // the connection right away (tested against OpenLDAP, Active Directory). + pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Extend Request") + pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, ber.TagEOC, er.Name, "Extension Name")) + if er.Value != "" { + pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, er.Value, "Extension Value")) + } + envelope.AppendChild(pkt) + return nil +} + +type ExtendResponse struct { + Name string + Value string +} + +func (l *Conn) Extended(er *ExtendedRequest) (*ExtendResponse, error) { + msgCtx, err := l.doRequest(er) + if err != nil { + return nil, err + } + defer l.finishMessage(msgCtx) + + // ExtendedResponse ::= [APPLICATION 24] SEQUENCE { + // COMPONENTS OF LDAPResult, + // responseName [10] LDAPOID OPTIONAL, + // responseValue [11] OCTET STRING OPTIONAL } + packet, err := l.readPacket(msgCtx) + if err != nil { + return nil, err + } + if len(packet.Children) < 2 || len(packet.Children[1].Children) < 4 { + return nil, fmt.Errorf( + "ldap: malformed extended response: expected 4 children, got: %d", + len(packet.Children), + ) + } + + response := new(ExtendResponse) + response.Name = packet.Children[1].Children[3].Data.String() + if len(packet.Children) == 4 { + response.Value = packet.Children[1].Children[4].Data.String() + } + + return response, nil +} From efdaca201aa5a4eb4298249b8b4a6896b1eb883c Mon Sep 17 00:00:00 2001 From: Christopher Puschmann Date: Tue, 23 Apr 2024 23:24:09 +0200 Subject: [PATCH 2/8] remove unnecessary `:` in returned error when trying to parse the extended response --- extend.go | 6 +++--- v3/extend.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extend.go b/extend.go index c95de140..fce73275 100644 --- a/extend.go +++ b/extend.go @@ -27,10 +27,10 @@ func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { // Despite the RFC documentation stating otherwise, the requestName field needs to be // of class application and type EOC, otherwise the directory server will terminate // the connection right away (tested against OpenLDAP, Active Directory). - pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Extend Request") + pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Extended Request") pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, ber.TagEOC, er.Name, "Extension Name")) if er.Value != "" { - pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, er.Value, "Extension Value")) + pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, ber.TagEOC, er.Value, "Extension Value")) } envelope.AppendChild(pkt) return nil @@ -58,7 +58,7 @@ func (l *Conn) Extended(er *ExtendedRequest) (*ExtendResponse, error) { } if len(packet.Children) < 2 || len(packet.Children[1].Children) < 4 { return nil, fmt.Errorf( - "ldap: malformed extended response: expected 4 children, got: %d", + "ldap: malformed extended response: expected 4 children, got %d", len(packet.Children), ) } diff --git a/v3/extend.go b/v3/extend.go index c95de140..fce73275 100644 --- a/v3/extend.go +++ b/v3/extend.go @@ -27,10 +27,10 @@ func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { // Despite the RFC documentation stating otherwise, the requestName field needs to be // of class application and type EOC, otherwise the directory server will terminate // the connection right away (tested against OpenLDAP, Active Directory). - pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Extend Request") + pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Extended Request") pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, ber.TagEOC, er.Name, "Extension Name")) if er.Value != "" { - pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, er.Value, "Extension Value")) + pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, ber.TagEOC, er.Value, "Extension Value")) } envelope.AppendChild(pkt) return nil @@ -58,7 +58,7 @@ func (l *Conn) Extended(er *ExtendedRequest) (*ExtendResponse, error) { } if len(packet.Children) < 2 || len(packet.Children[1].Children) < 4 { return nil, fmt.Errorf( - "ldap: malformed extended response: expected 4 children, got: %d", + "ldap: malformed extended response: expected 4 children, got %d", len(packet.Children), ) } From 49bb329f11687891b84f96ca9df5aa41a0250b03 Mon Sep 17 00:00:00 2001 From: Christopher Puschmann Date: Wed, 24 Apr 2024 00:13:30 +0200 Subject: [PATCH 3/8] Resort to generic ber packet design instead of fixed octet string in requestValue --- extend.go | 11 ++++++----- v3/extend.go | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/extend.go b/extend.go index fce73275..51caaae7 100644 --- a/extend.go +++ b/extend.go @@ -9,10 +9,10 @@ import ( // See: https://www.rfc-editor.org/rfc/rfc4511#section-4.12 type ExtendedRequest struct { Name string - Value string + Value *ber.Packet } -func NewExtendedRequest(name, value string) *ExtendedRequest { +func NewExtendedRequest(name string, value *ber.Packet) *ExtendedRequest { return &ExtendedRequest{ Name: name, Value: value, @@ -27,10 +27,11 @@ func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { // Despite the RFC documentation stating otherwise, the requestName field needs to be // of class application and type EOC, otherwise the directory server will terminate // the connection right away (tested against OpenLDAP, Active Directory). + pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Extended Request") - pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, ber.TagEOC, er.Name, "Extension Name")) - if er.Value != "" { - pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, ber.TagEOC, er.Value, "Extension Value")) + pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, ber.TagEOC, er.Name, "Extended Request Name")) + if er.Value != nil { + pkt.AppendChild(er.Value) } envelope.AppendChild(pkt) return nil diff --git a/v3/extend.go b/v3/extend.go index fce73275..51caaae7 100644 --- a/v3/extend.go +++ b/v3/extend.go @@ -9,10 +9,10 @@ import ( // See: https://www.rfc-editor.org/rfc/rfc4511#section-4.12 type ExtendedRequest struct { Name string - Value string + Value *ber.Packet } -func NewExtendedRequest(name, value string) *ExtendedRequest { +func NewExtendedRequest(name string, value *ber.Packet) *ExtendedRequest { return &ExtendedRequest{ Name: name, Value: value, @@ -27,10 +27,11 @@ func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { // Despite the RFC documentation stating otherwise, the requestName field needs to be // of class application and type EOC, otherwise the directory server will terminate // the connection right away (tested against OpenLDAP, Active Directory). + pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Extended Request") - pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, ber.TagEOC, er.Name, "Extension Name")) - if er.Value != "" { - pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, ber.TagEOC, er.Value, "Extension Value")) + pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, ber.TagEOC, er.Name, "Extended Request Name")) + if er.Value != nil { + pkt.AppendChild(er.Value) } envelope.AppendChild(pkt) return nil From b6483ead5dd8780a779382e4425ba4cd1b8ebd0b Mon Sep 17 00:00:00 2001 From: Christopher Puschmann Date: Wed, 24 Apr 2024 14:09:23 +0200 Subject: [PATCH 4/8] Resort to generic ber packet design instead of fixed octet string in requestValue --- extend.go | 10 +++++++--- v3/extend.go | 10 +++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/extend.go b/extend.go index 51caaae7..524d4ebb 100644 --- a/extend.go +++ b/extend.go @@ -39,7 +39,7 @@ func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { type ExtendResponse struct { Name string - Value string + Value *ber.Packet } func (l *Conn) Extended(er *ExtendedRequest) (*ExtendResponse, error) { @@ -57,7 +57,11 @@ func (l *Conn) Extended(er *ExtendedRequest) (*ExtendResponse, error) { if err != nil { return nil, err } - if len(packet.Children) < 2 || len(packet.Children[1].Children) < 4 { + if err = GetLDAPError(packet); err != nil { + return nil, err + } + + if len(packet.Children[1].Children) < 4 { return nil, fmt.Errorf( "ldap: malformed extended response: expected 4 children, got %d", len(packet.Children), @@ -67,7 +71,7 @@ func (l *Conn) Extended(er *ExtendedRequest) (*ExtendResponse, error) { response := new(ExtendResponse) response.Name = packet.Children[1].Children[3].Data.String() if len(packet.Children) == 4 { - response.Value = packet.Children[1].Children[4].Data.String() + response.Value = packet.Children[1].Children[4] } return response, nil diff --git a/v3/extend.go b/v3/extend.go index 51caaae7..524d4ebb 100644 --- a/v3/extend.go +++ b/v3/extend.go @@ -39,7 +39,7 @@ func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { type ExtendResponse struct { Name string - Value string + Value *ber.Packet } func (l *Conn) Extended(er *ExtendedRequest) (*ExtendResponse, error) { @@ -57,7 +57,11 @@ func (l *Conn) Extended(er *ExtendedRequest) (*ExtendResponse, error) { if err != nil { return nil, err } - if len(packet.Children) < 2 || len(packet.Children[1].Children) < 4 { + if err = GetLDAPError(packet); err != nil { + return nil, err + } + + if len(packet.Children[1].Children) < 4 { return nil, fmt.Errorf( "ldap: malformed extended response: expected 4 children, got %d", len(packet.Children), @@ -67,7 +71,7 @@ func (l *Conn) Extended(er *ExtendedRequest) (*ExtendResponse, error) { response := new(ExtendResponse) response.Name = packet.Children[1].Children[3].Data.String() if len(packet.Children) == 4 { - response.Value = packet.Children[1].Children[4].Data.String() + response.Value = packet.Children[1].Children[4] } return response, nil From 40b2662f6b033c4044b4c39ca5e98899479bc5f6 Mon Sep 17 00:00:00 2001 From: Christopher Puschmann Date: Wed, 30 Oct 2024 22:07:40 +0100 Subject: [PATCH 5/8] progress --- client.go | 2 +- extend.go => extended.go | 30 ++++++++++++------------- extended_test.go | 41 +++++++++++++++++++++++++++++++++++ v3/client.go | 2 +- v3/{extend.go => extended.go} | 30 ++++++++++++------------- v3/extended_test.go | 41 +++++++++++++++++++++++++++++++++++ 6 files changed, 112 insertions(+), 34 deletions(-) rename extend.go => extended.go (79%) create mode 100644 extended_test.go rename v3/{extend.go => extended.go} (79%) create mode 100644 v3/extended_test.go diff --git a/client.go b/client.go index 8d103dd6..ee473fc7 100644 --- a/client.go +++ b/client.go @@ -28,7 +28,7 @@ type Client interface { Modify(*ModifyRequest) error ModifyDN(*ModifyDNRequest) error ModifyWithResult(*ModifyRequest) (*ModifyResult, error) - Extended(*ExtendedRequest) (*ExtendResponse, error) + Extended(*ExtendedRequest) (*ExtendedResponse, error) Compare(dn, attribute, value string) (bool, error) PasswordModify(*PasswordModifyRequest) (*PasswordModifyResult, error) diff --git a/extend.go b/extended.go similarity index 79% rename from extend.go rename to extended.go index 524d4ebb..17d7628a 100644 --- a/extend.go +++ b/extended.go @@ -8,6 +8,10 @@ import ( // ExtendedRequest TODO // See: https://www.rfc-editor.org/rfc/rfc4511#section-4.12 type ExtendedRequest struct { + // ExtendedRequest ::= [APPLICATION 23] SEQUENCE { + // requestName [0] LDAPOID, + // requestValue [1] OCTET STRING OPTIONAL } + Name string Value *ber.Packet } @@ -20,14 +24,6 @@ func NewExtendedRequest(name string, value *ber.Packet) *ExtendedRequest { } func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { - // ExtendedRequest ::= [APPLICATION 23] SEQUENCE { - // requestName [0] LDAPOID, - // requestValue [1] OCTET STRING OPTIONAL } - // - // Despite the RFC documentation stating otherwise, the requestName field needs to be - // of class application and type EOC, otherwise the directory server will terminate - // the connection right away (tested against OpenLDAP, Active Directory). - pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Extended Request") pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, ber.TagEOC, er.Name, "Extended Request Name")) if er.Value != nil { @@ -37,22 +33,23 @@ func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { return nil } -type ExtendResponse struct { +type ExtendedResponse struct { + // ExtendedResponse ::= [APPLICATION 24] SEQUENCE { + // COMPONENTS OF LDAPResult, + // responseName [10] LDAPOID OPTIONAL, + // responseValue [11] OCTET STRING OPTIONAL } + Name string Value *ber.Packet } -func (l *Conn) Extended(er *ExtendedRequest) (*ExtendResponse, error) { +func (l *Conn) Extended(er *ExtendedRequest) (*ExtendedResponse, error) { msgCtx, err := l.doRequest(er) if err != nil { return nil, err } defer l.finishMessage(msgCtx) - // ExtendedResponse ::= [APPLICATION 24] SEQUENCE { - // COMPONENTS OF LDAPResult, - // responseName [10] LDAPOID OPTIONAL, - // responseValue [11] OCTET STRING OPTIONAL } packet, err := l.readPacket(msgCtx) if err != nil { return nil, err @@ -68,9 +65,10 @@ func (l *Conn) Extended(er *ExtendedRequest) (*ExtendResponse, error) { ) } - response := new(ExtendResponse) + response := new(ExtendedResponse) response.Name = packet.Children[1].Children[3].Data.String() - if len(packet.Children) == 4 { + + if len(packet.Children[1].Children) == 5 { response.Value = packet.Children[1].Children[4] } diff --git a/extended_test.go b/extended_test.go new file mode 100644 index 00000000..c0cf4e16 --- /dev/null +++ b/extended_test.go @@ -0,0 +1,41 @@ +package ldap + +import ( + "testing" +) + +func TestExtendedRequest_WhoAmI(t *testing.T) { + l, err := DialURL(ldapServer) + if err != nil { + t.Errorf("%s failed: %v", t.Name(), err) + return + } + defer l.Close() + + l.Bind("", "") // anonymous + defer l.Unbind() + + rfc4532req := NewExtendedRequest("1.3.6.1.4.1.4203.1.11.3", nil) // request value is + + var rfc4532resp *ExtendedResponse + if rfc4532resp, err = l.Extended(rfc4532req); err != nil { + t.Errorf("%s failed: %v", t.Name(), err) + return + } + t.Logf("%#v\n", rfc4532resp) +} + +func TestExtendedRequest_FastBind(t *testing.T) { + conn, err := DialURL(ldapServer) + if err != nil { + t.Error(err) + } + defer conn.Close() + + request := NewExtendedRequest(`1.3.6.1.4.1.4203.1.11.3`) + _, err = conn.Extended(request) + if err != nil { + t.Errorf("%s failed: %v", t.Name(), err) + return + } +} diff --git a/v3/client.go b/v3/client.go index 8d103dd6..ee473fc7 100644 --- a/v3/client.go +++ b/v3/client.go @@ -28,7 +28,7 @@ type Client interface { Modify(*ModifyRequest) error ModifyDN(*ModifyDNRequest) error ModifyWithResult(*ModifyRequest) (*ModifyResult, error) - Extended(*ExtendedRequest) (*ExtendResponse, error) + Extended(*ExtendedRequest) (*ExtendedResponse, error) Compare(dn, attribute, value string) (bool, error) PasswordModify(*PasswordModifyRequest) (*PasswordModifyResult, error) diff --git a/v3/extend.go b/v3/extended.go similarity index 79% rename from v3/extend.go rename to v3/extended.go index 524d4ebb..17d7628a 100644 --- a/v3/extend.go +++ b/v3/extended.go @@ -8,6 +8,10 @@ import ( // ExtendedRequest TODO // See: https://www.rfc-editor.org/rfc/rfc4511#section-4.12 type ExtendedRequest struct { + // ExtendedRequest ::= [APPLICATION 23] SEQUENCE { + // requestName [0] LDAPOID, + // requestValue [1] OCTET STRING OPTIONAL } + Name string Value *ber.Packet } @@ -20,14 +24,6 @@ func NewExtendedRequest(name string, value *ber.Packet) *ExtendedRequest { } func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { - // ExtendedRequest ::= [APPLICATION 23] SEQUENCE { - // requestName [0] LDAPOID, - // requestValue [1] OCTET STRING OPTIONAL } - // - // Despite the RFC documentation stating otherwise, the requestName field needs to be - // of class application and type EOC, otherwise the directory server will terminate - // the connection right away (tested against OpenLDAP, Active Directory). - pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Extended Request") pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, ber.TagEOC, er.Name, "Extended Request Name")) if er.Value != nil { @@ -37,22 +33,23 @@ func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { return nil } -type ExtendResponse struct { +type ExtendedResponse struct { + // ExtendedResponse ::= [APPLICATION 24] SEQUENCE { + // COMPONENTS OF LDAPResult, + // responseName [10] LDAPOID OPTIONAL, + // responseValue [11] OCTET STRING OPTIONAL } + Name string Value *ber.Packet } -func (l *Conn) Extended(er *ExtendedRequest) (*ExtendResponse, error) { +func (l *Conn) Extended(er *ExtendedRequest) (*ExtendedResponse, error) { msgCtx, err := l.doRequest(er) if err != nil { return nil, err } defer l.finishMessage(msgCtx) - // ExtendedResponse ::= [APPLICATION 24] SEQUENCE { - // COMPONENTS OF LDAPResult, - // responseName [10] LDAPOID OPTIONAL, - // responseValue [11] OCTET STRING OPTIONAL } packet, err := l.readPacket(msgCtx) if err != nil { return nil, err @@ -68,9 +65,10 @@ func (l *Conn) Extended(er *ExtendedRequest) (*ExtendResponse, error) { ) } - response := new(ExtendResponse) + response := new(ExtendedResponse) response.Name = packet.Children[1].Children[3].Data.String() - if len(packet.Children) == 4 { + + if len(packet.Children[1].Children) == 5 { response.Value = packet.Children[1].Children[4] } diff --git a/v3/extended_test.go b/v3/extended_test.go new file mode 100644 index 00000000..c0cf4e16 --- /dev/null +++ b/v3/extended_test.go @@ -0,0 +1,41 @@ +package ldap + +import ( + "testing" +) + +func TestExtendedRequest_WhoAmI(t *testing.T) { + l, err := DialURL(ldapServer) + if err != nil { + t.Errorf("%s failed: %v", t.Name(), err) + return + } + defer l.Close() + + l.Bind("", "") // anonymous + defer l.Unbind() + + rfc4532req := NewExtendedRequest("1.3.6.1.4.1.4203.1.11.3", nil) // request value is + + var rfc4532resp *ExtendedResponse + if rfc4532resp, err = l.Extended(rfc4532req); err != nil { + t.Errorf("%s failed: %v", t.Name(), err) + return + } + t.Logf("%#v\n", rfc4532resp) +} + +func TestExtendedRequest_FastBind(t *testing.T) { + conn, err := DialURL(ldapServer) + if err != nil { + t.Error(err) + } + defer conn.Close() + + request := NewExtendedRequest(`1.3.6.1.4.1.4203.1.11.3`) + _, err = conn.Extended(request) + if err != nil { + t.Errorf("%s failed: %v", t.Name(), err) + return + } +} From fce8ada28620dd760058223b6e9f559bcb17d5a7 Mon Sep 17 00:00:00 2001 From: Christopher Puschmann Date: Wed, 30 Oct 2024 22:15:17 +0100 Subject: [PATCH 6/8] progress --- extended_test.go | 2 +- v3/extended_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extended_test.go b/extended_test.go index c0cf4e16..6bd83a17 100644 --- a/extended_test.go +++ b/extended_test.go @@ -32,7 +32,7 @@ func TestExtendedRequest_FastBind(t *testing.T) { } defer conn.Close() - request := NewExtendedRequest(`1.3.6.1.4.1.4203.1.11.3`) + request := NewExtendedRequest("1.3.6.1.4.1.4203.1.11.3", nil) _, err = conn.Extended(request) if err != nil { t.Errorf("%s failed: %v", t.Name(), err) diff --git a/v3/extended_test.go b/v3/extended_test.go index c0cf4e16..6bd83a17 100644 --- a/v3/extended_test.go +++ b/v3/extended_test.go @@ -32,7 +32,7 @@ func TestExtendedRequest_FastBind(t *testing.T) { } defer conn.Close() - request := NewExtendedRequest(`1.3.6.1.4.1.4203.1.11.3`) + request := NewExtendedRequest("1.3.6.1.4.1.4203.1.11.3", nil) _, err = conn.Extended(request) if err != nil { t.Errorf("%s failed: %v", t.Name(), err) From 7fdf21775097bb553e55544298eef391bf099cb0 Mon Sep 17 00:00:00 2001 From: Christopher Puschmann Date: Thu, 31 Oct 2024 01:05:47 +0100 Subject: [PATCH 7/8] add missing comments --- extended.go | 9 ++++++++- v3/extended.go | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/extended.go b/extended.go index 17d7628a..03265b9e 100644 --- a/extended.go +++ b/extended.go @@ -5,7 +5,7 @@ import ( ber "github.com/go-asn1-ber/asn1-ber" ) -// ExtendedRequest TODO +// ExtendedRequest represents an extended request to send to the server // See: https://www.rfc-editor.org/rfc/rfc4511#section-4.12 type ExtendedRequest struct { // ExtendedRequest ::= [APPLICATION 23] SEQUENCE { @@ -16,6 +16,8 @@ type ExtendedRequest struct { Value *ber.Packet } +// NewExtendedRequest returns a new ExtendedRequest. The value can be +// nil depending on the type of request func NewExtendedRequest(name string, value *ber.Packet) *ExtendedRequest { return &ExtendedRequest{ Name: name, @@ -33,6 +35,9 @@ func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { return nil } +// ExtendedResponse represents the response from the directory server +// after sending an extended request +// See: https://www.rfc-editor.org/rfc/rfc4511#section-4.12 type ExtendedResponse struct { // ExtendedResponse ::= [APPLICATION 24] SEQUENCE { // COMPONENTS OF LDAPResult, @@ -43,6 +48,8 @@ type ExtendedResponse struct { Value *ber.Packet } +// Extended performs an extended request. The resulting +// ExtendedResponse may return a value in the form of a *ber.Packet func (l *Conn) Extended(er *ExtendedRequest) (*ExtendedResponse, error) { msgCtx, err := l.doRequest(er) if err != nil { diff --git a/v3/extended.go b/v3/extended.go index 17d7628a..03265b9e 100644 --- a/v3/extended.go +++ b/v3/extended.go @@ -5,7 +5,7 @@ import ( ber "github.com/go-asn1-ber/asn1-ber" ) -// ExtendedRequest TODO +// ExtendedRequest represents an extended request to send to the server // See: https://www.rfc-editor.org/rfc/rfc4511#section-4.12 type ExtendedRequest struct { // ExtendedRequest ::= [APPLICATION 23] SEQUENCE { @@ -16,6 +16,8 @@ type ExtendedRequest struct { Value *ber.Packet } +// NewExtendedRequest returns a new ExtendedRequest. The value can be +// nil depending on the type of request func NewExtendedRequest(name string, value *ber.Packet) *ExtendedRequest { return &ExtendedRequest{ Name: name, @@ -33,6 +35,9 @@ func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { return nil } +// ExtendedResponse represents the response from the directory server +// after sending an extended request +// See: https://www.rfc-editor.org/rfc/rfc4511#section-4.12 type ExtendedResponse struct { // ExtendedResponse ::= [APPLICATION 24] SEQUENCE { // COMPONENTS OF LDAPResult, @@ -43,6 +48,8 @@ type ExtendedResponse struct { Value *ber.Packet } +// Extended performs an extended request. The resulting +// ExtendedResponse may return a value in the form of a *ber.Packet func (l *Conn) Extended(er *ExtendedRequest) (*ExtendedResponse, error) { msgCtx, err := l.doRequest(er) if err != nil { From d9249ccc6fb544d312f1caa0d09908f5979fc645 Mon Sep 17 00:00:00 2001 From: Christopher Puschmann Date: Thu, 31 Oct 2024 14:44:48 +0100 Subject: [PATCH 8/8] add optional controls to extended operations --- extended.go | 29 +++++++++++++++++++++++------ v3/extended.go | 29 +++++++++++++++++++++++------ 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/extended.go b/extended.go index 03265b9e..e71d982f 100644 --- a/extended.go +++ b/extended.go @@ -12,8 +12,9 @@ type ExtendedRequest struct { // requestName [0] LDAPOID, // requestValue [1] OCTET STRING OPTIONAL } - Name string - Value *ber.Packet + Name string + Value *ber.Packet + Controls []Control } // NewExtendedRequest returns a new ExtendedRequest. The value can be @@ -32,6 +33,9 @@ func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { pkt.AppendChild(er.Value) } envelope.AppendChild(pkt) + if len(er.Controls) > 0 { + envelope.AppendChild(encodeControls(er.Controls)) + } return nil } @@ -44,8 +48,9 @@ type ExtendedResponse struct { // responseName [10] LDAPOID OPTIONAL, // responseValue [11] OCTET STRING OPTIONAL } - Name string - Value *ber.Packet + Name string + Value *ber.Packet + Controls []Control } // Extended performs an extended request. The resulting @@ -72,8 +77,20 @@ func (l *Conn) Extended(er *ExtendedRequest) (*ExtendedResponse, error) { ) } - response := new(ExtendedResponse) - response.Name = packet.Children[1].Children[3].Data.String() + response := &ExtendedResponse{ + Name: packet.Children[1].Children[3].Data.String(), + Controls: make([]Control, 0), + } + + if len(packet.Children) == 3 { + for _, child := range packet.Children[2].Children { + decodedChild, decodeErr := DecodeControl(child) + if decodeErr != nil { + return nil, fmt.Errorf("failed to decode child control: %s", decodeErr) + } + response.Controls = append(response.Controls, decodedChild) + } + } if len(packet.Children[1].Children) == 5 { response.Value = packet.Children[1].Children[4] diff --git a/v3/extended.go b/v3/extended.go index 03265b9e..e71d982f 100644 --- a/v3/extended.go +++ b/v3/extended.go @@ -12,8 +12,9 @@ type ExtendedRequest struct { // requestName [0] LDAPOID, // requestValue [1] OCTET STRING OPTIONAL } - Name string - Value *ber.Packet + Name string + Value *ber.Packet + Controls []Control } // NewExtendedRequest returns a new ExtendedRequest. The value can be @@ -32,6 +33,9 @@ func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { pkt.AppendChild(er.Value) } envelope.AppendChild(pkt) + if len(er.Controls) > 0 { + envelope.AppendChild(encodeControls(er.Controls)) + } return nil } @@ -44,8 +48,9 @@ type ExtendedResponse struct { // responseName [10] LDAPOID OPTIONAL, // responseValue [11] OCTET STRING OPTIONAL } - Name string - Value *ber.Packet + Name string + Value *ber.Packet + Controls []Control } // Extended performs an extended request. The resulting @@ -72,8 +77,20 @@ func (l *Conn) Extended(er *ExtendedRequest) (*ExtendedResponse, error) { ) } - response := new(ExtendedResponse) - response.Name = packet.Children[1].Children[3].Data.String() + response := &ExtendedResponse{ + Name: packet.Children[1].Children[3].Data.String(), + Controls: make([]Control, 0), + } + + if len(packet.Children) == 3 { + for _, child := range packet.Children[2].Children { + decodedChild, decodeErr := DecodeControl(child) + if decodeErr != nil { + return nil, fmt.Errorf("failed to decode child control: %s", decodeErr) + } + response.Controls = append(response.Controls, decodedChild) + } + } if len(packet.Children[1].Children) == 5 { response.Value = packet.Children[1].Children[4]