Skip to content

Instantly share code, notes, and snippets.

@mattrude
Created October 31, 2010 23:49
Show Gist options
  • Star 82 You must be signed in to star a gist
  • Fork 47 You must be signed in to fork a gist
  • Save mattrude/657334 to your computer and use it in GitHub Desktop.
Save mattrude/657334 to your computer and use it in GitHub Desktop.
LDAP PHP Change Password Page
<?php
/**
* LDAP PHP Change Password Webpage
* @author: Matt Rude <http://mattrude.com>
* @website: http://technology.mattrude.com/2010/11/ldap-php-change-password-webpage/
*
*
* GNU GENERAL PUBLIC LICENSE
* Version 2, June 1991
*
* Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
$message = array();
$message_css = "";
function changePassword($user,$oldPassword,$newPassword,$newPasswordCnf){
global $message;
global $message_css;
$server = "localhost";
$dn = "ou=People,dc=example";
error_reporting(0);
ldap_connect($server);
$con = ldap_connect($server);
ldap_set_option($con, LDAP_OPT_PROTOCOL_VERSION, 3);
// bind anon and find user by uid
$user_search = ldap_search($con,$dn,"(|(uid=$user)(mail=$user))");
$user_get = ldap_get_entries($con, $user_search);
$user_entry = ldap_first_entry($con, $user_search);
$user_dn = ldap_get_dn($con, $user_entry);
$user_id = $user_get[0]["uid"][0];
$user_givenName = $user_get[0]["givenName"][0];
$user_search_arry = array( "*", "ou", "uid", "mail", "passwordRetryCount", "passwordhistory" );
$user_search_filter = "(|(uid=$user_id)(mail=$user))";
$user_search_opt = ldap_search($con,$user_dn,$user_search_filter,$user_search_arry);
$user_get_opt = ldap_get_entries($con, $user_search_opt);
$passwordRetryCount = $user_get_opt[0]["passwordRetryCount"][0];
$passwordhistory = $user_get_opt[0]["passwordhistory"][0];
//$message[] = "Username: " . $user_id;
//$message[] = "DN: " . $user_dn;
//$message[] = "Current Pass: " . $oldPassword;
//$message[] = "New Pass: " . $newPassword;
/* Start the testing */
if ( $passwordRetryCount == 3 ) {
$message[] = "Error E101 - Your Account is Locked Out!!!";
return false;
}
if (ldap_bind($con, $user_dn, $oldPassword) === false) {
$message[] = "Error E101 - Current Username or Password is wrong.";
return false;
}
if ($newPassword != $newPasswordCnf ) {
$message[] = "Error E102 - Your New passwords do not match!";
return false;
}
$encoded_newPassword = "{SHA}" . base64_encode( pack( "H*", sha1( $newPassword ) ) );
$history_arr = ldap_get_values($con,$user_dn,"passwordhistory");
if ( $history_arr ) {
$message[] = "Error E102 - Your new password matches one of the last 10 passwords that you used, you MUST come up with a new password.";
return false;
}
if (strlen($newPassword) < 8 ) {
$message[] = "Error E103 - Your new password is too short.<br/>Your password must be at least 8 characters long.";
return false;
}
if (!preg_match("/[0-9]/",$newPassword)) {
$message[] = "Error E104 - Your new password must contain at least one number.";
return false;
}
if (!preg_match("/[a-zA-Z]/",$newPassword)) {
$message[] = "Error E105 - Your new password must contain at least one letter.";
return false;
}
if (!preg_match("/[A-Z]/",$newPassword)) {
$message[] = "Error E106 - Your new password must contain at least one uppercase letter.";
return false;
}
if (!preg_match("/[a-z]/",$newPassword)) {
$message[] = "Error E107 - Your new password must contain at least one lowercase letter.";
return false;
}
if (!$user_get) {
$message[] = "Error E200 - Unable to connect to server, you may not change your password at this time, sorry.";
return false;
}
$auth_entry = ldap_first_entry($con, $user_search);
$mail_addresses = ldap_get_values($con, $auth_entry, "mail");
$given_names = ldap_get_values($con, $auth_entry, "givenName");
$password_history = ldap_get_values($con, $auth_entry, "passwordhistory");
$mail_address = $mail_addresses[0];
$first_name = $given_names[0];
/* And Finally, Change the password */
$entry = array();
$entry["userPassword"] = "$encoded_newPassword";
if (ldap_modify($con,$user_dn,$entry) === false){
$error = ldap_error($con);
$errno = ldap_errno($con);
$message[] = "E201 - Your password cannot be change, please contact the administrator.";
$message[] = "$errno - $error";
} else {
$message_css = "yes";
mail($mail_address,"Password change notice","Dear $first_name,
Your password on http://support.example.com for account $user_id was just changed. If you did not make this change, please contact support@example.com.
If you were the one who changed your password, you may disregard this message.
Thanks
-Matt");
$message[] = "The password for $user_id has been changed.<br/>An informational email as been sent to $mail_address.<br/>Your new password is now fully Active.";
}
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Password Change Page</title>
<style type="text/css">
body { font-family: Verdana,Arial,Courier New; font-size: 0.7em; }
th { text-align: right; padding: 0.8em; }
#container { text-align: center; width: 500px; margin: 5% auto; }
.msg_yes { margin: 0 auto; text-align: center; color: green; background: #D4EAD4; border: 1px solid green; border-radius: 10px; margin: 2px; }
.msg_no { margin: 0 auto; text-align: center; color: red; background: #FFF0F0; border: 1px solid red; border-radius: 10px; margin: 2px; }
</style>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body>
<div id="container">
<h2>Password Change Page</h2>
<p>Your new password must be 8 characters long or longer and have at least:<br/>
one capital letter, one lowercase letter, &amp; one number.<br/>
You must use a new password, your current password<br/>can not be the same as your new password.</p>
<?php
if (isset($_POST["submitted"])) {
changePassword($_POST['username'],$_POST['oldPassword'],$_POST['newPassword1'],$_POST['newPassword2']);
global $message_css;
if ($message_css == "yes") {
?><div class="msg_yes"><?php
} else {
?><div class="msg_no"><?php
$message[] = "Your password was not changed.";
}
foreach ( $message as $one ) { echo "<p>$one</p>"; }
?></div><?php
} ?>
<form action="<?php print $_SERVER['PHP_SELF']; ?>" name="passwordChange" method="post">
<table style="width: 400px; margin: 0 auto;">
<tr><th>Username or Email Address:</th><td><input name="username" type="text" size="20px" autocomplete="off" /></td></tr>
<tr><th>Current password:</th><td><input name="oldPassword" size="20px" type="password" /></td></tr>
<tr><th>New password:</th><td><input name="newPassword1" size="20px" type="password" /></td></tr>
<tr><th>New password (again):</th><td><input name="newPassword2" size="20px" type="password" /></td></tr>
<tr><td colspan="2" style="text-align: center;" >
<input name="submitted" type="submit" value="Change Password"/>
<button onclick="$('frm').action='changepassword.php';$('frm').submit();">Cancel</button>
</td></tr>
</table>
</form>
</div>
</body>
</html>
@stampycode
Copy link

stampycode commented Sep 4, 2017

Anyone using these functions needs to escape the username or email address used to build a filter, using the 'ldap_escape' function.
This is the basis for an LDAP query injection attack.
Also means if your username or email address has special chars in, they might otherwise just break the ldap filter.

(The ldap_escape function was added in PHP5.6)

Adjustment suggested on line 34:

  $user_esc = ldap_escape($user);
  $user_search = ldap_search($con,$dn,"(|(uid=$user_esc)(mail=$user_esc))");

Pre-PHP5.6 Alternative:

  $user_esc = str_replace(
    array( '*',   '(',   ')',   '\\',  null  ),
    array( '\2a', '\28', '\29', '\5c', '\00' ),
    $user
  );
  $user_search = ldap_search($con,$dn,"(|(uid=$user_esc)(mail=$user_esc))");

@amitvsipl
Copy link

amitvsipl commented Nov 13, 2017

Hi please help to resolve this.

Connected to LDAP on Secure Port Sucessfully!
LDAP bound Sucessfully!

I have changed below detail

$users = "username";
$password = "password";

error_reporting(0);
ldap_connect($server);
//$con = ldap_connect($server);

$con = ldap_connect($server, $ldapsport) or die("Could not connect");

ldap_set_option($con, LDAP_OPT_PROTOCOL_VERSION, 3);

if ($con){
echo "Connected to LDAP on Secure Port Sucessfully!";
$ldap_bind = ldap_bind($con, $users, $password);
}

  if ($ldap_bind){
      echo "LDAP bound Sucessfully!";
    }

// bind anon and find user by uid

echo $user;

$user_search = ldap_search($con,$dn,"(|(uid=$user)(mail=$user))");

print_r($user_search);


Username:

DN:

Current Pass: *************

New Pass: *************

E201 - Your password cannot be change, please contact the administrator.

1 - Operations error

Your password was not changed.

ldap_search not searching specific user.

Thanks

@Scottking92
Copy link

Hi,

Thanks for the code, works beautifully.

Just wondering if there is a way to update the "shadowLastChange" field to today date when the password is reset from this script?

Any help would be appreciated.

@Al3ajandro
Copy link

Al3ajandro commented Apr 2, 2019

hi,
a questions. to connect to the LDAP and change password is neccesary using the master password of ldap database? or only my user of active directory? , thanks a lot

@jackhftang
Copy link

FYI, I made some modifications over this version. Some modification:

  1. use another bind DN (instead of anoymous to search)
  2. use pwdaccountlockedtime (instead of passwordRetryCount) for openldap.
  3. removed sending remail which I don't need.
  4. [fix] removed a redundent ldap_connect

https://gist.github.com/jackhftang/2592bb16971e4b60ce32022c48cc74e1

@huSSooxXx
Copy link

Thank you so much. Working perfectly

@wl-sre
Copy link

wl-sre commented May 26, 2020

@ALL
hello,every , could anyone explain the line with "Your new password matches one of the last 10 passwords that you used, you MUST come up with a new password" to me ?
appreciate your help!
I can not understand how to test the new-password with the history

@cmacias00
Copy link

cmacias00 commented Jun 5, 2020

@ALL
hello,every , could anyone explain the line with "Your new password matches one of the last 10 passwords that you used, you MUST come up with a new password" to me ?
appreciate your help!
I can not understand how to test the new-password with the history

Hi,

I think it's incomplete.
This worked for me:

$result = "";
$search = ldap_read($con, $user_dn, "(objectClass=*)", array("pwdHistory"));
$entry = ldap_first_entry($con, $search);
$password_history = ldap_get_values($con, $entry, "pwdHistory");

foreach ($password_history as $pass) {
    preg_match('/{SSHA}(\S+)/', $pass, $matches);
    $hash_history=$matches[0];
    $salt = substr(base64_decode(substr($hash_history,6)),20);
    $encrypted_password = '{SSHA}' . base64_encode(sha1( $newPassword.$salt, TRUE ). $salt);
    if ($hash_history == $encrypted_password)
        $result="pwdused";
}

if ($result) {
    $message[] = "Error E102 - Your new password matches one of the last 10 passwords that you used, you MUST come up with a new password.";
    return false;
}

I hope this will help you

@mattrude
Copy link
Author

mattrude commented Jun 6, 2020 via email

@qquattog
Copy link

Hello @mattrude,
I have a problem when I use URI connection
$server = "ldaps://x.x.x.x:636";
and use this logic to test if it connects:
$con=ldap_connect($server) or die("Could not connect");

it does connect but records of a legit ldap user will not be found.
"Error E101 - Current Username or Password is wrong.";

But if I change $server = "ldaps://x.x.x.x:636"; to $server = "x.x.x.x"; it will find the user and the password.

My LDAP is already with a certificate and is running ok.
ldap.conf
URI ldaps://x.x.x.x:636
TLS_CACERTDIR /etc/openldap/cacerts
TLS_CACERT /etc/openldap/cacerts/server.crt
TLS_REQCERT allow

Hope you can give light to this matter.
Thanks in advance. You have a good code to share.

@Nha-Krul
Copy link

Very good work!! Thanks

@SumairaButt
Copy link

E201 - Your password cannot be change, please contact the administrator.

50 - Insufficient access

Your password was not changed.

i am getting this error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment