@ -42,6 +42,11 @@ type Source struct {
AttributesInBind bool // fetch attributes in bind context (not user)
AttributesInBind bool // fetch attributes in bind context (not user)
Filter string // Query filter to validate entry
Filter string // Query filter to validate entry
AdminFilter string // Query filter to check if user is admin
AdminFilter string // Query filter to check if user is admin
GroupsEnabled bool // if the group checking is enabled
GroupDN string // Group Search Base
GroupFilter string // Group Name Filter
GroupMemberUid string // Group Attribute containing array of UserUID
UserUID string // User Attribute listed in Group
Enabled bool // if this source is disabled
Enabled bool // if this source is disabled
}
}
@ -67,6 +72,28 @@ func (ls *Source) sanitizedUserDN(username string) (string, bool) {
return fmt . Sprintf ( ls . UserDN , username ) , true
return fmt . Sprintf ( ls . UserDN , username ) , true
}
}
func ( ls * Source ) sanitizedGroupFilter ( group string ) ( string , bool ) {
// See http://tools.ietf.org/search/rfc4515
badCharacters := "\x00*\\"
if strings . ContainsAny ( group , badCharacters ) {
log . Trace ( "Group filter invalid query characters: %s" , group )
return "" , false
}
return group , true
}
func ( ls * Source ) sanitizedGroupDN ( groupDn string ) ( string , bool ) {
// See http://tools.ietf.org/search/rfc4514: "special characters"
badCharacters := "\x00()*\\'\"#+;<>"
if strings . ContainsAny ( groupDn , badCharacters ) || strings . HasPrefix ( groupDn , " " ) || strings . HasSuffix ( groupDn , " " ) {
log . Trace ( "Group DN contains invalid query characters: %s" , groupDn )
return "" , false
}
return groupDn , true
}
func ( ls * Source ) findUserDN ( l * ldap . Conn , name string ) ( string , bool ) {
func ( ls * Source ) findUserDN ( l * ldap . Conn , name string ) ( string , bool ) {
log . Trace ( "Search for LDAP user: %s" , name )
log . Trace ( "Search for LDAP user: %s" , name )
if ls . BindDN != "" && ls . BindPassword != "" {
if ls . BindDN != "" && ls . BindPassword != "" {
@ -194,15 +221,15 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str
return "" , "" , "" , "" , false , false
return "" , "" , "" , "" , false , false
}
}
log . Trace ( "Fetching attributes '%v', '%v', '%v', '%v' with filter '%s' and base '%s'" , ls . AttributeUsername , ls . AttributeName , ls . AttributeSurname , ls . AttributeMail , userFilter , userDN )
log . Trace ( "Fetching attributes '%v', '%v', '%v', '%v', '%v' with filter '%s' and base '%s'" , ls . AttributeUsername , ls . AttributeName , ls . AttributeSurname , ls . AttributeMail , ls . UserUID , userFilter , userDN )
search := ldap . NewSearchRequest (
search := ldap . NewSearchRequest (
userDN , ldap . ScopeWholeSubtree , ldap . NeverDerefAliases , 0 , 0 , false , userFilter ,
userDN , ldap . ScopeWholeSubtree , ldap . NeverDerefAliases , 0 , 0 , false , userFilter ,
[ ] string { ls . AttributeUsername , ls . AttributeName , ls . AttributeSurname , ls . AttributeMail } ,
[ ] string { ls . AttributeUsername , ls . AttributeName , ls . AttributeSurname , ls . AttributeMail , ls . UserUID } ,
nil )
nil )
sr , err := l . Search ( search )
sr , err := l . Search ( search )
if err != nil {
if err != nil {
log . Error ( 4 , "LDAP search failed: %v" , err )
log . Error ( 4 , "LDAP user search failed: %v" , err )
return "" , "" , "" , "" , false , false
return "" , "" , "" , "" , false , false
} else if len ( sr . Entries ) < 1 {
} else if len ( sr . Entries ) < 1 {
if directBind {
if directBind {
@ -218,6 +245,48 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str
firstname := sr . Entries [ 0 ] . GetAttributeValue ( ls . AttributeName )
firstname := sr . Entries [ 0 ] . GetAttributeValue ( ls . AttributeName )
surname := sr . Entries [ 0 ] . GetAttributeValue ( ls . AttributeSurname )
surname := sr . Entries [ 0 ] . GetAttributeValue ( ls . AttributeSurname )
mail := sr . Entries [ 0 ] . GetAttributeValue ( ls . AttributeMail )
mail := sr . Entries [ 0 ] . GetAttributeValue ( ls . AttributeMail )
uid := sr . Entries [ 0 ] . GetAttributeValue ( ls . UserUID )
// Check group membership
if ls . GroupsEnabled {
groupFilter , ok := ls . sanitizedGroupFilter ( ls . GroupFilter )
if ! ok {
return "" , "" , "" , "" , false , false
}
groupDN , ok := ls . sanitizedGroupDN ( ls . GroupDN )
if ! ok {
return "" , "" , "" , "" , false , false
}
log . Trace ( "Fetching groups '%v' with filter '%s' and base '%s'" , ls . GroupMemberUid , groupFilter , groupDN )
groupSearch := ldap . NewSearchRequest (
groupDN , ldap . ScopeWholeSubtree , ldap . NeverDerefAliases , 0 , 0 , false , groupFilter ,
[ ] string { ls . GroupMemberUid } ,
nil )
srg , err := l . Search ( groupSearch )
if err != nil {
log . Error ( 4 , "LDAP group search failed: %v" , err )
return "" , "" , "" , "" , false , false
} else if len ( sr . Entries ) < 1 {
log . Error ( 4 , "LDAP group search failed: 0 entries" )
return "" , "" , "" , "" , false , false
}
isMember := false
for _ , group := range srg . Entries {
for _ , member := range group . GetAttributeValues ( ls . GroupMemberUid ) {
if member == uid {
isMember = true
}
}
}
if ! isMember {
log . Error ( 4 , "LDAP group membership test failed" )
return "" , "" , "" , "" , false , false
}
}
isAdmin := false
isAdmin := false
if len ( ls . AdminFilter ) > 0 {
if len ( ls . AdminFilter ) > 0 {