php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #25827 PHP LDAP queries against Active Directory return incomplete arrays
Submitted: 2003-10-10 15:04 UTC Modified: 2004-04-30 16:20 UTC
Votes:2
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:1 (100.0%)
Same OS:1 (100.0%)
From: pennington at rhodes dot edu Assigned:
Status: Not a bug Package: LDAP related
PHP Version: 4.3.3 OS: Windows 2000
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: pennington at rhodes dot edu
New email:
PHP Version: OS:

 

 [2003-10-10 15:04 UTC] pennington at rhodes dot edu
Description:
------------
I am querying an Active Directory server with PHP via LDAP to retrieve all of a particular user's attributes. All of that user's attributes in the LDAP directory are placed in a multi-dimensional array that I can query for a particular attribute I am interested in and return all of those values from the array by looping through that part of the array, using the correct key value.

So, in other words, I am using PHP's LDAP to grab all information about a user in Active Directory and put it into a single, multi-dimensional array called $info. This array has three levels of keys, such that:

$info[0][description][0]

would equal

Staff

because that is what is set up for the description attribute for a person in Active Directory. I am then looping through the entire array looking for values set with certain keys that I am interested in, which could be holding data in any order.

The problem occurs when I loop through the multi-dimensional array for attributes that share the second key, such as:

$info[0][memberof]

Because several different memberof attributes can be stored for a person in Active Directory, the LDAP-built array has values like:

$info[0][memberof][0] = Domain Admin
$info[0][memberof][1] = Finance User
$info[0][memberof][2] = Local Admin

and so on. If I count the number of member attributes that are actually in the LDAP server, I get a particular value, say 15. When I loop through these attributes in the array and count them up, I also get that same number. However, when I try to report back all of these attributes by printing them out, only 14 appear.

In other words, while the correct number of attributes are put into the array by PHP using LDAP, one of the keys in the array has no data associated with it (and should have data associated with it). This holds true for any LDAP-created array where an LDAP attribute has more than one value associated with it. All of those values are reported back to the PHP via LDAP and keys are created in the array for all of those values, but strangely one (and only one) of the data values will disappear if a certain attribute has more than one value associated with it.

Reproduce code:
---------------
Here is the code I'm using to build the troubled array via PHP's LDAP. Of course, you have to authenticate to our LDAP server to do the test on a particular user, so I am not able to point to a place on the web to demonstrate this.

<?php
if ($name_submitted != "" && $passwd_submitted != "") {

	$ldap_host = "ldap://someserver.rhodes.edu";
	$base_dn = "CN=Users,DC=rhodes, DC=edu";

	if ($search_submitted == "") {
		$search_value = $name_submitted;
	} else {
		$search_value = $search_submitted;
	}

	$filter = "(CN=$search_value)";
	$ldap_user = "CN=$name_submitted, CN=Users, DC=rhodes, DC=edu";
	$ldap_pass = $passwd_submitted;

	$connect = ldap_connect( $ldap_host, $ldap_port)
       or exit("Could not connect to LDAP server");

	// required to search AD, according to note in PHP manual notes
	ldap_set_option($connect, LDAP_OPT_PROTOCOL_VERSION, 3);
	ldap_set_option($connect, LDAP_OPT_REFERRALS, 0);

	$bind = ldap_bind($connect, $ldap_user, $ldap_pass)
     or exit("Could not bind to $ldap_host");

	echo "Successful bind to $ldap_host with $bind<br><br>\n";

	$read = ldap_search($connect, $base_dn, $filter)
	     or exit("Unable to search ldap server");

	$info = ldap_get_entries($connect, $read);
	echo $info["count"]." entries returned for $filter<br><br>\n";

	$ii=0;
	for ($i=0; $ii<$info[$i]["count"]; $ii++){
		$data = $info[$i][$ii];
		if ($data == "memberof") {
			$total_memberof = (count($info[$i][$data]));
			echo "Total memberof entries returned: $total_memberof<br><br>\n";
			$total = 0;
			$total = count($info[$i][$data]);
			$jj=0;
			for ($jj=0; $jj<$total; $jj++) {
				if ($info[$i][$data][$jj] == "CN=STAFF,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu") {
					echo "<b>Got Staff Match</b> ";
					$user_type = "staff";
				} elseif (($info[$i][$data][$jj] == "CN=FACULTY,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu") && $user_type == "") {
					echo "<b>Got Faculty Match</b> ";
					$user_type = "faculty";
				} elseif (($info[$i][$data][$jj] == "CN=Students,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu") && $user_type == "") {
					echo "<b>Got Students Match</b> ";
					$user_type = "student";
				}
				echo $i." ".$ii." ".$jj." ".$data.":&nbsp;&nbsp;".$info[$i][$data][$jj]."<br>\n";
			}
		}

	}

	ldap_unbind($connect);

	echo "<br><br><b>User Type is: ";

	switch ($user_type) {
		case "staff":
			echo "STAFF";
			break;
		case "faculty":
			echo "FACULTY";
			break;
		case "student":
			echo "STUDENT";
			break;
		default:
			echo "UNKNOWN";
			break;
	}

	echo "</b><br><br>\n";

	echo "<br><br><a href=\"index.php\">Search again</a><br><br>\n";

} else {

echo "<html><head></head><body>\n";
echo "<form action=\"index.php\" method=\"POST\">\n";
echo "AD User Name: <input type=\"text\" name=\"name_submitted\"><br>\n";
echo "AD Password: <input type=\"password\" name=\"passwd_submitted\"><br>\n";
echo "Search User Name: <input type=\"text\" name=\"search_submitted\"><br>\n";
echo "<input type=\"submit\" value=\"Submit\">\n";
echo "</form>\n";
echo "</body></html>\n";

}
?>

