Skip to content

Commit

Permalink
VAULT 18227/introduce cap ldap library (#22185)
Browse files Browse the repository at this point in the history
  • Loading branch information
raymonstah committed Sep 14, 2023
1 parent 854ea77 commit 018e567
Show file tree
Hide file tree
Showing 10 changed files with 410 additions and 121 deletions.
84 changes: 15 additions & 69 deletions builtin/credential/ldap/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"strings"

"github.com/hashicorp/cap/ldap"
"github.com/hashicorp/go-secure-stdlib/strutil"

"github.com/hashicorp/vault/sdk/framework"
Expand Down Expand Up @@ -76,82 +77,25 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username stri
return "", nil, logical.ErrorResponse("password cannot be of zero length when passwordless binds are being denied"), nil, nil
}

ldapClient := ldaputil.Client{
Logger: b.Logger(),
LDAP: ldaputil.NewLDAP(),
}

c, err := ldapClient.DialLDAP(cfg.ConfigEntry)
ldapClient, err := ldap.NewClient(ctx, ldaputil.ConvertConfig(cfg.ConfigEntry))
if err != nil {
return "", nil, logical.ErrorResponse(err.Error()), nil, nil
}
if c == nil {
return "", nil, logical.ErrorResponse("invalid connection returned from LDAP dial"), nil, nil
}

// Clean connection
defer c.Close()

userBindDN, err := ldapClient.GetUserBindDN(cfg.ConfigEntry, c, username)
if err != nil {
if b.Logger().IsDebug() {
b.Logger().Debug("error getting user bind DN", "error", err)
}
return "", nil, logical.ErrorResponse(errUserBindFailed), nil, logical.ErrInvalidCredentials
}

if b.Logger().IsDebug() {
b.Logger().Debug("user binddn fetched", "username", username, "binddn", userBindDN)
}

// Try to bind as the login user. This is where the actual authentication takes place.
if len(password) > 0 {
err = c.Bind(userBindDN, password)
} else {
err = c.UnauthenticatedBind(userBindDN)
}
if err != nil {
if b.Logger().IsDebug() {
b.Logger().Debug("ldap bind failed", "error", err)
}
return "", nil, logical.ErrorResponse(errUserBindFailed), nil, logical.ErrInvalidCredentials
}
defer ldapClient.Close(ctx)

// 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 {
if b.Logger().IsDebug() {
b.Logger().Debug("error while attempting to re-bind with the BindDN User", "error", err)
}
return "", nil, logical.ErrorResponse("ldap operation failed: failed to re-bind with the BindDN user"), nil, logical.ErrInvalidCredentials
}
if b.Logger().IsDebug() {
b.Logger().Debug("re-bound to original binddn")
}
}

userDN, err := ldapClient.GetUserDN(cfg.ConfigEntry, c, userBindDN, username)
c, err := ldapClient.Authenticate(ctx, username, password, ldap.WithGroups(), ldap.WithUserAttributes())
if err != nil {
return "", nil, logical.ErrorResponse(err.Error()), nil, nil
}

if cfg.AnonymousGroupSearch {
c, err = ldapClient.DialLDAP(cfg.ConfigEntry)
if err != nil {
return "", nil, logical.ErrorResponse("ldap operation failed: failed to connect to LDAP server"), nil, nil
if strings.Contains(err.Error(), "discovery of user bind DN failed") ||
strings.Contains(err.Error(), "unable to bind user") {
return "", nil, logical.ErrorResponse(errUserBindFailed), nil, logical.ErrInvalidCredentials
}
defer c.Close() // Defer closing of this connection as the deferal above closes the other defined connection
}

ldapGroups, err := ldapClient.GetLdapGroups(cfg.ConfigEntry, c, userDN, username)
if err != nil {
return "", nil, logical.ErrorResponse(err.Error()), nil, nil
}
if b.Logger().IsDebug() {
b.Logger().Debug("groups fetched from server", "num_server_groups", len(ldapGroups), "server_groups", ldapGroups)
}

ldapGroups := c.Groups
ldapResponse := &logical.Response{
Data: map[string]interface{}{},
}
Expand All @@ -162,6 +106,10 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username stri
ldapResponse.AddWarning(errString)
}

for _, warning := range c.Warnings {
ldapResponse.AddWarning(string(warning))
}

var allGroups []string
canonicalUsername := username
cs := *cfg.CaseSensitiveNames
Expand Down Expand Up @@ -206,13 +154,11 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username stri
return username, policies, ldapResponse, allGroups, nil
}

entityAliasAttribute, err := ldapClient.GetUserAliasAttributeValue(cfg.ConfigEntry, c, username)
if err != nil {
return "", nil, logical.ErrorResponse(err.Error()), nil, nil
}
if entityAliasAttribute == "" {
userAttrValues := c.UserAttributes[cfg.UserAttr]
if len(userAttrValues) == 0 {
return "", nil, logical.ErrorResponse("missing entity alias attribute value"), nil, nil
}
entityAliasAttribute := userAttrValues[0]

return entityAliasAttribute, policies, ldapResponse, allGroups, nil
}
Expand Down
5 changes: 5 additions & 0 deletions changelog/22185.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
```release-note:improvement
auth/ldap: introduce cap/ldap.Client for LDAP authentication
auth/ldap: deprecates `connection_timeout` in favor of `request_timeout` for timeouts
sdk/ldaputil: deprecates Client in favor of cap/ldap.Client
```
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ require (
github.com/go-errors/errors v1.5.0
github.com/go-git/go-git/v5 v5.7.0
github.com/go-jose/go-jose/v3 v3.0.0
github.com/go-ldap/ldap/v3 v3.4.4
github.com/go-ldap/ldap/v3 v3.4.5
github.com/go-sql-driver/mysql v1.7.1
github.com/go-test/deep v1.1.0
github.com/go-zookeeper/zk v1.0.3
Expand All @@ -75,6 +75,7 @@ require (
github.com/google/go-metrics-stackdriver v0.2.0
github.com/google/tink/go v1.7.0
github.com/hashicorp/cap v0.3.4
github.com/hashicorp/cap/ldap v0.0.0-20230907231022-8e71bfc048ed
github.com/hashicorp/consul-template v0.33.0
github.com/hashicorp/consul/api v1.23.0
github.com/hashicorp/errwrap v1.1.0
Expand Down
Loading

0 comments on commit 018e567

Please sign in to comment.