diff --git a/builtin/credential/ldap/path_config.go b/builtin/credential/ldap/path_config.go index 5ab6b9a46..d9848b5f5 100644 --- a/builtin/credential/ldap/path_config.go +++ b/builtin/credential/ldap/path_config.go @@ -1,11 +1,14 @@ package ldap import ( + "fmt" + "net" "net/url" "strings" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" + "github.com/vanackere/ldap" ) func pathConfig(b *backend) *framework.Path { @@ -89,9 +92,14 @@ func (b *backend) pathConfigWrite( cfg.Domain = domain } - if !cfg.ValidateURL() { - return logical.ErrorResponse("LDAP URL is malformed"), nil + // Try to connect to the LDAP server, to validate the URL configuration + // We can also check the URL at this stage, as anything else would probably + // require authentication. + conn, cerr := cfg.DialLDAP() + if cerr != nil { + return logical.ErrorResponse(cerr.Error()), nil } + conn.Close() entry, err := logical.StorageEntryJSON("config", cfg) if err != nil { @@ -110,18 +118,37 @@ type ConfigEntry struct { UserAttr string } -func (c *ConfigEntry) ValidateURL() bool { +func (c *ConfigEntry) DialLDAP() (*ldap.Conn, error) { + u, err := url.Parse(c.Url) if err != nil { - return false + return nil, err } - if u.Scheme != "ldap" && u.Scheme != "ldaps" { - return false + host, port, err := net.SplitHostPort(u.Host) + if err != nil { + host = u.Host } - if u.Path != "" { - return false + + var conn *ldap.Conn + switch u.Scheme { + case "ldap": + if port == "" { + port = "389" + } + conn, err = ldap.Dial("tcp", host+":"+port) + case "ldaps": + if port == "" { + port = "636" + } + conn, err = ldap.DialTLS("tcp", host+":"+port, nil) + default: + return nil, fmt.Errorf("invalid LDAP scheme") } - return true + if err != nil { + return nil, fmt.Errorf("cannot connect to LDAP: %v", err) + } + + return conn, nil } func (c *ConfigEntry) SetDefaults() { diff --git a/builtin/credential/ldap/path_login.go b/builtin/credential/ldap/path_login.go index 6ac09968f..03c3bbb3b 100644 --- a/builtin/credential/ldap/path_login.go +++ b/builtin/credential/ldap/path_login.go @@ -2,12 +2,9 @@ package ldap import ( "fmt" - "net" - "net/url" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" - "github.com/mmitton/ldap" ) func pathLogin(b *backend) *framework.Path { @@ -47,37 +44,14 @@ func (b *backend) pathLogin( return logical.ErrorResponse("ldap backend not configured"), nil } - u, err := url.Parse(cfg.Url) + c, err := cfg.DialLDAP() if err != nil { - return nil, err - } - host, port, err := net.SplitHostPort(u.Host) - if err != nil { - host = u.Host - } - - var c *ldap.Conn - var cerr *ldap.Error - switch u.Scheme { - case "ldap": - if port == "" { - port = "389" - } - c, cerr = ldap.Dial("tcp", host+":"+port) - case "ldaps": - if port == "" { - port = "636" - } - c, cerr = ldap.DialSSL("tcp", host+":"+port) - default: - return logical.ErrorResponse("invalid LDAP URL scheme"), nil - } - if cerr != nil { - return nil, cerr + return logical.ErrorResponse(err.Error()), nil } + // Try to authenticate to the server using the provided credentials binddn := fmt.Sprintf("%s=%s,%s", cfg.UserAttr, username, cfg.Domain) - cerr = c.Bind(binddn, password) + cerr := c.Bind(binddn, password) if cerr != nil { return logical.ErrorResponse(fmt.Sprintf("LDAP bind failed: %s", cerr.Error())), nil }