Expected result:
----------------
Total memberof entries returned: 13

0 1 0 memberof:  CN=STAFF_DL,OU=Distribution Lists,OU=Groups,DC=rhodes,DC=edu
0 1 1 memberof:  CN=Planning,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu
0 1 2 memberof:  CN=FACSTAFF,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu
0 1 3 memberof:  CN=Council,OU=Distribution Lists,OU=Groups,DC=rhodes,DC=edu
0 1 4 memberof:  CN=PRESIDENT,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu
0 1 5 memberof:  CN=FACTBOOK,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu
0 1 6 memberof:  CN=INFO_SERVICES,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu
0 1 7 memberof:  CN=CABINET,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu
0 1 8 memberof:  CN=Senior2006,OU=Distribution Lists,OU=Groups,DC=rhodes,DC=edu
0 1 9 memberof:  CN=NT Users,CN=Users,DC=rhodes,DC=edu
0 1 10 memberof:  CN=NTSETUP,CN=Users,DC=rhodes,DC=edu
0 1 11 memberof:  CN=Domain Users,CN=Users,DC=rhodes,DC=edu
0 1 12 memberof:  CN=STAFF,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu

Actual result:
--------------
Total memberof entries returned: 13

0 1 0 memberof:  CN=STAFF_DL,OU=Distribution Lists,OU=Groups,DC=rhodes,DC=edu
0 1 1 memberof:  CN=Planning,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu
0 1 2 memberof:  CN=FACSTAFF,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu
0 1 3 memberof:  CN=Council,OU=Distribution Lists,OU=Groups,DC=rhodes,DC=edu
0 1 4 memberof:  CN=PRESIDENT,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu
0 1 5 memberof:  CN=FACTBOOK,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu
0 1 6 memberof:  CN=INFO_SERVICES,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu
0 1 7 memberof:  CN=CABINET,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu
0 1 8 memberof:  CN=Senior2006,OU=Distribution Lists,OU=Groups,DC=rhodes,DC=edu
0 1 9 memberof:  CN=NT Users,CN=Users,DC=rhodes,DC=edu
0 1 10 memberof:  CN=NTSETUP,CN=Users,DC=rhodes,DC=edu
0 1 11 memberof:  CN=Domain Users,CN=Users,DC=rhodes,DC=edu
0 1 12 memberof:  

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2003-10-12 22:10 UTC] sniper@php.net
Some var_dump() here and there will reveal you why your script doesn't work. Not PHP bug.

 [2003-10-13 11:50 UTC] pennington at rhodes dot edu
I added:

var_dump($info[$i][$data][$jj]);

