Git Product home page Git Product logo

ldap's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ldap's Issues

Add String() to DN

Would love a way to convert a DN back into the string representation. Right now, if I build a DN, there is no way to stringify it.

CompileFilter crashes while logging error if the filter is missing brackets

Example:

package main

import "github.com/go-ldap/ldap"

func main() {
	ldap.CompileFilter("((cn=)")
}

When running the example, it fails to parse the filter and crashes while logging the error message:

panic: runtime error: slice bounds out of range

goroutine 1 [running]:
panic(0x54ef60, 0xc82000a0e0)
	/usr/lib/golang/src/runtime/panic.go:481 +0x3e6
github.com/go-ldap/ldap.CompileFilter(0x57d790, 0x6, 0xc820014810, 0x0, 0x0)
	/root/src/github.com/go-ldap/ldap/filter.go:86 +0x41e
main.main()
	/root/test/test.go:6 +0x2c

The problem is with this line in ldap/filter.go, pos gets invalid value:

return nil, NewError(ErrorFilterCompile, errors.New("ldap: finished compiling filter with extra at end: "+fmt.Sprint(filter[pos:])))

please tag and version this project

Hello,

Can you please tag and version this project?

I am the Debian Maintainer for go-ldap and versioning would help Debian keep up with development.

Provision to check if connection is still alive or not

Hi,
I want a way to figure out whether an ldap connection i created has been closed or not.
I see that Conn struct has a bool isClosing to gain that info. But it is private.
Can you expose a public function which value of isClosing??

Right now, Whenever i receive an error, I simply retry the search query after restarting the connection.
My use case is,
I am exposing a web service, which uses ldap. For better performance, i created the ldap connection beforehand and kept reusing it for each request.

If i had a way to know whether the connection is lost or not, it would make it simpler for me.
Thanks You

filter by memberOf + nested groups

Is the following search filter possible with this library? Didn't seem like it worked, based on my limited testing with a hacked up example_test.go.

memberOf:CN=Some-App-Group,OU=Groups,DC=example,DC=com

Also, is it possible to search nested group membership, similar to the following?

memberOf:1.2.840.113556.1.4.1941:=CN=Some-App-Group,OU=Groups,DC=example,DC=com

Apologies if this is obvious in the code -- I'm not a Go developer but need to add this functionality to an app that uses this library.

GSSAPI support

In my application i have a /etc/krb.conf and /etc/krb5.keytab (configured by adcli join). It would be great if this ldap library could authenticate with this credentials as python ldap3 library.

ldap: finished compiling filter with extra at end:

Hi,
I am getting below error which authenticating ldap ,could some one please help me why i am this error?

ldap: finished compiling filter with extra at end: OU=User Policy 0,OU=All Users,DC=**,DC=**,DC=**,DC=com)(uniqueMember=CN=user,OU=User Policy 0,OU=All Users,DC=**,DC=**,DC=**,DC=com))

but when i trying to use ldap for user which is in different tree like below ,it working fine
dn: CN=user2,OU=Service Accounts,OU=Non-User,DC=**,DC=**,DC=**,DC=com

Does string OU=User Policy 0 is causing issue in first case?
https://github.com/go-ldap/ldap/search?utf8=%E2%9C%93&q=extra

Production ready ?

Hi,
I intend to use your lib for a personnal project. I would like to know if this lib is currently stable and production ready.

Thanks a lot for the amazing work anyway ! πŸ‘

Pooling connections

Having a pool of connections would be handy. My current implementation could also work with a few changes for this project. It would mean a new major version this project, as it needs the ldap.Conn as an interface:
https://github.com/zalando/go-ldap/tree/master/ldappool

Note: despite the same name, that go-ldap is a rewritten version of https://github.com/mqu/openldap, the pooling is adapted from https://github.com/fatih/pool.

(if that works, I can finally get rid of the C code :))

DialTLS: use tls.Dial instead of doing an explicit handshake

This was brought up in dexidp/dex#689

Currently DialTLS() uses net.Dial then performs an explicit handshake.[0]

We noticed this because tls.Dial does a few other things like inferring the ServerName[1], but since the LDAP client doesn't use that code path, users have to set it explicitly.

Is there a reason this client needs to do its own handshake?

[0]

ldap/conn.go

