#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2004
    Location
    Imatra, Finland
    Posts
    7
    Rep Power
    0

    Question PHP, Active Directory and LDAP Referrals


    Hello,

    I have written a test script to do searches to AD with PHP. I have tested this script with PHP 4.3.X and PHP 5.0.0.

    It works perfectly except it won't follow LDAP referrals. So if I try to search a user who has an account in separate subdomain than me, it won't find it... Example: I have an account (which I use to bind) in sd1.ad.domain.com and the user which I search have his/her account in sd2.ad.domain.com. When I search, I can found all accounts in sd1.ad.domain.com, but I can't get anything from sd2.ad.domain.com. However with COM objects I can find user from other domain also... But I don't want to use COM objects, becouse they won't work with Linux environment.

    So anyone knows how to follow LDAP referrals with PHP? I quess that this is the problem why I can't search from other subdomains, becouse I have tried separate LDAP browser programs and they can seach and display information from other domains as well with the same user account...

    If you find this code useful, please tell me! But remember that this is not very clean piece of code and it is only a quick hack to test user searching in AD.

    Test code is in the next post, becouse it didn't fit on this one :-)

    -Zipi
  2. #2
  3. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2004
    Location
    Imatra, Finland
    Posts
    7
    Rep Power
    0

    Red face The test code


    PHP Code:
    <html>
    <head>
    <title>LDAP Test</title>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
    <style>
    BODY {
        font-family: Verdana, Arial, Helvetica;
        font-size: 12px;
    }

    P {
        font-family: Verdana, Arial, Helvetica;
        font-size: 12px;
    }
    </style>
    </head>
    <?
    //Functions
    //Parse UserAccountControl Flgs to more human understandable form...
    function parseUACF($uacf) {
        
    //All flags array
        
    $flags = array(    "TRUSTED_TO_AUTH_FOR_DELEGATION"=>    16777216,
                        
    "PASSWORD_EXPIRED"                =>    8388608,
                        
    "DONT_REQ_PREAUTH"                =>    4194304,
                        
    "USE_DES_KEY_ONLY"                =>    2097152,
                        
    "NOT_DELEGATED"                    =>    1048576,
                        
    "TRUSTED_FOR_DELEGATION"        =>    524288,
                        
    "SMARTCARD_REQUIRED"            =>    262144,
                        
    "MNS_LOGON_ACCOUNT"                =>    131072,
                        
    "DONT_EXPIRE_PASSWORD"            =>    65536,
                        
    "SERVER_TRUST_ACCOUNT"            =>    8192,
                        
    "WORKSTATION_TRUST_ACCOUNT"        =>    4096,
                        
    "INTERDOMAIN_TRUST_ACCOUNT"        =>    2048,
                        
    "NORMAL_ACCOUNT"                =>    512,
                        
    "TEMP_DUPLICATE_ACCOUNT"        =>    256,
                        
    "ENCRYPTED_TEXT_PWD_ALLOWED"    =>    128,
                        
    "PASSWD_CANT_CHANGE"            =>    64,
                        
    "PASSWD_NOTREQD"                =>    32,
                        
    "LOCKOUT"                        =>    16,
                        
    "HOMEDIR_REQUIRED"                =>    8,
                        
    "ACCOUNTDISABLE"                =>    2,
                        
    "SCRIPT"                         =>     1);

        
    //Parse flags to text
        
    $retval = array();
        while (list(
    $flag$val) = each($flags)) {
            if (
    $uacf >= $val) {
                
    $uacf -= $val;
                
    $retval[] = $flag;
            }
        }
        
        
    //Return human friendly flags
        
    return($retval);
    }

    //parse SamAccountType value to text
    function parseSAT($samtype) {
        
    $stypes = array(    805306368    =>    "NORMAL_ACCOUNT",
                            
    805306369    =>    "WORKSTATION_TRUST",
                            
    805306370    =>    "INTERDOMAIN_TRUST",
                            
    268435456    =>    "SECURITY_GLOBAL_GROUP",
                            
    268435457    =>    "DISTRIBUTION_GROUP",
                            
    536870912    =>    "SECURITY_LOCAL_GROUP",
                            
    536870913    =>    "DISTRIBUTION_LOCAL_GROUP");
        
        
    $retval "";
        while (list(
    $sat$val) = each($stypes)) {
            if (
    $samtype == $sat) {
                
    $retval $val;
                break;
            }
        }
        if (empty(
    $retval)) $retval "UNKNOWN_TYPE_" $samtype;
        
        return(
    $retval);
    }

    //Parse GroupType value to text
    function parseGT($grouptype) {
        
    $gtypes = array(    -2147483643    =>    "SECURITY_BUILTIN_LOCAL_GROUP",
                            -
    2147483644    =>    "SECURITY_DOMAIN_LOCAL_GROUP",
                            -
    2147483646    =>    "SECURITY_GLOBAL_GROUP",
                            
    2            =>    "DISTRIBUTION_GLOBAL_GROUP",
                            
    4            =>    "DISTRIBUTION_DOMAIN_LOCAL_GROUP",
                            
    8            =>    "DISTRIBUTION_UNIVERSAL_GROUP");

        
    $retval "";
        while (list(
    $gt$val) = each($gtypes)) {
            if (
    $grouptype == $gt) {
                
    $retval $val;
                break;
            }
        }
        if (empty(
    $retval)) $retval "UNKNOWN_TYPE_" $grouptype;

        return(
    $retval);
    }

    function 
    getObjectSid($adConn$dn$distname) {
        
    //Select which attributes wa want
        
    $attrs = array("objectsid");

        
    //Filter creation
        
    $filter "distinguishedname=" addslashes($distname);

        
    //Do the seacrh!
        
    $search ldap_search($adConn$dn$filter$attrs) or die("**** happens, no connection!");

        
    $entry ldap_first_entry($adConn$search);
        
    $vals ldap_get_values_len($adConn$entry"objectsid");
        return(
    bin2hex($vals[0]));
    }
    if (empty(
    $_GET["dn"])) $_GET["dn"] = "DC=sd1,DC=ad,DC=domain,DC=com"
    if (empty(
    $_GET["host"])) $_GET["host"] = "ldap://dc.sd1.ad.domain.com"
    if (empty(
    $_GET["user"])) $_GET["user"] = "username@sd1.ad.domain.com"
    ?>
    <body>
    <h3>AD Search with plain LDAP queries</h3>
    <p>
      <form method="GET">
        <b>Search Criteria:</b><br>
        <select name="filter">
            <option value="cn"<? if ($_GET["filter"]=="cn") echo " selected"?>>cn</option>
            <option value="sn"<? if ($_GET["filter"]=="sn") echo " selected"?>>sn</option>
            <option value="userprincipalname"<? if ($_GET["filter"]=="userprincipalname") echo " selected"?>>userprincipalname</option>
            <option value="samaccountname"<? if ($_GET["filter"]=="samaccountname" or empty($_GET["filter"])) echo " selected"?>>samaccountname</option>
            <option value="telephonenumber"<? if ($_GET["filter"]=="telephonenumber") echo " selected"?>>telephonenumber</option>
            <option value="l"<? if ($_GET["filter"]=="l") echo " selected"?>>City</option>
        </select>
        =
        <input type="text" name="keyword" size="30" maxlength="100" value="<? echo $_GET["keyword"]; ?>" /><br>
        <input type="checkbox" name="debugarray" id="debugarray" value="1"<? if ($_GET["debugarray"] == 1) echo " checked"?>><label for="debugarray">View debug array</label><br>
        <input type="checkbox" name="test" id="test" value="1"<? if ($_GET["test"] == 1) echo " checked"?>><label for="test">Get results also with COM objects, it uses different ways than just an ldap, which is not good...</label><br>
        <br>
        DN = <input type="text" name="dn" size="50" maxlength="100" value="<? echo $_GET["dn"]; ?>" /><br>
        host = <input type="text" name="host" size="50" maxlength="100" value="<? echo $_GET["host"]; ?>" /><br>
        user/password = <input type="text" name="user" size="35" maxlength="100" value="<? echo $_GET["user"]; ?>" /> <input type="password" name="pass" size="10" maxlength="100" value="" /> If empty, defaults used.<br>
        <br>
        <br><input type="submit" value="Do the thing!">
      </form>
    </p>

    <?php
    //Connection parameters
    $host $_GET["host"];
    if (empty(
    $_GET["user"]) or empty($_GET["pass"])) {
      
    $user "default.user@sd1.ad.domain.com";
      
    $pass "default.password";
    } else {
      
    $user $_GET["user"];
      
    $pass $_GET["pass"];
    }

    //Just a test with COM objects... Not very nice though!
    if ($_GET["test"] == 1) {
    $objConn = new COM("ADODB.Connection");
    $objConn->Provider "ADsDSOObject";
    $objConn->Open("ADs Provider"$user$pass);

    $objRs = new COM("ADODB.Recordset");
    $objCom = new COM("ADODB.Command");

    $objCom->ActiveConnection $objConn;
    $objCom->CommandText "SELECT objectsid,samAccountname,userAccountControl,userPrincipalName,ADsPath,givenName,sn,telephoneNumber,mail,displayName, department, distinguishedName FROM 'GC://dc=corp,dc=storaenso,dc=com' WHERE '" $_GET["filter"] . "' = '" $_GET["keyword"] . "'";

    $objRs $objCom->Execute

    echo 
    "<p><b>COM object results:</b> " $objRs->RecordCount "</p>";

    while (!
    $objRs->EOF) {
        echo 
    "<p>";
        for (
    $i=0$i $objRs->Fields->Count$i++) {
            echo 
    "$i: " $objRs->Fields[$i]->value "<br>\n";
        }
        
    $test $objRs->Fields[11]->value;
        echo 
    "<b>ObjectSid:</b> " $test " (DEC?)";
        echo 
    "</p>";
    $objRs->MoveNext();
    }

    //end of test

    //The real thing with PHP
    if (!empty($_GET["keyword"]) and !empty($_GET["dn"])) {
        
    //Connect to the AD
        
    $adConn ldap_connect($host636) or die("Could not connect!");

        
    //Set protocol verison
        
    ldap_set_option($adConnLDAP_OPT_PROTOCOL_VERSION3) or die ("Could not set ldap protocol1");

        
    //Set referrals... Won't work without this...
        
    ldap_set_option($adConnLDAP_OPT_REFERRALS0) or die ("Could not set ldap protocol2");

        
    //Bind the user
        
    $bd ldap_bind($adConn$user$pass) or die ("Could not bind");

        
    //Create DN
        
    $dn $_GET["dn"];

        
    //Create the filter
        
    $filter $_GET['filter'] . "=" $_GET["keyword"];
        
        
    //Do the search
        
    $search ldap_search($adConn$dnutf8_encode($filter));

        
    //Get results to an array
        
    $entries ldap_get_entries($adConn$search);
        
        
    //Free willy! Err... Result :-)
        
    ldap_free_result($search);

        
    //End binding
        //ldap_unbind($adConn);
        
    } else {
        
    $entries = array();
    }

    echo 
    "<p><b>Search filter:</b> " $filter "<br>";
    echo 
    "<b>Number of entries:</b> " $entries["count"] . "</p>\n";

    if (
    $entries["count"] > 0) {

    //Print entries
    for ($i 0$i $entries["count"] ; $i++) {
        echo 
    "<hr>\n";
        echo 
    "<p><b>Entry:</b> " . ($i+1) . "<br>\n";
        
    $SamAccountType parseSAT(utf8_decode($entries[$i]["samaccounttype"][0]));
        echo 
    "<b>Sam-Account-Type:</b> " $SamAccountType "<br>\n";
        if (
    ereg("GROUP"$SamAccountType)) echo "<b>Group-Type:</b> " parseGT(utf8_decode($entries[$i]["grouptype"][0])) . "<br>\n";
        echo 
    implode("<br>\n"parseUACF(utf8_decode($entries[$i]["useraccountcontrol"][0]))) . "<br>\n";
        if (
    utf8_decode($entries[$i]["pwdlastset"][0]) == and utf8_decode($entries[$i]["pwdlastset"][0]) != "") echo "<b>User must change password at next logon</b><br>\n";
        echo 
    "<b>ObjectSid:</b> " bin2hex($entries[$i]["objectsid"][0]) . " <font size=\"1\">&lt;-Buggy method (HEX)</font><br>\n";
        
    $objectsid getObjectSid($adConn$dn$entries[$i]["distinguishedname"][0]);
        echo 
    "<b>ObjectSid:</b> " $objectsid " <font size=\"1\">&lt;-Less buggy? (HEX) At least it seems to work and the data is somehow sensible.</font><br>\n";
        echo 
    "<b>ObjectSid lenght:</b> " strlen($objectsid) . "<br>\n";
        
    //echo "<b>ObjectSid:</b> " . getObjectSid($adConn, $entries[$i]["objectcategory"][0], $entries[$i]["distinguishedname"][0]) . "<br>\n";
        
    echo "</p>";

        
    // $ii = attributes for entry
        // $iii = values per attribute
        
    for ($ii=0$ii<$entries[$i]["count"]; $ii++){
            
    $data $entries[$i][$ii];
            for (
    $iii=0$iii<$entries[$i][$data]["count"]; $iii++) {
                echo 
    "<b>" utf8_decode($data) . ":</b>&nbsp;&nbsp;" utf8_decode($entries[$i][$data][$iii]) . "<br>\n";
            }
        }
        
        if (
    $_GET["debugarray"] == 1) {
            echo 
    "<pre>";
            
    print_r($entries[$i]);
            echo 
    "</pre>";
        }
    }

    } else {
       echo 
    "<p>No results found!</p>";
    }

    ?>


    </body>
    </html>
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2004
    Location
    Imatra, Finland
    Posts
    7
    Rep Power
    0
    I think I'll have to study ldap_first_reference and ldap_next_reference...

    However I cannot find any examples, even trough I have googled myself almost to insanity :-) Does anybody know how to use these functions?
  6. #4
  7. Psycho Canadian
    Devshed Demi-God (4500 - 4999 posts)

    Join Date
    Jan 2001
    Location
    Canada
    Posts
    4,846
    Rep Power
    635
    note sorry I never got referalls to work
  8. #5
  9. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2004
    Location
    Imatra, Finland
    Posts
    7
    Rep Power
    0

    Exclamation


    Currently I'm using PHP 4.3.8 with Windows. It seems that ldap_parse_reference function is missing in Windows builds. Does anybody have PHP running on Windows with working ldap_parse_reference?
  10. #6
  11. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2004
    Location
    Imatra, Finland
    Posts
    7
    Rep Power
    0
    I got references working, check my comments from PHP manual:

    http://www.php.net/manual/en/function.ldap-first-reference.php

IMN logo majestic logo threadwatch logo seochat tools logo