diff --git a/builtin/credential/ldap/backend.go b/builtin/credential/ldap/backend.go index 85ea2d88b12b..9250fce6c939 100644 --- a/builtin/credential/ldap/backend.go +++ b/builtin/credential/ldap/backend.go @@ -104,13 +104,13 @@ func (b *backend) Login(req *logical.Request, username string, password string) // Clean connection defer c.Close() - bindDN, err := b.getBindDN(cfg, c, username) + userBindDN, err := b.getUserBindDN(cfg, c, username) if err != nil { return nil, logical.ErrorResponse(err.Error()), nil } if b.Logger().IsDebug() { - b.Logger().Debug("auth/ldap: BindDN fetched", "username", username, "binddn", bindDN) + b.Logger().Debug("auth/ldap: User BindDN fetched", "username", username, "binddn", userBindDN) } if cfg.DenyNullBind && len(password) == 0 { @@ -118,11 +118,22 @@ func (b *backend) Login(req *logical.Request, username string, password string) } // Try to bind as the login user. This is where the actual authentication takes place. - if err = c.Bind(bindDN, password); err != nil { + if err = c.Bind(userBindDN, password); err != nil { return nil, logical.ErrorResponse(fmt.Sprintf("LDAP bind failed: %v", err)), nil } - userDN, err := b.getUserDN(cfg, c, bindDN) + // We re-bind to the BindDN if it's defined because we assume + // the BindDN should be the one to search, not the user logging in. + if cfg.BindDN != "" && cfg.BindPassword != "" { + if err := c.Bind(cfg.BindDN, cfg.BindPassword); err != nil { + return nil, logical.ErrorResponse(fmt.Sprintf("Encountered an error while attempting to re-bind with the BindDN User: %s", err.Error())), nil + } + if b.Logger().IsDebug() { + b.Logger().Debug("auth/ldap: Re-Bound to original BindDN") + } + } + + userDN, err := b.getUserDN(cfg, c, userBindDN) if err != nil { return nil, logical.ErrorResponse(err.Error()), nil } @@ -165,7 +176,7 @@ func (b *backend) Login(req *logical.Request, username string, password string) policies = append(policies, group.Policies...) } } - if user !=nil && user.Policies != nil { + if user != nil && user.Policies != nil { policies = append(policies, user.Policies...) } // Policies from each group may overlap @@ -218,7 +229,7 @@ func (b *backend) getCN(dn string) string { * 2. If upndomain is set, the user dn is constructed as 'username@upndomain'. See https://msdn.microsoft.com/en-us/library/cc223499.aspx * */ -func (b *backend) getBindDN(cfg *ConfigEntry, c *ldap.Conn, username string) (string, error) { +func (b *backend) getUserBindDN(cfg *ConfigEntry, c *ldap.Conn, username string) (string, error) { bindDN := "" if cfg.DiscoverDN || (cfg.BindDN != "" && cfg.BindPassword != "") { if err := c.Bind(cfg.BindDN, cfg.BindPassword); err != nil { diff --git a/website/source/docs/auth/ldap.html.md b/website/source/docs/auth/ldap.html.md index 7ec754f2a8ba..3912192ba86c 100644 --- a/website/source/docs/auth/ldap.html.md +++ b/website/source/docs/auth/ldap.html.md @@ -122,7 +122,7 @@ There are two alternate methods of resolving the user object used to authenticat #### Binding - Authenticated Search -* `binddn` (string, optional) - Distinguished name of object to bind when performing user search. Example: `cn=vault,ou=Users,dc=example,dc=com` +* `binddn` (string, optional) - Distinguished name of object to bind when performing user and group search. Example: `cn=vault,ou=Users,dc=example,dc=com` * `bindpass` (string, optional) - Password to use along with `binddn` when performing user search. * `userdn` (string, optional) - Base DN under which to perform user search. Example: `ou=Users,dc=example,dc=com` * `userattr` (string, optional) - Attribute on user attribute object matching the username passed when authenticating. Examples: `sAMAccountName`, `cn`, `uid` @@ -146,6 +146,7 @@ Once a user has been authenticated, the LDAP auth backend must know how to resol * `groupdn` (string, required) - LDAP search base to use for group membership search. This can be the root containing either groups or users. Example: `ou=Groups,dc=example,dc=com` * `groupattr` (string, optional) - LDAP attribute to follow on objects returned by `groupfilter` in order to enumerate user group membership. Examples: for groupfilter queries returning _group_ objects, use: `cn`. For queries returning _user_ objects, use: `memberOf`. The default is `cn`. +*Note*: When using _Authenticated Search_ for binding parameters (see above) the distinguished name defined for `binddn` is used for the group search. Otherwise, the authenticating user is used to perform the group search. Use `vault path-help` for more details.