Solved

PHP Ldap, Return all Headers and Attributes

Posted on 2011-09-02
16
728 Views
Last Modified: 2012-05-12
I've got the following code(below) that return only 3 specific fields from AD. I know that if I modify 2 lines, I can query all of AD (I've used wireshark to verify), but I want to programatically add all possible attributes instead of the 3. The script is using COM+ADSI to make the query against AD.
Hopefully the comment's I've placed in the code help, again lines 13-14 should be all that is needed to query all of AD/LDAP, but again I don't know how to "foreach" all the possible field names (we have added some that aren't standard) and place the data into the correct columns. If it could be output as a CSV that would be awesome!

Naturally replace the following with your Domain details:
(Line 3 or 4) user id=DOMAIN\\USERNAME;password=PASSWORD;
(Line 10) <LDAP://10.1.2.3>
-rich

<?php
$objConnection = new COM ("ADODB.Connection");
$objConnection->Open("Provider=ADsDSOObject;user id=DOMAIN\\USERNAME;password=PASSWORD;");     //USER|PASSWORD AND LDAP DATA IN PLAIN_TEXT
//$objConnection->Open("Provider=ADsDSOObject;user id=DOMAIN\\USERNAME;password=PASSWORD;encrypt password=true;");     //ENCRYPTS PASSWORD!!
$objCommand = new COM("ADODB.Command");
$objCommand->ActiveConnection = $objConnection ;
$objCommand->Properties["Cache results"] = false;     //http://msdn.microsoft.com/en-us/library/aa746471%28v=vs.85%29.aspx
$objCommand->Properties["Page size"] = 1000;     //GET AROUND AD'S 1000 RECORD (DEFAULT) LIMIT
$objCommand->Properties["Asynchronous"] = False;     //SETTING TO TRUE DIDN'T MAKE A DIFFERENCE TO ME...
$Cmd  = "<LDAP://10.1.2.3>;";     //YOUR AD SEVER HERE
$Cmd .= "(&(objectClass=user)(mail=*)(!cn=SystemMailbox*));";
$Cmd .= "name,mail,telephoneNumber;subtree";
//$Cmd .= "(objectClass=*);";     //GETS ALL ITEMS IN AD
//$Cmd .= "ADSPath;subtree";     //GETS ALL ITEMS IN AD

$objCommand->Properties['Sort On']->Value = "name";

$objCommand->CommandText = $Cmd;
$objRecordSet = $objCommand->Execute();

$OrderNumber = 0;
while(!$objRecordSet->EOF())
 {
  $OrderNumber ++;
  $nn = $objRecordSet->Fields['name']->Value;
  $mm = $objRecordSet->Fields['mail']->Value;
  $ph = $objRecordSet->Fields['telephoneNumber']->Value;
  echo "$OrderNumber: $nn mail: $mm phone: $ph<br>";
  $objRecordSet->MoveNext();
 }

Echo $objRecordSet->RecordCount();
$objRecordSet->Close();
$objCommand->Close();
$objConnection->Close();

unset($objRecordSet);
unset($objCommand);
unset($objConnection);

?>

Open in new window

0
Comment
Question by:Rich Rumble
  • 8
  • 8
16 Comments
 
LVL 15

Expert Comment

by:dave4dl
ID: 36476928
0
 
LVL 38

Author Comment

by:Rich Rumble
ID: 36477005
This is ADSI, not php_ldap in the traditional sense. However... http://msdn.microsoft.com/en-us/library/aa746471%28v=vs.85%29.aspx seems to indicate there is a similar function in ADSI
"Column Names Only"      A Boolean value that indicates that the search should retrieve only the name of attributes to which values have been assigned. The default is false."
So maybe that?
-rich
0
 
LVL 15

Expert Comment

by:dave4dl
ID: 36477117
oh, sorry I didnt read your post closely enough.  Simply iterate through the Fields collection of your objRecordSet to get all the fields.  If you do columns only, your query will work faster but it would still work to iterate through the fields without setting Column Names Only to true.

Each field object in the Fields collection has the following properties: http://msdn.microsoft.com/en-us/library/ms675259(v=vs.85).aspx

so "Name" is what you are interested in
0
 
LVL 15

Expert Comment

by:dave4dl
ID: 36477123
for ( $counter = 0; $counter < $objRecordSet->Fields->Count; $counter += 1)
{
    $myName = $objRecordSet->Fields[$counter]->Name;
}
0
 
