Using a customized patch because the upstream provided patches reportedly against 1.4.8 were incorrectly formed. Resolves: - Security: close cross site scripting vulnerability in draft, compose and mailto functionality [CVE-2006-6142]. - Security: work around an issue in Internet Explorer that would guess the mime type of a file based on contents, not Content-Type header. - XSS fixes, all related to the magicHtml filter http://squirrelmail.svn.sourceforge.net/viewvc/squirrelmail/tags/rel-1_4_9a/squirrelmail/functions/mime.php?r1=11980&r2=11217 patch from 1.4.8 to 1.4.9 of mime.php http://squirrelmail.svn.sourceforge.net/viewvc/squirrelmail/tags/rel-1_4_9a/squirrelmail/functions/mime.php?r1=11217&r2=11967&diff_format=u This seemingly unrelated bugfix backed out diff -urN squirrelmail-1.4.8/functions/mime.php squirrelmail-1.4.8.mime.php/functions/mime.php --- squirrelmail-1.4.8/functions/mime.php 2006-06-20 02:14:53.000000000 -0400 +++ squirrelmail-1.4.8.mime.php/functions/mime.php 2007-01-16 00:24:20.000000000 -0500 @@ -477,7 +477,11 @@ if ($where && $what) { $defaultlink .= '&where='. urlencode($where).'&what='.urlencode($what); } - + // IE does make use of mime content sniffing. Forcing a download + // prohibit execution of XSS inside an application/octet-stream attachment + if ($type0 == 'application' && $type1 == 'octet-stream') { + $defaultlink .= '&absolute_dl=true'; + } /* This executes the attachment hook with a specific MIME-type. * If that doesn't have results, it tries if there's a rule * for a more generic type. @@ -1019,6 +1023,75 @@ } /** + * Translate all dangerous Unicode or Shift_JIS characters which are acepted by + * IE as regular characters. + * + * @param attvalue The attribute value before dangerous characters are translated. + * @return attvalue Nothing, modifies a reference value. + * @author Marc Groot Koerkamp. + */ +function sq_fixIE_idiocy(&$attvalue) { + // remove NUL + $attvalue = str_replace("\0", "", $attvalue); + // remove comments + $attvalue = preg_replace("/(\/\*.*?\*\/)/","",$attvalue); + + // IE has the evil habit of excepting every possible value for the attribute expression + // The table below contain characters which are valid in IE if they are used in the "expression" + // attribute value. + $aDangerousCharsReplacementTable = array( + array('ʟ', 'ʟ' ,/* L UNICODE IPA Extension */ + 'ʀ', 'ʀ' ,/* R UNICODE IPA Extension */ + 'ɴ', 'ɴ' ,/* N UNICODE IPA Extension */ + 'E', 'E' ,/* Unicode FULLWIDTH LATIN CAPITAL LETTER E */ + 'e', 'e' ,/* Unicode FULLWIDTH LATIN SMALL LETTER E */ + 'X', 'X',/* Unicode FULLWIDTH LATIN CAPITAL LETTER X */ + 'x', 'x',/* Unicode FULLWIDTH LATIN SMALL LETTER X */ + 'P', 'P',/* Unicode FULLWIDTH LATIN CAPITAL LETTER P */ + 'p', 'p',/* Unicode FULLWIDTH LATIN SMALL LETTER P */ + 'R', 'R',/* Unicode FULLWIDTH LATIN CAPITAL LETTER R */ + 'r', 'r',/* Unicode FULLWIDTH LATIN SMALL LETTER R */ + 'S', 'S',/* Unicode FULLWIDTH LATIN CAPITAL LETTER S */ + 's', 's',/* Unicode FULLWIDTH LATIN SMALL LETTER S */ + 'I', 'I',/* Unicode FULLWIDTH LATIN CAPITAL LETTER I */ + 'i', 'i',/* Unicode FULLWIDTH LATIN SMALL LETTER I */ + 'O', 'O',/* Unicode FULLWIDTH LATIN CAPITAL LETTER O */ + 'o', 'o',/* Unicode FULLWIDTH LATIN SMALL LETTER O */ + 'N', 'N',/* Unicode FULLWIDTH LATIN CAPITAL LETTER N */ + 'n', 'n',/* Unicode FULLWIDTH LATIN SMALL LETTER N */ + 'L', 'L',/* Unicode FULLWIDTH LATIN CAPITAL LETTER L */ + 'l', 'l',/* Unicode FULLWIDTH LATIN SMALL LETTER L */ + 'U', 'U',/* Unicode FULLWIDTH LATIN CAPITAL LETTER U */ + 'u', 'u',/* Unicode FULLWIDTH LATIN SMALL LETTER U */ + 'ⁿ', 'ⁿ' ,/* Unicode SUPERSCRIPT LATIN SMALL LETTER N */ + '艤', /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER E */ // in unicode this is some chinese char range + '芅', /* Shift JIS FULLWIDTH LATIN SMALL LETTER E */ + '艷', /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER X */ + '芘', /* Shift JIS FULLWIDTH LATIN SMALL LETTER X */ + '良', /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER P */ + '芐', /* Shift JIS FULLWIDTH LATIN SMALL LETTER P */ + '艱', /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER R */ + '芒', /* Shift JIS FULLWIDTH LATIN SMALL LETTER R */ + '色', /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER S */ + '芓', /* Shift JIS FULLWIDTH LATIN SMALL LETTER S */ + '艨', /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER I */ + '芉', /* Shift JIS FULLWIDTH LATIN SMALL LETTER I */ + '艮', /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER O */ + '芏', /* Shift JIS FULLWIDTH LATIN SMALL LETTER O */ + '艭', /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER N */ + '芎'), /* Shift JIS FULLWIDTH LATIN SMALL LETTER N */ + array('l', 'l', 'r','r','n','n', + 'E','E','e','e','X','X','x','x','P','P','p','p','S','S','s','s','I','I', + 'i','i','O','O','o','o','N','N','n','n','L','L','l','l','U','U','u','u','n', + 'E','e','X','x','P','p','S','s','I','i','O','o','N','n')); + $attvalue = str_replace($aDangerousCharsReplacementTable[0],$aDangerousCharsReplacementTable[1],$attvalue); + + // Escapes are usefull for special characters like "{}[]()'&. In other cases they are + // used for XSS + $attvalue = preg_replace("/(\\\\)([a-zA-Z]{1})/",'$2',$attvalue); +} + +/** * This function returns the final tag out of the tag name, an array * of attributes, and the type of the tag. This function is called by * sq_sanitize internally. @@ -1484,10 +1557,23 @@ } } } + + /** + * Workaround for IE quirks + */ + sq_fixIE_idiocy($attvalue); + /** * Remove any backslashes, entities, and extraneous whitespace. */ + $oldattvalue = $attvalue; sq_defang($attvalue); + if ($attname == 'style' && $attvalue !== $oldattvalue) { + // entities are used in the attribute value. In 99% of the cases it's there as XSS + // i.e.<div style="{ left:expʀessioɴ( alert('XSS') ) }"> + $attvalue = "idiocy"; + $attary{$attname} = $attvalue; + } sq_unspace($attvalue); /** @@ -1567,12 +1653,69 @@ function sq_fixstyle($body, $pos, $message, $id, $mailbox){ global $view_unsafe_images; $me = 'sq_fixstyle'; - $ret = sq_findnxreg($body, $pos, '</\s*style\s*>'); - if ($ret == FALSE){ + + // workaround for </style> in between comments + $iCurrentPos = $pos; + $content = ''; + $sToken = ''; + $bSucces = false; + $bEndTag = false; + for ($i=$pos,$iCount=strlen($body);$i<$iCount;++$i) { + $char = $body{$i}; + switch ($char) { + case '<': + $sToken .= $char; + break; + case '/': + if ($sToken == '<') { + $sToken .= $char; + $bEndTag = true; + } else { + $content .= $char; + } + break; + case '>': + if ($bEndTag) { + $sToken .= $char; + if (preg_match('/\<\/\s*style\s*\>/i',$sToken,$aMatch)) { + $newpos = $i + 1; + $bSucces = true; + break 2; + } else { + $content .= $sToken; + } + $bEndTag = false; + } else { + $content .= $char; + } + break; + case '!': + if ($sToken == '<') { + // possible comment + if (isset($body{$i+2}) && substr($body,$i,3) == '!--') { + $i = strpos($body,'-->',$i+3); + if ($i === false) { // no end comment + $i = strlen($body); + } + $sToken = ''; + } + } else { + $content .= $char; + } + break; + default: + if ($bEndTag) { + $sToken .= $char; + } else { + $content .= $char; + } + break; + } + } + if ($bSucces == FALSE){ return array(FALSE, strlen($body)); } - $newpos = $ret[0] + strlen($ret[2]); - $content = $ret[1]; + /** * First look for general BODY style declaration, which would be * like so: @@ -1581,12 +1724,18 @@ */ $content = preg_replace("|body(\s*\{.*?\})|si", ".bodyclass\\1", $content); $secremoveimg = '../images/' . _("sec_remove_eng.png"); + + // IE Sucks hard. We have a special function for it. + sq_fixIE_idiocy($content); + + // remove @import line + $content = preg_replace("/^\s*(@import.*)$/mi","\n<!-- @import rules forbidden -->\n",$content); + /** * Fix url('blah') declarations. */ - // remove NUL - $content = str_replace("\0", "", $content); // translate ur\l and variations into url (IE parses that) + // TODO check if the sq_fixIE_idiocy function already handles this. $content = preg_replace("/(\\\\)?u(\\\\)?r(\\\\)?l(\\\\)?/i",'url', $content); // NB I insert NUL characters to keep to avoid an infinite loop. They are removed after the loop. while (preg_match("/url\s*\(\s*[\'\"]?([^:]+):(.*)?[\'\"]?\s*\)/si", $content, $matches)) { @@ -1645,11 +1794,13 @@ * in IE. */ $match = Array('/\/\*.*\*\//', - '/expression/i', - '/behaviou*r/i', - '/binding/i', - '/include-source/i'); - $replace = Array('', 'idiocy', 'idiocy', 'idiocy', 'idiocy'); + '/expression/i', + '/behaviou*r/i', + '/binding/i', + '/include-source/i', + '/javascript/i', + '/script/i'); + $replace = Array('','idiocy', 'idiocy', 'idiocy', 'idiocy', 'idiocy', 'idiocy'); $contentNew = preg_replace($match, $replace, $contentTemp); if ($contentNew !== $contentTemp) { // insecure css declarations are used. From now on we don't care @@ -2191,11 +2342,15 @@ // This works for most types, but doesn't work with Word files header ("Content-Type: application/download; name=\"$filename\""); - + // This is to prevent IE for MIME sniffing and auto open a file in IE + header ("Content-Type: application/force-download; name=\"$filename\""); // These are spares, just in case. :-) //header("Content-Type: $type0/$type1; name=\"$filename\""); //header("Content-Type: application/x-msdownload; name=\"$filename\""); //header("Content-Type: application/octet-stream; name=\"$filename\""); + } else if ($isIE) { + // This is to prevent IE for MIME sniffing and auto open a file in IE + header ("Content-Type: application/force-download; name=\"$filename\""); } else { // another application/octet-stream forces download for Netscape header ("Content-Type: application/octet-stream; name=\"$filename\"");