Hilko Bengen wrote: > Micah Anderson <[EMAIL PROTECTED]> writes: > > > The Drupal package is vulnerable to the following to CVE advisories: > > http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2005-3973 > > http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2005-3975 > > > > Do you intend to have these fixed in Sarge? > > I did fix them and contacted the Security Team on Tue, 29 Nov 2005 > 16:48:27 +0100. However, I'm not aware of having received an answer.
I don't even have your mail. *sigh* Must have fallen through the cracks. > The packages I intended to upload to stable-security can be found at > <http://www.hilluzination.de/stuff/debian/>. Thanks a lot! I've reviewed them, well, I tried so, but due to the massive changes it is not exactly possible. However, I have detected a lot of unrelated changes to some template files, which I have stripped off of the source package. They are not suited for a security update. I'll build binary packages from the remaining source. I'm attaching the interdiff against the last version of Drupal. Here's the proposed advisory body: Package : drupal Vulnerability : several Problem type : remote Debian-specific: no CVE IDs : CVE-2005-3973 CVE-2005-3974CVE-2005-3975 CERT advisory : BugTraq IDs : 15674 15677 15663 Debian Bug : Several security related problems have been discovered in drupal, a fully-featured content management/discussion engine. The Common Vulnerabilities and Exposures project identifies the following vulnerabilities: CVE-2005-3973 Several cross-site scripting vulnerabilities allow remote attackers to inject arbitrary web script or HTML. CVE-2005-3974 When running on PHP5, Drupal does not correctly enforce user privileges, which allows remote attackers to bypass the "access user profiles" permission. CVE-2005-3975 An interpretation conflict allows remote authenticated users to inject arbitrary web script or HTML via HTML in a file with a GIF or JPEG file extension. The old stable distribution (woody) does not contain drupal packages. For the stable distribution (sarge) these problems have been fixed in version 4.5.3-5. For the unstable distribution (sid) these problems have been fixed in version XXXXXXXXXXXXXXXXXXXXXXXXXX We recommend that you upgrade your drupal package. Regards, Joey -- Have you ever noticed that "General Public Licence" contains the word "Pub"? Please always Cc to me when replying to me on the lists.
diff -u drupal-4.5.3/debian/changelog drupal-4.5.3/debian/changelog
--- drupal-4.5.3/debian/changelog
+++ drupal-4.5.3/debian/changelog
@@ -1,3 +1,15 @@
+drupal (4.5.3-5) stable-security; urgency=HIGH
+
+ * Maintainer upload for the Security Team
+ * Fixes three security vulnerabilities:
+ - DRUPAL-SA-2005-007: Cross-site-scripting vulnerability
+ - DRUPAL-SA-2005-008: It was possible to attach files that are able
+ to run Javascript under Internet Explorer.
+ - DRUPAL-SA-2005-009: It was possible to bypass the 'access user
+ profile' permission if the server was running PHP5
+
+ -- Hilko Bengen <[EMAIL PROTECTED]> Wed, 30 Nov 2005 23:23:05 +0100
+
drupal (4.5.3-4) stable-security; urgency=HIGH
* Maintainer upload for the Security Team
diff -u drupal-4.5.3/includes/bootstrap.inc drupal-4.5.3/includes/bootstrap.inc
--- drupal-4.5.3/includes/bootstrap.inc
+++ drupal-4.5.3/includes/bootstrap.inc
@@ -387,15 +387,6 @@
}
/**
- * Return the URI of the referring page.
- */
-function referer_uri() {
- if (isset($_SERVER['HTTP_REFERER'])) {
- return check_url($_SERVER['HTTP_REFERER']);
- }
-}
-
-/**
* Return a component of the current Drupal path.
*
* When viewing a page at the path "admin/node/configure", for example, arg(0)
@@ -426,19 +417,6 @@
}
/**
- * Prepare user input for use in a URI.
- *
- * We replace ( and ) with their entity equivalents to prevent XSS attacks.
- */
-function check_url($uri) {
- $uri = htmlspecialchars($uri, ENT_QUOTES);
-
- $uri = strtr($uri, array('(' => '&040;', ')' => '&041;'));
-
- return $uri;
-}
-
-/**
* Since request_uri() is only available on Apache, we generate an
* equivalent using other environment vars.
*/
@@ -456,7 +434,7 @@
}
}
- return check_url($uri);
+ return $uri;
}
/**
@@ -519,6 +497,13 @@
return $messages;
}
+/**
+ * Encode special characters in a plain-text string for display as HTML.
+ */
+function check_plain($text) {
+ return htmlspecialchars($text, ENT_QUOTES);
+}
+
unset($conf);
$config = conf_init();
diff -u drupal-4.5.3/modules/filter.module drupal-4.5.3/modules/filter.module
--- drupal-4.5.3/modules/filter.module
+++ drupal-4.5.3/modules/filter.module
@@ -14,9 +14,6 @@
define('FILTER_HTML_STRIP', 1);
define('FILTER_HTML_ESCAPE', 2);
-define('FILTER_STYLE_ALLOW', 0);
-define('FILTER_STYLE_STRIP', 1);
-
/**
* Implementation of hook_help().
*/
@@ -56,19 +53,99 @@
function filter_filter_tips($delta, $format, $long = false) {
switch ($delta) {
case 0:
- switch (variable_get("filter_html_$format", FILTER_HTML_STRIP)) {
+ if (variable_get("filter_html_$format", FILTER_HTML_STRIP) ==
FILTER_HTML_STRIP) {
+ if ($allowed_html = variable_get("allowed_html_$format", '<a> <em>
<strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>')) {
+ switch ($long) {
+ case 0:
+ return t('Allowed HTML tags') .': '. check_plain($allowed_html);
+ case 1:
+ $output = '<p>'. t('Allowed HTML tags') .': '.
check_plain($allowed_html) .'</p>';
+ if (!variable_get("filter_html_help_$format", 1)) {
+ return $output;
+ }
+
+ $output .= t('<p>This site allows HTML content. While learning
all of HTML may feel intimidating, learning how to use a very small number of
the most basic HTML "tags" is very easy. This table provides examples for each
tag that is enabled on this site.</p>
+ <p>For more information see W3C\'s <a href="http://www.w3.org/TR/html/">HTML
Specifications</a> or use your favorite search engine to find other sites that
explain HTML.</p>');
+ $tips = array(
+ 'a' => array( t('Anchors are used to make links to other
pages.'), '<a href="'. $base_url .'">'. variable_get('site_name', 'drupal')
.'</a>'),
+ 'br' => array( t('By default line break tags are automatically
added, so use this tag to add additional ones. Use of this tag is different
because it is not used with an open/close pair like all the others. Use the
extra " /" inside the tag to maintain XHTML 1.0 compatibility'), t('Text with
<br />line break')),
+ 'p' => array( t('By default paragraph tags are automatically
added, so use this tag to add additional ones.'), '<p>'. t('Paragraph one.')
.'</p> <p>'. t('Paragraph two.') .'</p>'),
+ 'strong' => array( t('Strong'), '<strong>'. t('Strong').
'</strong>'),
+ 'em' => array( t('Emphasized'), '<em>'. t('Emphasized')
.'</em>'),
+ 'cite' => array( t('Cited'), '<cite>'. t('Cited') .'</cite>'),
+ 'code' => array( t('Coded text used to show programming source
code'), '<code>'. t('Coded') .'</code>'),
+ 'b' => array( t('Bolded'), '<b>'. t('Bolded') .'</b>'),
+ 'u' => array( t('Underlined'), '<u>'. t('Underlined') .'</u>'),
+ 'i' => array( t('Italicized'), '<i>'. t('Italicized') .'</i>'),
+ 'sup' => array( t('Superscripted'),
t('<sup>Super</sup>scripted')),
+ 'sub' => array( t('Subscripted'), t('<sub>Sub</sub>scripted')),
+ 'pre' => array( t('Preformatted'), '<pre>'. t('Preformatted')
.'</pre>'),
+ 'blockquote' => array( t('Block quoted'), '<blockquote>'.
t('Block quoted') .'</blockquote>'),
+ 'q' => array( t('Quoted inline'), '<q>'. t('Quoted inline')
.'</q>'),
+ // Assumes and describes tr, td, th.
+ 'table' => array( t('Table'), '<table> <tr><th>'. t('Table
header') .'</th></tr> <tr><td>'. t('Table cell') .'</td></tr> </table>'),
+ 'tr' => NULL, 'td' => NULL, 'th' => NULL,
+ 'del' => array( t('Deleted'), '<del>'. t('Deleted') .'</del>'),
+ 'ins' => array( t('Inserted'), '<ins>'. t('Inserted')
.'</ins>'),
+ // Assumes and describes li.
+ 'ol' => array( t('Ordered list - use the <li> to begin
each list item'), '<ol> <li>'. t('First item') .'</li> <li>'. t('Second item')
.'</li> </ol>'),
+ 'ul' => array( t('Unordered list - use the <li> to begin
each list item'), '<ul> <li>'. t('First item') .'</li> <li>'. t('Second item')
.'</li> </ul>'),
+ 'li' => NULL,
+ // Assumes and describes dt and dd.
+ 'dl' => array( t('Definition lists are similar to other HTML
lists. <dl> begins the definition list, <dt> begins the definition
term and <dd> begins the definition description.'), '<dl> <dt>'. t('First
term') .'</dt> <dd>'. t('First definition') .'</dd> <dt>'. t('Second term')
.'</dt> <dd>'. t('Second definition') .'</dd> </dl>'),
+ 'dt' => NULL, 'dd' => NULL,
+ 'h1' => array( t('Header'), '<h1>'. t('Title') .'</h1>'),
+ 'h2' => array( t('Header'), '<h2>'. t('Subtitle') .'</h2>'),
+ 'h3' => array( t('Header'), '<h3>'. t('Subtitle three')
.'</h3>'),
+ 'h4' => array( t('Header'), '<h4>'. t('Subtitle four')
.'</h4>'),
+ 'h5' => array( t('Header'), '<h5>'. t('Subtitle five')
.'</h5>'),
+ 'h6' => array( t('Header'), '<h6>'. t('Subtitle six') .'</h6>')
+ );
+ $header = array(t('Tag Description'), t('You Type'), t('You
Get'));
+ preg_match_all('/<([a-z0-9]+)[^a-z0-9]/i', $allowed_html, $out);
+ foreach ($out[1] as $tag) {
+ if (array_key_exists($tag, $tips)) {
+ if ($tips[$tag]) {
+ $rows[] = array(
+ array('data' => $tips[$tag][0], 'class' =>
'description'),
+ array('data' => '<code>'. check_plain($tips[$tag][1])
.'</code>', 'class' => 'type'),
+ array('data' => $tips[$tag][1], 'class' => 'get')
+ );
+ }
+ }
+ else {
+ $rows[] = array(
+ array('data' => t('No help provided for tag %tag.',
array('%tag' => check_plain($tag))), 'class' => 'description', 'colspan' => 3),
+ );
+ }
+ }
+ $output .= theme('table', $header, $rows);
- case FILTER_HTML_STRIP:
- if ($allowed_html = variable_get("allowed_html_$format", '<a> <em>
<strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>')) {
- return t('Allowed HTML tags') .': '.
htmlspecialchars($allowed_html);
- }
- else {
+ $output .= t('<p>Most unusual characters can be directly entered
without any problems.</p>
+<p>If you do encounter problems, try using HTML character entities. A common
example looks like &amp; for an ampersand & character. For a full list
of entities see HTML\'s <a
href="http://www.w3.org/TR/html4/sgml/entities.html">entities</a> page. Some of
the available characters include:</p>');
+ $entities = array(
+ array( t('Ampersand'), '&'),
+ array( t('Greater than'), '>'),
+ array( t('Less than'), '<'),
+ array( t('Quotation mark'), '"'),
+ );
+ $header = array(t('Character Description'), t('You Type'),
t('You Get'));
+ unset($rows);
+ foreach ($entities as $entity) {
+ $rows[] = array(
+ array('data' => $entity[0], 'class' => 'description'),
+ array('data' => '<code>'. check_plain($entity[1])
.'</code>', 'class' => 'type'),
+ array('data' => $entity[1], 'class' => 'get')
+ );
+ }
+ $output .= theme('table', $header, $rows);
+ return $output;
+ }
+ }
+ }
+ else {
return t('No HTML tags allowed');
- }
-
- case FILTER_STYLE_STRIP:
- return t('No HTML tags allowed');
- }
+ }
break;
case 1:
@@ -831,9 +908,8 @@
* Settings for the HTML filter.
*/
function _filter_html_settings($format) {
- $group = form_radios(t('Filter HTML tags'), "filter_html_$format",
variable_get("filter_html_$format", FILTER_HTML_STRIP), array(FILTER_HTML_STRIP
=> t('Strip tags'), FILTER_HTML_ESCAPE => t('Escape tags')), t('How to deal
with HTML tags in user-contributed content. If set to "Strip tags", dangerous
tags are removed (see below). If set to "Escape tags", all HTML is escaped and
presented as it was typed.'));
+ $group = form_radios(t('Filter HTML tags'), "filter_html_$format",
variable_get("filter_html_$format", FILTER_HTML_STRIP), array(FILTER_HTML_STRIP
=> t('Strip disallowed tags'), FILTER_HTML_ESCAPE => t('Escape tags')), t('How
to deal with HTML tags in user-contributed content. If set to "Strip disallowed
tags", dangerous tags are removed (see below). If set to "Escape tags", all
HTML is escaped and presented as it was typed.'));
$group .= form_textfield(t('Allowed HTML tags'), "allowed_html_$format",
variable_get("allowed_html_$format", '<a> <em> <strong> <cite> <code> <ul> <ol>
<li> <dl> <dt> <dd>'), 64, 255, t('If "Strip tags" is selected, optionally
specify tags which should not be stripped. Javascript event attributes are
always stripped.'));
- $group .= form_radios(t('HTML style attributes'), "filter_style_$format",
variable_get("filter_style_$format", FILTER_STYLE_STRIP),
array(FILTER_STYLE_ALLOW => t('Allowed'), FILTER_STYLE_STRIP => t('Removed')),
t('If "Strip tags" is selected, you can choose whether "STYLE" attributes are
allowed or removed from input.'));
$output .= form_group(t('HTML filter'), $group);
return $output;
@@ -844,12 +920,8 @@
*/
function _filter_html($text, $format) {
if (variable_get("filter_html_$format", FILTER_HTML_STRIP) ==
FILTER_HTML_STRIP) {
- // Allow users to enter HTML, but filter it
- $text = strip_tags($text, variable_get("allowed_html_$format", '<a> <em>
<strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>'));
- if (variable_get("filter_style_$format", FILTER_STYLE_STRIP)) {
- $text = preg_replace('/\Wstyle\s*=[^>]+?>/i', '>', $text);
- }
- $text = preg_replace('/\Won[a-z]+\s*=[^>]+?>/i', '>', $text);
+ $allowed_tags = preg_split('/\s+|<|>/',
variable_get("allowed_html_$format", '<a> <em> <strong> <cite> <code> <ul> <ol>
<li> <dl> <dt> <dd>'), -1, PREG_SPLIT_NO_EMPTY);
+ $text = filter_xss($text, $allowed_tags);
}
if (variable_get("filter_html_$format", FILTER_HTML_STRIP) ==
FILTER_HTML_ESCAPE) {
@@ -886,6 +958,252 @@
}
/**
+ * Filters XSS. Based on kses by Ulf Harnhammar, see
+ * http://sourceforge.net/projects/kses
+ *
+ * For examples of various XSS attacks, see:
+ * http://ha.ckers.org/xss.html
+ *
+ * This code does four things:
+ * - Removes characters and constructs that can trick browsers
+ * - Makes sure all HTML entities are well-formed
+ * - Makes sure all HTML tags and attributes are well-formed
+ * - Makes sure no HTML tags contain URLs with a disallowed protocol (e.g.
javascript:)
+ *
+ * @param $string
+ * The string with raw HTML in it. It will be stripped of everything that
can cause
+ * an XSS attack.
+ * @param $allowed_tags
+ * An array of allowed tags.
+ * @param $format
+ * The format to use.
+ */
+function filter_xss($string, $allowed_tags = array('a', 'em', 'strong',
'cite', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd')) {
+ // Store the input format
+ _filter_xss_split($allowed_tags, TRUE);
+ // Remove NUL characters (ignored by some browsers)
+ $string = str_replace(chr(0), '', $string);
+ // Remove Netscape 4 JS entities
+ $string = preg_replace('%&\s*\{[^}]*(\}\s*;?|$)%', '', $string);
+
+ // Defuse all HTML entities
+ $string = str_replace('&', '&', $string);
+ // Change back only well-formed entities in our whitelist
+ // Named entities
+ $string = preg_replace('/&([A-Za-z][A-Za-z0-9]*;)/', '&\1', $string);
+ // Decimal numeric entities
+ $string = preg_replace('/&#0*([0-9]+;)/', '&#\1', $string);
+ // Hexadecimal numeric entities
+ $string = preg_replace('/&#[Xx]0*((?:[0-9A-Fa-f]{2})+;)/', '&#x\1',
$string);
+
+ return preg_replace_callback('%
+ (
+ <[^>]*.(>|$) # a string that starts with a <, up until the > or the end
of the string
+ | # or
+ > # just a >
+ )%x', '_filter_xss_split', $string);
+}
+
+/**
+ * Processes an HTML tag.
+ *
+ * @param @m
+ * An array with various meaning depending on the value of $store.
+ * If $store is TRUE then the array contains the allowed tags.
+ * If $store is FALSE then the array has one element, the HTML tag to
process.
+ * @param $store
+ * Whether to store $m.
+ * @return
+ * If the element isn't allowed, an empty string. Otherwise, the cleaned up
+ * version of the HTML element.
+ */
+function _filter_xss_split($m, $store = FALSE) {
+ static $allowed_html;
+
+ if ($store) {
+ $allowed_html = array_flip($m);
+ return;
+ }
+
+ $string = $m[1];
+
+ if (substr($string, 0, 1) != '<') {
+ // We matched a lone ">" character
+ return '>';
+ }
+
+ if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $string,
$matches)) {
+ // Seriously malformed
+ return '';
+ }
+
+ $slash = trim($matches[1]);
+ $elem = &$matches[2];
+ $attrlist = &$matches[3];
+
+ if (!isset($allowed_html[strtolower($elem)])) {
+ // Disallowed HTML element
+ return '';
+ }
+
+ if ($slash != '') {
+ return "</$elem>";
+ }
+ // Is there a closing XHTML slash at the end of the attributes?
+ $xhtml_slash = preg_match('%\s/\s*$%', $attr) ? '/' : '';
+
+ // Clean up attributes
+ $attr2 = implode(' ', _filter_xss_attributes($attrlist));
+ $attr2 = preg_replace('/[<>]/', '', $attr2);
+
+ return "<$elem $attr2$xhtml_slash>";
+}
+
+/**
+ * Processes a string of HTML attributes.
+ *
+ * @return
+ * Cleaned up version of the HTML attributes.
+ */
+function _filter_xss_attributes($attr) {
+ $attrarr = array();
+ $mode = 0;
+ $attrname = '';
+
+ while (strlen($attr) != 0) {
+ // Was the last operation successful?
+ $working = 0;
+
+ switch ($mode) {
+ case 0:
+ // Attribute name, href for instance
+ if (preg_match('/^([-a-zA-Z]+)/', $attr, $match)) {
+ $attrname = strtolower($match[1]);
+ $skip = ($attrname == 'style' || substr($attrname, 0, 2) == 'on');
+ $working = $mode = 1;
+ $attr = preg_replace('/^[-a-zA-Z]+/', '', $attr);
+ }
+
+ break;
+
+ case 1:
+ // Equals sign or valueless ("selected")
+ if (preg_match('/^\s*=\s*/', $attr)) {
+ $working = 1; $mode = 2;
+ $attr = preg_replace('/^\s*=\s*/', '', $attr);
+ break;
+ }
+
+ if (preg_match('/^\s+/', $attr)) {
+ $working = 1; $mode = 0;
+ if (!$skip) {
+ $attrarr[] = $attrname;
+ }
+ $attr = preg_replace('/^\s+/', '', $attr);
+ }
+
+ break;
+
+ case 2:
+ // Attribute value, a URL after href= for instance
+ if (preg_match('/^"([^"]*)"(\s+|$)/', $attr, $match)) {
+ $thisval = filter_xss_bad_protocol($match[1]);
+
+ if (!$skip) {
+ $attrarr[] = "$attrname=\"$thisval\"";
+ }
+ $working = 1;
+ $mode = 0;
+ $attr = preg_replace('/^"[^"]*"(\s+|$)/', '', $attr);
+ break;
+ }
+
+ if (preg_match("/^'([^']*)'(\s+|$)/", $attr, $match)) {
+ $thisval = filter_xss_bad_protocol($match[1]);
+
+ if (!$skip) {
+ $attrarr[] = "$attrname='$thisval'";;
+ }
+ $working = 1; $mode = 0;
+ $attr = preg_replace("/^'[^']*'(\s+|$)/", '', $attr);
+ break;
+ }
+
+ if (preg_match("%^([^\s\"']+)(\s+|$)%", $attr, $match)) {
+ $thisval = filter_xss_bad_protocol($match[1]);
+
+ if (!$skip) {
+ $attrarr[] = "$attrname=\"$thisval\"";
+ }
+ $working = 1; $mode = 0;
+ $attr = preg_replace("%^[^\s\"']+(\s+|$)%", '', $attr);
+ }
+
+ break;
+ }
+
+ if ($working == 0) {
+ // not well formed, remove and try again
+ $attr = preg_replace('/
+ ^
+ (
+ "[^"]*("|$) # - a string that starts with a double quote, up until
the next double quote or the end of the string
+ | # or
+ \'[^\']*(\'|$)| # - a string that starts with a quote, up until the
next quote or the end of the string
+ | # or
+ \S # - a non-whitespace character
+ )* # any number of the above three
+ \s* # any number of whitespaces
+ /x', '', $attr);
+ $mode = 0;
+ }
+ }
+
+ // the attribute list ends with a valueless attribute like "selected"
+ if ($mode == 1) {
+ $attrarr[] = $attrname;
+ }
+ return $attrarr;
+}
+
+/**
+ * Processes an HTML attribute value and ensures it does not contain an URL
+ * with a disallowed protocol (e.g. javascript:)
+ *
+ * @param $string
+ * The string with the attribute value.
+ * @param $decode
+ * Whether to decode entities in the $string. Set to FALSE if the $string
+ * is in plain text, TRUE otherwise. Defaults to TRUE.
+ * @return
+ * Cleaned up and HTML-escaped version of $string.
+ */
+function filter_xss_bad_protocol($string, $decode = TRUE) {
+ // Get the plain text representation of the attribute value (i.e. its
meaning)
+ if ($decode) {
+ $string = decode_entities($string);
+ }
+ // Remove soft hyphen
+ $string = str_replace(chr(194) . chr(173), '', $string);
+ $string2 = '';
+ // Strip protocols
+ do {
+ $before = $string;
+ $string = preg_replace_callback('/^([^:]+):/', '_filter_xss_bad_protocol',
$string);
+ } while ($before != $string);
+ return check_plain($string);
+}
+
+function _filter_xss_bad_protocol($m) {
+ static $allowed_protocols;
+ if (!isset($allowed_protocols)) {
+ $allowed_protocols = array_flip(variable_get('filter_allowed_protocols',
array('http', 'https', 'ftp', 'news', 'nntp', 'telnet', 'mailto', 'irc', 'ssh',
'sftp', 'webcal')));
+ }
+ $string = preg_replace('/\s+/', '', $m[1]);
+ return isset($allowed_protocols[$string]) ? "$string:" : '';
+}
+
+/**
* @} End of "Standard filters".
*/
only in patch2:
unchanged:
--- drupal-4.5.3.orig/modules/upload.module
+++ drupal-4.5.3/modules/upload.module
@@ -82,7 +82,7 @@
function upload_download() {
foreach ($_SESSION['file_uploads'] as $file) {
if ($file->_filename == $_GET['q']) {
- file_transfer($file->filepath, array('Content-Type: '. $file->filemime,
'Content-Length: '. $file->filesize));
+ file_transfer($file->filepath, array('Content-Type: '.
mime_header_encode($file->filemime), 'Content-Length: '. $file->filesize));
}
}
}
@@ -93,9 +93,10 @@
$result = db_query("SELECT * from {files} n " . node_access_join_sql() . "
WHERE filepath = '%s' AND ". node_access_where_sql(), $file);
if ($file = db_fetch_object($result)) {
$name = mime_header_encode($file->filename);
+ $type = mime_header_encode($file->filemime);
// Serve images and text inline for the browser to display rather than
download.
$disposition = ereg('^(text/|image/)', $file->filemime) ? 'inline' :
'attachment';
- return array('Content-Type: '. $file->filemime .'; name='. $name,
+ return array('Content-Type: '. $type .'; name='. $name,
'Content-Length: '. $file->filesize,
'Content-Disposition: '. $disposition .'; filename='.
$name);
}
@@ -171,14 +172,6 @@
}
}
- // Rename possibly executable scripts to prevent accidental execution.
- // Uploaded files are attachments and should be shown in their original
- // form, rather than run.
- if (preg_match('/\.(php|pl|py|cgi|asp)$/i', $file->filename)) {
- $file->filename .= '.txt';
- $file->filemime = 'text/plain';
- }
-
if ($error['extension'] == count($user->roles) && $user->uid != 1) {
form_set_error('upload', t('Error attaching file %name: invalid
extension', array('%name' => "<em>$file->filename</em>")));
}
only in patch2:
unchanged:
--- drupal-4.5.3.orig/modules/aggregator.module
+++ drupal-4.5.3/modules/aggregator.module
@@ -1,5 +1,5 @@
<?php
-/* $Id: aggregator.module,v 1.213.2.3 2004/12/02 18:38:04 dries Exp $ */
+/* $Id: aggregator.module,v 1.213.2.4 2005/11/30 10:07:39 dries Exp $ */
/**
* @file
@@ -486,9 +486,7 @@
foreach ($item as $key => $value) {
// TODO: Make handling of aggregated HTML more flexible/configurable.
$value = strtr(trim($value), $tt);
- $value = strip_tags($value, '<a> <b> <br> <dd> <dl> <dt> <em> <i> <li>
<ol> <p> <strong> <u> <ul>');
- $value = preg_replace('/\Wstyle\s*=[^>]+?>/i', '>', $value);
- $value = preg_replace('/\Won[a-z]+\s*=[^>]+?>/i', '>', $value);
+ $value = filter_xss($value);
$item[$key] = $value;
}
@@ -552,12 +550,7 @@
$entry = db_fetch_object(db_query("SELECT iid FROM {aggregator_item}
WHERE fid = %d AND title = '%s'", $feed['fid'], $title));
}
- if (!valid_input_data($item['DESCRIPTION'])) {
- drupal_set_message(t('Failed to parse entry from %site feed: suspicious
input data.', array('%site' => '<em>'. $feed['title'] .'</em>')), 'error');
- }
- else {
- aggregator_save_item(array('iid' => $entry->iid, 'fid' => $feed['fid'],
'timestamp' => $timestamp, 'title' => $title, 'link' => $link, 'author' =>
$item['AUTHOR'], 'description' => $item['DESCRIPTION']));
- }
+ aggregator_save_item(array('iid' => $entry->iid, 'fid' => $feed['fid'],
'timestamp' => $timestamp, 'title' => $title, 'link' => $link, 'author' =>
$item['AUTHOR'], 'description' => $item['DESCRIPTION']));
}
/*
only in patch2:
unchanged:
--- drupal-4.5.3.orig/modules/blogapi.module
+++ drupal-4.5.3/modules/blogapi.module
@@ -133,10 +133,6 @@
$edit['body'] = $params[3];
}
- if (!valid_input_data($edit['title'], $edit['body'])) {
- return blogapi_error(t('Terminated request because of suspicious input
data.'));
- }
-
$node = node_validate($edit);
if ($errors = form_get_errors()) {
only in patch2:
unchanged:
--- drupal-4.5.3.orig/modules/system.module
+++ drupal-4.5.3/modules/system.module
@@ -53,7 +53,7 @@
* Implementation of hook_perm().
*/
function system_perm() {
- return array('administer site configuration', 'access administration pages',
'bypass input data check');
+ return array('administer site configuration', 'access administration pages');
}
/**
only in patch2:
unchanged:
--- drupal-4.5.3.orig/includes/file.inc
+++ drupal-4.5.3/includes/file.inc
@@ -134,8 +134,24 @@
elseif ($_FILES["edit"]["name"][$source] &&
is_uploaded_file($_FILES["edit"]["tmp_name"][$source])) {
$file = new stdClass();
$file->filename = trim(basename($_FILES["edit"]["name"][$source]), '.');
- $file->filemime = $_FILES["edit"]["type"][$source];
$file->filepath = $_FILES["edit"]["tmp_name"][$source];
+
+ if (function_exists('mime_content_type')) {
+ $file->filemime = mime_content_type($file->filepath);
+ if ($file->filemime != $_FILES["edit"]["type"][$source]) {
+ watchdog('file', t('For %file the system thinks its MIME type is
%detected while the user has given %given for MIME type', array('%file' =>
theme('placeholder', $file->filepath), '%detected' => theme('placeholder',
$file>-filemime), '%given' => theme('placeholder',
$_FILES['edit']['type'][$source]))));
+ }
+ }
+ else {
+ $file->filemime = $_FILES["edit"]["type"][$source];
+ }
+ if (((substr($file->filemime, 0, 5) == 'text/' || strpos($file->filemime,
'javascript')) && (substr($file->filename, -4) != '.txt')) ||
preg_match('/\.(php|pl|py|cgi|asp)$/i', $file->filename)) {
+ $file->filemime = 'text/plain';
+ rename($file->filepath, $file->filepath .'.txt');
+ $file->filepath .= '.txt';
+ $file->filename .= '.txt';
+ }
+
$file->error = $_FILES["edit"]["error"][$source];
$file->filesize = $_FILES["edit"]["size"][$source];
$file->source = $source;
@@ -356,12 +372,6 @@
}
}
- if (!user_access('bypass input data check') && !valid_input_data($file)) {
- watchdog('error', t('Possible exploit abuse: invalid data.'));
- drupal_set_message(t('File upload failed: invalid data.'), 'error');
- return 0;
- }
-
// Check for file upload errors.
switch ($file->error) {
case 0: // UPLOAD_ERR_OK
@@ -400,12 +410,6 @@
* @return A string containing the resulting filename or 0 on error
*/
function file_save_data($data, $dest, $replace = FILE_EXISTS_RENAME) {
- if (!user_access('bypass input data check') && !valid_input_data($data)) {
- watchdog('error', t('Possible exploit abuse: invalid data.'));
- drupal_set_message(t('File upload failed: invalid data.'), 'error');
- return 0;
- }
-
$temp = variable_get('file_directory_temp', FILE_DIRECTORY_TEMP);
$file = tempnam($temp, 'file');
if (!$fp = fopen($file, 'wb')) {
@@ -433,6 +437,10 @@
ob_end_clean();
foreach ($headers as $header) {
+ // To prevent HTTP header injection, we delete new lines that are
+ // not followed by a space or a tab.
+ // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
+ $header = preg_replace('/\r?\n(?!\t| )/', '', $header);
header($header);
}
only in patch2:
unchanged:
--- drupal-4.5.3.orig/includes/common.inc
+++ drupal-4.5.3/includes/common.inc
@@ -562,54 +562,6 @@
}
/**
- * Validate data input by a user.
- *
- * Ensures that user data cannot be used to perform attacks on the site.
- *
- * @param $data
- * The input to check.
- * @return
- * TRUE if the input data is acceptable.
- */
-function valid_input_data($data) {
- if (is_array($data) || is_object($data)) {
- // Form data can contain a number of nested arrays.
- foreach ($data as $key => $value) {
- if (!valid_input_data($key) || !valid_input_data($value)) {
- return FALSE;
- }
- }
- }
- else if (isset($data)) {
- // Detect dangerous input data.
-
- // Decode all normal character entities.
- $data = decode_entities($data, array('<', '&', '"'));
-
- // Check strings:
- $match = preg_match('/\Wjavascript\s*:/i', $data);
- $match += preg_match('/\Wexpression\s*\(/i', $data);
- $match += preg_match('/\Walert\s*\(/i', $data);
-
- // Check attributes:
- $match +=
preg_match("/\W(dynsrc|datasrc|data|lowsrc|on[a-z]+)\s*=[^>]+?>/i", $data);
-
- // Check tags:
- $match +=
preg_match("/<\s*(applet|script|object|style|embed|form|blink|meta|html|frame|iframe|layer|ilayer|head|frameset|xml)/i",
$data);
-
- if ($match) {
- watchdog('warning', t('Terminated request because of suspicious input
data: %data.', array('%data' => '<em>'. drupal_specialchars($data) .'</em>')));
- return FALSE;
- }
- }
-
- return TRUE;
-}
-/**
- * @} End of "defgroup validation".
- */
-
-/**
* @defgroup search Search interface
* @{
* The Drupal search interface manages a global search mechanism.
@@ -746,6 +698,26 @@
}
/**
+ * Prepare a URL for use in an HTML attribute. Strips harmful protocols.
+ *
+ */
+function check_url($uri) {
+ $uri = htmlspecialchars($uri, ENT_QUOTES);
+ $uri = filter_xss_bad_protocol($uri, FALSE);
+
+ return $uri;
+}
+
+/**
+ * Return the URI of the referring page.
+ */
+function referer_uri() {
+ if (isset($_SERVER['HTTP_REFERER'])) {
+ return check_url($_SERVER['HTTP_REFERER']);
+ }
+}
+
+/**
* @defgroup format Formatting
* @{
* Functions to format numbers, strings, dates, etc.
@@ -1094,7 +1066,7 @@
* A themed HTML string representing the form item group.
*/
function form_group($legend, $group, $description = NULL) {
- return '<fieldset>' . ($legend ? '<legend>'. $legend .'</legend>' : '') .
$group . ($description ? '<div class="description">'. $description .'</div>' :
'') . "</fieldset>\n";
+ return '<fieldset>' . ($legend ? '<legend>'. check_plain($legend)
.'</legend>' : '') . $group . ($description ? '<div class="description">'.
$description .'</div>' : '') . "</fieldset>\n";
}
/**
@@ -1118,7 +1090,7 @@
* A themed HTML string representing the radio button.
*/
function form_radio($title, $name, $value = 1, $checked = FALSE, $description
= NULL, $attributes = NULL, $required = FALSE) {
- $element = '<input type="radio" class="'. _form_get_class('form-radio',
$required, _form_get_error($name)) .'" name="edit['. $name .']" value="'.
$value .'"'. ($checked ? ' checked="checked"' : '') .
drupal_attributes($attributes) .' />';
+ $element = '<input type="radio" class="'. _form_get_class('form-radio',
$required, _form_get_error($name)) .'" name="edit['. $name .']" value="'.
check_plain($value) .'"'. ($checked ? ' checked="checked"' : '') .
drupal_attributes($attributes) .' />';
if (!is_null($title)) {
$element = '<label class="option">'. $element .' '. $title .'</label>';
}
@@ -1150,7 +1122,7 @@
if (count($options) > 0) {
$choices = '';
foreach ($options as $key => $choice) {
- $choices .= '<label class="option"><input type="radio"
class="form-radio" name="edit['. $name .']" value="'. $key .'"'. ($key ==
$value ? ' checked="checked"' : ''). drupal_attributes($attributes). ' /> '.
$choice .'</label><br />';
+ $choices .= '<label class="option"><input type="radio"
class="form-radio" name="edit['. $name .']" value="'. check_plain($key) .'"'.
($key == $value ? ' checked="checked"' : ''). drupal_attributes($attributes) .'
/> '. $choice .'</label><br />';
}
return theme('form_element', $title, $choices, $description, NULL,
$required, _form_get_error($name));
}
@@ -1177,7 +1149,7 @@
* A themed HTML string representing the checkbox.
*/
function form_checkbox($title, $name, $value = 1, $checked = FALSE,
$description = NULL, $attributes = NULL, $required = FALSE) {
- $element = '<input type="checkbox" class="'.
_form_get_class('form-checkbox', $required, _form_get_error($name)) .'"
name="edit['. $name .']" id="edit-'. $name .'" value="'. $value .'"'. ($checked
? ' checked="checked"' : '') . drupal_attributes($attributes) .' />';
+ $element = '<input type="checkbox" class="'.
_form_get_class('form-checkbox', $required, _form_get_error($name)) .'"
name="edit['. $name .']" id="edit-'. $name .'" value="'. check_plain($value)
.'"'. ($checked ? ' checked="checked"' : '') . drupal_attributes($attributes)
.' />';
if (!is_null($title)) {
$element = '<label class="option">'. $element .' '. $title .'</label>';
}
@@ -1214,7 +1186,7 @@
}
$choices = '';
foreach ($options as $key => $choice) {
- $choices .= '<label class="option"><input type="checkbox"
class="form-checkbox" name="edit['. $name .'][]" value="'. $key .'"'.
(in_array($key, $values) ? ' checked="checked"' : '').
drupal_attributes($attributes). ' /> '. $choice .'</label><br />';
+ $choices .= '<label class="option"><input type="checkbox"
class="form-checkbox" name="edit['. $name .'][]" value="'. check_plain($key)
.'"'. (in_array($key, $values) ? ' checked="checked"' : '').
drupal_attributes($attributes) .' /> '. $choice .'</label><br />';
}
// Note: because unchecked boxes are not included in the POST data, we
// include a form_hidden() which will be overwritten as soon as there is at
@@ -1310,7 +1282,7 @@
}
}
- $output .= theme('form_element', $title, '<textarea wrap="virtual"'. $cols
.' rows="'. $rows .'" name="edit['. $name .']" id="edit-'. $name .'" class="'.
_form_get_class('textarea', $required, _form_get_error($name)) .'"'.
drupal_attributes($attributes) .'>'. check_form($value) .'</textarea>',
$description, 'edit-'. $name, $required, _form_get_error($name));
+ $output .= theme('form_element', $title, '<textarea wrap="virtual"'.
check_plain($cols) .' rows="'. check_plain($rows) .'" name="edit['. $name .']"
id="edit-'. $name .'" class="'. _form_get_class('textarea', $required,
_form_get_error($name)) .'"'. drupal_attributes($attributes) .'>'.
check_form($value) .'</textarea>', $description, 'edit-'. $name, $required,
_form_get_error($name));
// e.g. optionally plug in a WYSIWYG editor
foreach (module_list() as $module_name) {
@@ -1354,14 +1326,14 @@
$select = '';
foreach ($options as $key => $choice) {
if (is_array($choice)) {
- $select .= '<optgroup label="'. $key .'">';
+ $select .= '<optgroup label="'. check_plain($key) .'">';
foreach ($choice as $key => $choice) {
- $select .= '<option value="'. $key .'"'. (is_array($value) ?
(in_array($key, $value) ? ' selected="selected"' : '') : ($value == $key ? '
selected="selected"' : '')) .'>'. check_form($choice) .'</option>';
+ $select .= '<option value="'. check_plain($key) .'"'.
(is_array($value) ? (in_array($key, $value) ? ' selected="selected"' : '') :
($value == $key ? ' selected="selected"' : '')) .'>'. check_plain($choice)
.'</option>';
}
$select .= '</optgroup>';
}
else {
- $select .= '<option value="'. $key .'"'. (is_array($value) ?
(in_array($key, $value) ? ' selected="selected"' : '') : ($value == $key ? '
selected="selected"' : '')) .'>'. check_form($choice) .'</option>';
+ $select .= '<option value="'. check_plain($key) .'"'. (is_array($value)
? (in_array($key, $value) ? ' selected="selected"' : '') : ($value == $key ? '
selected="selected"' : '')) .'>'. check_plain($choice) .'</option>';
}
}
return theme('form_element', $title, '<select name="edit['. $name .']'.
($multiple ? '[]' : '') .'"'. ($multiple ? ' multiple="multiple" ' : '') .
($extra ? ' '. $extra : '') .' id="edit-'. $name .'">'. $select .'</select>',
$description, 'edit-'. $name, $required, _form_get_error($name));
@@ -1834,6 +1806,10 @@
if ($hex != '') {
$codepoint = base_convert($codepoint, 16, 10);
}
+ else {
+ // Decimal numerical entity (strip leading zeros to avoid PHP octal
notation)
+ $codepoint = preg_replace('/^0+/', '', $codepoint);
+ }
if ($codepoint < 0x80) {
$str = chr($codepoint);
}
@@ -1909,18 +1885,6 @@
// Initialize all enabled modules.
module_init();
-if (!user_access('bypass input data check')) {
- // We can't use $_REQUEST because it consists of the contents of $_POST,
- // $_GET and $_COOKIE: if any of the input arrays share a key, only one
- // value will be verified.
- if (!valid_input_data($_GET)
- || !valid_input_data($_POST)
- || !valid_input_data($_COOKIE)
- || !valid_input_data($_FILES)) {
- die('Terminated request because of suspicious input data.');
- }
-}
-
// Initialize the localization system.
$locale = locale_initialize();
only in patch2:
unchanged:
--- drupal-4.5.3.orig/includes/database.pgsql.inc
+++ drupal-4.5.3/includes/database.pgsql.inc
@@ -118,7 +118,8 @@
return $last_result;
}
else {
- trigger_error(pg_last_error() ."\nquery: ". htmlspecialchars($query),
E_USER_ERROR);
+ trigger_error(check_plain(pg_last_error() ."\nquery: ". $query),
E_USER_ERROR);
+ return FALSE;
}
}
only in patch2:
unchanged:
--- drupal-4.5.3.orig/includes/database.mysql.inc
+++ drupal-4.5.3/includes/database.mysql.inc
@@ -122,7 +122,8 @@
return $result;
}
else {
- trigger_error(mysql_error() ."\nquery: ". htmlspecialchars($query),
E_USER_ERROR);
+ trigger_error(check_plain(mysql_error() ."\nquery: ". $query),
E_USER_ERROR);
+ return FALSE;
}
}
signature.asc
Description: Digital signature

