diff -u horde3-3.2.2+debian0/debian/changelog horde3-3.2.2+debian0/debian/changelog --- horde3-3.2.2+debian0/debian/changelog +++ horde3-3.2.2+debian0/debian/changelog @@ -1,3 +1,12 @@ +horde3 (3.2.2+debian0-3) stable-security; urgency=high + + * Add patch stuff on debian/rules to have clean security patches. + * Backport security patches from 3.3.5, mainly fix vulnerability in image + form fields that allows overwriting of arbitrary local files. See + CVE-2009-3236 for more information. (Closes: #547318) + + -- Gregory Colpart Sun, 20 Sep 2009 18:24:12 +0200 + horde3 (3.2.2+debian0-2) unstable; urgency=high * Add informations in README.Debian about test.php files: these files should diff -u horde3-3.2.2+debian0/debian/rules horde3-3.2.2+debian0/debian/rules --- horde3-3.2.2+debian0/debian/rules +++ horde3-3.2.2+debian0/debian/rules @@ -5,9 +5,22 @@ # List all Files and directories here that should not be copied to # /usr/share/horde3 -COPY_EXCLUDE=^(\{arch\}|COPYING|README|po|scripts|config|debian|docs|build-stamp|configure-stamp)$ +COPY_EXCLUDE=^(\{arch\}|COPYING|README|po|scripts|config|debian|docs|build-stamp|configure-stamp|patch-stamp)$ -configure: configure-stamp +patch: patch-stamp +patch-stamp: + dh_testdir + set -e; test -e patch-stamp || \ + for i in `ls -1 debian/patches/*.patch || :`; do patch -p1 <$$i > /dev/null; done + touch $@ + +unpatch: + dh_testdir + set -e; ! test -e patch-stamp || \ + for i in `ls -1r debian/patches/*.patch || :`; do patch -p1 -R <$$i > /dev/null; done + rm -f patch-stamp + +configure: configure-stamp patch configure-stamp: dh_testdir # Add here commands to configure the package. @@ -16,11 +29,11 @@ build: check-external-libs build-stamp -build-stamp: configure-stamp +build-stamp: configure dh_testdir touch build-stamp -clean: +clean: unpatch dh_testdir dh_testroot dh_clean build-stamp configure-stamp @@ -205 +218 @@ -.PHONY: build clean binary-indep binary-arch binary install configure +.PHONY: patch unpatch build clean binary-indep binary-arch binary install configure only in patch2: unchanged: --- horde3-3.2.2+debian0.orig/debian/patches/0001-backport-security-patches-from-3.3.5.patch +++ horde3-3.2.2+debian0/debian/patches/0001-backport-security-patches-from-3.3.5.patch @@ -0,0 +1,288 @@ +diff -uNr horde-3.2.4/lib/Horde/Form.php horde-3.2.5/lib/Horde/Form.php +--- horde-3.2.4/lib/Horde/Form.php 2009-09-14 10:12:53.000000000 +0200 ++++ horde-3.2.5/lib/Horde/Form.php 2009-09-14 10:10:30.000000000 +0200 +@ -1648,7 +1648,14 @@ + * + * @var array + */ +- var $_img = array(); ++ var $_img; ++ ++ /** ++ * A random id that identifies the image information in the session data. ++ * ++ * @var string ++ */ ++ var $_random; + + function init($show_upload = true, $show_keeporig = false, $max_filesize = null) + { +@@ -1660,7 +1667,7 @@ + function onSubmit(&$var, &$vars) + { + /* Get the upload. */ +- $this->_getUpload($vars, $var); ++ $this->getImage($vars, $var); + + /* If this was done through the upload button override the submitted + * value of the form. */ +@@ -1671,25 +1678,24 @@ + + function isValid(&$var, &$vars, $value, &$message) + { +- $field = $vars->get($var->getVarName()); +- + /* Get the upload. */ +- $this->_getUpload($vars, $var); ++ $this->getImage($vars, $var); ++ $field = $vars->get($var->getVarName()); + + /* The upload generated a PEAR Error. */ + if (is_a($this->_uploaded, 'PEAR_Error')) { + /* Not required and no image upload attempted. */ +- if (!$var->isRequired() && empty($field['img']) && ++ if (!$var->isRequired() && empty($field['hash']) && + $this->_uploaded->getCode() == UPLOAD_ERR_NO_FILE) { + return true; + } + + if (($this->_uploaded->getCode() == UPLOAD_ERR_NO_FILE) && +- empty($field['img'])) { ++ empty($field['hash'])) { + /* Nothing uploaded and no older upload. */ + $message = _("This field is required."); + return false; +- } elseif (!empty($field['img'])) { ++ } elseif (!empty($field['hash'])) { + /* Nothing uploaded but older upload present. */ + return true; + } else { +@@ -1697,11 +1703,11 @@ + $message = $this->_uploaded->getMessage(); + return false; + } +- } elseif (empty($this->_img['size'])) { ++ } elseif (empty($this->_img['img']['size'])) { + $message = _("The image file size could not be determined or it was 0 bytes. The upload may have been interrupted."); + return false; + } elseif ($this->_max_filesize && +- $this->_img['size'] > $this->_max_filesize) { ++ $this->_img['img']['size'] > $this->_max_filesize) { + $message = sprintf(_("The image file was larger than the maximum allowed size (%d bytes)."), $this->_max_filesize); + return false; + } +@@ -1712,11 +1718,11 @@ + function getInfo(&$vars, &$var, &$info) + { + /* Get the upload. */ +- $this->_getUpload($vars, $var); ++ $this->getImage($vars, $var); + + /* Get image params stored in the hidden field. */ + $value = $var->getValue($vars); +- $info = $this->_img; ++ $info = $this->_img['img']; + if (empty($info['file'])) { + unset($info['file']); + return; +@@ -1771,7 +1777,7 @@ + if ($this->_uploaded === true) { + /* A file has been uploaded on this submit. Save to temp dir for + * preview work. */ +- $this->_img['type'] = $this->getUploadedFileType($varname . '[new]'); ++ $this->_img['img']['type'] = $this->getUploadedFileType($varname . '[new]'); + + /* Get the other parts of the upload. */ + require_once 'Horde/Array.php'; +@@ -1779,19 +1785,22 @@ + + /* Get the temporary file name. */ + $keys_path = array_merge(array($base, 'tmp_name'), $keys); +- $this->_img['file'] = Horde_Array::getElement($_FILES, $keys_path); ++ $this->_img['img']['file'] = Horde_Array::getElement($_FILES, $keys_path); + + /* Get the actual file name. */ +- $keys_path= array_merge(array($base, 'name'), $keys); +- $this->_img['name'] = Horde_Array::getElement($_FILES, $keys_path); ++ $keys_path = array_merge(array($base, 'name'), $keys); ++ $this->_img['img']['name'] = Horde_Array::getElement($_FILES, $keys_path); + + /* Get the file size. */ +- $keys_path= array_merge(array($base, 'size'), $keys); +- $this->_img['size'] = Horde_Array::getElement($_FILES, $keys_path); ++ $keys_path = array_merge(array($base, 'size'), $keys); ++ $this->_img['img']['size'] = Horde_Array::getElement($_FILES, $keys_path); + + /* Get any existing values for the image upload field. */ + $upload = $vars->get($var->getVarName()); +- $upload['img'] = @unserialize($upload['img']); ++ if (!empty($upload['hash'])) { ++ $upload['img'] = $_SESSION['horde_form'][$upload['hash']]; ++ unset($_SESSION['horde_form'][$upload['hash']]); ++ } + + /* Get the temp file if already one uploaded, otherwise create a + * new temporary file. */ +@@ -1802,19 +1811,21 @@ + } + + /* Move the browser created temp file to the new temp file. */ +- move_uploaded_file($this->_img['file'], $tmp_file); +- $this->_img['file'] = basename($tmp_file); +- +- /* Store the uploaded image file data to the hidden field. */ +- $upload['img'] = serialize($this->_img); +- $vars->set($var->getVarName(), $upload); ++ move_uploaded_file($this->_img['img']['file'], $tmp_file); ++ $this->_img['img']['file'] = basename($tmp_file); + } elseif ($this->_uploaded) { + /* File has not been uploaded. */ + $upload = $vars->get($var->getVarName()); +- if ($this->_uploaded->getCode() == 4 && !empty($upload['img'])) { +- $this->_img = @unserialize($upload['img']); ++ if ($this->_uploaded->getCode() == 4 && ++ !empty($upload['hash']) && ++ isset($_SESSION['horde_form'][$upload['hash']])) { ++ $this->_img['img'] = $_SESSION['horde_form'][$upload['hash']]; ++ unset($_SESSION['horde_form'][$upload['hash']]); + } + } ++ if (isset($this->_img['img'])) { ++ $_SESSION['horde_form'][$this->getRandomId()] = $this->_img['img']; ++ } + } + + function getUploadedFileType($field) +@@ -1865,6 +1876,27 @@ + } + + /** ++ * Returns the current image information. ++ * ++ * @return array The current image hash. ++ */ ++ function getImage($vars, $var) ++ { ++ $this->_getUpload($vars, $var); ++ if (!isset($this->_img)) { ++ $image = $vars->get($var->getVarName()); ++ if ($image) { ++ $this->loadImageData($image); ++ if (isset($image['img'])) { ++ $this->_img = $image; ++ $_SESSION['horde_form'][$this->getRandomId()] = $this->_img['img']; ++ } ++ } ++ } ++ return $this->_img; ++ } ++ ++ /** + * Loads any existing image data into the image field. Requires that the + * array $image passed to it contains the structure: + * $image['load']['file'] - the filename of the image; +@@ -1886,10 +1918,18 @@ + fclose($fd); + } + +- $image['img'] = serialize(array('file' => $image['load']['file'])); ++ $image['img'] = array('file' => $image['load']['file']); + unset($image['load']); + } + ++ function getRandomId() ++ { ++ if (!isset($this->_random)) { ++ $this->_random = uniqid(mt_rand()); ++ } ++ return $this->_random; ++ } ++ + /** + * Return info about field type. + */ +diff -uNr horde-3.2.4/lib/Horde/MIME/Viewer/simple.php horde-3.2.5/lib/Horde/MIME/Viewer/simple.php +--- horde-3.2.4/lib/Horde/MIME/Viewer/simple.php 2009-09-14 10:12:54.000000000 +0200 ++++ horde-3.2.5/lib/Horde/MIME/Viewer/simple.php 2009-09-14 10:10:30.000000000 +0200 +@@ -17,6 +17,21 @@ + class MIME_Viewer_simple extends MIME_Viewer { + + /** ++ * Renders out the contents. ++ * ++ * @param array $params Any parameters the Viewer may need. ++ * ++ * @return string The rendered contents. ++ */ ++ function render($params = array()) ++ { ++ // Bug #8311: Unknown text parts should not be rendered inline. ++ return MIME_Contents::viewAsAttachment() ++ ? parent::render($params) ++ : _("Can not display contents of text part inline."); ++ } ++ ++ /** + * Return the MIME type of the rendered content. + * + * @return string MIME-type of the output content. +diff -uNr horde-3.2.4/lib/Horde/Prefs/UI.php horde-3.2.5/lib/Horde/Prefs/UI.php +--- horde-3.2.4/lib/Horde/Prefs/UI.php 2009-09-14 10:12:56.000000000 +0200 ++++ horde-3.2.5/lib/Horde/Prefs/UI.php 2009-09-14 10:10:32.000000000 +0200 +@@ -120,9 +120,9 @@ + + case 'number': + $num = Util::getPost($pref); +- if (intval($num) != $num) { ++ if ((string)(double)$num !== $num) { + $notification->push(_("This value must be a number."), 'horde.error'); +- } elseif ($num == 0) { ++ } elseif (empty($num)) { + $notification->push(_("This number must be at least one."), 'horde.error'); + } else { + $updated = $updated | $save->setValue($pref, $num); +diff -uNr horde-3.2.4/lib/Horde/UI/VarRenderer/html.php horde-3.2.5/lib/Horde/UI/VarRenderer/html.php +--- horde-3.2.4/lib/Horde/UI/VarRenderer/html.php 2009-09-14 10:12:51.000000000 +0200 ++++ horde-3.2.5/lib/Horde/UI/VarRenderer/html.php 2009-09-14 10:10:26.000000000 +0200 +@@ -146,10 +146,7 @@ + function _renderVarInput_image($form, &$var, &$vars) + { + $varname = htmlspecialchars($var->getVarName()); +- $image = $var->getValue($vars); +- +- /* Check if existing image data is being loaded. */ +- $var->type->loadImageData($image); ++ $image = $var->type->getImage($vars, $var); + + Horde::addScriptFile('image.js', 'horde', true); + $graphics_dir = $GLOBALS['registry']->getImageDir('horde'); +@@ -159,13 +156,11 @@ + + /* Check if there is existing img information stored. */ + if (isset($image['img'])) { +- /* Hidden tag to store the preview image filename. */ ++ /* Hidden tag to store the preview image id. */ + $html = sprintf('', +- $varname . '[img]', +- $varname . '[img]', +- @htmlspecialchars($image['img'], ENT_QUOTES, $this->_charset)); +- /* Unserialize the img information to get the full array. */ +- $image['img'] = @unserialize($image['img']); ++ $varname . '[hash]', ++ $varname . '[hash]', ++ $var->type->getRandomId()); + } + + /* Output MAX_FILE_SIZE parameter to limit large files. */ +diff -uNr horde-3.2.4/lib/prefs.php horde-3.2.5/lib/prefs.php +--- horde-3.2.4/lib/prefs.php 2008-03-18 18:54:39.000000000 +0100 ++++ horde-3.2.5/lib/prefs.php 2009-07-12 01:39:12.000000000 +0200 +@@ -121,7 +121,7 @@ + } + + if ($prefs->isDirty('sidebar_width')) { +- $notification->push('if (window.parent && window.parent.document.getElementById(\'hf\') && window.parent.horde_menu && window.parent.horde_menu.document.getElementById(\'expandedSidebar\').style.display != \'hidden\') window.parent.document.getElementById(\'hf\').cols = window.parent.horde_menu.rtl ? \'*,' . $prefs->getValue('sidebar_width') . '\' : \'' . $prefs->getValue('sidebar_width') . ',*\';', 'javascript'); ++ $notification->push('if (window.parent && window.parent.document.getElementById(\'hf\') && window.parent.horde_menu && window.parent.horde_menu.document.getElementById(\'expandedSidebar\').style.display != \'hidden\') window.parent.document.getElementById(\'hf\').cols = window.parent.horde_menu.rtl ? \'*,' . (int)$prefs->getValue('sidebar_width') . '\' : \'' . (int)$prefs->getValue('sidebar_width') . ',*\';', 'javascript'); + } + + if ($prefs->isDirty('theme') ||