Drupal 7 urgent help needed: I need to be able to use MD5 with no salt for integrating with tomcat jdbcRealm. I have already created 2 views, one for users and the other for roles and it works fine if I manually update the table password with an MD5 value. What I want is for drupal to store the values in MD5 with no salt rather than a value with it's own hashing algorithm. I am not very proficient in PHP and I need the code urgently for a client deliverable tomorrow. Please help !!!

Comments

bigkevmcd’s picture

Have a look at

drupal/includes/password.inc
 * An alternative or custom version of this password hashing API may be
 * used by setting the variable password_inc to the name of the PHP file
 * containing replacement user_hash_password(), user_check_password(), and
 * user_needs_new_hash() functions.

I guess you only need to provide your own versions of those functions, changing the user_hash_password function to do whatever you want it to do.

nj_tom’s picture

Will anyone be kind enough to provide code for
user_hash_password(), user_check_password(), and user_needs_new_hash() to support only md5?

rdmmecomru’s picture

Hello! You have solved this problem?

aagbsn’s picture

I just did something similar, hopefully this is useful for anyone who stumbles across this post

1. Create a new module, I called mine projectname_legacy_auth

/**
 * supply alternate authentication schemes
 */
variable_set('password_inc', drupal_get_path('module', 'projectname_legacy_auth') . '/projectname_legacy_auth_password.inc');

function projectname_legacy_auth_uninstall() {
  // put it back the way it was...
  variable_set('password_inc', 'includes/password.inc');
}

function projectname_legacy_auth_disable() {
  // put it back the way it was...
  variable_set('password_inc', 'includes/password.inc');
}                                            

2. add the authentication mechanisms you need to projectname_legacy_auth_password.inc'.
In this case, it's md5().


/**
 * simplified example for md5, no salting
 */