Lines 124 to 138 in d0a5ced

func DialTLS(network, addr string, config *tls.Config) (*Conn, error) {
dc, err := net.DialTimeout(network, addr, DefaultTimeout)
if err != nil {
return nil, NewError(ErrorNetwork, err)
}
c := tls.Client(dc, config)
err = c.Handshake()
if err != nil {
// Handshake error, close the established connection before we return an error
dc.Close()
return nil, NewError(ErrorNetwork, err)
}
conn := NewConn(c, true)
conn.Start()
return conn, nil

[1] https://github.com/golang/go/blob/go1.7.3/src/crypto/tls/tls.go#L134-L141

Add dial timeout.

One critical item that is missing the ability to set a timeout during dialing. Would it be possible to have this feature added?

Code mentions a BSD license

Hello!

The code mentions a BSD license:

// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

The BSD license says:

* Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

However, the LICENSE file was changed in 8ad9ef0

Please respect the license terms.

drop support for < go1.4

1.4 is almost two years old at this point, and has atomic primitives that make race avoidance easier

would do this on a release boundary (maybe v3?)

Example of how to add a user to ldap

Here is an example of the LDIF I would use with the ldapadd, how do I do this in go code?

dn: cn=boobla,dc=urbn,dc=com
objectClass: top
objectClass: iNetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
cn: boobla
sn: boobla
gn: boobla
uid: c666f5ab-1b26-4421-9eb1-50775ed94bb9
mail: [email protected]
userPassword: {crypt}boobla

Would something like this do it?

func (l *LdapInfo)AddUser() error {
	req := ldap.AddRequest{
		DN: "cn=boobla,dc=example,dc=org",
		Attributes: []ldap.Attribute{
			{
				Type: "objectClass",
				Vals: []string{ "top", "iNetOrgPerson"  },
			},
			{
				Type: "cn",
				Vals: []string{ "boobla" },
			},
			{
				Type: "sn",
				Vals: []string{ "boobla" },
			},
			{
				Type: "gn",
				Vals: []string{ "boobla" },
			},
			{
				Type: "uid",
				Vals: []string{ "c666f5ab-1b26-4421-9eb1-50775ed96hf6" },
			},
			{
				Type: "ou",
				Vals: []string{ "Family" },
			},
			{
				Type: "mail",
				Vals: []string{ "[email protected]" },
			},
			{
				Type: "userPassword",
				Vals: []string{ "{crypt}x" },
			}},
	}
	return l.Conn.Add(req)
}

2017/05/22 14:44:19 LDAP Result Code 200 "": ldap: connection closed error

when separating the search and bind in separate functions, a connection returned as the type *ldap.Conn cannot be used in other functions.

The search function (searchrequest2) can be called from the bindldap function which than works...but called from main...it fails...

func Bindldap(ldaphost string, port int, principal string, password string) *ldap.Conn {

	l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldaphost, port))
	if err != nil {
		println("error dialing")
		log.Fatal(err)
	}


	err = l.Bind(principal, password)
	if err != nil {
		println("error binding")
		log.Fatal(err)

	}
	searchrequest2(l)
	println("separator##########################################################################")
	searchrequest2(l)
	return l
}

func searchrequest2(l *ldap.Conn) {
	searchrequest2 := ldap.NewSearchRequest(
		Searchbase1, // The base dn to search
		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
		"(&(objectClass=organizationalPerson))", // The filter to apply
		Searchattributes2,                       // A list attributes to retrieve
		nil,
	)
	sr, err := l.Search(searchrequest2)
	if err != nil {
		log.Fatal(err)
	}
	for _, entry := range sr.Entries {
		fmt.Printf("%s: %v, %v\n", entry.DN, entry.GetAttributeValue("cn"), entry.GetAttributeValue("sAMAcountName"))
	}
}

func main() {
	println("bind ldap1")
	A := Bindldap(Ldap1, Lport1, Prpl1, Pass1)
	println("#######################################################################3seperator")
	searchrequest2(A)
	A.Close()
}

The third search of the same function (triggered from func main) will yield:
2017/05/22 14:44:19 LDAP Result Code 200 "": ldap: connection closed error
wich is really weird

Problem with ParseDN

