Give this a try. Let me know. My comments are prefaced by (js). There
are hardcoded paths in place. It works for MD5 passwords which is what I
use in the ldap database.
If it doesn't work turn debug on, $debug = 1 in the file
--
Joe Sotham
------------
If the only prayer you say in your entire life is "Thank You",
that will suffice.
- Meister Eckhart<?php
// Change_ldappass 1.3 (29th October 2002)
//
// Copyright Simon Annetts 2000 - 2002.
//
// (js) Turn debug on in an attempt to resolve problems, set $debug = 0; when working
//
$debug = 0;
// (js) because I am running apache v2 + php 4.2.3 I need to specify absolute pathnames
// that's my understanding of the problem anyways
define("SM_PATH", "/usr/local/share/squirrelmail.rc2a/");
// (js) Don't know how this plugin worked without this include file,
// maybe previous versions did something magical?
include_once (SM_PATH . "include/validate.php");
chdir ("..");
session_start();
include_once (SM_PATH . "config/config.php");
include_once (SM_PATH . "functions/strings.php");
include_once (SM_PATH . "functions/page_header.php");
include_once (SM_PATH . "functions/imap.php");
if ($_POST['plugin_change_ldappass'] != null) $Messages = change_ldappass_check();
include_once (SM_PATH . "include/load_prefs.php");
displayPageHeader($color, "None");
?>
<br>
<table width=95% align=center cellpadding=2 cellspacing=2 border=0>
<tr><td bgcolor="<?php echo $color[0] ?>">
<center><b>Change Password</b></center>
</td><?php
if (isset($Messages) && count($Messages)) {
echo "<tr><td>\n";
foreach ($Messages as $line) {
echo $line . "<br>\n";
}
echo "</td></tr>\n";
}
?><tr><td>
<form method=post action="<?php print $_SERVER['PHP_SELF'] ?>">
<table>
<tr>
<th align=right>Old Password:</th>
<td><input type=password name=cp_oldpass value="" size=20></td>
</tr>
<tr>
<th align=right>New Password:</th>
<td><input type=password name=cp_newpass value="" size=20></td>
</tr>
<tr>
<th align=right>Verify New Password:</th>
<td><input type=password name=cp_verify value="" size=20></td>
</tr>
<tr>
<td align=center colspan=2><input type=submit value="Submit" name="plugin_change_ldappass"></td>
</tr>
</table>
</td></tr>
</tr></table>
</body></html>
<?php
function change_ldappass_check($debug = 0) {
$cp_oldpass = $_POST['cp_oldpass'];
$cp_newpass = $_POST['cp_newpass'];
$cp_verify = $_POST['cp_verify'];
$plugin_change_ldappass = $_POST['plugin_change_ldappass'];
$username = $_SESSION['username'];
$Messages = array();
if ($cp_oldpass == "") array_push($Messages, 'You must type in your old password.');
if ($cp_newpass == "") array_push($Messages, 'You must type in a new password.');
if ($cp_verify == "") array_push($Messages,'You must also type in your new password in the verify box.');
if ($cp_newpass != "" && $cp_verify != $cp_newpass) array_push($Messages,'Your new password doesn\'t match the verify password.');
if (!preg_match("/^[A-Za-z0-9\�\%\^\*\(\)\-\_\+\=\[\]\{\}\:[EMAIL PROTECTED],\.\? ]+$/",$cp_newpass)) array_push($Messages,'Passwords can only contain the following characters:<br>A-Z, a-z, 0-9, �%^*()-_+=[]{}:@#~,.?');
if (count($Messages)) return $Messages;
return change_ldappass_go($debug);
}
function change_ldappass_go($debug) {
$username = $_SESSION['username'];
$base_uri = $_SESSION['base_uri'];
$cp_oldpass = $_POST['cp_oldpass'];
$cp_newpass = $_POST['cp_newpass'];
include_once(SM_PATH . "plugins/change_ldappass/config.php");
$Messages = array();
if ($debug) array_push($Messages, "Connecting to LDAP Server");
$ds=ldap_connect($ldap_server);
if (! $ds) {
array_push($Messages,'Can\'t connect to Directory Server, please try later!');
return $Messages;
}
// first bind anonymously and try to determine the correct dn for the user with uid=$username
$r=ldap_bind($ds,"","");
$sr=ldap_search($ds,$ldap_base_dn,"($ldap_user_field=$username)",array("dn")); //search for uid
if (ldap_count_entries($ds,$sr)>1) {
array_push($Messages,'Duplicate login entries detected, cannot change password!');
return $Messages;
}
if (ldap_count_entries($ds,$sr)==0) {
array_push($Messages,'Your login account was not found in the LDAP database, cannot change password!');
return $Messages;
}
$info=ldap_get_entries($ds,$sr);
if ($debug) array_push($Messages,ldap_debug_print_array($info));
$dn=$info[0]["dn"]; //finally get the full users dn
// now rebind the the database as user or as root if required
if (! $ldap_bind_as_manager) {
if (! @ldap_bind($ds,$dn,$cp_oldpass)) { //if we can't bind as the user then the old passwd must be wrong
array_push($Messages,'Your old password is not correct.');
return $Messages;
} else {
if ($debug) array_push($Messages,"Bind to database as $dn successful.");
}
} else {
if (! @ldap_bind($ds,$ldap_manager_dn,$ldap_manager_pw)) {
array_push($Messages,"Could not bind to the LDAP server as $ldap_manager_dn. Is the password correct?");
return $Messages;
} else {
if ($debug) array_push($Messages,"Bind to database as $ldap_manager_dn successful.");
}
}
$sr=ldap_search($ds,$ldap_base_dn,"($ldap_user_field=$username)"); //check the db again, this time so we get the password field returned
$info = ldap_get_entries($ds, $sr);
if ($debug) array_push($Messages,ldap_debug_print_array($info));
$storedpass=$info[0][$ldap_password_field][0];
//this next code tries to identify the correct password type
//*Note* I've not tested all types here as I cannot reproduce some setups
//Please let me know if the code does not work for you, or if you have code that will work
//for a particular type.
//password types:
//{crypt} salted passwords of type DES, MD5 and Blowfish
//{MD5} unsalted password in MD5 format
//{SHA} unsalted password in SHA format
//lets try to determine the encrytion method of the stored password
//
$p=split("}",$storedpass); //split the password
$ctype=strtolower($p[0]); //into the {crypttype}
$lpass=$p[1]; //and the password
//if the stored password is {crypt} then its salted, but which sub-type?
//$ctype="{md5";
switch ($ctype) {
case "{crypt":
$pl=strlen($lpass); // We'll look at the length and salt
// of what's already stored to determine the crypt sub-type
$stype="DES"; // sensible default if not detected
if ($pl>=34 and substr($lpass,0,3)=="$1$") $stype="MD5";
if ($pl>=34 and substr($lpass,0,3)=="$2$") $stype="BLOWFISH";
if ($debug) array_push($Messages, "Password type is {crypt}, sub-type $stype");
$cpass=ldap_crypt_passwd($cp_oldpass,$lpass,$stype); //crypt up our old password so we can check it again
break;
case "{md5":
if ($debug) array_push($Messages, "Password type is {MD5}");
$cpass=ldap_md5_passwd($cp_oldpass);
break;
case "{sha":
if ($debug) array_push($Messages, "Password type is {SHA}");
$cpass=ldap_sha_passwd($cp_oldpass);
break;
default: // Use plain text password
$cpass=$cp_oldpass;
$lpass=$storedpass; // Override $lpass as it is truncated from the original
break;
}
//now check again the stored password against the encrypted version of the supplied old password
if ($lpass != $cpass) {
array_push($Messages,"Your old password is not correct.");
if ($debug) array_push($Messages,"Stored Password: $lpass Old Password: $cpass");
return $Messages;
}
//Make sure the new passwd generation uses the encryption method of the previous password
switch ($ctype) {
case "{crypt":
$newpass="{crypt}".ldap_crypt_passwd($cp_newpass,ldap_generate_salt($stype),$stype);
break;
case "{md5":
$newpass="{MD5}".ldap_md5_passwd($cp_newpass);
break;
case "{sha":
$newpass="{SHA}".ldap_sha_passwd($cp_newpass);
break;
// more password types should go here ;-) and the functions to drive them below.
default: // Use plain text password
$newpass=$cp_newpass;
break;
}
if ($debug) array_push($Messages,"New Password: $newpass");
$newinfo=array();
$newinfo[$ldap_password_field][0]=$newpass;
if (@ldap_modify($ds,$dn,$newinfo)) {
$smb=0;
if ($change_smb) {
$exe="echo \"$cp_oldpass\" |$smb_passwd -U $username -s \"$cp_newpass\" 2>&1";
if ($debug) array_push($Messages,$exe);
$r=exec($exe,$s);
if ($r=="Password changed for user $username") $smb=1;
} else {
$smb=1;
}
if ($debug) array_push($Messages, $r);
if ($smb) {
// Write new cookies for the password
$onetimepad = OneTimePadCreate(strlen($cp_newpass));
$_SESSION['onetimepad']=$onetimepad; //do I need to do this now?
$key = OneTimePadEncrypt($cp_newpass, $onetimepad);
$_COOKIES['key']=$key;
setcookie("key", $key, 0, $base_uri);
// Automatically forward back to the options screen if correct
if ($debug == 0) {
header("Location: $base_uri/src/options.php?plugin_change_ldappass=true");
exit(0);
}
return $Messages;
} else { //smbpasswd change failed so we must re sync the ldap password back to its original
$newinfo[$ldap_password_field][0]=$storedpass;
$r=ldap_modify($ds,$dn,$newinfo); //change it back so they're not out of sync.
array_push($Messages,'SMB Password change was not successful, so LDAP not changed!');
return $Messages;
}
} else {
array_push($Messages,'LDAP Password change was not successful!');
return $Messages;
}
@ldap_close($ds);
}
// Generate an unsalted SHA1 pw. This should work with Netscape messaging / directory server 4+
function ldap_sha_password($clear_pw) {
return base64_encode(mHash(MHASH_SHA1, $clear_pw));
}
// Generate an unsalted MD5 pw. This works fine with OpenLDAP.
function ldap_md5_passwd($clear_pw) {
return base64_encode(pack("H*", md5($clear_pw)));
}
// Generate salted crypt passwords
function ldap_crypt_passwd($password,$salt,$stype) {
if ($stype=="MD5") return crypt($password,substr($salt,0,12)); //MD5 uses 12 chr salt
if ($stype=="BLOWFISH") return crypt($password,substr($salt,0,16)); //BLOWFISH uses 16 chr salt
if ($stype=="DES") return crypt($password,substr($salt,0,2)); //crypt uses 2 chr salt
}
function ldap_generate_salt($stype) {
$salt=""; //generate a salt using characters [A-Z][a-z][0-9]./
$chars="./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
for ($i=0;$i<16;$i++) {
$salt.=$chars[mt_rand(0,strlen($chars))];
}
//return salts longer than we need but these will be trimmed in the ldap_crypt_passwd function
if ($stype=="MD5") return "$1$".$salt;
if ($stype=="BLOWFISH") return "$2$".$salt;
if ($stype=="DES") return $salt;
}
function ldap_debug_print_array($array) {
$out="<br>--------------------------------------------------------<br>\n";
$out.=ldap_debug_print_array1($array,"");
$out.= "<br>--------------------------------------------------------<br>\n";
return $out;
}
function ldap_debug_print_array1($array,$out) {
if(gettype($array)=="array") {
$out.="<ul>";
while (list($index, $subarray) = each($array) ) {
$out.="<li>$index <code>=></code>";
$out=ldap_debug_print_array1($subarray,$out);
$out.="</li>";
}
$out.="</ul>";
} else $out.="$array";
return $out;
}
?>