function user_check_password($password, $account) {
  if ($account->pass == md5($password)) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Simplified example for md5 hashes, no salting
 */
function user_hash_password($password, $count_log2 = 0) {
  return md5($password);
}

/**
 * this is probably a bad idea :-)
 */
function user_needs_new_hash($account) {
  return FALSE;
}
rhizomenetworks’s picture

Hi

Below is a simple implementation I created today and seems to work fine after testing with some general user management cases.
To use this implementation you will have to set variable 'pasword_inc' in the variable table. See tools for doing that below.

Instructions:
A. Connecting the database
1. Use some DB browsing tool such as MySQLAdmin or Squirrel SQL to connect with the database.
2. Backup you DB just for in case

B. The cleanup before change
1. Clean all caches
2. Disable all caching.
3. Execute these SQL commands:
DELETE FROM cache WHERE cid = 'variables';
DELETE FROM cache_bootstrap WHERE cid = 'variables';

C. Adding the variable 'password_inc' to 'variable' table.
1. Note that the variable is not there by default
2. Note that variable value is: Hex representation of serialization of a String object that contains the text: "includes/mypassword.inc"
Accordingly simply do insert of this text into database will NOT WORK

3. To make it easy to insert new variables into database I prepared a simple "tools.php" file. For the purpose of this change you should put this file in the ROOT of your drupal website. After that REMOVE it from there.

The code for this tools.php:
==========

  echo "tools start <br/>";
  define('DRUPAL_ROOT', getcwd()); 
  
  try {
    include_once DRUPAL_ROOT . '/includes/database/database.inc';
    include_once DRUPAL_ROOT . '/includes/cache.inc';
    require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
    require_once DRUPAL_ROOT . '/includes/update.inc';
    require_once DRUPAL_ROOT . '/includes/common.inc';
    require_once DRUPAL_ROOT . '/includes/file.inc';
    require_once DRUPAL_ROOT . '/includes/entity.inc';
    require_once DRUPAL_ROOT . '/includes/unicode.inc';
    require_once DRUPAL_ROOT . '/sites/default/settings.php';
  
  
    echo "mode=" . $_REQUEST["mode"] . "<br/>"; 
    if($_REQUEST["mode"] == "set-var") {
      variable_set($_REQUEST["name"], $_REQUEST["value"]);
      echo "variable '" . $_REQUEST["name"] . "' was set to  '" . $_REQUEST["value"] . "'"; 
    } else if($_REQUEST["mode"] == "get-var") {
      $var = variable_get($_REQUEST["name"]);
      echo "get variable '"  . $_REQUEST["name"] . "'  value= '" .  $var . "'";
      //NOT WORKING FOR SOME REASON BIU NOT REQUIRE FOR THIS CHANGE
    }
  
  }catch(Exception $e) {
    echo "<br/>error:<br/>";
    echo $e->getMessage();
  } 
  

===========

4. To set the variable simply use this URL from your browser:
http://[YOUR DOMAIN]/tools.php?mode=set-var&name=password_inc&value=includes/mypassword.inc

If you see: "variable 'password_inc' was set to 'includes/mypassword.inc'
then your variable is ready.

D. Adding mypassword.inc
1. Get the code below and create file "mypassword.inc" (according to value you set for variable above) .
2. Copy this file into the 'includes' directory of your D7 installation
3. Basically you are ready to change passwords.

E. Testing and notes
1. The code for mypassword.inc does not automatically handle the migration and does not support backward compatibility! It means that after this change users can not login into the website.
2. You should either change passwords manually, or ask users to use the "Reset password" link for changing the password into MD5

3. During your test you may reach to 5 bad logins. You user will be blocked. You can free the user by executing this SQL:
"delete from flood;"

4. For debugging of change effect here is what you can do:
4.a of /modules/user/user.module file add these echo commands temporarily into function "user_authenticate":

function user_authenticate($name, $password) {
 
 echo "<br/>DEBUG user_authenticate::name: '" . $name . "'";
  echo "<br/>DEBUG user_authenticate::password: '" . $password . "'";
  echo "<br/>DEBUG user_authenticate::password_inc: '" . variable_get('password_inc') . "'";
 
  
  $uid = FALSE;
  if (!empty($name) && !empty($password)) {
    $account = user_load_by_name($name);

    if ($account) {
    echo "<br/>DEBUG user_authenticate::acount exist";

      // Allow alternate password hashing schemes.
      require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
      if (user_check_password($password, $account)) {
        // Successful authentication.
        $uid = $account->uid;

        // Update user to new password scheme if needed.
        if (user_needs_new_hash($account)) {
          user_save($account, array('pass' => $password));
        }
      }
    }
  }
  return $uid;
}

4b. in mypassword.inc (or password.inc) open echo commands for function " user_check_password "

function user_check_password($password, $account) {
  
  $stored_hash = $account->pass;
  $hash = _password_crypt($password);

  echo "<br/>DEBUG mypassword.inc user_check_password::stored_hash = '" . $stored_hash . "'";
  echo "<br/>DEBUG mypassword.inc user_check_password::hash = '" . $hash . "'";
  return ($hash && $stored_hash == $hash);
}

4c. To see the message you should perform false logins with wring user password. If MD5 is active you will see passwords thatlooks like this: "53dbdb1102881c10fd095fc66e109a04" . If default D7 password.inc is active you will keep seeing passwords that looks like this:
$S$ChH36JXn4yz8.HHZxv.emXbfIk7MfJvrBIPr/BspL/TuOYaxiyjr

5. Note that caching can mislead with results. Make sure that no caching is working when doing the change.

Finally here is the code for mypassword.inc

==============START OF FILE


/**
 * @file
 * Secure password hashing functions for user authentication.
 *
 * Based on the Portable PHP password hashing framework.
 * @see http://www.openwall.com/phpass/
 *
 * An alternative or custom version of this password hashing API may be
 * used by setting the variable password_inc to the name of the PHP file
 * containing replacement user_hash_password(), user_check_password(), and
 * user_needs_new_hash() functions.
 */

/**
 * The standard log2 number of iterations for password stretching. This should
 * increase by 1 every Drupal version in order to counteract increases in the
 * speed and power of computers available to crack the hashes.
 */
define('DRUPAL_HASH_COUNT', 14);

/**
 * The minimum allowed log2 number of iterations for password stretching.
 */
define('DRUPAL_MIN_HASH_COUNT', 7);

/**
 * The maximum allowed log2 number of iterations for password stretching.
 */
define('DRUPAL_MAX_HASH_COUNT', 30);

/**
 * The expected (and maximum) number of characters in a hashed password.
 */
define('DRUPAL_HASH_LENGTH', 32);

/**
 * Returns a string for mapping an int to the corresponding base 64 character.
 */
function _password_itoa64() {
  return './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
}

/**
 * Encode bytes into printable base 64 using the *nix standard from crypt().
 *
 * @param $input
 *   The string containing bytes to encode.
 * @param $count
 *   The number of characters (bytes) to encode.
 *
 * @return
 *   Encoded string
 */
function _password_base64_encode($input, $count) {
  $output = '';
  $i = 0;
  $itoa64 = _password_itoa64();
  do {
    $value = ord($input[$i++]);
    $output .= $itoa64[$value & 0x3f];
    if ($i < $count) {
      $value |= ord($input[$i]) << 8;
    }
    $output .= $itoa64[($value >> 6) & 0x3f];
    if ($i++ >= $count) {
      break;
    }
    if ($i < $count) {
      $value |= ord($input[$i]) << 16;
    }
    $output .= $itoa64[($value >> 12) & 0x3f];
    if ($i++ >= $count) {
      break;
    }
    $output .= $itoa64[($value >> 18) & 0x3f];
  } while ($i < $count);

  return $output;
}

/**
 * Generates a random base 64-encoded salt prefixed with settings for the hash.
 *
 * Proper use of salts may defeat a number of attacks, including:
 *  - The ability to try candidate passwords against multiple hashes at once.
 *  - The ability to use pre-hashed lists of candidate passwords.
 *  - The ability to determine whether two users have the same (or different)
 *    password without actually having to guess one of the passwords.
 *
 * @param $count_log2
 *   Integer that determines the number of iterations used in the hashing
 *   process. A larger value is more secure, but takes more time to complete.
 *
 * @return
 *   A 12 character string containing the iteration count and a random salt.
 */
function _password_generate_salt($count_log2) {
  $output = '$S$';
  // Ensure that $count_log2 is within set bounds.
  $count_log2 = _password_enforce_log2_boundaries($count_log2);
  // We encode the final log2 iteration count in base 64.
  $itoa64 = _password_itoa64();
  $output .= $itoa64[$count_log2];
  // 6 bytes is the standard salt for a portable phpass hash.
  $output .= _password_base64_encode(drupal_random_bytes(6), 6);
  return $output;
}

/**
 * Ensures that $count_log2 is within set bounds.
 *
 * @param $count_log2
 *   Integer that determines the number of iterations used in the hashing
 *   process. A larger value is more secure, but takes more time to complete.
 *
 * @return
 *   Integer within set bounds that is closest to $count_log2.
 */
function _password_enforce_log2_boundaries($count_log2) {
  if ($count_log2 < DRUPAL_MIN_HASH_COUNT) {
    return DRUPAL_MIN_HASH_COUNT;
  }
  elseif ($count_log2 > DRUPAL_MAX_HASH_COUNT) {
    return DRUPAL_MAX_HASH_COUNT;
  }

  return (int) $count_log2;
}

/**
 * Hash a password using a secure stretched hash.
 *
 * By using a salt and repeated hashing the password is "stretched". Its
 * security is increased because it becomes much more computationally costly
 * for an attacker to try to break the hash by brute-force computation of the
 * hashes of a large number of plain-text words or strings to find a match.
 *
 * @param $algo
 *   The string name of a hashing algorithm usable by hash(), like 'sha256'.
 * @param $password
 *   The plain-text password to hash.
 * @param $setting
 *   An existing hash or the output of _password_generate_salt().  Must be
 *   at least 12 characters (the settings and salt).
 *
 * @return
 *   A string containing the hashed password (and salt) or FALSE on failure.
 *   The return string will be truncated at DRUPAL_HASH_LENGTH characters max.
 */
function _password_crypt($password) {
  //echo "<br/>DEBUG mypassword.inc _password_crypt::password = '" . $password . "'";
  return md5($password);
}

/**
 * Parse the log2 iteration count from a stored hash or setting string.
 */
function _password_get_count_log2($setting) {
  $itoa64 = _password_itoa64();
  return strpos($itoa64, $setting[3]);
}

/**
 * Hash a password using a secure hash.
 *
 * @param $password
 *   A plain-text password.
 * @param $count_log2
 *   Optional integer to specify the iteration count. Generally used only during
 *   mass operations where a value less than the default is needed for speed.
 *
 * @return
 *   A string containing the hashed password (and a salt), or FALSE on failure.
 */
function user_hash_password($password, $count_log2 = 0) {
  return _password_crypt($password);
}

/**
 * Check whether a plain text password matches a stored hashed password.
 *
 * Alternative implementations of this function may use other data in the
 * $account object, for example the uid to look up the hash in a custom table
 * or remote database.
 *
 * @param $password
 *   A plain-text password
 * @param $account
 *   A user object with at least the fields from the {users} table.
 *
 * @return
 *   TRUE or FALSE.
 */
function user_check_password($password, $account) {
  
  $stored_hash = $account->pass;
  $hash = _password_crypt($password);

  //echo "<br/>DEBUG mypassword.inc user_check_password::stored_hash = '" . $stored_hash . "'";
  //echo "<br/>DEBUG mypassword.inc user_check_password::hash = '" . $hash . "'";
  return ($hash && $stored_hash == $hash);
}

/**
 * Check whether a user's hashed password needs to be replaced with a new hash.
 *
 * This is typically called during the login process when the plain text
 * password is available. A new hash is needed when the desired iteration count
 * has changed through a change in the variable password_count_log2 or
 * DRUPAL_HASH_COUNT or if the user's password hash was generated in an update
 * like user_update_7000().
 *
 * Alternative implementations of this function might use other criteria based
 * on the fields in $account.
 *
 * @param $account
 *   A user object with at least the fields from the {users} table.
 *
 * @return
 *   TRUE or FALSE.
 */
function user_needs_new_hash($account) {
  // Check whether this was an updated password.
  if ((strlen($account->pass) != DRUPAL_HASH_LENGTH)) {
    return TRUE;
  }

  return FALSE;
  // Ensure that $count_log2 is within set bounds.
  //$count_log2 = _password_enforce_log2_boundaries(variable_get('password_count_log2', DRUPAL_HASH_COUNT));
  // Check whether the iteration count used differs from the standard number.
  //return (_password_get_count_log2($account->pass) !== $count_log2);
}


==============END OF FILE

mdusamaansari’s picture

The function user_check_password() does not return any value. The variable $hash and $stored_hash values are echoed. Here is my function where I made few changes.

function user_check_password($password, $account) {
	$pselect = "SELECT * FROM users WHERE uid='".$account."'";
	$psquery = mysql_query($pselect);
	$presult = mysql_fetch_array($psquery);
	$saved_p = $presult['pass'];
        $stored_hash = $saved_p;
	$hash = _password_crypt('sha512', $password, $stored_hash);

  echo "<br/>H Hash:".$hash;
  echo "<br/>S Hash:".$stored_hash;
  return ($hash && $stored_hash == $hash);
}

Please help me.