LVL 38

Author Comment

by:Rich Rumble
ID: 36477697
I don't know much about PHP yet, do I add this in the "while" loop?
-rich
0
 
LVL 15

Expert Comment

by:dave4dl
ID: 36479306
do that just before the while loop.  The purpose of the while loop in your code is to iterate through all the records returned by your query and the purpose of the for loop code I posted is to iterate through all the attributes (columns) returned in that query.

Where you put it really depends on what you want to do with it.  If you just want to print the list out, place the for loop just before the while loop (you dont need to even have the while loop in there at all in this case) and write out the $myName variable inside the for loop.
0
 
LVL 38

Author Comment

by:Rich Rumble
ID: 36480555
It's not working, just doing the RecordCount on line 36... Adding the print $myName only spits out the "ADSPath" a few thousand times :)
I believe the "movenext()" that is in the While loop will be needed to print out all the data, I don't think this For loop will. Again I only found the script, I don't write PHP- Can you write it for me please, with output in CSV?
AD has a default limit of 1000 records that AD will return, using the "Page Size" controls how many records the script want's returned, which I believe is why the while loop is there, to paginate the results.
 I found this, if it helps, it's in vbscript  rather than php, but looks very very similar:
http://www.serverwatch.com/tutorials/article.php/1476621/Wildcard-Active-Directory-Searches.htm
Thanks again!
-rich
<?php
$objConnection = new COM ("ADODB.Connection");
$objConnection->Open("Provider=ADsDSOObject;user id=DOMAIN\\USERNAME;password=PASSWORD;");     //USER|PASSWORD AND LDAP DATA IN PLAIN_TEXT
//$objConnection->Open("Provider=ADsDSOObject;user id=DOMAIN\\USERNAME;password=PASSWORD;encrypt password=true;");     //ENCRYPTS PASSWORD!!
$objCommand = new COM("ADODB.Command");
$objCommand->ActiveConnection = $objConnection ;
$objCommand->Properties["Cache results"] = false;     //http://msdn.microsoft.com/en-us/library/aa746471%28v=vs.85%29.aspx
$objCommand->Properties["Page size"] = 1000;     //GET AROUND AD'S 1000 RECORD (DEFAULT) LIMIT
$objCommand->Properties["Asynchronous"] = False;     //SETTING TO TRUE DIDN'T MAKE A DIFFERENCE TO ME...
$Cmd  = "<LDAP://10.1.2.3>;";     //YOUR AD SEVER HERE
//$Cmd .= "(&(objectClass=user)(mail=*)(!cn=SystemMailbox*));";
//$Cmd .= "name,mail,telephoneNumber;subtree";
$Cmd .= "(objectClass=*);";     //GETS ALL ITEMS IN AD
$Cmd .= "ADSPath;subtree";     //GETS ALL ITEMS IN AD

$objCommand->Properties['Sort On']->Value = "name";

$objCommand->CommandText = $Cmd;
$objRecordSet = $objCommand->Execute();
for ( $counter = 0; $counter < $objRecordSet->Fields->Count; $counter += 1){
    $myName = $objRecordSet->Fields[$counter]->Name;
    print $myName;
    }

//$OrderNumber = 0;
//while(!$objRecordSet->EOF())
// {
//  $OrderNumber ++;
//  $nn = $objRecordSet->Fields['name']->Value;
//  $mm = $objRecordSet->Fields['mail']->Value;
//  $ph = $objRecordSet->Fields['telephoneNumber']->Value;
//  echo "$OrderNumber: $nn mail: $mm phone: $ph<br>";
//  $objRecordSet->MoveNext();
// }

Echo $objRecordSet->RecordCount();
$objRecordSet->Close();
$objCommand->Close();
$objConnection->Close();

unset($objRecordSet);
unset($objCommand);
unset($objConnection);

?>

Open in new window

0
 
LVL 15

Expert Comment

by:dave4dl
ID: 36480721
Try replacing lines 20 - 23 with this
for ( $counter = 0; $counter < $objRecordSet->Fields->Count; $counter += 1)
{
    echo "Column number " . $counter . " is " . $objRecordSet->Fields[$counter]->Name;
}

Open in new window

0
What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 15

Expert Comment

by:dave4dl
ID: 36480724
I guess you could also tack a line break in there by changing it to something like this:

echo "Column number " . $counter . " is " . $objRecordSet->Fields[$counter]->Name . "<br />";