I'm using ParseDN to validate the DN format. from my understanding once ParseDN code hits ',' or '+' chars, the code assigns attribute.Value from the buffer and appends attribute to RDN.Attributes. This doesn't check whether attribute.Type is valid or not nil before appending.

The code @ https://github.com/go-ldap/ldap/blob/v2.5.0/dn.go#L147. If I enter "test,DC=example,DC=com" as DN it doesn't raise any errors even though its invalid DN input.

Deadlock when LDAP server sends unexpected response packet

I have encountered an issue where a Bind Request to a particular LDAP server response with more than one response packet for the corresponding request messageID. The Bind() method only expects a single response which it reads and then returns (running a deferred l.finishMessage(messageID)).

In the meantime, the processMessages() goroutine is blocking on sending this unexpected extra response packet to the result channel for the Bind request, but the Bind() method never receives.

Going back to the deferred l.finishMessage(messageID): The Bind() method wont return until this deferred call finishes but it is blocked on sending the MessageFinish message packet to the processMessages() goroutine which in turn is blocked because the Bind() method never receives from its channel. This is the deadlock.

It seems that this design for the proccesMessages() goroutine would, in general, always end up in deadlock if the server responds with more request packets than a request handler is expecting. This may be in violation of the protocol but I think that the client should be more resilient than this in handling a misbehaving server.

One solution that I've come up with is to make it so that calls to finishMessage() also signal to the processMessages() goroutine to stop blocking on an attempt to send a response packet for that message ID. Currently, the connection has a chanResults map which holds results channels which correspond to specific message IDs. We could replace this with a mapping of message IDs to context objects for that message. At the very least, this context would contain 2 items:

type messageContext struct {
    done      chan struct{}
    responses chan *PacketResponse
}

The job of finishMessage(messageID) would change to acquiring a lock on this message context mapping, closing the done channel, then send the MessageFinish packet. In processMessages() we will have to change the MessageResponse handler to have a select statement which attempts to either send the response packet or receive from the done channel. If this done channel is closed it will not block and return instantly. The value is not important, but this signals that the request handler is done and we can abandon sending response packets to it.

Possibility to make Conn.start exported?

Now it is possible to create new ldap.Conn with NewConn instead of Dial / DialTLS, but there is no way to call .start() on the created ldap.Conn object making NewConn not much useful.

User Authentication Example not working

I'm new to LDAP and was trying to see if I can use the examples presented here to set something up,
The following ldapsearch command executes successfully (that is my sanity check)
ldapsearch -D "uid=tester,ou=People,dc=example,dc=com" -w "password" -p 389 -h localhost -b "dc=example,dc=com" -s sub "(objectclass=*)"
However, running the example code returns this error:

LDAP Result Code 34 "Invalid DN Syntax": invalid DN
exit status 1

For reference, here is how I modified the example before running it:

package main

import (
	"crypto/tls"
	"fmt"
	"log"

	"gopkg.in/ldap.v2"
)

func main() {
	// The username and password we want to check
	username := "tester"
	password := "password"

	bindusername := "tester"
	bindpassword := "password"

	l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "localhost", 389))
	if err != nil {
		log.Fatal(err)
	}
	//Commenting this out as I'd like to connect without TLS
	//defer l.Close()

	// Reconnect with TLS
	//err = l.StartTLS(&tls.Config{InsecureSkipVerify: true})
	//if err != nil {
	//	log.Fatal(err)
	//}

	// First bind with a read only user
	err = l.Bind(bindusername, bindpassword)
	if err != nil {
		log.Fatal(err)
	}

	// Search for the given username
	searchRequest := ldap.NewSearchRequest(
		"dc=example,dc=com",
		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
		fmt.Sprintf("(&(objectClass=People)&(uid=%s))", username),
		[]string{"dn"},
		nil,
	)

	sr, err := l.Search(searchRequest)
	if err != nil {
		log.Fatal(err)
	}

	if len(sr.Entries) != 1 {
		log.Fatal("User does not exist or too many entries returned")
	}

	userdn := sr.Entries[0].DN

	// Bind as the user to verify their password
	err = l.Bind(userdn, password)
	if err != nil {
		log.Fatal(err)
	}

	// Rebind as the read only user for any futher queries
	err = l.Bind(bindusername, bindpassword)
	if err != nil {
		log.Fatal(err)
	}
}

nil pointer panic when StartTLS fails

