From: Matthew Thode Date: Tue, 8 Sep 2015 14:38:59 +0000 (-0500) Subject: sys-cluster/neutron: fixing CVE-2015-5240 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=c061e9d47edb942d6c2bfa1a241d415b75431111;p=gentoo.git sys-cluster/neutron: fixing CVE-2015-5240 Package-Manager: portage-2.2.20.1 --- diff --git a/sys-cluster/neutron/files/CVE-2015-5240_2015.1.1.patch b/sys-cluster/neutron/files/CVE-2015-5240_2015.1.1.patch new file mode 100644 index 000000000000..ccb2a66bce9b --- /dev/null +++ b/sys-cluster/neutron/files/CVE-2015-5240_2015.1.1.patch @@ -0,0 +1,155 @@ +From 8138e2fe38ad2cde5963685df47b1e4286776352 Mon Sep 17 00:00:00 2001 +From: Kevin Benton +Date: Tue, 25 Aug 2015 22:03:27 -0700 +Subject: [PATCH] Stop device_owner from being set to 'network:*' + +This patch adjusts the FieldCheck class in the policy engine to +allow a regex rule. It then leverages that to prevent users from +setting the device_owner field to anything that starts with +'network:' on networks which they do not own. + +This policy adjustment is necessary because any ports with a +device_owner that starts with 'network:' will not have any security +group rules applied because it is assumed they are trusted network +devices (e.g. router ports, DHCP ports, etc). These security rules +include the anti-spoofing protection for DHCP, IPv6 ICMP messages, +and IP headers. + +Without this policy adjustment, tenants can abuse this trust when +connected to a shared network with other tenants by setting their +VM port's device_owner field to 'network:' and hijack other +tenants' traffic via DHCP spoofing or MAC/IP spoofing. + +Closes-Bug: #1489111 +Change-Id: Ia64cf16142e0e4be44b5b0ed72c8e00792d770f9 +(cherry picked from commit 959a2f28cbbfc309381ea9ffb55090da6fb9c78f) +--- + etc/policy.json | 3 +++ + neutron/api/v2/attributes.py | 2 +- + neutron/policy.py | 3 +++ + neutron/tests/etc/policy.json | 3 +++ + neutron/tests/unit/test_policy.py | 16 ++++++++++++++++ + 5 files changed, 26 insertions(+), 1 deletion(-) + +diff --git a/etc/policy.json b/etc/policy.json +index 8a5de9b..0f04eb2 100644 +--- a/etc/policy.json ++++ b/etc/policy.json +@@ -46,7 +46,9 @@ + "update_network:router:external": "rule:admin_only", + "delete_network": "rule:admin_or_owner", + ++ "network_device": "field:port:device_owner=~^network:", + "create_port": "", ++ "create_port:device_owner": "not rule:network_device or rule:admin_or_network_owner or rule:context_is_advsvc", + "create_port:mac_address": "rule:admin_or_network_owner or rule:context_is_advsvc", + "create_port:fixed_ips": "rule:admin_or_network_owner or rule:context_is_advsvc", + "create_port:port_security_enabled": "rule:admin_or_network_owner or rule:context_is_advsvc", +@@ -61,6 +63,7 @@ + "get_port:binding:host_id": "rule:admin_only", + "get_port:binding:profile": "rule:admin_only", + "update_port": "rule:admin_or_owner or rule:context_is_advsvc", ++ "update_port:device_owner": "not rule:network_device or rule:admin_or_network_owner or rule:context_is_advsvc", + "update_port:mac_address": "rule:admin_only or rule:context_is_advsvc", + "update_port:fixed_ips": "rule:admin_or_network_owner or rule:context_is_advsvc", + "update_port:port_security_enabled": "rule:admin_or_network_owner or rule:context_is_advsvc", +diff --git a/neutron/api/v2/attributes.py b/neutron/api/v2/attributes.py +index b9c179a..9ceee78 100644 +--- a/neutron/api/v2/attributes.py ++++ b/neutron/api/v2/attributes.py +@@ -766,7 +766,7 @@ RESOURCE_ATTRIBUTE_MAP = { + 'is_visible': True}, + 'device_owner': {'allow_post': True, 'allow_put': True, + 'validate': {'type:string': DEVICE_OWNER_MAX_LEN}, +- 'default': '', ++ 'default': '', 'enforce_policy': True, + 'is_visible': True}, + 'tenant_id': {'allow_post': True, 'allow_put': False, + 'validate': {'type:string': TENANT_ID_MAX_LEN}, +diff --git a/neutron/policy.py b/neutron/policy.py +index 9e586dd..961ae21 100644 +--- a/neutron/policy.py ++++ b/neutron/policy.py +@@ -335,6 +335,7 @@ class FieldCheck(policy.Check): + + self.field = field + self.value = conv_func(value) ++ self.regex = re.compile(value[1:]) if value.startswith('~') else None + + def __call__(self, target_dict, cred_dict, enforcer): + target_value = target_dict.get(self.field) +@@ -344,6 +345,8 @@ class FieldCheck(policy.Check): + "%(target_dict)s", + {'field': self.field, 'target_dict': target_dict}) + return False ++ if self.regex: ++ return bool(self.regex.match(target_value)) + return target_value == self.value + + +diff --git a/neutron/tests/etc/policy.json b/neutron/tests/etc/policy.json +index 8a5de9b..0f04eb2 100644 +--- a/neutron/tests/etc/policy.json ++++ b/neutron/tests/etc/policy.json +@@ -46,7 +46,9 @@ + "update_network:router:external": "rule:admin_only", + "delete_network": "rule:admin_or_owner", + ++ "network_device": "field:port:device_owner=~^network:", + "create_port": "", ++ "create_port:device_owner": "not rule:network_device or rule:admin_or_network_owner or rule:context_is_advsvc", + "create_port:mac_address": "rule:admin_or_network_owner or rule:context_is_advsvc", + "create_port:fixed_ips": "rule:admin_or_network_owner or rule:context_is_advsvc", + "create_port:port_security_enabled": "rule:admin_or_network_owner or rule:context_is_advsvc", +@@ -61,6 +63,7 @@ + "get_port:binding:host_id": "rule:admin_only", + "get_port:binding:profile": "rule:admin_only", + "update_port": "rule:admin_or_owner or rule:context_is_advsvc", ++ "update_port:device_owner": "not rule:network_device or rule:admin_or_network_owner or rule:context_is_advsvc", + "update_port:mac_address": "rule:admin_only or rule:context_is_advsvc", + "update_port:fixed_ips": "rule:admin_or_network_owner or rule:context_is_advsvc", + "update_port:port_security_enabled": "rule:admin_or_network_owner or rule:context_is_advsvc", +diff --git a/neutron/tests/unit/test_policy.py b/neutron/tests/unit/test_policy.py +index 3888ce3..4be404f 100644 +--- a/neutron/tests/unit/test_policy.py ++++ b/neutron/tests/unit/test_policy.py +@@ -232,6 +232,7 @@ class NeutronPolicyTestCase(base.BaseTestCase): + "regular_user": "role:user", + "shared": "field:networks:shared=True", + "external": "field:networks:router:external=True", ++ "network_device": "field:port:device_owner=~^network:", + "default": '@', + + "create_network": "rule:admin_or_owner", +@@ -243,6 +244,7 @@ class NeutronPolicyTestCase(base.BaseTestCase): + "create_subnet": "rule:admin_or_network_owner", + "create_port:mac": "rule:admin_or_network_owner or " + "rule:context_is_advsvc", ++ "create_port:device_owner": "not rule:network_device", + "update_port": "rule:admin_or_owner or rule:context_is_advsvc", + "get_port": "rule:admin_or_owner or rule:context_is_advsvc", + "delete_port": "rule:admin_or_owner or rule:context_is_advsvc", +@@ -312,6 +314,20 @@ class NeutronPolicyTestCase(base.BaseTestCase): + self._test_nonadmin_action_on_attr('create', 'shared', True, + common_policy.PolicyNotAuthorized) + ++ def test_create_port_device_owner_regex(self): ++ blocked_values = ('network:', 'network:abdef', 'network:dhcp', ++ 'network:router_interface') ++ for val in blocked_values: ++ self._test_advsvc_action_on_attr( ++ 'create', 'port', 'device_owner', val, ++ common_policy.PolicyNotAuthorized ++ ) ++ ok_values = ('network', 'networks', 'my_network:test', 'my_network:') ++ for val in ok_values: ++ self._test_advsvc_action_on_attr( ++ 'create', 'port', 'device_owner', val ++ ) ++ + def test_advsvc_get_network_works(self): + self._test_advsvc_action_on_attr('get', 'network', 'shared', False) + +-- +1.9.1 + diff --git a/sys-cluster/neutron/files/cve-2015-3221_2015.1.0.patch b/sys-cluster/neutron/files/cve-2015-3221_2015.1.0.patch deleted file mode 100644 index c6c2230c9bd3..000000000000 --- a/sys-cluster/neutron/files/cve-2015-3221_2015.1.0.patch +++ /dev/null @@ -1,127 +0,0 @@ -From e0c8cbc5dd610b4c580935ea56436495a6d4eb26 Mon Sep 17 00:00:00 2001 -From: Aaron Rosen -Date: Wed, 3 Jun 2015 16:19:39 -0700 -Subject: [PATCH] Provide work around for 0.0.0.0/0 ::/0 for ipset - -Previously, the ipset_manager would pass in 0.0.0.0/0 or ::/0 if -these addresses were inputted as allowed address pairs. This causes -ipset to raise an error as it does not work with zero prefix sizes. -To solve this problem we use two ipset rules to represent this: - -Ipv4: 0.0.0.0/1 and 128.0.0.1/1 -IPv6: ::/1' and '8000::/1 - -All of this logic is handled via _sanitize_addresses() in the ipset_manager -which is called to convert the input. - -Closes-bug: 1461054 - -Conflicts: - neutron/agent/linux/ipset_manager.py - neutron/tests/unit/agent/linux/test_ipset_manager.py - -(cherry picked from commit 80a0fc3ba063e036b76e05e89b0cc54fc2d47c81) ---- - neutron/agent/linux/ipset_manager.py | 23 ++++++++++++++++++++++ - .../tests/unit/agent/linux/test_ipset_manager.py | 19 +++++++++++++++--- - 2 files changed, 39 insertions(+), 3 deletions(-) - -diff --git a/neutron/agent/linux/ipset_manager.py b/neutron/agent/linux/ipset_manager.py -index 0f76418..af59f1f 100644 ---- a/neutron/agent/linux/ipset_manager.py -+++ b/neutron/agent/linux/ipset_manager.py -@@ -11,6 +11,8 @@ - # See the License for the specific language governing permissions and - # limitations under the License. - -+import netaddr -+ - from neutron.agent.linux import utils as linux_utils - from neutron.common import utils - -@@ -31,6 +33,26 @@ class IpsetManager(object): - self.namespace = namespace - self.ipset_sets = {} - -+ def _sanitize_addresses(self, addresses): -+ """This method converts any address to ipset format. -+ -+ If an address has a mask of /0 we need to cover to it to a mask of -+ /1 as ipset does not support /0 length addresses. Instead we use two -+ /1's to represent the /0. -+ """ -+ sanitized_addresses = [] -+ for ip in addresses: -+ if (netaddr.IPNetwork(ip).prefixlen == 0): -+ if(netaddr.IPNetwork(ip).version == 4): -+ sanitized_addresses.append('0.0.0.0/1') -+ sanitized_addresses.append('128.0.0.0/1') -+ elif (netaddr.IPNetwork(ip).version == 6): -+ sanitized_addresses.append('::/1') -+ sanitized_addresses.append('8000::/1') -+ else: -+ sanitized_addresses.append(ip) -+ return sanitized_addresses -+ - @staticmethod - def get_name(id, ethertype): - """Returns the given ipset name for an id+ethertype pair. -@@ -51,6 +73,7 @@ class IpsetManager(object): - add / remove new members, or swapped atomically if - that's faster. - """ -+ member_ips = self._sanitize_addresses(member_ips) - set_name = self.get_name(id, ethertype) - if not self.set_exists(id, ethertype): - # The initial creation is handled with create/refresh to -diff --git a/neutron/tests/unit/agent/linux/test_ipset_manager.py b/neutron/tests/unit/agent/linux/test_ipset_manager.py -index 4484008..a1c6dc5 100644 ---- a/neutron/tests/unit/agent/linux/test_ipset_manager.py -+++ b/neutron/tests/unit/agent/linux/test_ipset_manager.py -@@ -38,7 +38,7 @@ class BaseIpsetManagerTest(base.BaseTestCase): - def expect_set(self, addresses): - temp_input = ['create NETIPv4fake_sgid-new hash:net family inet'] - temp_input.extend('add NETIPv4fake_sgid-new %s' % ip -- for ip in addresses) -+ for ip in self.ipset._sanitize_addresses(addresses)) - input = '\n'.join(temp_input) - self.expected_calls.extend([ - mock.call(['ipset', 'restore', '-exist'], -@@ -55,13 +55,16 @@ class BaseIpsetManagerTest(base.BaseTestCase): - self.expected_calls.extend( - mock.call(['ipset', 'add', '-exist', TEST_SET_NAME, ip], - process_input=None, -- run_as_root=True) for ip in addresses) -+ run_as_root=True) -+ for ip in self.ipset._sanitize_addresses(addresses)) - - def expect_del(self, addresses): -+ - self.expected_calls.extend( - mock.call(['ipset', 'del', TEST_SET_NAME, ip], - process_input=None, -- run_as_root=True) for ip in addresses) -+ run_as_root=True) -+ for ip in self.ipset._sanitize_addresses(addresses)) - - def expect_create(self): - self.expected_calls.append( -@@ -113,6 +116,16 @@ class IpsetManagerTestCase(BaseIpsetManagerTest): - self.ipset.set_members(TEST_SET_ID, ETHERTYPE, FAKE_IPS) - self.verify_mock_calls() - -+ def test_set_members_adding_all_zero_ipv4(self): -+ self.expect_set(['0.0.0.0/0']) -+ self.ipset.set_members(TEST_SET_ID, ETHERTYPE, ['0.0.0.0/0']) -+ self.verify_mock_calls() -+ -+ def test_set_members_adding_all_zero_ipv6(self): -+ self.expect_set(['::/0']) -+ self.ipset.set_members(TEST_SET_ID, ETHERTYPE, ['::/0']) -+ self.verify_mock_calls() -+ - def test_destroy(self): - self.add_first_ip() - self.expect_destroy() --- -1.9.1 diff --git a/sys-cluster/neutron/neutron-2015.1.1-r1.ebuild b/sys-cluster/neutron/neutron-2015.1.1-r1.ebuild new file mode 100644 index 000000000000..b262ff95b909 --- /dev/null +++ b/sys-cluster/neutron/neutron-2015.1.1-r1.ebuild @@ -0,0 +1,252 @@ +# 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 linux-info user + +DESCRIPTION="A virtual network service for Openstack" +HOMEPAGE="https://launchpad.net/neutron" +SRC_URI="https://launchpad.net/${PN}/kilo/${PV}/+download/${P}.tar.gz" + +LICENSE="Apache-2.0" +SLOT="0" +KEYWORDS="~amd64 ~x86" +IUSE="compute-only dhcp doc l3 metadata openvswitch linuxbridge server test sqlite mysql postgres" +REQUIRED_USE="!compute-only? ( || ( mysql postgres sqlite ) ) + compute-only? ( !mysql !postgres !sqlite !dhcp !l3 !metadata !server + || ( openvswitch linuxbridge ) )" + +DEPEND=" + dev-python/setuptools[${PYTHON_USEDEP}] + >=dev-python/pbr-0.8[${PYTHON_USEDEP}] + =dev-python/hacking-0.10.0[${PYTHON_USEDEP}] + =dev-python/cliff-1.10.0[${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/subunit-0.0.18[${PYTHON_USEDEP}] + >=dev-python/requests-mock-0.6.0[${PYTHON_USEDEP}] + >=dev-python/sphinx-1.1.2[${PYTHON_USEDEP}] + !~dev-python/sphinx-1.2.0[${PYTHON_USEDEP}] + =dev-python/oslo-sphinx-2.5.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/testscenarios-0.4[${PYTHON_USEDEP}] + >=dev-python/webtest-2.0[${PYTHON_USEDEP}] + >=dev-python/oslotest-1.5.1[${PYTHON_USEDEP}] + =dev-python/tempest-lib-0.4.0[${PYTHON_USEDEP}] +