Index: trunk/phase3/includes/SpecialUpload.php |
— | — | @@ -156,14 +156,18 @@ |
157 | 157 | # Chop off any directories in the given filename |
158 | 158 | $basename = basename( $this->mOname ); |
159 | 159 | |
160 | | - if( preg_match( '/^(.*)\.([^.]*)$/', $basename, $matches ) ) { |
161 | | - $partname = $matches[1]; |
162 | | - $ext = $matches[2]; |
| 160 | + /** |
| 161 | + * We'll want to blacklist against *any* 'extension', and use |
| 162 | + * only the final one for the whitelist. |
| 163 | + */ |
| 164 | + list( $partname, $ext ) = $this->splitExtensions( $basename ); |
| 165 | + if( count( $ext ) ) { |
| 166 | + $finalExt = $ext[count( $ext ) - 1]; |
163 | 167 | } else { |
164 | | - $partname = $basename; |
165 | | - $ext = ''; |
| 168 | + $finalExt = ''; |
166 | 169 | } |
167 | | - |
| 170 | + $fullExt = implode( '.', $ext ); |
| 171 | + |
168 | 172 | if ( strlen( $partname ) < 3 ) { |
169 | 173 | $this->mainUploadForm( wfMsg( 'minlength' ) ); |
170 | 174 | return; |
— | — | @@ -192,9 +196,10 @@ |
193 | 197 | /* Don't allow users to override the blacklist */ |
194 | 198 | global $wgStrictFileExtensions; |
195 | 199 | global $wgFileExtensions, $wgFileBlacklist; |
196 | | - if( $this->checkFileExtension( $ext, $wgFileBlacklist ) || |
197 | | - ($wgStrictFileExtensions && !$this->checkFileExtension( $ext, $wgFileExtensions ) ) ) { |
198 | | - return $this->uploadError( wfMsg( 'badfiletype', htmlspecialchars( $ext ) ) ); |
| 200 | + if( $this->checkFileExtensionList( $ext, $wgFileBlacklist ) || |
| 201 | + ($wgStrictFileExtensions && |
| 202 | + !$this->checkFileExtension( $finalExt, $wgFileExtensions ) ) ) { |
| 203 | + return $this->uploadError( wfMsg( 'badfiletype', htmlspecialchars( $fullExt ) ) ); |
199 | 204 | } |
200 | 205 | |
201 | 206 | /** |
— | — | @@ -202,7 +207,7 @@ |
203 | 208 | * type but it's corrupt or data of the wrong type, we should |
204 | 209 | * probably not accept it. |
205 | 210 | */ |
206 | | - if( !$this->verify( $this->mUploadTempName, $ext ) ) { |
| 211 | + if( !$this->verify( $this->mUploadTempName, $finalExt ) ) { |
207 | 212 | return $this->uploadError( wfMsg( 'uploadcorrupt' ) ); |
208 | 213 | } |
209 | 214 | |
— | — | @@ -217,8 +222,8 @@ |
218 | 223 | |
219 | 224 | global $wgCheckFileExtensions; |
220 | 225 | if ( $wgCheckFileExtensions ) { |
221 | | - if ( ! $this->checkFileExtension( $ext, $wgFileExtensions ) ) { |
222 | | - $warning .= '<li>'.wfMsg( 'badfiletype', htmlspecialchars( $ext ) ).'</li>'; |
| 226 | + if ( ! $this->checkFileExtension( $finalExt, $wgFileExtensions ) ) { |
| 227 | + $warning .= '<li>'.wfMsg( 'badfiletype', htmlspecialchars( $fullExt ) ).'</li>'; |
223 | 228 | } |
224 | 229 | } |
225 | 230 | |
— | — | @@ -535,6 +540,20 @@ |
536 | 541 | /* -------------------------------------------------------------- */ |
537 | 542 | |
538 | 543 | /** |
| 544 | + * Split a file into a base name and all dot-delimited 'extensions' |
| 545 | + * on the end. Some web server configurations will fall back to |
| 546 | + * earlier pseudo-'extensions' to determine type and execute |
| 547 | + * scripts, so the blacklist needs to check them all. |
| 548 | + * |
| 549 | + * @return array |
| 550 | + */ |
| 551 | + function splitExtensions( $filename ) { |
| 552 | + $bits = explode( '.', $filename ); |
| 553 | + $basename = array_shift( $bits ); |
| 554 | + return array( $basename, $bits ); |
| 555 | + } |
| 556 | + |
| 557 | + /** |
539 | 558 | * Perform case-insensitive match against a list of file extensions. |
540 | 559 | * Returns true if the extension is in the list. |
541 | 560 | * |
— | — | @@ -547,6 +566,23 @@ |
548 | 567 | } |
549 | 568 | |
550 | 569 | /** |
| 570 | + * Perform case-insensitive match against a list of file extensions. |
| 571 | + * Returns true if any of the extensions are in the list. |
| 572 | + * |
| 573 | + * @param array $ext |
| 574 | + * @param array $list |
| 575 | + * @return bool |
| 576 | + */ |
| 577 | + function checkFileExtensionList( $ext, $list ) { |
| 578 | + foreach( $ext as $e ) { |
| 579 | + if( in_array( strtolower( $e ), $list ) ) { |
| 580 | + return true; |
| 581 | + } |
| 582 | + } |
| 583 | + return false; |
| 584 | + } |
| 585 | + |
| 586 | + /** |
551 | 587 | * Returns false if the file is of a known type but can't be recognized, |
552 | 588 | * indicating a corrupt file. |
553 | 589 | * Returns true otherwise; unknown file types are not checked if given |
Index: branches/REL1_4/phase3/RELEASE-NOTES |
— | — | @@ -4,8 +4,11 @@ |
5 | 5 | setting since version 1.2.0. If you have it on, turn it *off* if you can. |
6 | 6 | |
7 | 7 | |
8 | | -== MediaWiki 1.4 BETA 2 == |
| 8 | +== MediaWiki 1.4 BETA 3 == |
9 | 9 | |
| 10 | +Users of earlier betas, take note that there is a security fix for |
| 11 | +uploads in this release. |
| 12 | + |
10 | 13 | ''''' Thinking of using MySQL 4.1? Please read this first! ''''' |
11 | 14 | ''''' Your PHP installation probably uses the OLD protocol ''''' |
12 | 15 | ''''' http://dev.mysql.com/doc/mysql/en/Old_client.html ''''' |
— | — | @@ -133,6 +136,8 @@ |
134 | 137 | * Caching and load limiting options for Recentchanges RSS/Atom feed |
135 | 138 | * (bug 1074) Add stock icons for non-image files in gallery/Newimages |
136 | 139 | * Add width and height attributes on thumbs in gallery/Newimages |
| 140 | +* Enhance upload extension blacklist to protect against vulnerable |
| 141 | + Apache configurations |
137 | 142 | |
138 | 143 | === Caveats === |
139 | 144 | |