0
 
LVL 38

Author Comment

by:Rich Rumble
ID: 36480858
Seems to be counting, but that's about all:

Column number 0 is ADSPath
30663

That's all the output there is. Wireshark is showing lot's of data coming in, but nothing is printing.
-rich
0
 
LVL 38

Author Comment

by:Rich Rumble
ID: 36490904
I've been trying to "print_r" in various ways with no luck, I'm pretty far out of my league with this one.
-rich
0
 
LVL 15

Expert Comment

by:dave4dl
ID: 36492897
for some reason the $counter variable is not incrementing so the script just repeatedly prints out the first value (0 and ADSPath).  I dont see any reason the variable would not be set correctly.  Can you try putting in a few hard coded values to make sure we are correct on the other portions of the code?

echo "Column number 0 is " . $objRecordSet->Fields[0]->Name . "<br />";
echo "Column number 1 is " . $objRecordSet->Fields[1]->Name . "<br />";
echo "Column number 2 is " . $objRecordSet->Fields[2]->Name . "<br />";
etc.
0
 
LVL 38

Author Comment

by:Rich Rumble
ID: 36492969
Seems to stop after 0, doesn't like the line after (see attached)
Fatal error: Uncaught exception 'com_exception' with message 'Source: ADODB.Fields
Description: Item cannot be found in the collection corresponding to the requested name or ordinal.' on line 39

A friend of mine sent me something, it doesn't work, the script times out, wireshark shows data coming in, but stops after a time. He assumed the first result would be a "header" but it doesn't appear to be according to wireshark at least. His code replaced the While loop:
while(!$objRecordSet->EOF()) {
  $OrderNumber++;
  $header = "\"Order Number\",";
  $line = "\"" . $OrderNumber . "\",";
  foreach($objRecordSet->Fields as $field)
    if (!isset($first)) {
      $header .= "\"" . (string)$field->Name . "\",";
    };
    $line .= "\"" . (string)$field->Value . "\",";
  };
  if (!isset($first)) {
    $first = "done";
    print substr($header, 0, -1) . "\n";
  };
  print substr($line, 0, -1) . "\n";
  $objRecordSet->MoveNext() ;

He was also trying to put the result's into a CSV, maybe there is a way to stop the "counting" after a certain period, maybe 200 results, and debug that way. I'm setting up a test domain that is much smaller to see if his code will even finish on a new domain, let alone one with 30k objects.
-rich
$objRecordSet = $objCommand->Execute() ;
for ( $counter = 0; $counter < $objRecordSet->Fields->Count; $counter += 1)
{
   // echo "Column number " . $counter . " is " . $objRecordSet->Fields[$counter]->Name;
    echo "Column number 0 is " . $objRecordSet->Fields[0]->Name . "<br />";
    echo "Column number 1 is " . $objRecordSet->Fields[1]->Name . "<br />"; //<-- this line errors
    echo "Column number 2 is " . $objRecordSet->Fields[2]->Name . "<br />";
}

Open in new window

0
 
LVL 15

Expert Comment

by:dave4dl
ID: 36493670
hmm, I guess I am at a loss.  Sorry I couldnt help you.
0
 
LVL 38

Accepted Solution

by:
Rich Rumble earned 0 total points
ID: 36519197
I've made some progress getting possible values, see below.
I'll have to pass these to the query and then get them I suppose.
-rich
<?PHP
$objSchemaComputer = new COM("LDAP://schema/computer");
foreach ($objSchemaComputer->MandatoryProperties as $strAttribute){
      Echo $strAttribute . ",";
}
Echo "\r\n" . "Optional (May-Contain) attributes";
foreach ($objSchemaComputer->OptionalProperties as $strAttribute){
 Echo $strAttribute . ",";
}
?>

Open in new window

0
 
LVL 38

Author Closing Comment

by:Rich Rumble
ID: 37230476
I have it working ok, but not like I intended.
0

Featured Post

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Shadow IT is coming out of the shadows as more businesses are choosing cloud-based applications. It is now a multi-cloud world for most organizations. Simultaneously, most businesses have yet to consolidate with one cloud provider or define an offic…
Nothing in an HTTP request can be trusted, including HTTP headers and form data.  A form token is a tool that can be used to guard against request forgeries (CSRF).  This article shows an improved approach to form tokens, making it more difficult to…
The viewer will learn how to count occurrences of each item in an array.
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now