to the output of the test script and got this output for the last two values of the array for that attribute:

0 1 11 memberof:  CN=Domain Users,CN=Users,DC=rhodes,DC=edu
string(41) "CN=Domain Users,CN=Users,DC=rhodes,DC=edu"
0 1 12 memberof:  
NULL 

Now, I know that there are 13 LDAP values for this attribute (memberof). I can see this in various LDAP tools pointed to Active Directory for this user. And, when I do a:

count($info[$i][memberof]);

I get 13, which is correct.

As you can see, the final value has a key value in the array for this attribute, but no data is returned with that key. Obviously, PHP is not putting the last value for that attribute in the array but has created a key value to hold the data.

How is this a problem in the script? Looks like a bug in PHP to me...
 [2003-10-13 11:55 UTC] sniper@php.net
Please provide access to this 'active directory' thing.
(If you can't, we can't fix it either)

 [2003-10-13 12:05 UTC] pennington at rhodes dot edu
I obviously can't provide general access for testing to our Active Directory server, because you need an account on the Active Directory server to even search the directory, as with most LDAP servers.

I find it strange that no one has seen this before, because Microsoft's Active Directory is probably the most widely-used commercial LDAP server in the world.

However, it is obvious that, if an attribute has 13 values when looking at it via another LDAP query tool and if PHP thinks that it has the same number of values because it creates that many keys in the array, the error must be that PHP is not setting the last value in the array (for which there already is a key) or Active Directory is not giving the last value to PHP for it to set in the array.

Is anyone else using AD and PHP/LDAP seeing similar behavior? Can we not put this in feedback mode and request more information?
 [2003-10-14 11:12 UTC] sniper@php.net
PHP uses OpenLDAP libraries for ldap functionality in the windows binaries. So try your query with the openldap 'ldapsearch.exe' found in this package:

http://prdownloads.sf.net/acctsync/openldap-binaries-2.1.16-04APR03.zip


 [2003-10-14 11:59 UTC] pennington at rhodes dot edu
It appears that ldapsearch at that URL is not compiled with Kerberos support, which may be required to search Active Directory LDAP servers. I'm still doing research, however...

D:\openldap\bin>ldapsearch -LLL -H ldap://someserver.rhodes.edu -P 3 -D pennington -k
ldapsearch: not compiled with Kerberos support

I tried it with just SASL and that wasn't appreciated either:

D:\openldap\bin>ldapsearch -LLL -H ldap://someserver.rhodes.edu -P 3 -D pennington -I
ldap_sasl_interactive_bind_s: Unknown authentication method (86)
        additional info: SASL(-4): no mechanism available: Unable to find a call
back: 2

Can anyone verify that Kerberos support is required to search Active Directory LDAP servers? Is anyone using the OpenLDAP ldapsearch program to search Active Directory LDAP servers? If so, what kind of command should I use to get ldapsearch to search Active Directory?

I am using "CN=Users,DC=rhodes,DC=edu" for the Base DN, "CN=_search_value_" for the name to search for, and to bind to the Active Directory LDAP server, you have to use this string as the username: "CN=_authorized_user_,CN=Users,DC=rhodes,DC=edu"
 [2003-10-14 12:26 UTC] sniper@php.net
There is no kerberos support in PHP ldap either, and that partially works, so why would you need it with the command line binary?

 [2003-10-14 14:08 UTC] pennington at rhodes dot edu
OK, you're right, after a few minutes, I found an ldapsearch command that would work:

ldapsearch -x -b "CN=_some_user_,CN=Users,DC=rhodes,DC=edu" -D "CN=_search_auth_user_,CN=Users,DC=rhodes,DC=edu" -H ldap://someserver.rhodes.edu -W

The "memberof" attribute results look like this:

memberOf: CN=STAFF_DL,OU=Distribution Lists,OU=Groups,DC=rhodes,DC=edu
memberOf: CN=Planning,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu
memberOf: CN=FACSTAFF,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu
memberOf: CN=Council,OU=Distribution Lists,OU=Groups,DC=rhodes,DC=edu
memberOf: CN=PRESIDENT,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu
memberOf: CN=FACTBOOK,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu
memberOf: CN=INFO_SERVICES,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu
memberOf: CN=CABINET,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu
memberOf: CN=Senior2006,OU=Distribution Lists,OU=Groups,DC=rhodes,DC=edu
memberOf: CN=NT Users,CN=Users,DC=rhodes,DC=edu
memberOf: CN=NTSETUP,CN=Users,DC=rhodes,DC=edu
memberOf: CN=Domain Users,CN=Users,DC=rhodes,DC=edu