Thanks for creating and sharing this library!

I'm reproducibly getting a nil pointer panic when StartTLS fails. I can reproduce using a free account on jumpcloud.com and trying to use StartTLS on port 686 (the TLS-enabled port). Of course, this isn't expected to work, but hopefully we can avoid a panic.

conn, err := ldap.Dial("tcp", "ldap.jumpcloud.com:686")
if err != nil {
  log.Fatal("Error opening connection")
  return;
}
conn.StartTLS(&tls.Config{InsecureSkipVerify: ldapConfig.ServerTLSInsecure, ServerName: serverName})

The last line there will panic and the stack trace takes us through:
https://github.com/go-ldap/ldap/blob/v2.2.1/conn.go#L182

ldap/error.go

Line 102 in 07a7330

if len(packet.Children) >= 2 {

where we get a nil pointer. It seems that packet or Children must be nil.

The panic does not occur when conn.Debug is set to true. When Debugging is enabled, we get the following output (our own logging intermingled with this package's).

2016/04/11 19:18:11 /connect/src/connect/auth/providers/ldap/client.go:129: (ldap) Dialing unencrypted LDAP connection: ldap.jumpcloud.com:636
2016/04/11 19:18:11 /connect/src/connect/auth/providers/ldap/client.go:139: (ldap) Using StartTLS on LDAP connection: ldap.jumpcloud.com:636
LDAP Request: (Universal, Constructed, Sequence and Sequence of) Len=29 "<nil>"
 MessageID: (Universal, Primitive, Integer) Len=1 "1"
 Start TLS: (Application, Constructed, 0x17) Len=24 "<nil>"
  TLS Extended Command: (Context, Primitive, 0x00) Len=22 "1.3.6.1.4.1.1466.20037"
2016/04/11 19:18:11 flags&startTLS = 1
2016/04/11 19:18:11 1: waiting for response
2016/04/11 19:18:11 Sending message 1
2016/04/11 19:18:11 reader error: unexpected EOF
2016/04/11 19:18:11 Sending quit message and waiting for confirmation
2016/04/11 19:18:11 Shutting down - quit message received
2016/04/11 19:18:11 Closing channel for MessageID 1
2016/04/11 19:18:11 1: got response 0x0
2016/04/11 19:18:11 Closing network connection
2016/04/11 19:18:11 /connect/src/connect/auth/providers/ldap/client.go:143: (ldap) Failed to connect with TLS: LDAP Result Code 203 "": ldap: cannot process packet to add descriptions
2016/04/11 19:18:11 /connect/src/connect/auth/providers/ldap/ldap.go:180: (ldap) Unable to verify credentials: LDAP Result Code 203 "": ldap: cannot process packet to add descriptions

I'll have a PR opened with one solution for this shortly which will either defer a recover in the getLDAPResultCode function or check packet.Children for nil before checking length. But open to other approaches if anyone has any ideas.

DialTLS() not working for me

I may be using DialTLS() wrong, but it doesn't work for me.

Consider this simple script:

package main

import (
    "fmt"
    "crypto/tls"
    "github.com/go-ldap/ldap"
)

func main() {
    conn,err := ldap.DialTLS("tcp","173.9.229.241:389",&tls.Config{InsecureSkipVerify:true})
    fmt.Printf("conn: %s\nerr: %s\n", conn,err)
}

Which outputs:

Connected? false
err: LDAP Result Code 200 "": read tcp 173.9.229.241:389: connection reset by peer

However, if I change it to call StartTLS after the connection is established, it works:

package main

import (
    "fmt"
    "crypto/tls"
    "github.com/go-ldap/ldap"
)

func main() {
    conn,_ := ldap.Dial("tcp","173.9.229.241:389")
    err := conn.StartTLS(&tls.Config{InsecureSkipVerify:true})
    fmt.Printf("Connected? %t\nerr: %s\n", conn != nil, err)
}

Connected? true
err: %!s()

Am I using DialTLS() incorrectly?

Active Directory TLS access updating password

This code almost works but can't change user password although password encoding and connection authentication works with ldmodify in command line. I can connect via port 636 and do almost all actions except unicodePwd change.

Error:

Password could not be changed: LDAP Result Code 53 "Unwilling To Perform": 0000001F: SvcErr: DSID-031A0FC0, problem 5003 (WILL_NOT_PERFORM), data 0

Possibly something having to do with TLS handshake, any ideas ?

func main() {

const rootPEM =`XXXX`

roots := x509.NewCertPool()
ok := roots.AppendCertsFromPEM([]byte(rootPEM))
if !ok {
  panic("failed to parse root certificate")
}

 l, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", "xxxxx.domain.com", 636), &tls.Config{
   RootCAs: roots,
   ServerName: "xxxxx.domain.com",
   InsecureSkipVerify: false,
 })
 if err != nil {
     log.Fatal(err)
 }
 defer l.Close()

  err = l.Bind("CN=administrator,DC=domain,DC=com", "xxxxxxxxxx")
  if err != nil {
      log.Fatal(err)
  }
  log.Println("Successfull")

  utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM)

  encoded, err := utf16.NewEncoder().String("\"xxxxxxxxxxxxxx\"")
  dn := "CN=testldap,DC=domain,DC=com"
  fmt.Println(encoded)
  str := base64.StdEncoding.EncodeToString([]byte(encoded))
  fmt.Println(str)
  modify := ldap.NewModifyRequest(dn)
