|
|
|
|
@@ -27,36 +27,36 @@ import (
|
|
|
|
|
ldap "github.com/go-ldap/ldap/v3"
|
|
|
|
|
"github.com/minio/minio-go/v7/pkg/set"
|
|
|
|
|
"github.com/minio/minio/internal/auth"
|
|
|
|
|
xldap "github.com/minio/pkg/v2/ldap"
|
|
|
|
|
xldap "github.com/minio/pkg/v3/ldap"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// LookupUserDN searches for the full DN and groups of a given short/login
|
|
|
|
|
// username.
|
|
|
|
|
func (l *Config) LookupUserDN(username string) (string, []string, error) {
|
|
|
|
|
func (l *Config) LookupUserDN(username string) (*xldap.DNSearchResult, []string, error) {
|
|
|
|
|
conn, err := l.LDAP.Connect()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", nil, err
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
defer conn.Close()
|
|
|
|
|
|
|
|
|
|
// Bind to the lookup user account
|
|
|
|
|
if err = l.LDAP.LookupBind(conn); err != nil {
|
|
|
|
|
return "", nil, err
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Lookup user DN
|
|
|
|
|
bindDN, err := l.LDAP.LookupUserDN(conn, username)
|
|
|
|
|
lookupRes, err := l.LDAP.LookupUsername(conn, username)
|
|
|
|
|
if err != nil {
|
|
|
|
|
errRet := fmt.Errorf("Unable to find user DN: %w", err)
|
|
|
|
|
return "", nil, errRet
|
|
|
|
|
return nil, nil, errRet
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
groups, err := l.LDAP.SearchForUserGroups(conn, username, bindDN)
|
|
|
|
|
groups, err := l.LDAP.SearchForUserGroups(conn, username, lookupRes.NormDN)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", nil, err
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bindDN, groups, nil
|
|
|
|
|
return lookupRes, groups, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetValidatedDNForUsername checks if the given username exists in the LDAP directory.
|
|
|
|
|
@@ -68,28 +68,28 @@ func (l *Config) LookupUserDN(username string) (string, []string, error) {
|
|
|
|
|
// LDAP specific normalization (including Unicode normalization).
|
|
|
|
|
//
|
|
|
|
|
// If the user is not found, err = nil, otherwise, err != nil.
|
|
|
|
|
func (l *Config) GetValidatedDNForUsername(username string) (string, error) {
|
|
|
|
|
func (l *Config) GetValidatedDNForUsername(username string) (*xldap.DNSearchResult, error) {
|
|
|
|
|
conn, err := l.LDAP.Connect()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer conn.Close()
|
|
|
|
|
|
|
|
|
|
// Bind to the lookup user account
|
|
|
|
|
if err = l.LDAP.LookupBind(conn); err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if the passed in username is a valid DN.
|
|
|
|
|
if !l.ParsesAsDN(username) {
|
|
|
|
|
// We consider it as a login username and attempt to check it exists in
|
|
|
|
|
// the directory.
|
|
|
|
|
bindDN, err := l.LDAP.LookupUserDN(conn, username)
|
|
|
|
|
bindDN, err := l.LDAP.LookupUsername(conn, username)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if strings.Contains(err.Error(), "User DN not found for") {
|
|
|
|
|
return "", nil
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
return "", fmt.Errorf("Unable to find user DN: %w", err)
|
|
|
|
|
return nil, fmt.Errorf("Unable to find user DN: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return bindDN, nil
|
|
|
|
|
}
|
|
|
|
|
@@ -98,124 +98,126 @@ func (l *Config) GetValidatedDNForUsername(username string) (string, error) {
|
|
|
|
|
// under a configured base DN in the LDAP directory.
|
|
|
|
|
validDN, isUnderBaseDN, err := l.GetValidatedUserDN(conn, username)
|
|
|
|
|
if err == nil && !isUnderBaseDN {
|
|
|
|
|
return "", fmt.Errorf("Unable to find user DN: %w", err)
|
|
|
|
|
return nil, fmt.Errorf("Unable to find user DN: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return validDN, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetValidatedUserDN validates the given user DN. Will error out if conn is nil. The returned
|
|
|
|
|
// boolean is true iff the user DN is found under one of the LDAP user base DNs.
|
|
|
|
|
func (l *Config) GetValidatedUserDN(conn *ldap.Conn, userDN string) (string, bool, error) {
|
|
|
|
|
return l.GetValidatedDNUnderBaseDN(conn, userDN, l.LDAP.UserDNSearchBaseDistNames)
|
|
|
|
|
func (l *Config) GetValidatedUserDN(conn *ldap.Conn, userDN string) (*xldap.DNSearchResult, bool, error) {
|
|
|
|
|
return l.GetValidatedDNUnderBaseDN(conn, userDN,
|
|
|
|
|
l.LDAP.GetUserDNSearchBaseDistNames(), l.LDAP.GetUserDNAttributesList())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetValidatedGroupDN validates the given group DN. If conn is nil, creates a
|
|
|
|
|
// connection. The returned boolean is true iff the group DN is found under one
|
|
|
|
|
// of the configured LDAP base DNs.
|
|
|
|
|
func (l *Config) GetValidatedGroupDN(conn *ldap.Conn, groupDN string) (string, bool, error) {
|
|
|
|
|
func (l *Config) GetValidatedGroupDN(conn *ldap.Conn, groupDN string) (*xldap.DNSearchResult, bool, error) {
|
|
|
|
|
if conn == nil {
|
|
|
|
|
var err error
|
|
|
|
|
conn, err = l.LDAP.Connect()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", false, err
|
|
|
|
|
return nil, false, err
|
|
|
|
|
}
|
|
|
|
|
defer conn.Close()
|
|
|
|
|
|
|
|
|
|
// Bind to the lookup user account
|
|
|
|
|
if err = l.LDAP.LookupBind(conn); err != nil {
|
|
|
|
|
return "", false, err
|
|
|
|
|
return nil, false, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return l.GetValidatedDNUnderBaseDN(conn, groupDN, l.LDAP.GroupSearchBaseDistNames)
|
|
|
|
|
return l.GetValidatedDNUnderBaseDN(conn, groupDN,
|
|
|
|
|
l.LDAP.GetGroupSearchBaseDistNames(), nil)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetValidatedDNUnderBaseDN checks if the given DN exists in the LDAP directory
|
|
|
|
|
// and returns the DN value sent by the LDAP server. The value returned by the
|
|
|
|
|
// server may not be equal to the input DN, as LDAP equality is not a simple
|
|
|
|
|
// Golang string equality. However, we assume the value returned by the LDAP
|
|
|
|
|
// server is canonical. Additionally, the attribute type names in the DN are
|
|
|
|
|
// lower-cased.
|
|
|
|
|
// GetValidatedDNUnderBaseDN checks if the given DN exists in the LDAP
|
|
|
|
|
// directory.
|
|
|
|
|
//
|
|
|
|
|
// The `NormDN` value returned here in the search result may not be equal to the
|
|
|
|
|
// input DN, as LDAP equality is not a simple Golang string equality. However,
|
|
|
|
|
// we assume the value returned by the LDAP server is canonical. Additionally,
|
|
|
|
|
// the attribute type names in the DN are lower-cased.
|
|
|
|
|
//
|
|
|
|
|
// Return values:
|
|
|
|
|
//
|
|
|
|
|
// If the DN is found, the normalized (string) value is returned and error is
|
|
|
|
|
// nil.
|
|
|
|
|
// If the DN is found, the normalized (string) value and any requested
|
|
|
|
|
// attributes are returned and error is nil.
|
|
|
|
|
//
|
|
|
|
|
// If the DN is not found, the string returned is empty and the error is nil.
|
|
|
|
|
// If the DN is not found, a nil result and error are returned.
|
|
|
|
|
//
|
|
|
|
|
// The returned boolean is true iff the DN is found under one of the LDAP
|
|
|
|
|
// subtrees listed in `baseDNList`.
|
|
|
|
|
func (l *Config) GetValidatedDNUnderBaseDN(conn *ldap.Conn, dn string, baseDNList []xldap.BaseDNInfo) (string, bool, error) {
|
|
|
|
|
func (l *Config) GetValidatedDNUnderBaseDN(conn *ldap.Conn, dn string, baseDNList []xldap.BaseDNInfo, attrs []string) (*xldap.DNSearchResult, bool, error) {
|
|
|
|
|
if len(baseDNList) == 0 {
|
|
|
|
|
return "", false, errors.New("no Base DNs given")
|
|
|
|
|
return nil, false, errors.New("no Base DNs given")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check that DN exists in the LDAP directory.
|
|
|
|
|
validatedDN, err := xldap.LookupDN(conn, dn)
|
|
|
|
|
searchRes, err := xldap.LookupDN(conn, dn, attrs)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", false, fmt.Errorf("Error looking up DN %s: %w", dn, err)
|
|
|
|
|
return nil, false, fmt.Errorf("Error looking up DN %s: %w", dn, err)
|
|
|
|
|
}
|
|
|
|
|
if validatedDN == "" {
|
|
|
|
|
return "", false, nil
|
|
|
|
|
if searchRes == nil {
|
|
|
|
|
return nil, false, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This will not return an error as the argument is validated to be a DN.
|
|
|
|
|
pdn, _ := ldap.ParseDN(validatedDN)
|
|
|
|
|
pdn, _ := ldap.ParseDN(searchRes.NormDN)
|
|
|
|
|
|
|
|
|
|
// Check that the DN is under a configured base DN in the LDAP
|
|
|
|
|
// directory.
|
|
|
|
|
for _, baseDN := range baseDNList {
|
|
|
|
|
if baseDN.Parsed.AncestorOf(pdn) {
|
|
|
|
|
return validatedDN, true, nil
|
|
|
|
|
return searchRes, true, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Not under any configured base DN so return false.
|
|
|
|
|
return validatedDN, false, nil
|
|
|
|
|
return searchRes, false, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Bind - binds to ldap, searches LDAP and returns the distinguished name of the
|
|
|
|
|
// user and the list of groups.
|
|
|
|
|
func (l *Config) Bind(username, password string) (string, []string, error) {
|
|
|
|
|
func (l *Config) Bind(username, password string) (*xldap.DNSearchResult, []string, error) {
|
|
|
|
|
conn, err := l.LDAP.Connect()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", nil, err
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
defer conn.Close()
|
|
|
|
|
|
|
|
|
|
var bindDN string
|
|
|
|
|
// Bind to the lookup user account
|
|
|
|
|
if err = l.LDAP.LookupBind(conn); err != nil {
|
|
|
|
|
return "", nil, err
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Lookup user DN
|
|
|
|
|
bindDN, err = l.LDAP.LookupUserDN(conn, username)
|
|
|
|
|
lookupResult, err := l.LDAP.LookupUsername(conn, username)
|
|
|
|
|
if err != nil {
|
|
|
|
|
errRet := fmt.Errorf("Unable to find user DN: %w", err)
|
|
|
|
|
return "", nil, errRet
|
|
|
|
|
return nil, nil, errRet
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Authenticate the user credentials.
|
|
|
|
|
err = conn.Bind(bindDN, password)
|
|
|
|
|
err = conn.Bind(lookupResult.NormDN, password)
|
|
|
|
|
if err != nil {
|
|
|
|
|
errRet := fmt.Errorf("LDAP auth failed for DN %s: %w", bindDN, err)
|
|
|
|
|
return "", nil, errRet
|
|
|
|
|
errRet := fmt.Errorf("LDAP auth failed for DN %s: %w", lookupResult.NormDN, err)
|
|
|
|
|
return nil, nil, errRet
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Bind to the lookup user account again to perform group search.
|
|
|
|
|
if err = l.LDAP.LookupBind(conn); err != nil {
|
|
|
|
|
return "", nil, err
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// User groups lookup.
|
|
|
|
|
groups, err := l.LDAP.SearchForUserGroups(conn, username, bindDN)
|
|
|
|
|
groups, err := l.LDAP.SearchForUserGroups(conn, username, lookupResult.NormDN)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", nil, err
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bindDN, groups, nil
|
|
|
|
|
return lookupResult, groups, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetExpiryDuration - return parsed expiry duration.
|
|
|
|
|
@@ -250,7 +252,7 @@ func (l Config) IsLDAPUserDN(user string) bool {
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
for _, baseDN := range l.LDAP.UserDNSearchBaseDistNames {
|
|
|
|
|
for _, baseDN := range l.LDAP.GetUserDNSearchBaseDistNames() {
|
|
|
|
|
if baseDN.Parsed.AncestorOf(udn) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
@@ -264,7 +266,7 @@ func (l Config) IsLDAPGroupDN(group string) bool {
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
for _, baseDN := range l.LDAP.GroupSearchBaseDistNames {
|
|
|
|
|
for _, baseDN := range l.LDAP.GetGroupSearchBaseDistNames() {
|
|
|
|
|
if baseDN.Parsed.AncestorOf(gdn) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|