2

I've been stumped on this for a couple days now. I've tried various solutions to no avail. Please help...

Problem: We have two domain controller, which are not under our management. We are able to connect via LDAP on port 389, but are not able to connect securely over port 636.

We are developing a system to allow a number of self-service facilities, one of which is a password recovery tool. This works up to the point of resetting the users password.

I have found some code via the PHP manual which appears to do what we need, but cant seem to get it to work.

This is the code I have thus far

if ($caller==="change"){
if (($newPword1 === NULL)||($newPword1 === "" )){ return false;}
if (($newPword2 === NULL)||($newPword2 === "" )){ return false;}
if ($newPword1 != $newPword2) {
  $result["ERROR"]="1";
  $result["DETAILS"]="Your new password and the confirmation must match!";
  exit();
}
try { 
    $adldap = new adLDAP(); 
} catch (adLDAPException $e) {
    $result["ERROR"]="1";
    $result["DETAILS"]="An error occurred in adLDAP";
    echo json_encode($result);
    exit();
}

$userinfo = $adldap->user()->info($username, array("givenname","dn","lockouttime"));
$res = $userinfo[0]["lockouttime"];
$userDN = $userinfo[0]["dn"];
$firstName = $userinfo[0]["givenname"];
$authUser = $adldap->authenticate($username,$currentPword);
if ($authUser){
    try {
        $adminUsername = $domain."\\".$adminUsername;
        $srvDN = "LDAP://".$server."/";

        try {
            $ADSI = new COM("LDAP:");
        } catch (exception $e){
            $result["ERROR"]="1";
            $result["ERRORmsg"]=$e->getMessage();
            echo json_encode($result);
            exit();
        }
        try {
            $user = $ADSI->OpenDSObject($srvDN.$userDN, $adminUsername, $adminPassword, 1);
        } catch (exception $e){
            $result["ERROR"]="2";
            $result["ERRORmsg"]= $e->getMessage();
            echo json_encode($result);
            exit();
        }
        try { //set password
            if ($user){
                $result["object"]="Success";
            } else {
                $result["object"]="Failed";
            }
            $user->SetPassword($newPword1);  //line:114 -> error occurring on this line
            $user->SetInfo();
            $result["ERROR"]="0";
            $result["DETAILS"]="Thank you $firstName[0]<br><strong>Your password has been changed</strong><br><br>This may take up to 30 minutes to take effect depending on your location";
        } catch (exception $e) {
            $result["ERROR"]="3";
            $result["ERRORmsg"]=$e." - ".$e->getMessage();
            $result["DETAILS"]="An Error Occurred.";
        }
        unset($user);
        unset($ADSI);
    } catch (exception $e){
        $result["ERROR"]="1";
        $result["DETAILS"]="An Error Occurred in the ADSI COM";
        echo json_encode($result);
        exit();
    }
} else {
    if ($res[0] != "0"){
        $result["ERROR"]="1";
        $result["DETAILS"]="Im sorry $firstName[0].<br>Your account is now locked.  Please contact the IT Service Desk for advice";
    } else {
        $result["ERROR"]="1";
        $result["DETAILS"]="Im sorry $firstName[0].<br>Your current password is incorrect";
    }
}

In testing $result["object"] returns "Success". But the code seems to fail on the $user->SetPassword($newPword1); line.

The error that is being returned is:

ERROR -> "3"
object -> "Success"
ERRORmsg -> "exception 'com_exception' with message '<b>Source:</b> Unknown<br/><b>Description:</b> Unknown' in C:\inetpub\wwwroot\<path>\<filename>.php:114
Stack trace:
#0 C:\inetpub\wwwroot\<path>\<filename>.php(114): variant->SetPassword('P@ssw0rd')
#1 {main} - <b>Source:</b> Unknown<br/><b>Description:</b> Unknown"
DETAILS -> "An Error Occurred."

The above code is in a php doc on an IIS Web Server which is called by a user viewable page over https

Are you able to offer any advice or guidance?

4
  • 1
    I am pretty sure AD is quite picky about not setting passwords over clear text LDAP. You need them to enable SSL over LDAP. Commented Dec 23, 2012 at 20:44
  • 1
    Have you tried phpLdapAdmin? Commented Dec 24, 2012 at 16:54
  • UPDATE Dec 29 '12 at 16:46 I've since been able to prove that new COM("LDAP:") successfully initialises and $ADSI->OpenDSObject successfully opens the AD object. I've tried against other user AD accounts and the script fails on the same line **Dec 29 '12 at 17:57 ** have replaced $user->SetPassword($newPword1); with $user->Put("pwdLastSet",0); and it successfully updated the appropriate AD attribute. So this shows that the ADSI connection is working Commented Jan 4, 2013 at 9:04
  • Does anyone know if GSS-API/Kerberos will allow me to do this by binding with ldap_sasl? Commented Jan 4, 2013 at 9:05

2 Answers 2

1

I'm doing pretty much the same thing and have now got it to work, at least when targeting a Server 2k8R2 DC.

I'm not sure why you're getting the failure you are but a couple of things I've found helpful are:

1) In your catch() handler retrieve the error returned from SetPassword(), process and display it as follows

$rawErr = $e->getCode();
$processedErr = $rawErr + 0x100000000;
printf( 'Error code 0x%x', $processedErr );

Then see if you can find it listed here. You may also find this helpful.

2) Try varying the SetPassword() call to ChangePassword(). This requires that you enter the old password too but has less stringent privilege requirements on the DC. If you can get that to work it could suggest that your problem with SetPassword() is that the admin account you're using to authenticate in the OpenDSObject() call does not have sufficient privileges on the target domain.

A word of warning though about ChangePassword() which is that, unlike SetPassword(), password policy is strictly enforced. So you need to take account of things like minimum password age and complexity, and also history. I came unstuck on that last one.

Very best of luck with it, Ian.

Sign up to request clarification or add additional context in comments.

1 Comment

Hi Ian, Thanks for the input. In response: 1) I will add in the additional error code. From memory the error code being returned was not in the selfADSI list, I think it was -215... but will check and confirm this. 2) The Service Account I'm using has domain admin priviledges. This level will be lowered to elevated priviledges over user objects once I get the code working. Instructions from the powers that be, strict checking is in place to enforce the domain password policy. Am working on a SASL/GSS-API/Kerberos solution. Will post code once working. How did you get yours working?
0

I've found that the OS you're using has a direct impact on using this code.

I was doing my development work on my desktop using a local version IIS (Windows 7 Enterprise, IIS 7.5) and kept running into this unknown error.

However, Once I tested my ADSI password reset code on an actual server (Windows Server 2008 R2), it worked flawlessly.


For reference: The error code I got (using Ian's answer) was 0x80020009 which appears to be related to this question: (Registering a dll returns 0x80020009 error)

So whatever is going wrong appears to be deeper than ADSI and PHP/COM.

NOTE: Like ChrisM, I was able to use the ADSI connection to query information the failure only occurred when I tried to use SetPassword()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.