modify.Replace("unicodePwd", []string{str})
err = l.Modify(modify)
  if err != nil {
        log.Fatalf("Password could not be changed: %s", err.Error())
  }

}

cannot decode encoded paging control

The following code fails with a panic:

package ldap_test

import (
    "testing"
    "gopkg.in/ldap.v2"
)

func TestControlPagingEncodeDecode(t *testing.T) {
    c := ldap.NewControlPaging(100)
    packet := c.Encode()
    _ = ldap.DecodeControl(packet)
}
--- FAIL: TestControlPagingEncodeDecode (0.00s)
panic: interface conversion: interface is uint64, not int64 [recovered]
        panic: interface conversion: interface is uint64, not int64

goroutine 5 [running]:
panic(0x7d5760, 0xc820056780)
        /usr/local/go/src/runtime/panic.go:481 +0x3e6
testing.tRunner.func1(0xc8200841b0)
        /usr/local/go/src/testing/testing.go:467 +0x192
panic(0x7d5760, 0xc820056780)
        /usr/local/go/src/runtime/panic.go:443 +0x4e9
gopkg.in/ldap%2ev2.DecodeControl(0xc820018310, 0x0, 0x0)
        /home/hanno/go/src/gopkg.in/ldap.v2/control.go:283 +0x682
_/home/hanno/git/go-ldap_test.TestControlPagingEncodeDecode(0xc8200841b0)
        /home/hanno/git/go-ldap/control_test.go:11 +0x5b
testing.tRunner(0xc8200841b0, 0xa3fbb8)
        /usr/local/go/src/testing/testing.go:473 +0x98
created by testing.RunTests
        /usr/local/go/src/testing/testing.go:582 +0x892
exit status 2
FAIL    _/home/hanno/git/go-ldap        0.007s

The PagingSize value is encoded as uint64, but decoded as int64

addControlDescriptions would panic at control without critcality and value

a fix similar to #83 should be applied here also (ldap.go)

