LDAP authorization with groups in rfc2307bis schema on OpenDirectory
-
SERVER
FOG Version: 1.5.6
OS: Ubuntu 16.04.6 LTSDESCRIPTION:
I don’t know if this is by accident (belongs in general problems) or design (should be a feature request), but it appears that the LDAP plugin distributed with FOG 1.5.6 can not successfully query group information from an OpenDirectory implementation using an rfc2307bis schema. I have an LDAP configuration that works when group matching is not enabled. When I enable group matching, I receive errors such as this in /var/log/apache2/error.log (names changed to protect the guilty):AH01071: Got error 'PHP message: Plugin LDAP::_result(). Search Method: search; Filter: (&(|(name=admins))(member=uid=JohnDoe,ou=People,dc=domain,dc=org)); Result: 0\nPHP message: Plugin LDAP::_result(). Search Method: search; Filter: (&(|(name=admins))(member=uid=JohnDoe,ou=People,dc=domain,dc=org)); Result: 0\nPHP message: Plugin LDAP::_result(). Search Method: search; Filter: (member=*); Result: 0\nPHP message: Plugin LDAP::_getAccessLevel() Group Search DN did not return any results. Group Search DN: ou=groups,dc=domain,dc=org\nPHP message: Plugin LDAP::authLDAP() Access level is still 0 or false. No access is allowed!\n', referer: http://10.10.25.15/fog/management/index.php?node=home
The filter recorded here is not correct for our LDAP structure: performing an ldapsearch for (&(|(name=admins))(member=uid=JohnDoe,ou=People,dc=domain,dc=org)) against our group OU will produce no results, but querying for (&(|(cn=admins))(member=uid=JohnDoe,ou=People,dc=domain,dc=org)) produces the expected results. I don’t see anywhere in the web ui to change the attribute for the name of the group, but I went in and changed the attribute in lines 616-617 and 640-641 of ldap.class.php. After doing so, however, I continue to see the same errors, but with the correct ldap filter.
AH01071: Got error 'PHP message: Plugin LDAP::_result(). Search Method: search; Filter: (&(|(cn=admins))(member=uid=JohnDoe,ou=People,dc=domain,dc=org)); Result: 0\nPHP message: Plugin LDAP::_result(). Search Method: search; Filter: (&(|(cn=admins))(member=uid=JohnDoe,ou=People,dc=domain,dc=org)); Result: 0\nPHP message: Plugin LDAP::_result(). Search Method: search; Filter: (member=*); Result: 0\nPHP message: Plugin LDAP::_getAccessLevel() Group Search DN did not return any results. Group Search DN: ou=groups,dc=domain,dc=org\nPHP message: Plugin LDAP::authLDAP() Access level is still 0 or false. No access is allowed!\n', referer: http://10.10.25.15/fog/management/index.php
I also get similar errors if I change the Group Search DN to a known bad value.
AH01071: Got error 'PHP message: Plugin LDAP::_result(). Search Method: search; Filter: (&(|(cn=admins))(member=uid=JohnDoe,ou=People,dc=domain,dc=org)); Result: 0\nPHP message: Plugin LDAP::_result(). Search Method: search; Filter: (&(|(cn=admins))(member=uid=JohnDoe,ou=People,dc=domain,dc=org)); Result: 0\nPHP message: Plugin LDAP::_result(). Search Method: search; Filter: (member=*); Result: 0\nPHP message: Plugin LDAP::_getAccessLevel() Group Search DN did not return any results. Group Search DN: ou=group,dc=domain,dc=org\nPHP message: Plugin LDAP::authLDAP() Access level is still 0 or false. No access is allowed!\n', referer: http://10.10.25.15/fog/management/index.php?node=home
Based on this, it looks like something isn’t behaving with respect to $searchdn in _result() during a group search, but I don’t see any smoking guns. I have tried changing the search scope in the web interface to each of the available options: “Base only” fails the initial user lookup (expected with the configuration) and “subtree” and “subtree and below” throw the same errors with the expected change in search scope.
Any suggestions where to go from here?
-
@Daniel-Miller said in LDAP authorization with groups in rfc2307bis schema on OpenDirectory:
Plugin LDAP::authLDAP() Access level is still 0 or false. No access is allowed!\n
That statement makes me a bit curious. Are you binding with a proper account to be able to query ldap? The original code was tested again openldap but its been modified a few times since. I would say
(&(|(cn=admins))
should be the proper syntax but that’s off the top of my head.if you use a command line ldapsearch does the bind credentials you provided in the FOG UI work correctly?
Tagging @Fernando-Gietz since he has more recent experience with the ldap plugin than I do.
-
@george1421 Like I said, I can successfully authenticate with the match groups disabled, so that should mean that the search bind dn and password are correct, but if I change the bind password to a known bad value I get:
AH01071: Got error 'PHP message: Plugin LDAP::authLDAP() Cannot bind to the LDAP server ldap://ldap.domain.org:389\n', referer: http://10.10.25.15/fog/management/index.php?node=home
-
@Daniel-Miller said in LDAP authorization with groups in rfc2307bis schema on OpenDirectory:
LDAP::authLDAP() Access level is still 0 or false. No access is allowed!
Well then we need to explain why its saying no access is allowed. I’m pretty sure the other errors are related to that. Its been a while, but what we may need to do is put in some echo statements to display the variables used for ldap authenticate to see if FOG is eating some of them. Your bind user ID and password are all low order ascii? No European characters?
edit: var_dump() was the command I couldn’t remember over echo to display runtime variables. That command will stop the processing of the php file.
-
@george1421
Well, var_export() to get the string. after beating my head against things, looks like var_dump() drops it to console.Passwords and user account names are all in the lower 127 ASCII
While poking around getting the variable exports in place, I ended up putting wireshark on it to troubleshoot how I broke things (think I moved some parentheses). When I put the original file back, I noticed the following traffic:
- Simple Bind as search user
- search for the user dn
- Simple Bind as the authenticating user
- Search for the user dn again
- Search for the admin group with the user listed in the member attribute
- search for the mobile group with the user listed in the member attribute
- search for the user listed in any group
- Request an unbind
I could see where this may cause an issue if the binding context were changed to the authenticating user during the second simple bind; in our implementation, non-system users don’t have permission to search the directory for anything other than themselves.
Inserting another call to
$bind = @$this->bind($bindDN, $bindPass);
before the call to_getAccessLevel()
on line 547 made things happy and got me to the management page. -
I had an issue like this in March, not the same but …
The problem was the filter and the work around was a little change in the code. We can try to see where is the problem and make a little change.
The filter:
&(|(name=admins))(member=uid=JohnDoe,ou=People,dc=domain,dc=org)
Doesnt work well, but the next one:
&(|(cn=admins))(member=uid=JohnDoe,ou=People,dc=domain,dc=org)
Works fine.
I think if we do a little change in the code in line 640 of ldap.class.php:
$userGroups = explode(',', $userGroup); $userGroups = array_map('trim', $userGroups); $filter = sprintf( '(&(|(name=%s))(%s=%s))', implode(')(name=', (array)$userGroups), $grpMemAttr, $this->escape($userDN, null, LDAP_ESCAPE_FILTER) ); /** * The attribute to get. */ $attr = array($grpMemAttr); /** * Execute the ldap query */ $result = $this->_result($grpSearchDN, $filter, $attr);
To:
$userGroups = explode(',', $userGroup); $userGroups = array_map('trim', $userGroups); $filter = sprintf( '(&(|(cn=%s))(%s=%s))', /*<------------ CHANGE THIS*/ implode(')(name=', (array)$userGroups), $grpMemAttr, $this->escape($userDN, null, LDAP_ESCAPE_FILTER) ); /** * The attribute to get. */ $attr = array($grpMemAttr); /** * Execute the ldap query */ $result = $this->_result($grpSearchDN, $filter, $attr);
Works?
-
@Fernando-Gietz
I actually changed it both on that line and for the implode() ‘glue’ for both the $adminGroups and $userGroups. if either of those two arrays were to contain more than one entry, the second and following groups would have the incorrect attribute name.$adminGroups = explode(',', $adminGroup); $adminGroups = array_map('trim', $adminGroups); $filter = sprintf( '(&(|(cn=%s))(%s=%s))', implode(')(cn=', (array)$adminGroups), $grpMemAttr, $this->escape($userDN, null, LDAP_ESCAPE_FILTER) ); /** * The attribute to get. */ $attr = array($grpMemAttr);
and
$userGroups = explode(',', $userGroup); $userGroups = array_map('trim', $userGroups); $filter = sprintf( '(&(|(cn=%s))(%s=%s))', implode(')(cn=', (array)$userGroups), $grpMemAttr, $this->escape($userDN, null, LDAP_ESCAPE_FILTER) ); /** * The attribute to get. */ $attr = array($grpMemAttr);
-
@Daniel-Miller I think that the change in the implode lines are not necessary. We can try to debug the code to see the value of the variables $grpMemAttr,$filter,$adminGroups:
$adminGroups = explode(',', $adminGroup); $adminGroups = array_map('trim', $adminGroups); $filter = sprintf( '(&(|(cn=%s))(%s=%s))', /*<------------ CHANGE THIS*/ implode(')(name=', (array)$adminGroups), $grpMemAttr, $this->escape($userDN, null, LDAP_ESCAPE_FILTER) ); //********* DEBUG ******************** var_dump($grpMemAttr,$filter,$adminGroups); exit; //************************************ /** * The attribute to get. */ $attr = array($grpMemAttr); /** * Read in the attributes */ $result = $this->_result($grpSearchDN, $filter, $attr); if (false !== $result) { return 2; } /** * If no record is returned then user is not in the * admin group. Change the filter and check the mobile * group for membership. */ $userGroups = explode(',', $userGroup); $userGroups = array_map('trim', $userGroups); $filter = sprintf( '(&(|(cn=%s))(%s=%s))', /*<------------ CHANGE THIS*/ implode(')(name=', (array)$userGroups), $grpMemAttr, $this->escape($userDN, null, LDAP_ESCAPE_FILTER) ); /** * The attribute to get. */ $attr = array($grpMemAttr);
Please paste the output.
-
@Fernando-Gietz
Names changed to protect the guiltySingle point change with one group:
string(6) "member" string(77) "(&(|(cn=admins))(member=uid=JohnDoe,ou=People,dc=domain,dc=org))" array(1) { [0]=> string(15) "admins" }
Single point change with two groups:
string(6) "member" string(102) "(&(|(cn=admins)(name=admins2))(member=uid=JohnDoe,ou=People,dc=domain,dc=org))" array(2) { [0]=> string(15) "admins" [1]=> string(18) "admins2" }
Two point change with one group:
string(6) "member" string(77) "(&(|(cn=admins))(member=uid=JohnDoe,ou=People,dc=domain,dc=org))" array(1) { [0]=> string(15) "admins" }
Two point change with two groups:
string(6) "member" string(100) "(&(|(cn=admins)(cn=admins2))(member=uid=JohnDoe,ou=People,dc=domain,dc=org))" array(2) { [0]=> string(15) "admins" [1]=> string(18) "admins2" }
So long as no one puts in two or more groups, the filter is correct. If someone does put in two or more comma separated group names, the second and following attribute searches will not be consistent with the first.
-
The LDAP plugin is not prepare to support two or more groups
To the next version we need to implement the support to two or more groups and the feature to customize the filters. -
@Fernando-Gietz
Well, you may not intend to support such at the moment, but it does work in 1.5.6 provided the groups are specified in the web interface as a comma separated list, which may cause its own problems for some group names. It actually doesn’t look like much more needs to be done to flesh out that first enhancement beyond the UI elements and input validation.I would suggest taking a look at the sequence of events for the bindings and searches, or at least making sure that the permissions contexts in which those searches occur are consistent. I think the quick and dirty fix I put in at line 547 won’t mess up anything, but I don’t have a lot of experience with either php and ldap.
And for posterity (read: when I forget what I did), diff against ldap.class.php released with 1.5.6 for the changes to address the issues for this thread:
547a548 > $bind = @$this->bind($bindDN, $bindPass); 616,617c617,618 < '(&(|(name=%s))(%s=%s))', < implode(')(name=', (array)$adminGroups), --- > '(&(|(cn=%s))(%s=%s))', > implode(')(cn=', (array)$adminGroups), 640,641c641,642 < '(&(|(name=%s))(%s=%s))', < implode(')(name=', (array)$userGroups), --- > '(&(|(cn=%s))(%s=%s))', > implode(')(cn=', (array)$userGroups),