Package: libmediawiki-perl Version: 1.13-1 Severity: important Tags: lenny upstream
Hi! The wikipedia has released a security update of the 1.15 release branch of mediawiki that fixes an issue with the login code: http://lists.wikimedia.org/pipermail/mediawiki-announce/2010-April/000090.html We are planning to update the package in lenny with a backport of these changes. However, this change will break the libmediawiki-perl package so a coordinated upload should be necessary. The proposed patch against mediawiki 1.12.0-2lenny4 is attached to this report. Romain -- System Information: Debian Release: squeeze/sid APT prefers unstable APT policy: (500, 'unstable'), (1, 'experimental') Architecture: amd64 (x86_64) Kernel: Linux 2.6.30-2-amd64 (SMP w/2 CPU cores) Locale: LANG=fr_FR.UTF8, LC_CTYPE=fr_FR.UTF8 (charmap=UTF-8) Shell: /bin/sh linked to /bin/bash
Index: debian/changelog =================================================================== --- debian/changelog (révision 141) +++ debian/changelog (copie de travail) @@ -1,3 +1,15 @@ +mediawiki (1:1.12.0-2lenny5) stable-security; urgency=high + + * Security upload. Fixes the following issue: + "MediaWiki was found to be vulnerable to login CSRF. An attacker who + controls a user account on the target wiki can force the victim to log + in as the attacker, via a script on an external website. If the wiki is + configured to allow user scripts, say with "$wgAllowUserJs = true" in + LocalSettings.php, then the attacker can proceed to mount a + phishing-style attack against the victim to obtain their password. + + -- Romain Beauxis <to...@rastageeks.org> Fri, 16 Apr 2010 14:59:06 -0500 + mediawiki (1:1.12.0-2lenny4) stable-security; urgency=high * Security upload. Fixes two security issue: Index: debian/patches/1.15.3-security.patch =================================================================== --- debian/patches/1.15.3-security.patch (révision 0) +++ debian/patches/1.15.3-security.patch (révision 0) @@ -0,0 +1,264 @@ +Index: mediawiki-1.12.0/includes/api/ApiLogin.php +=================================================================== +--- mediawiki-1.12.0.orig/includes/api/ApiLogin.php 2010-04-16 16:55:24.000000000 -0500 ++++ mediawiki-1.12.0/includes/api/ApiLogin.php 2010-04-16 16:55:26.000000000 -0500 +@@ -88,6 +88,7 @@ + 'wpName' => $name, + 'wpPassword' => $password, + 'wpDomain' => $domain, ++ 'wpLoginToken' => $token, + 'wpRemember' => '' + )); + +@@ -111,6 +112,15 @@ + $result['cookieprefix'] = $wgCookiePrefix; + $result['sessionid'] = session_id(); + break; ++ ++ case LoginForm::NEED_TOKEN: ++ $result['result'] = 'NeedToken'; ++ $result['token'] = $loginForm->getLoginToken(); ++ break; ++ ++ case LoginForm::WRONG_TOKEN: ++ $result['result'] = 'WrongToken'; ++ break; + + case LoginForm :: NO_NAME : + $result['result'] = 'NoName'; +@@ -224,7 +234,8 @@ + return array ( + 'name' => null, + 'password' => null, +- 'domain' => null ++ 'domain' => null, ++ 'token' => null, + ); + } + +@@ -232,7 +243,8 @@ + return array ( + 'name' => 'User Name', + 'password' => 'Password', +- 'domain' => 'Domain (optional)' ++ 'domain' => 'Domain (optional)', ++ 'token' => 'Login token obtained in first request', + ); + } + +@@ -253,7 +265,7 @@ + } + + public function getVersion() { +- return __CLASS__ . ': $Id: ApiLogin.php 30222 2008-01-28 19:05:26Z catrope $'; ++ return __CLASS__ . ': $Id: ApiLogin.php 64680 2010-04-07 00:13:46Z tstarling $'; + } + } + +Index: mediawiki-1.12.0/includes/templates/Userlogin.php +=================================================================== +--- mediawiki-1.12.0.orig/includes/templates/Userlogin.php 2010-04-16 16:55:24.000000000 -0500 ++++ mediawiki-1.12.0/includes/templates/Userlogin.php 2010-04-16 16:55:26.000000000 -0500 +@@ -86,6 +86,7 @@ + </tr> + </table> + <?php if( @$this->haveData( 'uselang' ) ) { ?><input type="hidden" name="uselang" value="<?php $this->text( 'uselang' ); ?>" /><?php } ?> ++<?php if( @$this->haveData( 'token' ) ) { ?><input type="hidden" name="wpLoginToken" value="<?php $this->text( 'token' ); ?>" /><?php } ?> + </form> + </div> + <div id="loginend"><?php $this->msgWiki( 'loginend' ); ?></div> +Index: mediawiki-1.12.0/includes/User.php +=================================================================== +--- mediawiki-1.12.0.orig/includes/User.php 2010-04-16 16:55:24.000000000 -0500 ++++ mediawiki-1.12.0/includes/User.php 2010-04-16 16:55:26.000000000 -0500 +@@ -2312,7 +2312,7 @@ + return EDIT_TOKEN_SUFFIX; + } else { + if( !isset( $_SESSION['wsEditToken'] ) ) { +- $token = $this->generateToken(); ++ $token = self::generateToken(); + $_SESSION['wsEditToken'] = $token; + } else { + $token = $_SESSION['wsEditToken']; +@@ -2329,7 +2329,7 @@ + * Could be made more cryptographically sure if someone cares. + * @return string + */ +- function generateToken( $salt = '' ) { ++ public static function generateToken( $salt = '' ) { + $token = dechex( mt_rand() ) . dechex( mt_rand() ); + return md5( $token . $salt ); + } +@@ -2411,7 +2411,7 @@ + $expires = $now + 7 * 24 * 60 * 60; + $expiration = wfTimestamp( TS_MW, $expires ); + +- $token = $this->generateToken( $this->mId . $this->mEmail . $expires ); ++ $token = self::generateToken( $this->mId . $this->mEmail . $expires ); + $hash = md5( $token ); + + $dbw = wfGetDB( DB_MASTER ); +Index: mediawiki-1.12.0/includes/SpecialUserlogin.php +=================================================================== +--- mediawiki-1.12.0.orig/includes/SpecialUserlogin.php 2010-04-16 16:55:24.000000000 -0500 ++++ mediawiki-1.12.0/includes/SpecialUserlogin.php 2010-04-16 16:55:26.000000000 -0500 +@@ -32,10 +32,14 @@ + const EMPTY_PASS = 6; + const RESET_PASS = 7; + const ABORTED = 8; ++ const USER_BLOCKED = 11; ++ const NEED_TOKEN = 12; ++ const WRONG_TOKEN = 13; + + var $mName, $mPassword, $mRetype, $mReturnTo, $mCookieCheck, $mPosted; + var $mAction, $mCreateaccount, $mCreateaccountMail, $mMailmypassword; + var $mLoginattempt, $mRemember, $mEmail, $mDomain, $mLanguage; ++ var $mToken; + + /** + * Constructor +@@ -62,6 +66,7 @@ + $this->mAction = $request->getVal( 'action' ); + $this->mRemember = $request->getCheck( 'wpRemember' ); + $this->mLanguage = $request->getText( 'uselang' ); ++ $this->mToken = $request->getVal( 'wpLoginToken' ); + + if( $wgEnableEmail ) { + $this->mEmail = $request->getText( 'wpEmail' ); +@@ -368,6 +373,22 @@ + if ( '' == $this->mName ) { + return self::NO_NAME; + } ++ ++ // We require a login token to prevent login CSRF ++ // Handle part of this before incrementing the throttle so ++ // token-less login attempts don't count towards the throttle ++ // but wrong-token attempts do. ++ ++ // If the user doesn't have a login token yet, set one. ++ if ( !self::getLoginToken() ) { ++ self::setLoginToken(); ++ return self::NEED_TOKEN; ++ } ++ // If the user didn't pass a login token, tell them we need one ++ if ( !$this->mToken ) { ++ return self::NEED_TOKEN; ++ } ++ + $u = User::newFromName( $this->mName ); + if( is_null( $u ) || !User::isUsableName( $u->getName() ) ) { + return self::ILLEGAL; +@@ -391,6 +412,11 @@ + } else { + $u->load(); + } ++ ++ // Validate the login token ++ if ( $this->mToken !== self::getLoginToken() ) { ++ return self::WRONG_TOKEN; ++ } + + // Give general extensions, such as a captcha, a chance to abort logins + $abort = self::ABORTED; +@@ -455,6 +481,7 @@ + $wgUser->invalidateCache(); + } + $wgUser->setCookies(); ++ self::clearLoginToken(); + + if( $this->hasSessionCookie() ) { + return $this->successfulLogin( wfMsg( 'loginsuccess', $wgUser->getName() ) ); +@@ -462,7 +489,11 @@ + return $this->cookieRedirectCheck( 'login' ); + } + break; +- ++ ++ case self::NEED_TOKEN: ++ case self::WRONG_TOKEN: ++ $this->mainLoginForm( wfMsg( 'sessionfailure' ) ); ++ break; + case self::NO_NAME: + case self::ILLEGAL: + $this->mainLoginForm( wfMsg( 'noname' ) ); +@@ -726,6 +757,11 @@ + $template->set( 'canreset', $wgAuth->allowPasswordChange() ); + $template->set( 'remember', $wgUser->getOption( 'rememberpassword' ) or $this->mRemember ); + ++ if ( !self::getLoginToken() ) { ++ self::setLoginToken(); ++ } ++ $template->set( 'token', self::getLoginToken() ); ++ + # Prepare language selection links as needed + if( $wgLoginLanguageSelector ) { + $template->set( 'languages', $this->makeLanguageSelector() ); +@@ -774,6 +810,32 @@ + global $wgDisableCookieCheck, $wgRequest; + return $wgDisableCookieCheck ? true : $wgRequest->checkSessionCookie(); + } ++ ++ /** ++ * Get the login token from the current session ++ */ ++ public static function getLoginToken() { ++ global $wgRequest; ++ return $wgRequest->getSessionData( 'wsLoginToken' ); ++ } ++ ++ /** ++ * Generate a new login token and attach it to the current session ++ */ ++ public static function setLoginToken() { ++ global $wgRequest; ++ // Use User::generateToken() instead of $user->editToken() ++ // because the latter reuses $_SESSION['wsEditToken'] ++ $wgRequest->setSessionData( 'wsLoginToken', User::generateToken() ); ++ } ++ ++ /** ++ * Remove any login token attached to the current session ++ */ ++ public static function clearLoginToken() { ++ global $wgRequest; ++ $wgRequest->setSessionData( 'wsLoginToken', null ); ++ } + + /** + * @private +Index: mediawiki-1.12.0/maintenance/archives/patch-indexes.sql +=================================================================== +--- mediawiki-1.12.0.orig/maintenance/archives/patch-indexes.sql 2010-04-16 16:55:24.000000000 -0500 ++++ mediawiki-1.12.0/maintenance/archives/patch-indexes.sql 2010-04-16 16:55:26.000000000 -0500 +@@ -4,7 +4,7 @@ + -- Fix up table indexes; new to stable release in November 2003 + -- + +-ALTER TABLE IF EXISTS/*$wgDBprefix*/links ++ALTER TABLE IF EXISTS /*$wgDBprefix*/links + DROP INDEX l_from, + ADD INDEX l_from (l_from); + +Index: mediawiki-1.12.0/includes/WebRequest.php +=================================================================== +--- mediawiki-1.12.0.orig/includes/WebRequest.php 2010-04-16 16:57:07.000000000 -0500 ++++ mediawiki-1.12.0/includes/WebRequest.php 2010-04-16 16:58:42.000000000 -0500 +@@ -588,6 +588,18 @@ + } + return $this->_response; + } ++ ++ /* ++ * Get data from $_SESSION ++ */ ++ function getSessionData( $key ) { ++ if( !isset( $_SESSION[$key] ) ) ++ return null; ++ return $_SESSION[$key]; ++ } ++ function setSessionData( $key, $data ) { ++ $_SESSION[$key] = $data; ++ } + + } + Index: debian/patches/series =================================================================== --- debian/patches/series (révision 141) +++ debian/patches/series (copie de travail) @@ -7,3 +7,4 @@ 1.12.4-security.patch CSS-no-CVE_rev-63429.patch DataLeakage-no-CVE_rev-63436.patch +1.15.3-security.patch