* Changed password hash format, see wikitech-l
authorTim Starling <tstarling@users.mediawiki.org>
Thu, 5 Jun 2008 12:58:02 +0000 (12:58 +0000)
committerTim Starling <tstarling@users.mediawiki.org>
Thu, 5 Jun 2008 12:58:02 +0000 (12:58 +0000)
* 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
includes/User.php
maintenance/changePassword.php
maintenance/updaters.inc

index 5828200..cf6a562 100644 (file)
@@ -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
  */
index bd1c9d9..a3a7144 100644 (file)
@@ -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;
+               }
+       }
 }
index a2af7b4..d437037 100644 (file)
@@ -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();
        }
 }
index 773fa9f..7ffbe03 100644 (file)
@@ -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;
 }
+