diff --git a/go.mod b/go.mod index 22458553..a7f39327 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/deckarep/golang-set v1.8.0 github.com/gabriel-vasile/mimetype v1.4.1 github.com/ghodss/yaml v1.0.0 - github.com/go-ldap/ldap/v3 v3.4.2 + github.com/go-ldap/ldap/v3 v3.4.4 github.com/gofrs/uuid v4.2.0+incompatible github.com/golang-jwt/jwt/v4 v4.4.3 github.com/google/go-querystring v1.1.0 @@ -26,7 +26,7 @@ require ( github.com/russellhaering/goxmldsig v1.1.1 github.com/sirupsen/logrus v1.9.0 github.com/spf13/cobra v1.5.0 - golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 + golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d golang.org/x/net v0.8.0 golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 @@ -37,11 +37,11 @@ require ( ) require ( - github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect + github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect - github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect diff --git a/go.sum b/go.sum index 55d9fab7..c36a2afa 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= -github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU= +github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -79,9 +79,8 @@ github.com/gabriel-vasile/mimetype v1.4.1 h1:TRWk7se+TOjCYgRth7+1/OYLNiRNIotknkF github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= -github.com/go-asn1-ber/asn1-ber v1.5.3 h1:u7utq56RUFiynqUzgVMFDymapcOtQ/MZkh3H4QYkxag= -github.com/go-asn1-ber/asn1-ber v1.5.3/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-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -89,8 +88,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-ldap/ldap/v3 v3.4.2 h1:zFZKcXKLqZpFMrMQGHeHWKXbDTdNCmhGY9AK41zPh+8= -github.com/go-ldap/ldap/v3 v3.4.2/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= +github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs= +github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= @@ -282,8 +281,9 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -298,11 +298,10 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s= -golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -584,8 +583,9 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/identifier/backends/ldap/ldap.go b/identifier/backends/ldap/ldap.go index 217f84e7..47b288cf 100644 --- a/identifier/backends/ldap/ldap.go +++ b/identifier/backends/ldap/ldap.go @@ -26,6 +26,7 @@ import ( "net/url" "strconv" "strings" + "sync" "time" "github.com/go-ldap/ldap/v3" @@ -71,6 +72,9 @@ type LDAPIdentifierBackend struct { timeout int limiter *rate.Limiter + + mutex sync.Mutex + conn *ldap.Conn } type ldapAttributeMapping map[string]string @@ -115,7 +119,7 @@ func newLdapUser(entryID string, mapping ldapAttributeMapping, entry *ldap.Entry for n, mapped := range mapping { // LDAP attribute descriptors / short names are case insensitive. See // https://tools.ietf.org/html/rfc4512#page-4. - if strings.ToLower(attribute.Name) == strings.ToLower(mapped) { + if strings.EqualFold(attribute.Name, mapped) { // Check if we need conversion. switch mapping[fmt.Sprintf("%s_type", n)] { case AttributeValueTypeBinary: @@ -370,9 +374,8 @@ func (b *LDAPIdentifierBackend) Logon(ctx context.Context, audience, username, p l, err := b.connect(ctx) if err != nil { - return false, nil, nil, nil, fmt.Errorf("ldap identifier backend logon connect error: %v", err) + return false, nil, nil, nil, fmt.Errorf("ldap identifier backend logon global connect error: %v", err) } - defer l.Close() // Search for the given username. entry, err := b.searchUsername(l, username, b.attributeMapping.attributes()) @@ -387,8 +390,15 @@ func (b *LDAPIdentifierBackend) Logon(ctx context.Context, audience, username, p return false, nil, nil, nil, fmt.Errorf("ldap identifier backend logon search returned wrong user") } - // Bind as the user to verify the password. - err = l.Bind(entry.DN, password) + // Use a temporary connection for the user Bind to verify the password. + ul, err := b.createConnection(ctx, entry.DN, password) + if err != nil { + return false, nil, nil, nil, fmt.Errorf("ldap identifier backend logon user connect error: %v", err) + } + // Always close the temporary connection + defer ul.Close() + + err = ul.Bind(entry.DN, password) switch { case ldap.IsErrorWithCode(err, ldap.LDAPResultInvalidCredentials): return false, nil, nil, nil, nil @@ -431,7 +441,6 @@ func (b *LDAPIdentifierBackend) ResolveUserByUsername(ctx context.Context, usern if err != nil { return nil, fmt.Errorf("ldap identifier backend resolve connect error: %v", err) } - defer l.Close() // Search for the given username. entry, err := b.searchUsername(l, username, b.attributeMapping.attributes()) @@ -464,7 +473,6 @@ func (b *LDAPIdentifierBackend) GetUser(ctx context.Context, entryID string, ses if err != nil { return nil, fmt.Errorf("ldap identifier backend get user connect error: %v", err) } - defer l.Close() entry, err := b.getUser(l, entryID, b.attributeMapping.attributes()) if err != nil { @@ -517,7 +525,7 @@ func (b *LDAPIdentifierBackend) Name() string { return ldapIdentifierBackendName } -func (b *LDAPIdentifierBackend) connect(parentCtx context.Context) (*ldap.Conn, error) { +func (b *LDAPIdentifierBackend) createConnection(parentCtx context.Context, bindDN, bindPassword string) (*ldap.Conn, error) { // A timeout for waiting for a limiter slot. The timeout also includes the // time to connect to the LDAP server which as a consequence means that both // getting a free slot and establishing the connection are one timeout. @@ -550,9 +558,8 @@ func (b *LDAPIdentifierBackend) connect(parentCtx context.Context) (*ldap.Conn, l.Start() - // Bind with general user (which is preferably read only). - if b.bindDN != "" { - err = l.Bind(b.bindDN, b.bindPassword) + if bindDN != "" { + err = l.Bind(bindDN, bindPassword) if err != nil { return nil, err } @@ -561,6 +568,20 @@ func (b *LDAPIdentifierBackend) connect(parentCtx context.Context) (*ldap.Conn, return l, nil } +// connect establishes a global connection that is used for all requests except the user Bind to check the password in the Logon call. +func (b *LDAPIdentifierBackend) connect(parentCtx context.Context) (*ldap.Conn, error) { + b.mutex.Lock() + defer b.mutex.Unlock() + + if b.conn != nil && !b.conn.IsClosing() { + return b.conn, nil + } + var err error + b.conn, err = b.createConnection(parentCtx, b.bindDN, b.bindPassword) + + return b.conn, err +} + func (b *LDAPIdentifierBackend) searchUsername(l *ldap.Conn, username string, attributes []string) (*ldap.Entry, error) { base, filter := b.baseAndSearchFilterFromUsername(username) // Search for the given username.