Again, only 12 results were returned rather than the 13 that exist there in Active Directory.

However, I've started doing a count based on attributes found by ldapsearch and Softerra's LDAPBrowser (which I think also uses openldap) and found that people missing an attibute value in ldapsearch were missing the same value in LDAPBrowser.

Anyway, I think what we are down to is one of two possibilities:

1) OpenLDAP's search tool is not receiving all attribute values for a particular search; or

2) Active Directory is not supplying the missing value when queried for it using LDAP but does reply properly when Microsoft admin tools are used.

I guess we could solve this if someone knows a good, freely-available, non-openldap-based LDAP search utility.

Regardless, this doesn't look like a PHP bug per se, thought it could be a OpenLDAP bug that has found its way into PHP with the rest of the OpenLDAP code...
 [2003-10-14 19:56 UTC] sniper@php.net
We only wrap around the OpenLDAP libraries. So it's definately  not PHP bug -> bogus.

You should report this to the openldap folks, they propably already know about it or are very willing to fix it if it's not already fixed. Please let us know what the response is so we can possibly update the used openldap libraries in the win32 binaries build.

 [2004-04-20 07:55 UTC] info at dbnet dot com dot au
Hi, I'm seeing your post 6 months too late, but let me alert you to something that I found after similar grief with AD.

It turns out that ONE of the AD group memberships, (in our case 'Domain Users', in your case perhaps one of the others), is handled "differently" in AD.  It will be the one listed as the "default" group when you look up a user in any AD admin tool, and incredibly it just doesn't get fed back to you by AD when you ask for the memberOf attribute using any standard LDAP technique.
I vaguely remember an MSDN KB article describing an astonishingly complex workaround for victims of this behaviour using ADSI.. sorry I have no link to it now.
Our solution was to accept that the so-called "default" group for each user is just not treated properly by AD in its LDAP interface.  It's a special case.
 [2004-04-20 17:50 UTC] pennington at rhodes dot edu
Info@dbnet:

