From 0cbb7fc1a349f095ff77fea9b8857c57e7c5fd54 Mon Sep 17 00:00:00 2001 From: Matthew Thode Date: Sat, 3 Oct 2015 14:13:15 -0500 Subject: [PATCH] app-admin/glance: fixing CVE-2015-5286 Package-Manager: portage-2.2.20.1 --- .../glance/files/CVE-2015-3289_2015.1.0.patch | 50 ----- .../glance/files/CVE-2015-5286_2015.1.1.patch | 137 +++++++++++++ app-admin/glance/glance-2015.1.1-r3.ebuild | 191 ++++++++++++++++++ 3 files changed, 328 insertions(+), 50 deletions(-) delete mode 100644 app-admin/glance/files/CVE-2015-3289_2015.1.0.patch create mode 100644 app-admin/glance/files/CVE-2015-5286_2015.1.1.patch create mode 100644 app-admin/glance/glance-2015.1.1-r3.ebuild diff --git a/app-admin/glance/files/CVE-2015-3289_2015.1.0.patch b/app-admin/glance/files/CVE-2015-3289_2015.1.0.patch deleted file mode 100644 index 5b697741e408..000000000000 --- a/app-admin/glance/files/CVE-2015-3289_2015.1.0.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 88f92eb11d556bf43e2800a05973ad2da0db0195 Mon Sep 17 00:00:00 2001 -From: Flavio Percoco -Date: Fri, 8 May 2015 11:44:14 +0200 -Subject: [PATCH] Save image data after setting the data - -The image's locations are missing when image's are imported using tasks -because the ImportToStore task is not saving the image metadata after -the import. This patch fixes that. - -Change-Id: I43dec450d5fc4bee2131d78dbe3c2b2373c3f739 -Closes-bug: #1453068 -(cherry picked from commit 4efb56aae9288952bdb0d368a7c307e8524b80d8) ---- - glance/async/flows/base_import.py | 4 ++++ - glance/tests/unit/async/flows/test_import.py | 4 ++++ - 2 files changed, 8 insertions(+) - -diff --git a/glance/async/flows/base_import.py b/glance/async/flows/base_import.py -index 487247c..7656bde 100644 ---- a/glance/async/flows/base_import.py -+++ b/glance/async/flows/base_import.py -@@ -283,6 +283,10 @@ class _ImportToStore(task.Task): - - image_import.set_image_data(image, file_path or self.uri, None) - -+ # NOTE(flaper87): We need to save the image again after the locations -+ # have been set in the image. -+ self.image_repo.save(image) -+ - - class _SaveImage(task.Task): - -diff --git a/glance/tests/unit/async/flows/test_import.py b/glance/tests/unit/async/flows/test_import.py -index 0f355bc..7acd599 100644 ---- a/glance/tests/unit/async/flows/test_import.py -+++ b/glance/tests/unit/async/flows/test_import.py -@@ -112,6 +112,10 @@ class TestImportTask(test_utils.BaseTestCase): - "%s.tasks_import" % image_path) - self.assertFalse(os.path.exists(tmp_image_path)) - self.assertTrue(os.path.exists(image_path)) -+ self.assertEqual(1, len(list(self.image.locations))) -+ self.assertEqual("file://%s/%s" % (self.test_dir, -+ self.image.image_id), -+ self.image.locations[0]['url']) - - def test_import_flow_missing_work_dir(self): - self.config(engine_mode='serial', group='taskflow_executor') --- -2.3.6 - diff --git a/app-admin/glance/files/CVE-2015-5286_2015.1.1.patch b/app-admin/glance/files/CVE-2015-5286_2015.1.1.patch new file mode 100644 index 000000000000..04781355e2ba --- /dev/null +++ b/app-admin/glance/files/CVE-2015-5286_2015.1.1.patch @@ -0,0 +1,137 @@ +From 5bebd513fa71edcdb84f7dec7b16f3523c0c1092 Mon Sep 17 00:00:00 2001 +From: Mike Fedosin +Date: Sun, 20 Sep 2015 17:01:22 +0300 +Subject: Cleanup chunks for deleted image if token expired + +In patch I47229b366c25367ec1bd48aec684e0880f3dfe60 it was +introduced the logic that if image was deleted during file +upload when we want to update image status from 'saving' +to 'active' it's expected to get Duplicate error and delete +stale chunks after that. But if user's token is expired +there will be Unathorized exception and chunks will stay +in store and clog it. +And when, the upload operation for such an image is +completed the operator configured quota can be exceeded. + +This patch fixes the issue of left over chunks for an image +which was deleted from saving status, by correctly handle +auth exceptions from registry server. + +Partial-bug: #1498163 + +Conflicts: + glance/api/v1/upload_utils.py + (Kilo catches NotFound instead of ImagenotFound) + +Change-Id: I17a66eca55bfb83107046910e69c4da01415deec +(cherry picked from commit 98a8832777a0639a4031e52c69f0d565b3f500c5) + +diff --git a/glance/api/v1/upload_utils.py b/glance/api/v1/upload_utils.py +index 7adb2dc..ad4f724 100644 +--- a/glance/api/v1/upload_utils.py ++++ b/glance/api/v1/upload_utils.py +@@ -171,6 +171,14 @@ def upload_data_to_store(req, image_meta, image_data, store, notifier): + raise exception.NotFound() + else: + raise ++ ++ except exception.NotAuthenticated as e: ++ # Delete image data due to possible token expiration. ++ LOG.debug("Authentication error - the token may have " ++ "expired during file upload. Deleting image data for " ++ " %s " % image_id) ++ initiate_deletion(req, location_data, image_id) ++ raise webob.exc.HTTPUnauthorized(explanation=e.msg, request=req) + except exception.NotFound: + msg = _LI("Image %s could not be found after upload. The image may" + " have been deleted during the upload.") % image_id +diff --git a/glance/api/v2/image_data.py b/glance/api/v2/image_data.py +index 4025eeb..9967662 100644 +--- a/glance/api/v2/image_data.py ++++ b/glance/api/v2/image_data.py +@@ -88,7 +88,19 @@ class ImageDataController(object): + raise webob.exc.HTTPGone(explanation=msg, + request=req, + content_type='text/plain') +- ++ except exception.NotAuthenticated: ++ msg = (_("Authentication error - the token may have " ++ "expired during file upload. Deleting image data for " ++ "%s.") % image_id) ++ LOG.debug(msg) ++ try: ++ image.delete() ++ except exception.NotAuthenticated: ++ # NOTE: Ignore this exception ++ pass ++ raise webob.exc.HTTPUnauthorized(explanation=msg, ++ request=req, ++ content_type='text/plain') + except ValueError as e: + LOG.debug("Cannot save data for image %(id)s: %(e)s", + {'id': image_id, 'e': utils.exception_to_str(e)}) +diff --git a/glance/tests/unit/v1/test_upload_utils.py b/glance/tests/unit/v1/test_upload_utils.py +index 1afaf00..8d05515 100644 +--- a/glance/tests/unit/v1/test_upload_utils.py ++++ b/glance/tests/unit/v1/test_upload_utils.py +@@ -323,3 +323,29 @@ class TestUploadUtils(base.StoreClearingUnitTest): + 'metadata': {}}, image_meta['id']) + mock_safe_kill.assert_called_once_with( + req, image_meta['id'], 'saving') ++ ++ @mock.patch.object(registry, 'update_image_metadata', ++ side_effect=exception.NotAuthenticated) ++ @mock.patch.object(upload_utils, 'initiate_deletion') ++ def test_activate_image_with_expired_token( ++ self, mocked_delete, mocked_update): ++ """Test token expiration during image upload. ++ ++ If users token expired before image was uploaded then if auth error ++ was caught from registry during changing image status from 'saving' ++ to 'active' then it's required to delete all image data. ++ """ ++ context = mock.Mock() ++ req = mock.Mock() ++ req.context = context ++ with self._get_store_and_notifier() as (location, checksum, image_meta, ++ image_data, store, notifier, ++ update_data): ++ self.assertRaises(webob.exc.HTTPUnauthorized, ++ upload_utils.upload_data_to_store, ++ req, image_meta, image_data, store, notifier) ++ self.assertEqual(2, mocked_update.call_count) ++ mocked_delete.assert_called_once_with( ++ req, ++ {'url': 'file://foo/bar', 'status': 'active', 'metadata': {}}, ++ 'c80a1a6c-bd1f-41c5-90ee-81afedb1d58d') +diff --git a/glance/tests/unit/v2/test_image_data_resource.py b/glance/tests/unit/v2/test_image_data_resource.py +index bc8891e..7458eda 100644 +--- a/glance/tests/unit/v2/test_image_data_resource.py ++++ b/glance/tests/unit/v2/test_image_data_resource.py +@@ -192,6 +192,23 @@ class TestImagesController(base.StoreClearingUnitTest): + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.upload, + request, unit_test_utils.UUID1, 'YYYY', 4) + ++ def test_upload_with_expired_token(self): ++ def side_effect(image, from_state=None): ++ if from_state == 'saving': ++ raise exception.NotAuthenticated() ++ ++ mocked_save = mock.Mock(side_effect=side_effect) ++ mocked_delete = mock.Mock() ++ request = unit_test_utils.get_fake_request() ++ image = FakeImage('abcd') ++ image.delete = mocked_delete ++ self.image_repo.result = image ++ self.image_repo.save = mocked_save ++ self.assertRaises(webob.exc.HTTPUnauthorized, self.controller.upload, ++ request, unit_test_utils.UUID1, 'YYYY', 4) ++ self.assertEqual(3, mocked_save.call_count) ++ mocked_delete.assert_called_once_with() ++ + def test_upload_non_existent_image_during_save_initiates_deletion(self): + def fake_save_not_found(self): + raise exception.NotFound() +-- +cgit v0.10.2 + diff --git a/app-admin/glance/glance-2015.1.1-r3.ebuild b/app-admin/glance/glance-2015.1.1-r3.ebuild new file mode 100644 index 000000000000..10eda7d31012 --- /dev/null +++ b/app-admin/glance/glance-2015.1.1-r3.ebuild @@ -0,0 +1,191 @@ +# Copyright 1999-2015 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Id$ + +EAPI=5 +PYTHON_COMPAT=( python2_7 ) + +inherit distutils-r1 user + +DESCRIPTION="Provides services for discovering, registering, and retrieving +virtual machine images" +HOMEPAGE="https://launchpad.net/glance" +SRC_URI="https://launchpad.net/${PN}/kilo/${PV}/+download/${P}.tar.gz" + +LICENSE="Apache-2.0" +SLOT="0" +KEYWORDS="~amd64 ~x86" +IUSE="doc mysql postgres +sqlite +swift test" +REQUIRED_USE="|| ( mysql postgres sqlite )" + +DEPEND=" + dev-python/setuptools[${PYTHON_USEDEP}] + >=dev-python/pbr-0.8.0[${PYTHON_USEDEP}] + =dev-python/hacking-0.10.0[${PYTHON_USEDEP}] + =dev-python/Babel-1.3[${PYTHON_USEDEP}] + >=dev-python/coverage-3.6[${PYTHON_USEDEP}] + >=dev-python/fixtures-0.3.14[${PYTHON_USEDEP}] + =dev-python/mock-1.0[${PYTHON_USEDEP}] + =dev-python/sphinx-1.1.2[${PYTHON_USEDEP}] + !~dev-python/sphinx-1.2.0[${PYTHON_USEDEP}] + =dev-python/requests-2.2.0[${PYTHON_USEDEP}] + !~dev-python/requests-2.4.0[${PYTHON_USEDEP}] + >=dev-python/testrepository-0.0.18[${PYTHON_USEDEP}] + >=dev-python/testtools-0.9.36[${PYTHON_USEDEP}] + !~dev-python/testtools-1.2.0[${PYTHON_USEDEP}] + >=dev-python/psutil-1.1.1[${PYTHON_USEDEP}] + =dev-python/oslotest-1.5.1[${PYTHON_USEDEP}] + =dev-python/pyxattr-0.5.0[${PYTHON_USEDEP}] + >=dev-python/oslo-sphinx-2.5.0[${PYTHON_USEDEP}] + =dev-python/elasticsearch-py-1.3.0[${PYTHON_USEDEP}] + )" + +#note to self, wsgiref is a python builtin, no need to package it +#>=dev-python/wsgiref-0.1.2[${PYTHON_USEDEP}] + +RDEPEND=" + >=dev-python/greenlet-0.3.2[${PYTHON_USEDEP}] + sqlite? ( + >=dev-python/sqlalchemy-0.9.7[sqlite,${PYTHON_USEDEP}] + <=dev-python/sqlalchemy-0.9.99[sqlite,${PYTHON_USEDEP}] + ) + mysql? ( + dev-python/mysql-python + >=dev-python/sqlalchemy-0.9.7[${PYTHON_USEDEP}] + <=dev-python/sqlalchemy-0.9.99[${PYTHON_USEDEP}] + ) + postgres? ( + dev-python/psycopg:2 + >=dev-python/sqlalchemy-0.9.7[${PYTHON_USEDEP}] + <=dev-python/sqlalchemy-0.9.99[${PYTHON_USEDEP}] + ) + >=dev-python/anyjson-0.3.3[${PYTHON_USEDEP}] + >=dev-python/eventlet-0.16.1[${PYTHON_USEDEP}] + !~dev-python/eventlet-0.17.0[${PYTHON_USEDEP}] + >=dev-python/pastedeploy-1.5.0[${PYTHON_USEDEP}] + >=dev-python/routes-1.12.3[${PYTHON_USEDEP}] + !~dev-python/routes-2.0[${PYTHON_USEDEP}] + >=dev-python/webob-1.2.3[${PYTHON_USEDEP}] + >=dev-python/sqlalchemy-migrate-0.9.5[${PYTHON_USEDEP}] + >=dev-python/httplib2-0.7.5[${PYTHON_USEDEP}] + >=dev-python/kombu-2.5.0[${PYTHON_USEDEP}] + >=dev-python/pycrypto-2.6[${PYTHON_USEDEP}] + >=dev-python/iso8601-0.1.9[${PYTHON_USEDEP}] + dev-python/ordereddict[${PYTHON_USEDEP}] + >=dev-python/oslo-config-1.9.3[${PYTHON_USEDEP}] + =dev-python/oslo-concurrency-1.8.0[${PYTHON_USEDEP}] + =dev-python/oslo-context-0.2.0[${PYTHON_USEDEP}] + =dev-python/oslo-utils-1.4.0[${PYTHON_USEDEP}] + =dev-python/stevedore-1.3.0[${PYTHON_USEDEP}] + =dev-python/taskflow-0.7.1[${PYTHON_USEDEP}] + =dev-python/keystonemiddleware-1.5.0[${PYTHON_USEDEP}] + =dev-python/WSME-0.6[${PYTHON_USEDEP}] + =dev-python/python-swiftclient-2.2.0[${PYTHON_USEDEP}] + =dev-python/oslo-vmware-0.11.1[${PYTHON_USEDEP}] + =dev-python/jsonschema-2.0.0[${PYTHON_USEDEP}] + =dev-python/python-keystoneclient-1.2.0[${PYTHON_USEDEP}] + =dev-python/pyopenssl-0.11[${PYTHON_USEDEP}] + >=dev-python/six-1.9.0[${PYTHON_USEDEP}] + >=dev-python/oslo-db-1.7.0[${PYTHON_USEDEP}] + =dev-python/oslo-i18n-1.5.0[${PYTHON_USEDEP}] + =dev-python/oslo-log-1.0.0[${PYTHON_USEDEP}] + =dev-python/oslo-messaging-1.8.0[${PYTHON_USEDEP}] + =dev-python/oslo-policy-0.3.1[${PYTHON_USEDEP}] + =dev-python/oslo-serialization-1.4.0[${PYTHON_USEDEP}] + =dev-python/retrying-1.2.3[${PYTHON_USEDEP}] + !~dev-python/retrying-1.3.0[${PYTHON_USEDEP}] + >=dev-python/osprofiler-0.3.0[${PYTHON_USEDEP}] + >=dev-python/glance_store-0.3.0[${PYTHON_USEDEP}] + =dev-python/semantic_version-2.3.1[${PYTHON_USEDEP}] +" + +PATCHES=( + "${FILESDIR}/cve-2015-5163-stable-kilo.patch" + "${FILESDIR}/cve-2015-5251-stable-kilo.patch" + "${FILESDIR}/CVE-2015-5286_2015.1.1.patch" +) + +pkg_setup() { + enewgroup glance + enewuser glance -1 -1 /var/lib/glance glance +} + +python_prepare_all() { + sed -i '/xattr/d' test-requirements.txt || die + sed -i '/pysendfile/d' test-requirements.txt || die + distutils-r1_python_prepare_all +} + +python_compile_all() { + use doc && "${PYTHON}" setup.py build_sphinx +} + +python_test() { + # https://bugs.launchpad.net/glance/+bug/1251105 + # https://bugs.launchpad.net/glance/+bug/1242501 + testr init + testr run --parallel || die "failed testsuite under python2.7" +} + +python_install() { + distutils-r1_python_install + + for svc in api registry scrubber; do + newinitd "${FILESDIR}/glance.initd" glance-${svc} + done + + diropts -m 0750 -o glance -g glance + dodir /var/log/glance /var/lib/glance/images /var/lib/glance/scrubber + keepdir /etc/glance + keepdir /var/log/glance + keepdir /var/lib/glance/images + keepdir /var/lib/glance/scrubber + + insinto /etc/glance + insopts -m 0640 -o glance -g glance + doins etc/*.ini + doins etc/*.conf + doins etc/*.sample +} + +python_install_all() { + use doc && local HTML_DOCS=( doc/build/html/. ) + distutils-r1_python_install_all +} -- 2.26.2