Index: includes/User.php =================================================================== --- includes/User.php (revision 104297) +++ includes/User.php (working copy) @@ -3131,7 +3131,7 @@ * @return Boolean: True if the given password is correct, otherwise False. */ public function checkPassword( $password ) { - global $wgAuth, $wgLegacyEncoding; + global $wgAuth, $wgLegacyEncoding, $wgPasswordUpdate; $this->load(); // Even though we stop people from creating passwords that @@ -3153,6 +3153,11 @@ return false; } if ( self::comparePasswords( $this->mPassword, $password, $this->mId ) ) { + if( substr( $this->mPassword, 0, 3 ) != ':C:' && $wgPasswordUpdate ) { + /* Old password hash. Update it. */ + $this->setPassword( $password ); + $this->saveSettings(); + } return true; } elseif ( $wgLegacyEncoding ) { # Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted @@ -3898,13 +3903,13 @@ } /** - * Make an old-style password hash + * Make an really-old-style password hash * * @param $password String Plain-text password * @param $userId String User ID * @return String Password hash */ - public static function oldCrypt( $password, $userId ) { + public static function reallyOldCrypt( $password, $userId ) { global $wgPasswordSalt; if ( $wgPasswordSalt ) { return md5( $userId . '-' . md5( $password ) ); @@ -3914,7 +3919,7 @@ } /** - * Make a new-style password hash + * Make a old-style password hash * * @param $password String Plain-text password * @param bool|string $salt Optional salt, may be random or the user ID. @@ -3922,7 +3927,7 @@ * If unspecified or false, will generate one automatically * @return String Password hash */ - public static function crypt( $password, $salt = false ) { + public static function oldCrypt( $password, $salt = false ) { global $wgPasswordSalt; $hash = ''; @@ -3940,6 +3945,47 @@ } } + /** + * Make a new-style password hash + * + * @param $password String Plain-text password + * @param bool|string $salt Optional salt, may be random or the user ID. + + * If unspecified or false, will generate one automatically + * @return String Password hash + */ + public static function crypt( $password, $salt = false ) { + global $wgPasswordGlobalSalt, $wgPasswordLength, $wgPasswordIterations; + // return self::oldCrypt( $password, $salt = false ); + + $hash = ''; + if( !wfRunHooks( 'UserCryptPassword', array( &$password, &$salt, &$wgPasswordSalt, &$hash ) ) ) { + return $hash; + } + + if( $salt === false ) { + $salt = substr( wfGenerateToken(), 0, 8 ); + } + + $realSalt = $salt; + + if( $wgPasswordGlobalSalt !== false ) { + $realSalt .= $wgPasswordGlobalSalt; + } + + $blocks = ceil( $wgPasswordLength / 64 ); + + for( $block = 1; $block <= $blocks; $block++ ) { + $block_total = $block_step = hash_hmac( 'sha512', $realSalt . pack( 'N', $block ), $password, true ); + for( $i = 1; $i < $wgPasswordIterations; $i++ ) { + $block_total ^= ( $block_step = hash_hmac( 'sha512', $block_step, $password, true ) ); + } + $hash .= $block_total; + } + + return ':C:' . $salt . ':' . substr( base64_encode( $hash ), 0, $wgPasswordLength ); + } + /** * 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. @@ -3965,9 +4011,13 @@ # Salted list( $salt, $realHash ) = explode( ':', substr( $hash, 3 ), 2 ); return md5( $salt.'-'.md5( $password ) ) == $realHash; - } else { + } elseif ( $type == ':C:' ) { + # New-style + list( $salt, $realHash ) = explode( ':', substr( $hash, 3 ), 2 ); + return self::crypt( $password, $salt ) == $hash; + } else { # Old-style - return self::oldCrypt( $password, $userId ) === $hash; + return self::reallyOldCrypt( $password, $userId ) === $hash; } } Index: includes/DefaultSettings.php =================================================================== --- includes/DefaultSettings.php (revision 104297) +++ includes/DefaultSettings.php (working copy) @@ -3096,6 +3096,31 @@ $wgPasswordSalt = true; /** + * Specifies a global salt to be added to the salt of every user password. + * IF CHANGED AFTER INSTALLATION ALL PASSWORDS WILL BECOME INVALID! + */ +$wgPasswordGlobalSalt = false; + +/** + * Specifies the length in bytes every password should be stretched to. The + * default is good for most installations. + * + * DO NOT USE VALUES OVER 243 AS IT WILL NOT FIT IN DATABASE! + */ +$wgPasswordLength = 200; + +/** + * Specifies the number of hash iterations to apply to the password. RFC 2898 + * recommends at least 1000. + */ +$wgPasswordIterations = 10000; + +/** + * Specifies whether to automatically update passwords to new hashing methods. + */ +$wgPasswordUpdate = true; + +/** * Specifies the minimal length of a user password. If set to 0, empty pass- * words are allowed. */