summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/DefaultSettings.php4
-rw-r--r--includes/Sanitizer.php43
-rw-r--r--includes/api/ApiQueryAllUsers.php6
-rw-r--r--includes/specials/SpecialUserlogin.php91
-rw-r--r--includes/templates/Userlogin.php1
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>