--- /dev/null
+<?php
+/**
+ * Change password ldap backend
+ *
+ * @version $Id$
+ * @package plugins
+ * @subpackage change_password
+ */
+
+/** Default plugin configuration.*/
+/**
+ * Address of ldap server.
+ * You can use any URL format that is supported by your ldap extension.
+ * Examples:
+ * <ul>
+ * <li>'ldap.example.com' - connect to server on ldap.example.com address
+ * <li>'ldaps://ldap.example.com' - connect to server on ldap.example.com address
+ * and use SSL encrypted connection to default ldaps port.
+ * </ul>
+ * defaults to imap server address.
+ * @link http://www.php.net/ldap-connect
+ * @global string $cpw_ldap_server
+ */
+global $cpw_ldap_server;
+$cpw_ldap_server='localhost';
+
+/**
+ * Port of ldap server.
+ * Used only when $cpw_ldap_server specifies ip address or dns name.
+ * @global integer $cpw_ldap_port
+ */
+global $cpw_ldap_port;
+$cpw_ldap_port=389;
+
+/**
+ * ldap basedn that is used for binding to ldap server.
+ * this option must be set to correct value.
+ * @global $cpw_ldap_basedn;
+ */
+global $cpw_ldap_basedn;
+$cpw_ldap_basedn='';
+
+/**
+ * LDAP connection options
+ * @link http://www.php.net/ldap-set-option
+ * @global array $cpw_ldap_connect_opts
+ */
+global $cpw_ldap_connect_opts;
+$cpw_ldap_connect_opts=array();
+
+/**
+ * Controls use of starttls on ldap connection.
+ * Requires php 4.2+, php ldap extension with ssl support and
+ * PROTOCOL_VERSION => 3 setting in $cpw_ldap_connect_opts
+ * @global boolean $cpw_ldap_use_tls
+ */
+global $cpw_ldap_use_tls;
+$cpw_ldap_use_tls=false;
+
+/**
+ * BindDN that should be able to search ldap directory and find DN used by user.
+ * Uses anonymous bind if set to empty string. You should not use DN with write
+ * access to ldap directory here. Write access is not required.
+ * @global string $cpw_ldap_binddn
+ */
+global $cpw_ldap_binddn;
+$cpw_ldap_binddn='';
+
+/**
+ * password used for $cpw_ldap_binddn
+ * @global string $cpw_ldap_bindpw
+ */
+global $cpw_ldap_bindpw;
+$cpw_ldap_bindpw='';
+
+/**
+ * BindDN that should be able to change password.
+ * WARNING: usually user has enough privileges to change own password.
+ * If you leave default value, plugin will try to connect with dn that
+ * is detected in $cpw_ldap_username_attr=$username search and current
+ * user password will be used for authentication.
+ * @global string $cpw_ldap_admindn
+ */
+global $cpw_ldap_admindn;
+$cpw_ldap_admindn='';
+
+/**
+ * password used for $cpw_ldap_admindn
+ * @global string $cpw_ldap_adminpw
+ */
+global $cpw_ldap_adminpw;
+$cpw_ldap_adminpw='';
+
+/**
+ * ldap attribute that stores username.
+ * username entry should be unique for $cpw_ldap_basedn
+ * @global string $cpw_ldap_userid_attr
+ */
+global $cpw_ldap_userid_attr;
+$cpw_ldap_userid_attr='uid';
+
+/**
+ * crypto that is used to encode new password
+ * If set to empty string, system tries to keep same encoding/hashing algorithm
+ * @global string $cpw_ldap_default_crypto
+ */
+global $cpw_ldap_default_crypto;
+$cpw_ldap_default_crypto='';
+
+/** end of default config */
+
+/** configuration overrides from config file */
+if (isset($cpw_ldap['server'])) $cpw_ldap_server=$cpw_ldap['server'];
+if (isset($cpw_ldap['port'])) $cpw_ldap_port=$cpw_ldap['port'];
+if (isset($cpw_ldap['basedn'])) $cpw_ldap_basedn=$cpw_ldap['basedn'];
+if (isset($cpw_ldap['connect_opts'])) $cpw_ldap_connect_opts=$cpw_ldap['connect_opts'];
+if (isset($cpw_ldap['use_tls'])) $cpw_ldap_use_tls=$cpw_ldap['use_tls'];
+if (isset($cpw_ldap['binddn'])) $cpw_ldap_binddn=$cpw_ldap['binddn'];
+if (isset($cpw_ldap['bindpw'])) $cpw_ldap_bindpw=$cpw_ldap['bindpw'];
+if (isset($cpw_ldap['admindn'])) $cpw_ldap_admindn=$cpw_ldap['admindn'];
+if (isset($cpw_ldap['adminpw'])) $cpw_ldap_adminpw=$cpw_ldap['adminpw'];
+if (isset($cpw_ldap['userid_attr'])) $cpw_ldap_userid_attr=$cpw_ldap['userid_attr'];
+if (isset($cpw_ldap['default_crypto'])) $cpw_ldap_default_crypto=$cpw_ldap['default_crypto'];
+
+/**
+ * Adding plugin hooks
+ */
+global $squirrelmail_plugin_hooks;
+$squirrelmail_plugin_hooks['change_password_dochange']['ldap'] =
+ 'cpw_ldap_dochange';
+$squirrelmail_plugin_hooks['change_password_init']['ldap'] =
+ 'cpw_ldap_init';
+
+/**
+ * Backend specific functions
+ */
+function cpw_ldap_init() {
+ global $color;
+ global $cpw_ldap_basedn;
+
+ /**
+ * If SM_PATH isn't defined, define it. Required to include files.
+ * @ignore
+ */
+ if (!defined('SM_PATH')) define('SM_PATH','../../../');
+
+ // load error_box() function
+ include_once(SM_PATH . 'functions/display_messages.php');
+
+ // set initial value for error tracker
+ $cpw_ldap_initerr=false;
+
+ // check for ldap support in php
+ if (! function_exists('ldap_connect')) {
+ error_box(_("Current configuration requires ldap support in php."),$color);
+ $cpw_ldap_initerr=true;
+ }
+
+ // chech required configuration settings.
+ if ($cpw_ldap_basedn=='') {
+ error_box(_("Plugin is not configured correctly."),$color);
+ $cpw_ldap_initerr=true;
+ }
+
+ // if error var is positive, close html and stop execution
+ if ($cpw_ldap_initerr) {
+ echo '</body></html>';
+ exit;
+ }
+}
+
+
+/**
+ * @param array $data The username/curpw/newpw data.
+ * @return array Array of error messages.
+ */
+function cpw_ldap_dochange($data) {
+ global $cpw_ldap_server, $cpw_ldap_port, $cpw_ldap_basedn,
+ $cpw_ldap_connect_opts,$cpw_ldap_use_tls,
+ $cpw_ldap_binddn, $cpw_ldap_bindpw,
+ $cpw_ldap_admindn, $cpw_ldap_adminpw;
+
+ // unfortunately, we can only pass one parameter to a hook function,
+ // so we have to pass it as an array.
+ $username = $data['username'];
+ $curpw = $data['curpw'];
+ $newpw = $data['newpw'];
+
+ // globalize current password.
+
+ $msgs = array();
+
+ /**
+ * connect to ldap server
+ * hide ldap_connect() function call errors, because they are processed in script.
+ * any script execution error is treated as critical, error messages are dumped
+ * to $msgs and ldap connection is closed with ldap_unbind(). all ldap_unbind()
+ * errors are suppressed. Any other error suppression should be explained.
+ */
+ $cpw_ldap_con=@ldap_connect($cpw_ldap_server);
+
+ if ($cpw_ldap_con) {
+ $cpw_ldap_con_err=false;
+ // set connection options
+ if (is_array($cpw_ldap_connect_opts) && $cpw_ldap_connect_opts!=array()) {
+ foreach ($cpw_ldap_connect_opts as $opt => $value) {
+ if (! ldap_set_option($cpw_ldap_con,constant('LDAP_OPT_' . $opt),$value)) {
+ // set error message
+ array_push($msgs,sprintf(_("Setting of ldap connection option %s to value %s failed."),$opt,$value));
+ // FIXME: check if ldap_set_option modifies ldap_error.
+ array_push($msgs,sprintf(_("Error: %s"),ldap_error($cpw_ldap_con)));
+ $cpw_ldap_con_err=true;
+ }
+ }
+ }
+
+ // check for connection errors and stop execution if something is wrong
+ if ($cpw_ldap_con_err) {
+ @ldap_unbind($cpw_ldap_con);
+ return $msgs;
+ }
+
+ // enable tls
+ // FIXME: untested. use of undocumented ldap function
+ if ($cpw_ldap_use_tls &&
+ check_php_version(4,2,0) &&
+ isset($cpw_ldap_connect_opts['PROTOCOL_VERSION']) &&
+ $cpw_ldap_connect_opts['PROTOCOL_VERSION']>=3) {
+ if (! ldap_use_tls($cpw_ldap_con)) {
+ array_push($msgs,_("Unable to use TLS."));
+ array_push($msgs,sprintf(_("Error: %s"),ldap_error($cpw_ldap_con)));
+ $cpw_ldap_con_err=true;
+ }
+ } elseif ($cpw_ldap_use_tls) {
+ array_push($msgs,_("Unable to use LDAP TLS in current setup."));
+ $cpw_ldap_con_err=true;
+ }
+
+ // check for connection errors and stop execution if something is wrong
+ if ($cpw_ldap_con_err) {
+ @ldap_unbind($cpw_ldap_con);
+ return $msgs;
+ }
+
+ /**
+ * bind to ldap (use anonymous bind or unprivileged dn) in order to get user's dn
+ * hide ldap_bind() function call errors, because errors are processed in script
+ */
+ if ($cpw_ldap_binddn!='') {
+ // authenticated bind
+ $cpw_ldap_binding=@ldap_bind($cpw_ldap_con,$cpw_ldap_binddn,$cpw_ldap_bindpw);
+ } else {
+ // anonymous bind
+ $cpw_ldap_binding=@ldap_bind($cpw_ldap_con);
+ }
+
+ // check ldap_bind errors
+ if (! $cpw_ldap_binding) {
+ array_push($msgs,_("Unable to bind to ldap server"));
+ array_push($msgs,sprintf(_("Server replied: %s"),ldap_error($cpw_ldap_con)));
+ @ldap_unbind($cpw_ldap_con);
+ return $msgs;
+ }
+
+ // find userdn
+ $cpw_ldap_search_err=cpw_ldap_uid_search($cpw_ldap_con,$cpw_ldap_basedn,$msgs,$cpw_ldap_res,$cpw_ldap_userdn);
+
+ // check for search errors and stop execution if something is wrong
+ if (! $cpw_ldap_search_err) {
+ ldap_unbind($cpw_ldap_con);
+ return $msgs;
+ }
+
+ /**
+ * unset $cpw_ldap_res2 variable, if such var exists.
+ * $cpw_ldap_res2 object can be set in two places and second place checks,
+ * if object was created in first place. if variable name matches (somebody
+ * uses $cpw_ldap_res2 in code or globals), incorrect validation might
+ * cause script errors.
+ */
+ if (isset($cpw_ldap_res2)) unset($cpw_ldap_res2);
+
+ // rebind as userdn or admindn
+ if ($cpw_ldap_admindn!='') {
+ // admindn bind
+ $cpw_ldap_binding=@ldap_bind($cpw_ldap_con,$cpw_ldap_admindn,$cpw_ldap_adminpw);
+
+ if ($cpw_ldap_binding) {
+ // repeat search in order to get password info. Password info should be unavailable in unprivileged bind.
+ $cpw_ldap_search_err=cpw_ldap_uid_search($cpw_ldap_con,$cpw_ldap_basedn,$msgs,$cpw_ldap_res2,$cpw_ldap_userdn);
+
+ // check for connection errors and stop execution if something is wrong
+ if (! $cpw_ldap_search_err) {
+ @ldap_unbind($cpw_ldap_con);
+ return $msgs;
+ }
+
+ // we should check user password here.
+ $cpw_ldap_cur_pass_array=ldap_get_values($cpw_ldap_con,
+ ldap_first_entry($cpw_ldap_con,$cpw_ldap_res2),'userpassword');
+ // FIXME: check if ldap_get_values() found userpassword field. Currently it might cause php errors
+
+ // compare passwords
+ if (! cpw_ldap_compare_pass($cpw_ldap_cur_pass_array[0],$curpw,$msgs)) {
+ @ldap_unbind($cpw_ldap_con);
+ return $msgs;
+ }
+ }
+ } else {
+ $cpw_ldap_binding=@ldap_bind($cpw_ldap_con,$cpw_ldap_userdn,$curpw);
+ }
+
+ if (! $cpw_ldap_binding) {
+ array_push($msgs,_("Unable to rebind to ldap server"));
+ array_push($msgs,sprintf(_("Server replied: %s"),ldap_error($cpw_ldap_con)));
+ @ldap_unbind($cpw_ldap_con);
+ return $msgs;
+ }
+
+ // repeat search in order to get password info
+ if (! isset($cpw_ldap_res2))
+ $cpw_ldap_search_err=cpw_ldap_uid_search($cpw_ldap_con,$cpw_ldap_basedn,$msgs,$cpw_ldap_res2,$cpw_ldap_userdn);
+
+ // check for connection errors and stop execution if something is wrong
+ if (! $cpw_ldap_search_err) {
+ @ldap_unbind($cpw_ldap_con);
+ return $msgs;
+ }
+
+ // getpassword
+ $cpw_ldap_cur_pass_array=ldap_get_values($cpw_ldap_con,ldap_first_entry($cpw_ldap_con,$cpw_ldap_res2),'userpassword');
+ // FIXME: check if ldap_get_values() found userpassword field
+
+ // encrypt new password (old password is needed for plaintext encryption detection)
+ $cpw_ldap_new_pass=cpw_ldap_encrypt_pass($newpw,$cpw_ldap_cur_pass_array[0],$msgs,$curpw);
+
+ if (! $cpw_ldap_new_pass) {
+ @ldap_unbind($cpw_ldap_con);
+ return $msgs;
+ }
+
+ // set new password
+ $ldap_pass_change=ldap_modify($cpw_ldap_con,$cpw_ldap_userdn,array('userpassword'=>$cpw_ldap_new_pass));
+
+ // check if ldap_modify was successful
+ if(! $ldap_pass_change) {
+ array_push($msgs,ldap_error($cpw_ldap_con));
+ }
+
+ // close connection
+ @ldap_unbind($cpw_ldap_con);
+ } else {
+ array_push($msgs,_("Unable to connect to LDAP server."));
+ }
+ return $msgs;
+}
+
+/** backend support functions **/
+
+/**
+ * Sanitizes ldap query strings.
+ * original code - ldapquery plugin.
+ * See rfc2254
+ * @link http://www.faqs.org/rfcs/rfc2254.html
+ * @param string $string
+ * @return string sanitized string
+ */
+function cpw_ldap_specialchars($string) {
+ $sanitized=array('\\' => '\5c',
+ '*' => '\2a',
+ '(' => '\28',
+ ')' => '\29',
+ "\x00" => '\00');
+
+ return str_replace(array_keys($sanitized),array_values($sanitized),$string);
+}
+
+/**
+ * returns crypto algorithm used in password.
+ * @param string $pass encrypted/hashed password
+ * @return string lowercased crypto algorithm name
+ */
+function cpw_ldap_get_crypto($pass,$curpass='') {
+ $ret = false;
+
+ if (preg_match("/^\{(.+)\}+/",$pass,$crypto)) {
+ $ret=strtolower($crypto[1]);
+ }
+
+ if ($ret=='crypt') {
+ // {CRYPT} can be standard des crypt, extended des crypt, md5 crypt or blowfish
+ // depends on first salt symbols (ext_des = '_', md5 = '$1$', blowfish = '$2$')
+ // and length of salt (des = 2 chars, ext_des = 9, md5 = 12, blowfish = 16).
+ if (preg_match("/^\{crypt\}\\\$1\\\$+/i",$pass)) {
+ $ret='md5crypt';
+ } elseif (preg_match("/^\{crypt\}\\\$2\\\$+/i",$pass)) {
+ $ret='blowfish';
+ } elseif (preg_match("/^\{crypt\}_+/i",$pass)) {
+ $ret='extcrypt';
+ }
+ }
+
+ // maybe password is plaintext
+ if (! $ret && $curpass!='' && $pass==$curpass) $ret='plaintext';
+
+ return $ret;
+}
+
+/**
+ * search ldap for user id.
+ * @param object $ldap_con ldap connection
+ * @param string $ldap_basedn ldap basedn
+ * @param array $msgs error messages
+ * @param object $results ldap search results
+ * @param string $userdn DN of found entry
+ * @param boolean $onlyone require unique search results
+ * @return boolean false if connection failed.
+ */
+function cpw_ldap_uid_search($ldap_con,$ldap_basedn,&$msgs,&$results,&$userdn,$onlyone=true) {
+ global $cpw_ldap_userid_attr,$username;
+
+ $ret=true;
+
+ $results=ldap_search($ldap_con,$ldap_basedn,cpw_ldap_specialchars($cpw_ldap_userid_attr . '=' . $username));
+
+ if (! $results) {
+ array_push($msgs,_("Unable to find user's dn.") . _("Search error."));
+ array_push($msgs,sprintf(_("Error: %s"),ldap_error($ldap_con)));
+ $ret=false;
+ } elseif ($onlyone && ldap_count_entries($ldap_con,$results)>1) {
+ array_push($msgs,_("Multiple userid matches found."));
+ $ret=false;
+ } elseif (! $userdn = ldap_get_dn($ldap_con,ldap_first_entry($ldap_con,$results))) {
+ // ldap_get_dn() returned error
+ array_push($msgs,_("Unable to find user's dn.") . _("ldap_get_dn error."));
+ $ret=false;
+ }
+ return $ret;
+}
+
+/**
+ * encrypts ldap password
+ *
+ * if $cpw_ldap_default_crypto is set to empty string or $same_crypto is set,
+ * uses same crypto as in old password.
+ * See phpldapadmin password_hash() function
+ * @link http://phpldapadmin.sf.net
+ * @param string $pass string that has to be encrypted/hashed
+ * @param string $cur_pass_hash old password hash
+ * @param array $msgs error message
+ * @param string $curpass current password. Used for plaintext password detection.
+ * @return string encrypted/hashed password or false
+ */
+function cpw_ldap_encrypt_pass($pass,$cur_pass_hash,&$msgs,$curpass='') {
+ global $cpw_ldap_default_crypto;
+
+ // which crypto should be used to encode/hash password
+ if ($cpw_ldap_default_crypto=='') {
+ $ldap_crypto=cpw_ldap_get_crypto($cur_pass_hash,$curpass);
+ } else {
+ $ldap_crypto=$cpw_ldap_default_crypto;
+ }
+ return cpw_ldap_password_hash($pass,$ldap_crypto,$msgs);
+}
+
+/**
+ * create hashed password
+ * @param string $pass plain text password
+ * @param string $crypto used crypto algorithm
+ * @param array $msgs array used for error messages
+ * @param string $forced_salt salt that should be used during hashing.
+ * Is used only when is not set to empty string. Salt should be formated
+ * according to $crypto requirements.
+ * @return hashed password or false.
+ */
+function cpw_ldap_password_hash($pass,$crypto,&$msgs,$forced_salt='') {
+ // set default return code
+ $ret=false;
+
+ // lowercase crypto just in case
+ $crypto=strtolower($crypto);
+
+ // extra symbols used for random string in crypt salt
+ // squirrelmail GenerateRandomString() adds alphanumerics with third argument = 7.
+ $extra_salt_chars='./';
+
+ // encrypt/hash password
+ switch ($crypto) {
+ case 'md5':
+ $ret='{MD5}' . base64_encode(pack('H*',md5($pass)));
+ break;
+ case 'smd5':
+ // minimal requirement mhash extension and php 4.0.4.
+ if( function_exists( 'mhash' ) && function_exists( 'mhash_keygen_s2k' ) ) {
+ sq_mt_seed( (double) microtime() * 1000000 );
+ if ($forced_salt!='') {
+ $salt=$forced_salt;
+ } else {
+ $salt = mhash_keygen_s2k( MHASH_MD5, $pass, substr( pack( "h*", md5( mt_rand() ) ), 0, 8 ), 4 );
+ }
+ $ret = "{SMD5}".base64_encode( mhash( MHASH_MD5, $pass.$salt ).$salt );
+ } else {
+ array_push($msgs,sprintf(_("Unsupported crypto: %s"),'smd5') . _("php mhash extension is missing."));
+ }
+ break;
+ case 'sha':
+ // minimal requirement = mhash extension
+ if( function_exists( 'mhash' ) ) {
+ $ret = '{SHA}' . base64_encode( mhash( MHASH_SHA1, $pass) );
+ } else {
+ array_push($msgs,sprintf(_("Unsupported crypto: %s"),'sha') . _("php mhash extension is missing."));
+ }
+ break;
+ case 'ssha':
+ // minimal requirement = mhash extension and php 4.0.4
+ if( function_exists( 'mhash' ) && function_exists( 'mhash_keygen_s2k' ) ) {
+ sq_mt_seed( (double) microtime() * 1000000 );
+ if ($forced_salt!='') {
+ $salt=$forced_salt;
+ } else {
+ $salt = mhash_keygen_s2k( MHASH_SHA1, $pass, substr( pack( "h*", md5( mt_rand() ) ), 0, 8 ), 4 );
+ }
+ $ret = "{SSHA}".base64_encode( mhash( MHASH_SHA1, $pass.$salt ).$salt );
+ } else {
+ array_push($msgs,sprintf(_("Unsupported crypto: %s"),'ssha')
+ . _("php mhash extension is missing."));
+ }
+ break;
+ case 'crypt':
+ if (defined('CRYPT_STD_DES') && CRYPT_STD_DES==1) {
+ $ret = '{CRYPT}' . crypt($pass,GenerateRandomString(2,$extra_salt_chars,7));
+ } else {
+ array_push($msgs,sprintf(_("Unsupported crypto: %s"),'crypt')
+ . _("System crypt library doesn't support standard des crypt."));
+ }
+ break;
+ case 'md5crypt':
+ // check if crypt() supports md5
+ if (defined('CRYPT_MD5') && CRYPT_MD5==1) {
+ $ret = '{CRYPT}' . crypt($pass,'$1$' . GenerateRandomString(9,$extra_salt_chars,7));
+ } else {
+ array_push($msgs,sprintf(_("Unsupported crypto: %s"),'md5crypt')
+ . _("System crypt library doesn't have md5 support."));
+ }
+ break;
+ case 'extcrypt':
+ // check if crypt() supports extended des
+ if (defined('CRYPT_EXT_DES') && CRYPT_EXT_DES==1) {
+ // FIXME: guinea pigs with extended des support needed.
+ $ret = '{CRYPT}' . crypt($pass,'_' . GenerateRandomString(8,$extra_salt_chars,7));
+ } else {
+ array_push($msgs,sprintf(_("Unsupported crypto: %s"),'ext_des')
+ . _("System crypt library doesn't support extended des crypt."));
+ }
+ break;
+ case 'blowfish':
+ // check if crypt() supports blowfish
+ if (defined('CRYPT_BLOWFISH') && CRYPT_BLOWFISH==1) {
+ // FIXME: guinea pigs with blowfish support needed.
+ $ret = '{CRYPT}' . crypt($pass,'$2$' . GenerateRandomString(13,$extra_salt_chars,7));
+ } else {
+ array_push($msgs,sprintf(_("Unsupported crypto: %s"),'blowfish')
+ . _("System crypt library doesn't have blowfish support."));
+ }
+ break;
+ case 'plaintext':
+ // clear plain text password
+ $ret=$pass;
+ break;
+ default:
+ array_push($msgs,sprintf(_("Unsupported crypto: %s"),
+ (is_string($ldap_crypto) ? htmlspecialchars($ldap_crypto) : _("unknown"))));
+ }
+ return $ret;
+}
+
+/**
+ * compares two passwords
+ * Code reuse. See phpldapadmin password_compare() function.
+ * Some parts of code was rewritten to backend specifics.
+ * @link http://phpldapadmin.sf.net
+ * @param string $pass_hash
+ * @param string $pass_clear
+ * @param array $msgs
+ * @return boolean true, if passwords match
+ */
+function cpw_ldap_compare_pass($pass_hash,$pass_clear,&$msgs) {
+ $ret=false;
+
+ if( preg_match( "/{([^}]+)}(.*)/", $pass_hash, $cypher ) ) {
+ $pass_hash = $cypher[2];
+ $_cypher = strtolower($cypher[1]);
+ } else {
+ $_cypher = NULL;
+ }
+
+ switch( $_cypher ) {
+ case 'ssha':
+ // Salted SHA
+ // check for mhash support
+ if ( function_exists('mhash') ) {
+ $hash = base64_decode($pass_hash);
+ $salt = substr($hash, -4);
+ $new_hash = base64_encode( mhash( MHASH_SHA1, $pass_clear.$salt).$salt );
+ if( strcmp( $pass_hash, $new_hash ) == 0 )
+ $ret=true;
+ } else {
+ array_push($msgs,_("Unable to validate user's password."));
+ array_push($msgs, _("php mhash extension is missing."));
+ }
+ break;
+ case 'smd5':
+ // Salted MD5
+ // check for mhash support
+ if ( function_exists('mhash') ) {
+ $hash = base64_decode($pass_hash);
+ $salt = substr($hash, -4);
+ $new_hash = base64_encode( mhash( MHASH_MD5, $pass_clear.$salt).$salt );
+ if( strcmp( $pass_hash, $new_hash ) == 0)
+ $ret=true;
+ } else {
+ array_push($msgs,_("Unable to validate user's password."));
+ array_push($msgs, _("php mhash extension is missing."));
+ }
+ break;
+ case 'sha':
+ // SHA crypted passwords
+ if( strcasecmp( cpw_ldap_password_hash($pass_clear,'sha',$msgs), "{SHA}".$pass_hash ) == 0)
+ $ret=true;
+ break;
+ case 'md5':
+ // MD5 crypted passwords
+ if( strcasecmp( cpw_ldap_password_hash( $pass_clear,'md5',$msgs), "{MD5}".$pass_hash ) == 0 )
+ $ret=true;
+ break;
+ case 'crypt':
+ // Crypt passwords
+ if( strstr( $pass_hash, '$2$' ) ) { // Check if it's blowfish crypt
+ // check CRYPT_BLOWFISH here.
+ // ldap server might support it, but php can be on other OS
+ if (defined('CRYPT_BLOWFISH') && CRYPT_BLOWFISH==1) {
+ list(,$type,$salt,$hash) = explode('$',$pass_hash);
+ if( crypt( $pass_clear, '$2$' .$salt ) == $pass_hash )
+ $ret=true;
+ } else {
+ array_push($msgs,_("Unable to validate user's password."));
+ array_push($msgs,_("Blowfish is not supported by webserver's system crypt library."));
+ }
+ } elseif( strstr( $pass_hash, '$1$' ) ) { // Check if it's md5 crypt
+ // check CRYPT_MD5 here.
+ // ldap server might support it, but php might be on other OS
+ if (defined('CRYPT_MD5') && CRYPT_MD5==1) {
+ list(,$type,$salt,$hash) = explode('$',$pass_hash);
+ if( crypt( $pass_clear, '$1$' .$salt ) == $pass_hash )
+ $ret=true;
+ } else {
+ array_push($msgs,_("Unable to validate user's password."));
+ array_push($msgs,_("MD5 is not supported by webserver's system crypt library."));
+ }
+ } elseif( strstr( $pass_hash, '_' ) ) { // Check if it's extended des crypt
+ // check CRYPT_EXT_DES here.
+ // ldap server might support it, but php might be on other OS
+ if (defined('CRYPT_EXT_DES') && CRYPT_EXT_DES==1) {
+ if( crypt( $pass_clear, $pass_hash ) == $pass_hash )
+ $ret=true;
+ } else {
+ array_push($msgs,_("Unable to validate user's password."));
+ array_push($msgs,_("Extended DES crypt is not supported by webserver's system crypt library."));
+ }
+ } else {
+ // it is possible that this test is useless and any crypt library supports it, but ...
+ if (defined('CRYPT_STD_DES') && CRYPT_STD_DES==1) {
+ // plain crypt password
+ if( crypt($pass_clear, $pass_hash ) == $pass_hash )
+ $ret=true;
+ } else {
+ array_push($msgs,_("Unable to validate user's password."));
+ array_push($msgs,_("Standard DES crypt is not supported by webserver's system crypt library."));
+ }
+ }
+ break;
+ // No crypt is given assume plaintext passwords are used
+ default:
+ if( $pass_clear == $pass_hash )
+ $ret=true;
+ break;
+ }
+ if (! $ret) {
+ array_push($msgs,CPW_CURRENT_NOMATCH);
+ }
+ return $ret;
+}
+?>
\ No newline at end of file