diff options
Diffstat (limited to 'includes')
-rw-r--r-- | includes/DefaultSettings.php | 4 | ||||
-rw-r--r-- | includes/Sanitizer.php | 43 | ||||
-rw-r--r-- | includes/api/ApiQueryAllUsers.php | 6 | ||||
-rw-r--r-- | includes/specials/SpecialUserlogin.php | 91 | ||||
-rw-r--r-- | includes/templates/Userlogin.php | 1 |
5 files changed, 111 insertions, 34 deletions
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index fab77806..4efa682f 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -33,7 +33,7 @@ if ( !defined( 'MW_PHP4' ) ) { } /** MediaWiki version number */ -$wgVersion = '1.15.3'; +$wgVersion = '1.15.4'; /** Name of the site. It must be changed in LocalSettings.php */ $wgSitename = 'MediaWiki'; @@ -2561,7 +2561,7 @@ $wgAutoloadClasses = array(); * $wgExtensionCredits[$type][] = array( * 'name' => 'Example extension', * 'version' => 1.9, - * 'svn-revision' => '$LastChangedRevision: 64681 $', + * 'svn-revision' => '$LastChangedRevision: 66992 $', * 'author' => 'Foo Barstein', * 'url' => 'http://wwww.example.com/Example%20Extension/', * 'description' => 'An example extension', diff --git a/includes/Sanitizer.php b/includes/Sanitizer.php index 0b70e002..678bfcfb 100644 --- a/includes/Sanitizer.php +++ b/includes/Sanitizer.php @@ -607,10 +607,6 @@ class Sanitizer { # http://msdn.microsoft.com/workshop/author/dhtml/overview/recalc.asp if( $attribute == 'style' ) { $value = Sanitizer::checkCss( $value ); - if( $value === false ) { - # haxx0r - continue; - } } if ( $attribute === 'id' ) { @@ -664,10 +660,8 @@ class Sanitizer { $value = StringUtils::delimiterReplace( '/*', '*/', ' ', $value ); // Decode escape sequences and line continuation - // See the grammar in the CSS 2 spec, appendix D, Mozilla implements it accurately. - // IE 8 doesn't implement it at all, but there's no way to introduce url() into - // IE that doesn't hit Mozilla also. - static $decodeRegex; + // See the grammar in the CSS 2 spec, appendix D. + static $decodeRegex, $reencodeTable; if ( !$decodeRegex ) { $space = '[\\x20\\t\\r\\n\\f]'; $nl = '(?:\\n|\\r\\n|\\r|\\f)'; @@ -676,29 +670,40 @@ class Sanitizer { (?: ($nl) | # 1. Line continuation ([0-9A-Fa-f]{1,6})$space? | # 2. character number - (.) # 3. backslash cancelling special meaning + (.) | # 3. backslash cancelling special meaning + () | # 4. backslash at end of string )/xu"; } - $decoded = preg_replace_callback( $decodeRegex, + $value = preg_replace_callback( $decodeRegex, array( __CLASS__, 'cssDecodeCallback' ), $value ); - if ( preg_match( '!expression|https?://|url\s*\(!i', $decoded ) ) { - // Not allowed - return false; - } else { - // Allowed, return CSS with comments stripped - return $value; + + // Reject problematic keywords and control characters + if ( preg_match( '/[\000-\010\016-\037\177]/', $value ) ) { + return '/* invalid control char */'; + } elseif ( preg_match( '! expression | filter\s*: | accelerator\s*: | url\s*\( !ix', $value ) ) { + return '/* insecure input */'; } + return $value; } static function cssDecodeCallback( $matches ) { if ( $matches[1] !== '' ) { + // Line continuation return ''; } elseif ( $matches[2] !== '' ) { - return codepointToUtf8( hexdec( $matches[2] ) ); + $char = codepointToUtf8( hexdec( $matches[2] ) ); } elseif ( $matches[3] !== '' ) { - return $matches[3]; + $char = $matches[3]; + } else { + $char = '\\'; + } + if ( $char == "\n" || $char == '"' || $char == "'" || $char == '\\' ) { + // These characters need to be escaped in strings + // Clean up the escape sequence to avoid parsing errors by clients + return '\\' . dechex( ord( $char ) ) . ' '; } else { - throw new MWException( __METHOD__.': invalid match' ); + // Decode unnecessary escape + return $char; } } diff --git a/includes/api/ApiQueryAllUsers.php b/includes/api/ApiQueryAllUsers.php index 5f9ff064..76d97abd 100644 --- a/includes/api/ApiQueryAllUsers.php +++ b/includes/api/ApiQueryAllUsers.php @@ -71,7 +71,7 @@ class ApiQueryAllUsers extends ApiQueryBase { } if ($params['witheditsonly']) - $this->addWhere('user_editcount > 0'); + $this->addWhere('u1.user_editcount > 0'); if ($fld_groups) { // Show the groups the given users belong to @@ -232,6 +232,6 @@ class ApiQueryAllUsers extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryAllUsers.php 46845 2009-02-05 14:30:59Z catrope $'; + return __CLASS__ . ': $Id: ApiQueryAllUsers.php 66948 2010-05-27 07:47:39Z tstarling $'; } -}
\ No newline at end of file +} diff --git a/includes/specials/SpecialUserlogin.php b/includes/specials/SpecialUserlogin.php index 0f5f69b7..8616ae28 100644 --- a/includes/specials/SpecialUserlogin.php +++ b/includes/specials/SpecialUserlogin.php @@ -69,7 +69,7 @@ class LoginForm { $this->mRemember = $request->getCheck( 'wpRemember' ); $this->mLanguage = $request->getText( 'uselang' ); $this->mSkipCookieCheck = $request->getCheck( 'wpSkipCookieCheck' ); - $this->mToken = $request->getVal( 'wpLoginToken' ); + $this->mToken = ($this->mType == 'signup' ) ? $request->getVal( 'wpCreateaccountToken' ) : $request->getVal( 'wpLoginToken' ); if ( $wgRedirectOnLogin ) { $this->mReturnTo = $wgRedirectOnLogin; @@ -246,6 +246,25 @@ class LoginForm { return false; } + # Request forgery checks. + if ( !self::getCreateaccountToken() ) { + self::setCreateaccountToken(); + $this->mainLoginForm( wfMsg( 'sessionfailure' ) ); + return false; + } + + # The user didn't pass a createaccount token + if ( !$this->mToken ) { + $this->mainLoginForm( wfMsg( 'sessionfailure' ) ); + return false; + } + + # Validate the createaccount token + if ( $this->mToken !== self::getCreateaccountToken() ) { + $this->mainLoginForm( wfMsg( 'sessionfailure' ) ); + return false; + } + # Check permissions if ( !$wgUser->isAllowed( 'createaccount' ) ) { $this->userNotPrivilegedMessage(); @@ -260,7 +279,7 @@ class LoginForm { $wgUser->inSorbsBlacklist( $ip ) ) { $this->mainLoginForm( wfMsg( 'sorbs_create_account_reason' ) . ' (' . htmlspecialchars( $ip ) . ')' ); - return; + return false; } # Now create a dummy user ($u) and check if it is valid @@ -336,6 +355,7 @@ class LoginForm { return false; } + self::clearCreateaccountToken(); return $this->initUser( $u, false ); } @@ -638,13 +658,26 @@ class LoginForm { return; } - # Check against blocked IPs - # fixme -- should we not? + # Check against blocked IPs so blocked users can't flood admins + # with password resets if( $wgUser->isBlocked() ) { $this->mainLoginForm( wfMsg( 'blocked-mailpassword' ) ); return; } + # If the user doesn't have a login token yet, set one. + if ( !self::getLoginToken() ) { + self::setLoginToken(); + $this->mainLoginForm( wfMsg( 'sessionfailure' ) ); + return; + } + + # If the user didn't pass a login token, tell them we need one + if ( !$this->mToken ) { + $this->mainLoginForm( wfMsg( 'sessionfailure' ) ); + return; + } + # Check against the rate limiter if( $wgUser->pingLimiter( 'mailpassword' ) ) { $wgOut->rateLimited(); @@ -665,6 +698,12 @@ class LoginForm { return; } + # Validate the login token + if ( $this->mToken !== self::getLoginToken() ) { + $this->mainLoginForm( wfMsg( 'sessionfailure' ) ); + return; + } + # Check against password throttle if ( $u->isPasswordReminderThrottled() ) { global $wgPasswordReminderResendTime; @@ -680,6 +719,7 @@ class LoginForm { $this->mainLoginForm( wfMsg( 'mailerror', $result->getMessage() ) ); } else { $this->mainLoginForm( wfMsg( 'passwordsent', $u->getName() ), 'success' ); + self::clearLoginToken(); } } @@ -911,11 +951,18 @@ class LoginForm { $template->set( 'canremember', ( $wgCookieExpiration > 0 ) ); $template->set( 'remember', $wgUser->getOption( 'rememberpassword' ) or $this->mRemember ); - if ( !self::getLoginToken() ) { - self::setLoginToken(); + if ( $this->mType == 'signup' ) { + if ( !self::getCreateaccountToken() ) { + self::setCreateaccountToken(); + } + $template->set( 'token', self::getCreateaccountToken() ); + } else { + if ( !self::getLoginToken() ) { + self::setLoginToken(); + } + $template->set( 'token', self::getLoginToken() ); } - $template->set( 'token', self::getLoginToken() ); - + # Prepare language selection links as needed if( $wgLoginLanguageSelector ) { $template->set( 'languages', $this->makeLanguageSelector() ); @@ -974,7 +1021,7 @@ class LoginForm { } /** - * Generate a new login token and attach it to the current session + * Randomly generate a new login token and attach it to the current session */ public static function setLoginToken() { global $wgRequest; @@ -986,12 +1033,36 @@ class LoginForm { /** * Remove any login token attached to the current session */ - public static function clearLoginToken() { + public static function clearLoginToken() { global $wgRequest; $wgRequest->setSessionData( 'wsLoginToken', null ); } /** + * Get the createaccount token from the current session + */ + public static function getCreateaccountToken() { + global $wgRequest; + return $wgRequest->getSessionData( 'wsCreateaccountToken' ); + } + + /** + * Randomly generate a new createaccount token and attach it to the current session + */ + public static function setCreateaccountToken() { + global $wgRequest; + $wgRequest->setSessionData( 'wsCreateaccountToken', User::generateToken() ); + } + + /** + * Remove any createaccount token attached to the current session + */ + public static function clearCreateaccountToken() { + global $wgRequest; + $wgRequest->setSessionData( 'wsCreateaccountToken', null ); + } + + /** * @private */ function cookieRedirectCheck( $type ) { diff --git a/includes/templates/Userlogin.php b/includes/templates/Userlogin.php index 432d3003..2ca9c3c4 100644 --- a/includes/templates/Userlogin.php +++ b/includes/templates/Userlogin.php @@ -268,6 +268,7 @@ class UsercreateTemplate extends QuickTemplate { </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="wpCreateaccountToken" value="<?php $this->text( 'token' ); ?>" /><?php } ?> </form> </div> <div id="signupend"><?php $this->msgWiki( 'signupend' ); ?></div> |