func addControlDescriptions(packet *ber.Packet) {
    packet.Description = "Controls"
    for _, child := range packet.Children {
        child.Description = "Control"
        child.Children[0].Description = "Control Type (" + ControlDescription(child.Children[0].Value.(string)) + ")"
        value := child.Children[1]
        if len(child.Children) == 3 {
            child.Children[1].Description = "Criticality"
            value = child.Children[2]
        }
        value.Description = "Control Value"

Handling time ?

Hello,

Sorry for opening yet another issue, but any idea on how I can handle timestamps ?

Thanks in advance πŸ˜„

start using error returning BER API calls

DecodePacketErr returns error information, we should update to use this and propagate errors

also, we should add bounds checking helpers that return a child packet or an error

would do this on a major release boundary since it would probably mean tweaking function return values

Bind returns nil

When i set the password parameter as an empty string the

func (l *Conn) Bind(username, password string) error

returns nil as error so that my users can login with username and empty password.

Problem with non-latin characters

Hi! The problem appears when the search string contains Cyrillic (non-ascii) symbols.


Example for latin (ascii):

Search request: (|(displayName=Smith)(cn=Smith))

Debug from ldap library:

Attribute: (Universal, Primitive, Octet String) Len=11 "displayName"
Substrings Any: (Context, Primitive, 0x01) Len=5 "Smith"
Attribute: (Universal, Primitive, Octet String) Len=2 "cn"
Substrings Any: (Context, Primitive, 0x01) Len=5 "Smith"

Debug by my printf in file gopkg.in/ldap.v1/filter.go, function compileFilter, line number 193:
image

Filter:(|(displayName=*Smith*)(cn=*Smith*)) / filter[newPos]: * / newPos: 15
Filter:(|(displayName=*Smith*)(cn=*Smith*)) / filter[newPos]: S / newPos: 16
Filter:(|(displayName=*Smith*)(cn=*Smith*)) / filter[newPos]: m / newPos: 17
Filter:(|(displayName=*Smith*)(cn=*Smith*)) / filter[newPos]: i / newPos: 18
Filter:(|(displayName=*Smith*)(cn=*Smith*)) / filter[newPos]: t / newPos: 19
Filter:(|(displayName=*Smith*)(cn=*Smith*)) / filter[newPos]: h / newPos: 20
Filter:(|(displayName=*Smith*)(cn=*Smith*)) / filter[newPos]: * / newPos: 21

Debug from LDAP Server:

55fbcb6d ==>backsql_search(): base="ou=sl it,ou=aup,ou=tsg,ou=quadra,o=enterprise", filter="(|(displayName=smith)(cn=smith))", scope=2, deref=0, attrsonly=0, attributes to load: custom list

55fbcb72 Constructed query: SELECT DISTINCT ldap_entries.id,ldapx_persons.id,text('inetOrgPerson') AS objectClass,ldap_entries.dn AS dn FROM ldap_entries,ldapx_persons WHERE ldapx_persons.id=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND lower(ldap_entries.dn) LIKE lower('%'||?) AND ldapx_persons.lang=0 AND ((lower(text(ldapx_persons.fullname)) LIKE '%SMITH%') OR (lower(text(ldapx_persons.surname||' '||ldapx_persons.name)) LIKE '%SMITH%'))

All Good!



Example for Cyrilic (non-ascii):

I'll put pictures to avoid problems with Cyrillic and non-printable symbols.

Search request:
image

Debug from ldap library:
image

Debug by my printf in file gopkg.in/ldap.v1/filter.go, function compileFilter, line number 193:
image

Debug From LDAP Server:
image

...


User Bind to AD with zero length password succeeds using example

Probably not a bug in the code itself, but the bind example is misleading when using Active Directory.

We are essentially using the code from https://godoc.org/gopkg.in/ldap.v2#ex-package--UserAuthentication to authenticate users. We loop through multiple LDAP and AD servers that may be used for authentication in our application. If we enter a valid username (login) but a zero length password, during the final bind operation as the user, the LDAP servers return an LDAP Result Code 53 "Unwilling To Perform" error, which is good. But on Active Directory 2012, the final user bind simply succeeds. If the wrong password is given, both the Active Directory and LDAP servers fail with a code 49 (invalid credentials), which is good. With a zero length password the AD will subsequently not let that unauthenticated but bound user see anything, but in the example as is, you have successfully authenticated so the user can then do whatever your application code allows her/him to do. We do check for zero length passwords as part of our own checks so this should never happen, but just in case we have further added a second search AS the user. That second search as the user then fails on AD with LDAP Result Code 1 "Operations Error": 000004DC: LdapErr: DSID-0C090752, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v2580

Just thought you should know, and maybe fix or change the example. Also here in case other folks using the library against AD get confused by this behaviour.

cheers,

API design: remove string typing

Consider the SearchRequest type:

type SearchRequest struct {
    BaseDN       string
    Scope        int
    DerefAliases int
    SizeLimit    int
    TimeLimit    int
    TypesOnly    bool
    Filter       string  /* <-- stringly typed */
    Attributes   []string
    Controls     []Control
}

The Filter attribute uses "string typing". Since a filter is actually structured information (similar to an abstract syntax tree with the &, |, … nodes), using a string here is inappropriate.

Say I want to generate filters programmatically in a secure and correct way (which happens to be the case, see hashicorp/vault#1030), then the only chance I have with the current design is

  1. Compile BER packets in user code, decompile them using decompileFilter, and construct a SearchRequest object using the string that I obtained; which works but is terrible.
  2. Implement string filter composition in user code, i.e. same as 1. but without the trip through BER

My suggestion is to represent filters as proper structured data internally and only use the string representation on the peripherals, i.e. input and output. Here's a sketch of the data structure. (Sorry that this is in Haskell; I'm not very familiar with Go.)

data Filter
  = Not Filter
  | Eq Attrib Attrib
  | LessEqual Attrib Attrib
  …

parseFilter :: String -> Filter
filterAsString :: Filter -> String

Note that this may apply to other data than filters as well, maybe there are more cases of "stringly typing" in this library.

Expose the isClosing method from Conn

Hey,

Would it be possible to expose the isClosing method from the Connection package ?
I'm running a service and inside I use (and reuse) a connection to an LDAP but at some point the TLS connection closed and I need to know if I can reuse it.
Right now I'm checking the error string but this is not the best way to achieve it I guess.

Thanks,

Non-typed Errors Returned

Currently, errors from the LDAP server are returned untyped. It would be useful to have a separate type for each error. I'll work on this.

help with knowing how to reuse a connection

Hi, thank you for the lib!

I'm trying to use it in a web server and I want to reuse the connection for the duration of the request, do you have any suggestions? Right now, I'm dialing and closing every time I want to make a query, and I make multiple queries per request.

thank you!

RFC: Persistent Search

There's an initial implementation of persistent search (https://www.ietf.org/proceedings/50/I-D/ldapext-psearch-03.txt) in https://github.com/vetinari/ldap/tree/persistent-search

That one works e.g. with an OpenDJ (or OpenDS probably) which supports this control(s)

Code to get it running:

package main
import (
    "crypto/tls"
    "fmt"
    "gopkg.in/ldap.v2"
)

func main() {
    l, err := ldap.DialTLS("tcp", "ldap.example.org:636", &tls.Config{InsecureSkipVerify: true})
    if err != nil {
        panic("DialTLS: " + err.Error())
    }
    _, err = l.SimpleBind(ldap.NewSimpleBindRequest("uid=someone,dc=example,dc=org", "MySecret", nil))
    if err != nil {
        panic("SimpleBind(): " + err.Error())
    }
    req := &ldap.SearchRequest{
        BaseDN:     "ou=people,dc=example,dc=org",
        Scope:      ldap.ScopeWholeSubtree,
        Filter:     "(uid=*)",
        Attributes: []string{"uid", "cn"},
    }
    l.Debug = true
    err = l.PersistentSearch(req, []string{"any"}, true, true, callBack)
    if err != nil {
        panic("PersistentSearch(): " + err.Error())
    }
}

func callBack(res *ldap.SearchResult) bool {
    if len(res.Entries) != 0 {
        entry := res.Entries[0]
        fmt.Printf("%s (%s)\n", entry.GetAttributeValue("cn"), entry.GetAttributeValue("uid"))
    }
    if len(res.Controls) != 0 {
        fmt.Printf("CTRL=%s\n", res.Controls[0].String())
    }
    return true
}

Note that the PersistentSearch() will never (except for errors) return.
When this is running any changes on "uid" or "cn" will cause the "callBack()" function to run.

Password could not be changed: LDAP Result Code 2 "Protocol Error": 0000203D: LdapErr: DSID-0C090D9A, comment: Unknown extended request OID, data 0, v1db1 exit status 1

I do not know if it's from me. Maybe it's a bug. Maybe "Basic LDAP v3 functionality" is missing coding.

When I run code from Windows 2008 Domain Controller, I get the following error. What I'm trying to do is run the modify password function

Password could not be changed: LDAP Result Code 2 "Protocol Error": 0000203D: LdapErr: DSID-0C090D9A, comment: Unknown extended request OID, data 0, v1db1 exit status 1

The ldap code I wrote with PHP v2 and v3 performs all functional operations successfully. It turns out that there is not a situation about the version from here either.

Could you tell me what the problem is ?

Support LDAP Unbind

Do you have a plan to support LDAP Unbind?

Now following sequese dosen't exec unbind but I want to unbind between 3 and 4.

  1. dial
  2. bind
  3. search
    4 bind
  4. close

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.