Reputation: 53
I was wondering if someone will be able to help with PHP LDAP Active Directory.
Currently using a PHP LDAP class created by Scott Barnett & Richard Hyland (http://adldap.sourceforge.net/) version 4.0.4.
Now logging in is no problem, and if a user has entered their password in incorrectly too many times, their account is locked as set by Active Directory.
The problem I am having is now trying to determine when a user has locked their account and when their password has expired after 30 days, each scenario needs to redirect the user to a different page as it will be for external users.
Now when a user logs in normally the following code is executed:
include (dirname(__FILE__) . "./adLDAP/src/adLDAP.php");
try
{
$adldap = new adLDAP();
}
catch (adLDAPException $e)
{
echo $e;
exit();
}
if ($adldap->authenticate($username, $password))
{
//establish your session and redirect
session_start();
$_SESSION["username"] = $username;
$_SESSION["userinfo"] = $adldap->user()->info($username);
$redir = "Location: https://" . $_SERVER['HTTP_HOST'] . dirname($_SERVER['PHP_SELF']) . "user_page.php";
header($redir);
exit;
}
This code links to adLDAP.php, creates a connection to AD and then Authenticates the user and logs them in.
Now within the adLDAPUsers.php Class, there is a public function "passwordExpiry($username, $isGUID = false)" that is supposed to be used to determine if a user's password has expired, but I am struggling to get this function to work, as when it executes the following line:
if (!$this->adldap->getLdapBind()) { return false; }
It always returns FALSE. It does not allow a connection to AD with the user's correct credentials, and the rest of the code is never executed to determine password expiry.
From my main login page I have the following code:
if (!$adldap->checkAccPassExp($username))
{
$redir = "Location: https://" . $_SERVER['HTTP_HOST'] . dirname($_SERVER['PHP_SELF']) . "changepassword.php";
header($redir);
exit;
}
It then links to adLDAP.php:
public function checkAccPassExp($username, $password)
{
$PassExpire = $this->user()->passwordExpiry($username, $isGUID);
}
Which in turn then links to adLDAPUsers.php Class and executes the code.
As for determining if a user's account has been locked, there isn't really an indepth function determine that, just a protected function within the class:
protected function accountControl($options)
{
$val=0;
if (is_array($options)) {
if (in_array("SCRIPT",$options)){ $val=$val+1; }
if (in_array("ACCOUNTDISABLE",$options)){ $val=$val+2; }
if (in_array("HOMEDIR_REQUIRED",$options)){ $val=$val+8; }
if (in_array("LOCKOUT",$options)){ $val=$val+16; }
if (in_array("PASSWD_NOTREQD",$options)){ $val=$val+32; }
//PASSWD_CANT_CHANGE Note You cannot assign this permission by directly modifying the UserAccountControl attribute.
//For information about how to set the permission programmatically, see the "Property flag descriptions" section.
if (in_array("ENCRYPTED_TEXT_PWD_ALLOWED",$options)){ $val=$val+128; }
if (in_array("TEMP_DUPLICATE_ACCOUNT",$options)){ $val=$val+256; }
if (in_array("NORMAL_ACCOUNT",$options)){ $val=$val+512; }
if (in_array("INTERDOMAIN_TRUST_ACCOUNT",$options)){ $val=$val+2048; }
if (in_array("WORKSTATION_TRUST_ACCOUNT",$options)){ $val=$val+4096; }
if (in_array("SERVER_TRUST_ACCOUNT",$options)){ $val=$val+8192; }
if (in_array("DONT_EXPIRE_PASSWORD",$options)){ $val=$val+65536; }
if (in_array("MNS_LOGON_ACCOUNT",$options)){ $val=$val+131072; }
if (in_array("SMARTCARD_REQUIRED",$options)){ $val=$val+262144; }
if (in_array("TRUSTED_FOR_DELEGATION",$options)){ $val=$val+524288; }
if (in_array("NOT_DELEGATED",$options)){ $val=$val+1048576; }
if (in_array("USE_DES_KEY_ONLY",$options)){ $val=$val+2097152; }
if (in_array("DONT_REQ_PREAUTH",$options)){ $val=$val+4194304; }
if (in_array("PASSWORD_EXPIRED",$options)){ $val=$val+8388608; }
if (in_array("TRUSTED_TO_AUTH_FOR_DELEGATION",$options)){val=$val+16777216; }
}
return $val;
}
Now this code I had to copy from the class to the adLDAP.php file and call like this as it is a protected function and would not allow me to call it any other way:
public function checkAccLocked()
{
$options = array("LOCKOUT");
$userlocked[0] = $this->accountControl($options);
if ($userlocked[0] == 16)
{
$ret = false;
}
else
{
$ret = true;
}
return $ret;
}
Now I have determined, if Anything is wrong with a user's account, you can send any value through to $options and it will return the corresponding value i.e. my user's password could be expired, but I can call any variable from the function and it will return a value, instead of just the password_expire value.
I have found on the LDAPWiki (https://ldapwiki.com/wiki/Common%20Active%20Directory%20Bind%20Errors) that LDAP code 49 returns quite a lot of values, so the LDAP code is useless to me. I was hoping that maybe the code would allow me to retrieve the error number and then I can point the user in the correct direction, but I would need the Decimal Code:
Code - 1330 for ERROR_PASSWORD_EXPIRED or Code - 1907 for ERROR_PASSWORD_MUST_CHANGE and Code - 1909 for ERROR_ACCOUNT_LOCKED_OUT
Has anyone ever used this code before? Could someone please provide assistance as to what code I am to use where and how to get this working? If not, is there maybe some other code that I need to use?
Upvotes: 1
Views: 3828
Reputation: 2869
You could get the extended error code you're looking for after a failed authentication/bind by using a function like this:
function getExtendedErrorNumber($adLdap)
{
$errorNumber = 0;
ldap_get_option($adLdap->getLdapConnection(), LDAP_OPT_ERROR_STRING, $extendedError);
if (!empty($extendedError) && preg_match('/, data (\d+),?/', $extendedError, $matches)) {
$errorNumber = hexdec(intval($matches[1]));
}
return $errorNumber;
}
Use it like:
const PWD_EXPIRED = 1330;
const PWD_MUST_CHANGE = 1907;
const ACCOUNT_LOCKED = 1909;
const ACCOUNT_DISABLED = 1331;
$errorNumber = getExtendedErrorNumber($adldap);
// The account password has expired
if ($errorNumber === PWD_EXPIRED) {
// The user must change their password before they can login
} elseif ($errorNumber === PWD_MUST_CHANGE) {
// The user's account is locked
} elseif ($errorNumber === ACCOUNT_LOCKED) {
// The user's account is disabled
} elseif ($errorNumber === ACCOUNT_DISABLED) {
}
Side note: The adLdap library you're using is no longer maintained. I'd suggest upgrading to AdLdap2 or LdapTools.
Upvotes: 2
Reputation: 11026
You have two conditions that MUST be determined from at least two separate methods. Password_Expired is a condition where the Password has expired. For Microsoft Active Directory we have a normative description.
Account_lock is where the Account has been either Administratively locked or the account has be locked by Intruder Detection.
Upvotes: 0