From 461a770a6fa5b43107d204c1230721751875236b Mon Sep 17 00:00:00 2001 From: Tim Starling Date: Thu, 5 Jun 2008 12:58:02 +0000 Subject: [PATCH] * Changed password hash format, see wikitech-l * Made the PasswordReset and Maintenance extensions, and maintenance/changePassword.php work with CentralAuth, by calling User::setPassword() instead of updating the database directly. They work now even if you use an object cache. * Don't automatically log in as the user in question when CentralAuthUser::setPassword() is called, it's kind of uncool when an administrator is setting the password of another user. * Fix bug 14330 by setting the local passwords on demerge --- includes/GlobalFunctions.php | 17 ------- includes/User.php | 84 ++++++++++++++++++++++++++-------- maintenance/changePassword.php | 11 +---- maintenance/updaters.inc | 24 ++++++++++ 4 files changed, 90 insertions(+), 46 deletions(-) diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 5828200188..cf6a562ae5 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -1794,23 +1794,6 @@ function wfPercent( $nr, $acc = 2, $round = true ) { return $round ? round( $ret, $acc ) . '%' : "$ret%"; } -/** - * Encrypt a username/password. - * - * @param string $userid ID of the user - * @param string $password Password of the user - * @return string Hashed password - */ -function wfEncryptPassword( $userid, $password ) { - global $wgPasswordSalt; - $p = md5( $password); - - if($wgPasswordSalt) - return md5( "{$userid}-{$p}" ); - else - return $p; -} - /** * Appends to second array if $value differs from that in $default */ diff --git a/includes/User.php b/includes/User.php index bd1c9d90a3..a3a7144651 100644 --- a/includes/User.php +++ b/includes/User.php @@ -1515,18 +1515,6 @@ class User { return ($timestamp >= $this->mTouched); } - /** - * Encrypt a password. - * It can eventually salt a password. - * @see User::addSalt() - * @param string $p clear Password. - * @return string Encrypted password. - */ - function encryptPassword( $p ) { - $this->load(); - return wfEncryptPassword( $this->mId, $p ); - } - /** * Set the password and reset the random token * Calls through to authentication plugin if necessary; @@ -1579,7 +1567,7 @@ class User { // Save an invalid hash... $this->mPassword = ''; } else { - $this->mPassword = $this->encryptPassword( $str ); + $this->mPassword = self::crypt( $str ); } $this->mNewpassword = ''; $this->mNewpassTime = null; @@ -1623,7 +1611,7 @@ class User { */ function setNewpassword( $str, $throttle = true ) { $this->load(); - $this->mNewpassword = $this->encryptPassword( $str ); + $this->mNewpassword = self::crypt( $str ); if ( $throttle ) { $this->mNewpassTime = wfTimestampNow(); } @@ -2505,14 +2493,13 @@ class User { /* Auth plugin doesn't allow local authentication for this user name */ return false; } - $ep = $this->encryptPassword( $password ); - if ( 0 == strcmp( $ep, $this->mPassword ) ) { + if ( self::comparePasswords( $this->mPassword, $password, $this->mId ) ) { return true; } elseif ( function_exists( 'iconv' ) ) { # Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted # Check for this with iconv - $cp1252hash = $this->encryptPassword( iconv( 'UTF-8', 'WINDOWS-1252//TRANSLIT', $password ) ); - if ( 0 == strcmp( $cp1252hash, $this->mPassword ) ) { + $cp1252Password = iconv( 'UTF-8', 'WINDOWS-1252//TRANSLIT', $password ); + if ( self::comparePasswords( $this->mPassword, $cp1252Password, $this->mId ) ) { return true; } } @@ -2525,8 +2512,7 @@ class User { * @return bool */ function checkTemporaryPassword( $plaintext ) { - $hash = $this->encryptPassword( $plaintext ); - return $hash === $this->mNewpassword; + return self::comparePasswords( $this->mNewpassword, $plaintext, $this->getId() ); } /** @@ -3001,4 +2987,62 @@ class User { ? $right : $name; } + + /** + * Make an old-style password hash + * + * @param string $password Plain-text password + * @param string $userId User ID + */ + static function oldCrypt( $password, $userId ) { + global $wgPasswordSalt; + if ( $wgPasswordSalt ) { + return md5( $userId . '-' . md5( $password ) ); + } else { + return md5( $password ); + } + } + + /** + * Make a new-style password hash + * + * @param string $password Plain-text password + * @param string $salt Salt, may be random or the user ID. False to generate a salt. + */ + static function crypt( $password, $salt = false ) { + global $wgPasswordSalt; + + if($wgPasswordSalt) { + if ( $salt === false ) { + $salt = substr( wfGenerateToken(), 0, 8 ); + } + return ':B:' . $salt . ':' . md5( $salt . '-' . md5( $password ) ); + } else { + return ':A:' . md5( $password); + } + } + + /** + * Compare a password hash with a plain-text password. Requires the user + * ID if there's a chance that the hash is an old-style hash. + * + * @param string $hash Password hash + * @param string $password Plain-text password to compare + * @param string $userId User ID for old-style password salt + */ + static function comparePasswords( $hash, $password, $userId = false ) { + $m = false; + $type = substr( $hash, 0, 3 ); + if ( $type == ':A:' ) { + # Unsalted + return md5( $password ) === substr( $hash, 3 ); + } elseif ( $type == ':B:' ) { + # Salted + list( $salt, $realHash ) = explode( ':', substr( $hash, 3 ), 2 ); + return md5( $salt.'-'.md5( $password ) ) == $realHash; + } else { + # Old-style + return self::oldCrypt( $password, $userId ) === $hash; + } + } } diff --git a/maintenance/changePassword.php b/maintenance/changePassword.php index a2af7b4d6c..d43703768b 100644 --- a/maintenance/changePassword.php +++ b/maintenance/changePassword.php @@ -52,14 +52,7 @@ class ChangePassword { function main() { $fname = 'ChangePassword::main'; - $this->dbw->update( 'user', - array( - 'user_password' => wfEncryptPassword( $this->user->getId(), $this->password ) - ), - array( - 'user_id' => $this->user->getId() - ), - $fname - ); + $this->user->setPassword( $this->password ); + $this->user->saveSettings(); } } diff --git a/maintenance/updaters.inc b/maintenance/updaters.inc index 773fa9f421..7ffbe03ebc 100644 --- a/maintenance/updaters.inc +++ b/maintenance/updaters.inc @@ -142,6 +142,7 @@ $wgMysqlUpdates = array( array( 'check_bin', 'protected_titles', 'pt_title', 'patch-pt_title-encoding.sql', ), array( 'maybe_do_profiling_memory_update' ), array( 'do_filearchive_indices_update' ), + array( 'update_password_format' ), ); @@ -1195,6 +1196,28 @@ function do_populate_parent_id() { populate_rev_parent_id( $wgDatabase ); } +function update_password_format() { + if ( update_row_exists( 'password format' ) ) { + echo "...password hash format already changed\n"; + return; + } + + echo "Updating password hash format..."; + + global $wgDatabase, $wgPasswordSalt; + if ( $wgPasswordSalt ) { + $sql = "UPDATE user SET user_password=CONCAT(':B:', user_id, ':', user_password) " . + "WHERE user_password NOT LIKE ':%'"; + } else { + $sql = "UPDATE user SET user_password=CONCAT(':A:', user_password) " . + "WHERE user_password NOT LIKE ':%'"; + } + $wgDatabase->query( $sql, __METHOD__ ); + $wgDatabase->insert( 'updatelog', array( 'ul_key' => 'password format' ), __METHOD__ ); + + echo "done\n"; +} + function pg_describe_table($table) { @@ -1690,3 +1713,4 @@ function do_postgres_updates() { return; } + -- 2.20.1