I appreciate you posting your note about the unique way in which the "Domain Users" group is handled (I didn't know that at all - very interesting), I think my problem reported with this bug is actually a different issue.

Even if I assumed that the number of groups returned by LDAP query is 1 less than the actual total (such as if the Domain Users group wasn't counted), I still can't get a listing of all AD groups. One of the groups, which seems to be selected at random (i.e. it is not always Domain Users) is simply left out. If I change the array loop to purposefully give me more members of the LDAP group array than the person actually has groups, I still don't get all of that person's groups.

In other words, the problem isn't limited to the Domain User's group - LDAP through PHP simply does not let me see every one of the person's groups. For some reason (and it is related to the OpenLDAP libraries PHP uses), one of the groups, randomly selected, is always dropped.
 [2004-04-29 15:50 UTC] whampton at new-albany dot k12 dot oh dot us
hey pennington! i am having this exact same problem as well, although i have only started playing with php/ldap support for active directory yesterday.  i realize that you have had this open for a very long time and wondered if you have found any other useful information.  i am using win2k server and php 4.3.5rc1. i did, however, want to confirm that i am having this problem as well, in reponse to your frusteration "I find it strange that no one has seen this before, because Microsoft's Active Directory is probably the most widely-used commercial LDAP server in the world."  apparently not many "microsoft shops" are thinking outside of the box or looking to develop opensource solutions, well...at least not based on active directory.  anyway, the problem is plain to see.  here's the breakdown for everyone:

you count the number of memberships that a user is part of in the "Member Of" tab in the admin program for active directory on your domain controller, and it is one more that php returns with its builtin supprt.  one is missing, and it depends per user.  it's simple as that.  here is one interesting thing though, and that is for every test that i have run, the "memberof value" that is not returned by php is ALWAYS the primary group that the user is set to in active directoy.  its the little button you hit at the bottom of the "Member Of" tab.  whatever is set there will not be returned in the memberof values of php's ldap support.  any thoughts anyone has would be greatly appreciated, or please provide a link to the solution if i have thick-headedly missed it in my extensive searches...

thanks, wes
 [2004-04-29 16:26 UTC] pennington at rhodes dot edu
Wes, thanks for confirming this bug for me. Yes, it is frustrating. PHP (especially the sniper@php.net person) basically refuse to belive this is a bug, and they say that even if it is, they use the OpenLDAP libraries so they can do nothing about it. I pursued this with the OpenLDAP folks (after verifying that this bug also happens on a variety of platforms running OpenLDAP, including Windows and Linux), and they just ignored me. I'm not sure who else to notify about it or how to get it fixed.

We sorta worked around the bug here in the application we were building by looking for a particular attribute in a user's Active Directory record via LDAP, such as Department or Position. We test for a particular value there instead of querying by AD group, which is much better way of doing it.

Your note about primary groups being the ones left out of the list is interesting. I'll have to check that out. Thanks.
 [2004-04-29 16:53 UTC] whampton at new-albany dot k12 dot oh dot us
hey pennington, thanks for the quick response!  anyway, i have been searching around some more information, but due to my relative inexperience in this area (admittedly) some of this stuff is way over my head.  it seems that at least this first article from our buddies at microsoft indicated this is a known issue/feature...

http://support.microsoft.com/default.aspx?scid=kb;en-us;275523

this second article kinda addresses it too, but it is pretty confusing to me. it mentions the possibility of doing something with the primarygroupid instead.

http://support.microsoft.com/default.aspx?scid=kb;en-us;321360

anyway it seems that this problem exists in microsoft's active directory, although i have no idea if it is exclusive to AD or not, but AD is the current "solution" we are using for LDAP here.  the solutions and workarounds seem to be far beyond my skill level and patience, and since i am just trying to learn, and not necessarily NEEDING this solution, i really probably should move on i guess, but i leave this area darn frusterated.  maybe this is a combo AD/PHP bug, or maybe PHP is just handing the AD bug...errrr feature, as best it can.  i know i'm not much help here, but sometimes that affirmation in knowing you are not the only one on earth having the problem helps bring some closure...

good luck!  let me know if you need me to help test anything.

wes
 [2004-04-30 16:20 UTC] pennington at rhodes dot edu
Wes, I think you have really hit on the source of the problem here, and it is actually based with how Microsoft Active Directory reports/stores group information and what most people would expect when they query for that information. The document you linked to covers this issue well:

http://support.microsoft.com/default.aspx?scid=kb;en-us;275523

I did some testing and, sure enough, Active Directory does not report back the user's primary group as a part of the memberof attribute. Rather, the primary group is reported as the group ID number in the PrimaryGroupID attribute.

I think that most people expect to get all of a user's groups in the memberof attribute, even the one set to primary group, but this is not the way Active Directory is configured. (The link above is truly a perfect example of dodging a bug and calling it a feature.)

Now that we know this, we can simply look up the PrimaryGroupID number and try to determine what that group is and then add that to the conditional statement looking for a match to the group we are using to protect access. However, we have yet to find a method to look up an Active Directory group name using the group's ID number via LDAP.

For example, if we query for PrimaryGroupID via LDAP for a user in AD, we get this response:

primarygroupid:  1448

The issue is how do we turn the 1448 number into the actual name of the group, which is returned by the memberof attribute like this:

CN=STAFF,OU=Security Groups,OU=Groups,DC=rhodes,DC=edu

Anyone have any ideas?
 [2004-07-02 18:34 UTC] NoEmail at noDom dot com
See http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=uGurWNlJBHA.1720%40tkmsftngp07&rnum=4

The key phrase in the posted link is: "do an
additional LDAP search for all users who have the attribute primaryGroupID equal to the groups attribute primaryGroupToken". I've done similar to this, but with one query for all groups' distinguishedName and primaryGroupToken and another query for all users' distinguishedName and primaryGroupID. Then matched and sorted in code -- this is much faster than taking the overhead of multiple ldapsearch queries.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Wed Apr 24 16:01:31 2024 UTC