diff --git a/builtin/credential/ldap/backend.go b/builtin/credential/ldap/backend.go index eb8f5b1547..f25e9a10c0 100644 --- a/builtin/credential/ldap/backend.go +++ b/builtin/credential/ldap/backend.go @@ -113,6 +113,10 @@ func (b *backend) Login(req *logical.Request, username string, password string) b.Logger().Debug("auth/ldap: BindDN fetched", "username", username, "binddn", bindDN) } + if cfg.DenyNullBind && len(password) == 0 { + return nil, logical.ErrorResponse("password cannot be of zero length when passwordless binds are being denied"), nil + } + // Try to bind as the login user. This is where the actual authentication takes place. if err = c.Bind(bindDN, password); err != nil { return nil, logical.ErrorResponse(fmt.Sprintf("LDAP bind failed: %v", err)), nil diff --git a/builtin/credential/ldap/backend_test.go b/builtin/credential/ldap/backend_test.go index a5602bba00..9fa226892b 100644 --- a/builtin/credential/ldap/backend_test.go +++ b/builtin/credential/ldap/backend_test.go @@ -175,6 +175,11 @@ func TestBackend_configDefaultsAfterUpdate(t *testing.T) { t.Errorf("Default mismatch: userattr. Expected: '%s', received :'%s'", defaultUserAttr, cfg["userattr"]) } + defaultDenyNullBind := true + if cfg["deny_null_bind"] != defaultDenyNullBind { + t.Errorf("Default mismatch: deny_null_bind. Expected: '%s', received :'%s'", defaultDenyNullBind, cfg["deny_null_bind"]) + } + return nil }, }, @@ -365,6 +370,7 @@ func testAccStepLogin(t *testing.T, user string, pass string) logicaltest.TestSt } } + func testAccStepLoginNoGroupDN(t *testing.T, user string, pass string) logicaltest.TestStep { return logicaltest.TestStep{ Operation: logical.UpdateOperation, diff --git a/builtin/credential/ldap/path_config.go b/builtin/credential/ldap/path_config.go index 53171514e9..26ee943886 100644 --- a/builtin/credential/ldap/path_config.go +++ b/builtin/credential/ldap/path_config.go @@ -106,6 +106,11 @@ Default: cn`, Default: "tls12", Description: "Maximum TLS version to use. Accepted values are 'tls10', 'tls11' or 'tls12'. Defaults to 'tls12'", }, + "deny_null_bind": &framework.FieldSchema{ + Type: framework.TypeBool, + Default: true, + Description: "Denies an unauthenticated LDAP bind request if the user's password is empty; defaults to true", + }, }, Callbacks: map[logical.Operation]framework.OperationFunc{ @@ -256,6 +261,10 @@ func (b *backend) newConfigEntry(d *framework.FieldData) (*ConfigEntry, error) { if bindPass != "" { cfg.BindPassword = bindPass } + denyNullBind := d.Get("deny_null_bind").(bool) + if denyNullBind { + cfg.DenyNullBind = denyNullBind + } discoverDN := d.Get("discoverdn").(bool) if discoverDN { cfg.DiscoverDN = discoverDN @@ -297,6 +306,7 @@ type ConfigEntry struct { StartTLS bool `json:"starttls" structs:"starttls" mapstructure:"starttls"` BindDN string `json:"binddn" structs:"binddn" mapstructure:"binddn"` BindPassword string `json:"bindpass" structs:"bindpass" mapstructure:"bindpass"` + DenyNullBind bool `json:"deny_null_bind" structs:"deny_null_bind" mapstructure:"deny_null_bind"` DiscoverDN bool `json:"discoverdn" structs:"discoverdn" mapstructure:"discoverdn"` TLSMinVersion string `json:"tls_min_version" structs:"tls_min_version" mapstructure:"tls_min_version"` TLSMaxVersion string `json:"tls_max_version" structs:"tls_max_version" mapstructure:"tls_max_version"` diff --git a/website/source/docs/auth/ldap.html.md b/website/source/docs/auth/ldap.html.md index e9a4362030..7cd55fad3d 100644 --- a/website/source/docs/auth/ldap.html.md +++ b/website/source/docs/auth/ldap.html.md @@ -132,6 +132,7 @@ There are two alternate methods of resolving the user object used to authenticat * `discoverdn` (bool, optional) - If true, use anonymous bind to discover the bind DN of a user * `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` +* `deny_null_bind` (bool, optional) - This option prevents users from bypassing authentication when providing an empty password. The default is `true`. #### Binding - User Principal Name (AD) diff --git a/website/source/docs/install/upgrade-to-0.6.3.html.md b/website/source/docs/install/upgrade-to-0.6.3.html.md new file mode 100644 index 0000000000..7a175e0f81 --- /dev/null +++ b/website/source/docs/install/upgrade-to-0.6.3.html.md @@ -0,0 +1,21 @@ +--- +layout: "install" +page_title: "Upgrading to Vault 0.6.3" +sidebar_current: "docs-install-upgrade-to-0.6.3" +description: |- + Learn how to upgrade to Vault 0.63. +--- + +# Overview + +This page contains the list of deprecations and important or breaking changes +for Vault 0.6.3. Please read it carefully. + +## LDAP Null Binds Disabled By Default + +When using the LDAP Auth Backend, `deny_null_bind` has a default value of +`true`, preventing a successful user authentication when an empty password +is provided. If you utilize passwordless LDAP binds, `deny_null_bind` must +be set to `false`. Upgrades will keep previous behavior until the LDAP +configuration information is rewritten, at which point the new behavior +will be utilized.